00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gfx_layout.h"
00014 #include "string_func.h"
00015 #include "strings_func.h"
00016
00017 #include "table/control_codes.h"
00018
00019 #ifdef WITH_ICU
00020 #include <unicode/ustring.h>
00021 #endif
00022
00023
00025 Layouter::LineCache *Layouter::linecache;
00026
00028 Layouter::FontColourMap Layouter::fonts[FS_END];
00029
00030
00036 Font::Font(FontSize size, TextColour colour) :
00037 fc(FontCache::Get(size)), colour(colour)
00038 {
00039 assert(size < FS_END);
00040 }
00041
00042 #ifdef WITH_ICU
00043
00044
00045 le_int32 Font::getUnitsPerEM() const
00046 {
00047 return this->fc->GetUnitsPerEM();
00048 }
00049
00050 le_int32 Font::getAscent() const
00051 {
00052 return this->fc->GetAscender();
00053 }
00054
00055 le_int32 Font::getDescent() const
00056 {
00057 return -this->fc->GetDescender();
00058 }
00059
00060 le_int32 Font::getLeading() const
00061 {
00062 return this->fc->GetHeight();
00063 }
00064
00065 float Font::getXPixelsPerEm() const
00066 {
00067 return (float)this->fc->GetHeight();
00068 }
00069
00070 float Font::getYPixelsPerEm() const
00071 {
00072 return (float)this->fc->GetHeight();
00073 }
00074
00075 float Font::getScaleFactorX() const
00076 {
00077 return 1.0f;
00078 }
00079
00080 float Font::getScaleFactorY() const
00081 {
00082 return 1.0f;
00083 }
00084
00085 const void *Font::getFontTable(LETag tableTag) const
00086 {
00087 size_t length;
00088 return this->getFontTable(tableTag, length);
00089 }
00090
00091 const void *Font::getFontTable(LETag tableTag, size_t &length) const
00092 {
00093 return this->fc->GetFontTable(tableTag, length);
00094 }
00095
00096 LEGlyphID Font::mapCharToGlyph(LEUnicode32 ch) const
00097 {
00098 if (IsTextDirectionChar(ch)) return 0;
00099 return this->fc->MapCharToGlyph(ch);
00100 }
00101
00102 void Font::getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const
00103 {
00104 advance.fX = glyph == 0xFFFF ? 0 : this->fc->GetGlyphWidth(glyph);
00105 advance.fY = 0;
00106 }
00107
00108 le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const
00109 {
00110 return FALSE;
00111 }
00112
00113 size_t Layouter::AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
00114 {
00115
00116 int32 length = 0;
00117 UErrorCode err = U_ZERO_ERROR;
00118 u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
00119 return length;
00120 }
00121
00122 ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
00123 {
00124 int32 length = buff_end - buff;
00125
00126 if (length == 0) {
00127
00128 buff[0] = ' ';
00129 length = 1;
00130 fontMapping.End()[-1].first++;
00131 }
00132
00133
00134 FontRuns runs(fontMapping.Length());
00135 for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
00136 runs.add(iter->second, iter->first);
00137 }
00138
00139 LEErrorCode status = LE_NO_ERROR;
00140
00141
00142 return new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
00143 }
00144
00145 #else
00146
00147
00148
00156 ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_count, int x) :
00157 font(font), glyph_count(char_count)
00158 {
00159 this->glyphs = MallocT<GlyphID>(this->glyph_count);
00160
00161
00162 this->positions = MallocT<float>(this->glyph_count * 2 + 2);
00163 this->positions[0] = x;
00164 this->positions[1] = 0;
00165
00166 for (int i = 0; i < this->glyph_count; i++) {
00167 this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]);
00168 this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]);
00169 this->positions[2 * i + 3] = 0;
00170 }
00171 }
00172
00174 ParagraphLayout::VisualRun::~VisualRun()
00175 {
00176 free(this->positions);
00177 free(this->glyphs);
00178 }
00179
00184 Font *ParagraphLayout::VisualRun::getFont() const
00185 {
00186 return this->font;
00187 }
00188
00193 int ParagraphLayout::VisualRun::getGlyphCount() const
00194 {
00195 return this->glyph_count;
00196 }
00197
00202 const GlyphID *ParagraphLayout::VisualRun::getGlyphs() const
00203 {
00204 return this->glyphs;
00205 }
00206
00211 float *ParagraphLayout::VisualRun::getPositions() const
00212 {
00213 return this->positions;
00214 }
00215
00220 int ParagraphLayout::VisualRun::getLeading() const
00221 {
00222 return this->getFont()->fc->GetHeight();
00223 }
00224
00229 int ParagraphLayout::Line::getLeading() const
00230 {
00231 int leading = 0;
00232 for (const VisualRun * const *run = this->Begin(); run != this->End(); run++) {
00233 leading = max(leading, (*run)->getLeading());
00234 }
00235
00236 return leading;
00237 }
00238
00243 int ParagraphLayout::Line::getWidth() const
00244 {
00245 if (this->Length() == 0) return 0;
00246
00247
00248
00249
00250
00251
00252 const VisualRun *run = this->getVisualRun(this->countRuns() - 1);
00253 return run->getPositions()[run->getGlyphCount() * 2];
00254 }
00255
00260 int ParagraphLayout::Line::countRuns() const
00261 {
00262 return this->Length();
00263 }
00264
00269 ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(int run) const
00270 {
00271 return *this->Get(run);
00272 }
00273
00280 ParagraphLayout::ParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs)
00281 {
00282 assert(runs.End()[-1].first == length);
00283 }
00284
00288 void ParagraphLayout::reflow()
00289 {
00290 this->buffer = this->buffer_begin;
00291 }
00292
00298 ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width)
00299 {
00300
00301
00302
00303
00304 if (this->buffer == NULL) return NULL;
00305
00306 Line *l = new Line();
00307
00308 if (*this->buffer == '\0') {
00309
00310 this->buffer = NULL;
00311 *l->Append() = new VisualRun(this->runs.Begin()->second, this->buffer, 0, 0);
00312 return l;
00313 }
00314
00315 const WChar *begin = this->buffer;
00316 const WChar *last_space = NULL;
00317 const WChar *last_char = begin;
00318 int width = 0;
00319
00320 int offset = this->buffer - this->buffer_begin;
00321 FontMap::iterator iter = this->runs.Begin();
00322 while (iter->first <= offset) {
00323 iter++;
00324 assert(iter != this->runs.End());
00325 }
00326
00327 const FontCache *fc = iter->second->fc;
00328 const WChar *next_run = this->buffer_begin + iter->first;
00329
00330 for (;;) {
00331 WChar c = *this->buffer;
00332 last_char = this->buffer;
00333
00334 if (c == '\0') {
00335 this->buffer = NULL;
00336 break;
00337 }
00338
00339 if (this->buffer == next_run) {
00340 int w = l->getWidth();
00341 *l->Append() = new VisualRun(iter->second, begin, this->buffer - begin, w);
00342 iter++;
00343 assert(iter != this->runs.End());
00344
00345 next_run = this->buffer_begin + iter->first;
00346 begin = this->buffer;
00347
00348 last_space = NULL;
00349 }
00350
00351 if (IsWhitespace(c)) last_space = this->buffer;
00352
00353 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00354 int char_width = GetCharacterWidth(fc->GetSize(), c);
00355 width += char_width;
00356 if (width > max_width) {
00357
00358
00359 if (width == char_width) {
00360
00361
00362 this->buffer = NULL;
00363 return l;
00364 }
00365
00366 if (last_space == NULL) {
00367
00368
00369
00370
00371
00372
00373 last_char = this->buffer;
00374 } else {
00375
00376 this->buffer = last_space + 1;
00377 last_char = last_space;
00378 }
00379 break;
00380 }
00381 }
00382
00383 this->buffer++;
00384 }
00385
00386 if (l->Length() == 0 || last_char - begin != 0) {
00387 int w = l->getWidth();
00388 *l->Append() = new VisualRun(iter->second, begin, last_char - begin, w);
00389 }
00390 return l;
00391 }
00392
00400 size_t Layouter::AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
00401 {
00402 *buff = c;
00403 return 1;
00404 }
00405
00413 ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
00414 {
00415 return new ParagraphLayout(buff, buff_end - buff, fontMapping);
00416 }
00417 #endif
00418
00426 Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize)
00427 {
00428 FontState state(colour, fontsize);
00429 WChar c = 0;
00430
00431 do {
00432
00433 const char *lineend = str;
00434 for (;;) {
00435 size_t len = Utf8Decode(&c, lineend);
00436 if (c == '\0' || c == '\n') break;
00437 lineend += len;
00438 }
00439
00440 LineCacheItem& line = GetCachedParagraphLayout(str, lineend - str, state);
00441 if (line.layout != NULL) {
00442
00443 str = lineend + 1;
00444 state = line.state_after;
00445 line.layout->reflow();
00446 } else {
00447
00448 const CharType *buffer_last = lastof(line.buffer);
00449 CharType *buff_begin = line.buffer;
00450 CharType *buff = buff_begin;
00451 FontMap &fontMapping = line.runs;
00452 Font *f = GetFont(state.fontsize, state.cur_colour);
00453
00454
00455
00456
00457
00458
00459 for (; buff < buffer_last;) {
00460 c = Utf8Consume(const_cast<const char **>(&str));
00461 if (c == '\0' || c == '\n') {
00462 break;
00463 } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
00464 state.SetColour((TextColour)(c - SCC_BLUE));
00465 } else if (c == SCC_PREVIOUS_COLOUR) {
00466 state.SetPreviousColour();
00467 } else if (c == SCC_TINYFONT) {
00468 state.SetFontSize(FS_SMALL);
00469 } else if (c == SCC_BIGFONT) {
00470 state.SetFontSize(FS_LARGE);
00471 } else {
00472 buff += AppendToBuffer(buff, buffer_last, c);
00473 continue;
00474 }
00475
00476 if (!fontMapping.Contains(buff - buff_begin)) {
00477 fontMapping.Insert(buff - buff_begin, f);
00478 }
00479 f = GetFont(state.fontsize, state.cur_colour);
00480 }
00481
00482
00483 *buff = '\0';
00484
00485 if (!fontMapping.Contains(buff - buff_begin)) {
00486 fontMapping.Insert(buff - buff_begin, f);
00487 }
00488 line.layout = GetParagraphLayout(buff_begin, buff, fontMapping);
00489 line.state_after = state;
00490 }
00491
00492
00493 ParagraphLayout::Line *l;
00494 while ((l = line.layout->nextLine(maxw)) != NULL) {
00495 *this->Append() = l;
00496 }
00497
00498 } while (c != '\0');
00499 }
00500
00505 Dimension Layouter::GetBounds()
00506 {
00507 Dimension d = { 0, 0 };
00508 for (ParagraphLayout::Line **l = this->Begin(); l != this->End(); l++) {
00509 d.width = max<uint>(d.width, (*l)->getWidth());
00510 d.height += (*l)->getLeading();
00511 }
00512 return d;
00513 }
00514
00518 Font *Layouter::GetFont(FontSize size, TextColour colour)
00519 {
00520 FontColourMap::iterator it = fonts[size].Find(colour);
00521 if (it != fonts[size].End()) return it->second;
00522
00523 Font *f = new Font(size, colour);
00524 *fonts[size].Append() = FontColourMap::Pair(colour, f);
00525 return f;
00526 }
00527
00532 void Layouter::ResetFontCache(FontSize size)
00533 {
00534 for (FontColourMap::iterator it = fonts[size].Begin(); it != fonts[size].End(); ++it) {
00535 delete it->second;
00536 }
00537 fonts[size].Clear();
00538
00539
00540 ResetLineCache();
00541 }
00542
00551 Layouter::LineCacheItem &Layouter::GetCachedParagraphLayout(const char *str, size_t len, const FontState &state)
00552 {
00553 if (linecache == NULL) {
00554
00555 linecache = new LineCache();
00556 }
00557
00558 LineCacheKey key;
00559 key.state_before = state;
00560 key.str.assign(str, len);
00561 return (*linecache)[key];
00562 }
00563
00567 void Layouter::ResetLineCache()
00568 {
00569 if (linecache != NULL) linecache->clear();
00570 }
00571
00575 void Layouter::ReduceLineCache()
00576 {
00577 if (linecache != NULL) {
00578
00579 if (linecache->size() > 4096) ResetLineCache();
00580 }
00581 }