diff --git a/ChangeLog b/ChangeLog index 026dda5e..c251dd09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,10 @@ VERSION 0.8.2cvs ================ +2007-11-20 Vincent Richard + + * text, word: fixed incorrect white-space between words. + 2007-07-09 Vincent Richard * IMAPUtils.cpp: fixed bug in modified UTF-7 encoding (IMAP). diff --git a/src/text.cpp b/src/text.cpp index 7386a698..c6b20c52 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -335,11 +335,12 @@ void text::encodeAndFold(utility::outputStream& os, const string::size_type maxL const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags) const { string::size_type curLineLength = firstLineOffset; + word::generatorState state; for (int wi = 0 ; wi < getWordCount() ; ++wi) { getWordAt(wi)->generate(os, maxLineLength, curLineLength, - &curLineLength, flags, (wi == 0)); + &curLineLength, flags, &state); } if (lastLineLength) diff --git a/src/word.cpp b/src/word.cpp index a33ab2ca..5c83ef98 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -309,16 +309,21 @@ void word::parse(const string& buffer, const string::size_type position, void word::generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { - generate(os, maxLineLength, curLinePos, newLinePos, 0, true); + generate(os, maxLineLength, curLinePos, newLinePos, 0, NULL); } void word::generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos, const int flags, - const bool isFirstWord) const + generatorState* state) const { string::size_type curLineLength = curLinePos; + generatorState defaultGeneratorState; + + if (state == NULL) + state = &defaultGeneratorState; + // Calculate the number of ASCII chars to check whether encoding is needed // and _which_ encoding to use. const string::size_type asciiCount = @@ -374,7 +379,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe // we write the full line no matter of the max line length... if (!newLine && p != end && lastWSpos == end && - !isFirstWord && curLineStart == m_buffer.begin()) + !state->isFirstWord && curLineStart == m_buffer.begin()) { // Here, we are continuing on the line of previous encoded // word, but there is not even enough space to put the @@ -428,7 +433,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe // last white-space. #if 1 - if (curLineLength != 1 && !isFirstWord) + if (curLineLength != NEW_LINE_SEQUENCE_LENGTH && !state->isFirstWord && state->prevWordIsEncoded) os << " "; // Separate from previous word #endif @@ -521,7 +526,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe } // Encode and fold input buffer - if (curLineLength != 1 && !isFirstWord) + if (!startNewLine && !state->isFirstWord && state->prevWordIsEncoded) { os << " "; // Separate from previous word ++curLineLength; @@ -554,11 +559,15 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe // End of the encoded word os << wordEnd; + + state->prevWordIsEncoded = true; } } if (newLinePos) *newLinePos = curLineLength; + + state->isFirstWord = false; } diff --git a/tests/parser/textTest.cpp b/tests/parser/textTest.cpp index 28a72150..e63dd4c8 100644 --- a/tests/parser/textTest.cpp +++ b/tests/parser/textTest.cpp @@ -41,6 +41,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testWordConstructors) VMIME_TEST(testWordParse) VMIME_TEST(testWordGenerate) + VMIME_TEST(testWordGenerateSpace) VMIME_TEST(testWordGenerateMultiBytes) VMIME_TEST_LIST_END @@ -269,6 +270,31 @@ VMIME_TEST_SUITE_BEGIN vmime::word("\xf1\xf2\xf3\xf4\xf5", vmime::charset("foo")).generate()); } + void testWordGenerateSpace() + { + // No white-space between an unencoded word and a encoded one + VASSERT_EQ("1", "Bonjour =?utf-8?Q?Fran=C3=A7ois?=", + vmime::text::newFromString("Bonjour Fran\xc3\xa7ois", + vmime::charset("utf-8"))->generate()); + + // White-space between two encoded words + vmime::text txt; + txt.appendWord(vmime::create ("\xc3\x89t\xc3\xa9", "utf-8")); + txt.appendWord(vmime::create ("Fran\xc3\xa7ois", "utf-8")); + + const vmime::string decoded = "\xc3\x89t\xc3\xa9""Fran\xc3\xa7ois"; + const vmime::string encoded = "=?utf-8?B?w4l0w6k=?= =?utf-8?Q?Fran=C3=A7ois?="; + + // -- test encoding + VASSERT_EQ("2", encoded, txt.generate()); + + // -- ensure no space is added when decoding + vmime::text txt2; + txt2.parse(encoded, 0, encoded.length()); + + VASSERT_EQ("3", decoded, txt2.getWholeBuffer()); + } + void testWordGenerateMultiBytes() { // Ensure we don't encode a non-integral number of characters diff --git a/vmime/word.hpp b/vmime/word.hpp index 6f6c2798..d4bddc16 100644 --- a/vmime/word.hpp +++ b/vmime/word.hpp @@ -109,13 +109,29 @@ public: ref clone() const; +#ifndef VMIME_BUILDING_DOC + class generatorState + { + public: + + generatorState() + : isFirstWord(true), prevWordIsEncoded(false) + { + } + + bool isFirstWord; + bool prevWordIsEncoded; + }; +#endif + + using component::parse; using component::generate; void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; - void generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos, const int flags, const bool isFirstWord) const; + void generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos, const int flags, generatorState* state) const; const std::vector > getChildComponents() const;