(svn r25482) -Fix [FS#5620]: forced newlines were not properly handled

This commit is contained in:
rubidium 2013-06-27 16:24:19 +00:00
parent bd2f4b9a24
commit 569eaf0e11
2 changed files with 79 additions and 60 deletions

View File

@ -105,25 +105,25 @@ size_t Layouter::AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
return length; return length;
} }
ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff) ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
{ {
int32 length = buff - this->buffer; int32 length = buff_end - buff;
if (length == 0) { if (length == 0) {
/* ICU's ParagraphLayout cannot handle empty strings, so fake one. */ /* ICU's ParagraphLayout cannot handle empty strings, so fake one. */
this->buffer[0] = ' '; buff[0] = ' ';
length = 1; length = 1;
this->fonts.End()[-1].first++; fontMapping.End()[-1].first++;
} }
/* Fill ICU's FontRuns with the right data. */ /* Fill ICU's FontRuns with the right data. */
FontRuns runs(this->fonts.Length()); FontRuns runs(fontMapping.Length());
for (FontMap::iterator iter = this->fonts.Begin(); iter != this->fonts.End(); iter++) { for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
runs.add(iter->second, iter->first); runs.add(iter->second, iter->first);
} }
LEErrorCode status = LE_NO_ERROR; LEErrorCode status = LE_NO_ERROR;
return new ParagraphLayout(this->buffer, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); return new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
} }
#else /* WITH_ICU */ #else /* WITH_ICU */
@ -277,20 +277,27 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width)
* - split a line at a newline character, or at a space where we can break a line. * - split a line at a newline character, or at a space where we can break a line.
* - split for a visual run whenever a new line happens, or the font changes. * - split for a visual run whenever a new line happens, or the font changes.
*/ */
if (this->buffer == NULL|| *this->buffer == '\0') return NULL; if (this->buffer == NULL) return NULL;
Line *l = new Line(); Line *l = new Line();
if (*this->buffer == '\0') {
/* Only a newline. */
this->buffer = NULL;
*l->Append() = new VisualRun(this->runs.Begin()->second, this->buffer, 0, 0);
return l;
}
const WChar *begin = this->buffer; const WChar *begin = this->buffer;
WChar *last_space = NULL; WChar *last_space = NULL;
const WChar *last_char = begin; const WChar *last_char = begin;
int width = 0; int width = 0;
int offset = this->buffer - this->buffer_begin; int offset = this->buffer - this->buffer_begin;
FontMap::iterator iter = runs.Begin(); FontMap::iterator iter = this->runs.Begin();
while (iter->first <= offset) { while (iter->first <= offset) {
iter++; iter++;
assert(iter != runs.End()); assert(iter != this->runs.End());
} }
const FontCache *fc = iter->second->fc; const FontCache *fc = iter->second->fc;
@ -303,12 +310,11 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width)
this->buffer = NULL; this->buffer = NULL;
break; break;
} }
if (c == '\n') break;
if (this->buffer == next_run) { if (this->buffer == next_run) {
*l->Append() = new VisualRun(iter->second, begin, this->buffer - begin, l->getWidth()); *l->Append() = new VisualRun(iter->second, begin, this->buffer - begin, l->getWidth());
iter++; iter++;
assert(iter != runs.End()); assert(iter != this->runs.End());
next_run = this->buffer_begin + iter->first + 1; next_run = this->buffer_begin + iter->first + 1;
begin = this->buffer; begin = this->buffer;
@ -350,7 +356,7 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width)
} }
} }
if (last_char - begin != 0) { if (l->Length() == 0 || last_char - begin != 0) {
*l->Append() = new VisualRun(iter->second, begin, last_char - begin, l->getWidth()); *l->Append() = new VisualRun(iter->second, begin, last_char - begin, l->getWidth());
} }
return l; return l;
@ -371,12 +377,14 @@ size_t Layouter::AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
/** /**
* Get the actual ParagraphLayout for the given buffer. * Get the actual ParagraphLayout for the given buffer.
* @param buff The begin of the buffer.
* @param buff_end The location after the last element in the buffer. * @param buff_end The location after the last element in the buffer.
* @param fontMapping THe mapping of the fonts.
* @return The ParagraphLayout instance. * @return The ParagraphLayout instance.
*/ */
ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff_end) ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
{ {
return new ParagraphLayout(this->buffer, buff_end - this->buffer, this->fonts); return new ParagraphLayout(buff, buff_end - buff, fontMapping);
} }
#endif /* !WITH_ICU */ #endif /* !WITH_ICU */
@ -393,61 +401,70 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi
CharType *buff = this->buffer; CharType *buff = this->buffer;
TextColour cur_colour = colour, prev_colour = colour; TextColour cur_colour = colour, prev_colour = colour;
Font *f = new Font(fontsize, cur_colour); WChar c;
/* do {
* Go through the whole string while adding Font instances to the font map Font *f = new Font(fontsize, cur_colour);
* whenever the font changes, and convert the wide characters into a format CharType *buff_begin = buff;
* usable by ParagraphLayout. FontMap fontMapping;
*/
for (; buff < buffer_last;) { /*
WChar c = Utf8Consume(const_cast<const char **>(&str)); * Go through the whole string while adding Font instances to the font map
if (c == 0) { * whenever the font changes, and convert the wide characters into a format
break; * usable by ParagraphLayout.
} else if (c >= SCC_BLUE && c <= SCC_BLACK) { */
prev_colour = cur_colour; for (; buff < buffer_last;) {
cur_colour = (TextColour)(c - SCC_BLUE); c = Utf8Consume(const_cast<const char **>(&str));
} else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour. if (c == '\0' || c == '\n') {
Swap(prev_colour, cur_colour); break;
} else if (c == SCC_TINYFONT) { } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
fontsize = FS_SMALL; prev_colour = cur_colour;
} else if (c == SCC_BIGFONT) { cur_colour = (TextColour)(c - SCC_BLUE);
fontsize = FS_LARGE; } else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour.
} else { Swap(prev_colour, cur_colour);
buff += AppendToBuffer(buff, buffer_last, c); } else if (c == SCC_TINYFONT) {
continue; fontsize = FS_SMALL;
} else if (c == SCC_BIGFONT) {
fontsize = FS_LARGE;
} else {
buff += AppendToBuffer(buff, buffer_last, c);
continue;
}
if (!fontMapping.Contains(buff - buff_begin)) {
fontMapping.Insert(buff - buff_begin, f);
*this->fonts.Append() = f;
} else {
delete f;
}
f = new Font(fontsize, cur_colour);
} }
if (!this->fonts.Contains(buff - this->buffer)) { /* Better safe than sorry. */
this->fonts.Insert(buff - this->buffer, f); *buff = '\0';
} else {
delete f; if (!fontMapping.Contains(buff - buff_begin)) {
fontMapping.Insert(buff - buff_begin, f);
*this->fonts.Append() = f;
} }
f = new Font(fontsize, cur_colour); ParagraphLayout *p = GetParagraphLayout(buff_begin, buff, fontMapping);
}
/* Better safe than sorry. */ /* Copy all lines into a local cache so we can reuse them later on more easily. */
*buff = '\0'; ParagraphLayout::Line *l;
while ((l = p->nextLine(maxw)) != NULL) {
*this->Append() = l;
}
if (!this->fonts.Contains(buff - this->buffer)) { delete p;
this->fonts.Insert(buff - this->buffer, f);
}
ParagraphLayout *p = GetParagraphLayout(buff);
/* Copy all lines into a local cache so we can reuse them later on more easily. */ } while (c != '\0' && buff < buffer_last);
ParagraphLayout::Line *l;
while ((l = p->nextLine(maxw)) != NULL) {
*this->Append() = l;
}
delete p;
} }
/** Free everything we allocated. */ /** Free everything we allocated. */
Layouter::~Layouter() Layouter::~Layouter()
{ {
for (FontMap::iterator iter = this->fonts.Begin(); iter != this->fonts.End(); iter++) { for (Font **iter = this->fonts.Begin(); iter != this->fonts.End(); iter++) {
delete iter->second; delete *iter;
} }
} }

View File

@ -16,6 +16,8 @@
#include "gfx_func.h" #include "gfx_func.h"
#include "core/smallmap_type.hpp" #include "core/smallmap_type.hpp"
#undef WITH_ICU
#ifdef WITH_ICU #ifdef WITH_ICU
#include "layout/ParagraphLayout.h" #include "layout/ParagraphLayout.h"
#define ICU_FONTINSTANCE : public LEFontInstance #define ICU_FONTINSTANCE : public LEFontInstance
@ -125,10 +127,10 @@ class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
#endif /* WITH_ICU */ #endif /* WITH_ICU */
size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c); size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c);
ParagraphLayout *GetParagraphLayout(CharType *buff); ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping);
CharType buffer[DRAW_STRING_BUFFER]; ///< Buffer for the text that is going to be drawn. CharType buffer[DRAW_STRING_BUFFER]; ///< Buffer for the text that is going to be drawn.
FontMap fonts; ///< The fonts needed for drawing. SmallVector<Font *, 4> fonts; ///< The fonts needed for drawing.
public: public:
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL); Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);