1/* 2 ********************************************************************** 3 * Copyright (C) 2002-2014, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ********************************************************************** 6 */ 7 8/* 9 * paragraphLayout doesn't make much sense without 10 * BreakIterator... 11 */ 12#include "layout/LETypes.h" 13#include "layout/LEScripts.h" 14#include "layout/LELanguages.h" 15#include "layout/LayoutEngine.h" 16#include "layout/LEFontInstance.h" 17 18#include "unicode/ubidi.h" 19#include "unicode/uchriter.h" 20#include "unicode/brkiter.h" 21 22#if ! UCONFIG_NO_BREAK_ITERATION 23#include "LXUtilities.h" 24#include "usc_impl.h" /* this is currently private! */ 25#include "cstring.h" /* this too! */ 26 27#include "layout/ParagraphLayout.h" 28 29U_NAMESPACE_BEGIN 30 31#define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) 32 33/* Leave this copyright notice here! It needs to go somewhere in this library. */ 34static const char copyright[] = U_COPYRIGHT_STRING; 35 36class StyleRuns 37{ 38public: 39 StyleRuns(const RunArray *styleRunArrays[], le_int32 styleCount); 40 41 ~StyleRuns(); 42 43 le_int32 getRuns(le_int32 runLimits[], le_int32 styleIndices[]); 44 45private: 46 le_int32 fStyleCount; 47 le_int32 fRunCount; 48 49 le_int32 *fRunLimits; 50 le_int32 *fStyleIndices; 51}; 52 53StyleRuns::StyleRuns(const RunArray *styleRunArrays[], le_int32 styleCount) 54 : fStyleCount(styleCount), fRunCount(0), fRunLimits(NULL), fStyleIndices(NULL) 55{ 56 le_int32 maxRunCount = 0; 57 le_int32 style, run, runStyle; 58 le_int32 *currentRun = LE_NEW_ARRAY(le_int32, styleCount); 59 60 for (int i = 0; i < styleCount; i += 1) { 61 maxRunCount += styleRunArrays[i]->getCount(); 62 } 63 64 maxRunCount -= styleCount - 1; 65 66 fRunLimits = LE_NEW_ARRAY(le_int32, maxRunCount); 67 fStyleIndices = LE_NEW_ARRAY(le_int32, maxRunCount * styleCount); 68 69 for (style = 0; style < styleCount; style += 1) { 70 currentRun[style] = 0; 71 } 72 73 run = 0; 74 runStyle = 0; 75 76 /* 77 * Since the last run limit for each style run must be 78 * the same, all the styles will hit the last limit at 79 * the same time, so we know when we're done when the first 80 * style hits the last limit. 81 */ 82 while (currentRun[0] < styleRunArrays[0]->getCount()) { 83 fRunLimits[run] = 0x7FFFFFFF; 84 85 // find the minimum run limit for all the styles 86 for (style = 0; style < styleCount; style += 1) { 87 if (styleRunArrays[style]->getLimit(currentRun[style]) < fRunLimits[run]) { 88 fRunLimits[run] = styleRunArrays[style]->getLimit(currentRun[style]); 89 } 90 } 91 92 // advance all styles whose current run is at this limit to the next run 93 for (style = 0; style < styleCount; style += 1) { 94 fStyleIndices[runStyle++] = currentRun[style]; 95 96 if (styleRunArrays[style]->getLimit(currentRun[style]) == fRunLimits[run]) { 97 currentRun[style] += 1; 98 } 99 } 100 101 run += 1; 102 } 103 104 fRunCount = run; 105 LE_DELETE_ARRAY(currentRun); 106} 107 108StyleRuns::~StyleRuns() 109{ 110 fRunCount = 0; 111 112 LE_DELETE_ARRAY(fStyleIndices); 113 fStyleIndices = NULL; 114 115 LE_DELETE_ARRAY(fRunLimits); 116 fRunLimits = NULL; 117} 118 119le_int32 StyleRuns::getRuns(le_int32 runLimits[], le_int32 styleIndices[]) 120{ 121 if (runLimits != NULL) { 122 LE_ARRAY_COPY(runLimits, fRunLimits, fRunCount); 123 } 124 125 if (styleIndices != NULL) { 126 LE_ARRAY_COPY(styleIndices, fStyleIndices, fRunCount * fStyleCount); 127 } 128 129 return fRunCount; 130} 131 132/* 133 * NOTE: This table only has "TRUE" values for 134 * those scripts which the LayoutEngine can currently 135 * process, rather for all scripts which require 136 * complex processing for correct rendering. 137 */ 138static const le_bool complexTable[scriptCodeCount] = { 139 FALSE , /* Zyyy */ 140 FALSE, /* Qaai */ 141 TRUE, /* Arab */ 142 FALSE, /* Armn */ 143 TRUE, /* Beng */ 144 FALSE, /* Bopo */ 145 FALSE, /* Cher */ 146 FALSE, /* Copt=Qaac */ 147 FALSE, /* Cyrl */ 148 FALSE, /* Dsrt */ 149 TRUE, /* Deva */ 150 FALSE, /* Ethi */ 151 FALSE, /* Geor */ 152 FALSE, /* Goth */ 153 FALSE, /* Grek */ 154 TRUE, /* Gujr */ 155 TRUE, /* Guru */ 156 FALSE, /* Hani */ 157 FALSE, /* Hang */ 158 TRUE, /* Hebr */ 159 FALSE, /* Hira */ 160 TRUE, /* Knda */ 161 FALSE, /* Kana */ 162 FALSE, /* Khmr */ 163 FALSE, /* Laoo */ 164 FALSE, /* Latn */ 165 TRUE, /* Mlym */ 166 FALSE, /* Mong */ 167 FALSE, /* Mymr */ 168 FALSE, /* Ogam */ 169 FALSE, /* Ital */ 170 TRUE, /* Orya */ 171 FALSE, /* Runr */ 172 FALSE, /* Sinh */ 173 FALSE, /* Syrc */ 174 TRUE, /* Taml */ 175 TRUE, /* Telu */ 176 FALSE, /* Thaa */ 177 TRUE, /* Thai */ 178 FALSE, /* Tibt */ 179 FALSE, /* Cans */ 180 FALSE, /* Yiii */ 181 FALSE, /* Tglg */ 182 FALSE, /* Hano */ 183 FALSE, /* Buhd */ 184 FALSE, /* Tagb */ 185 FALSE, /* Brai */ 186 FALSE, /* Cprt */ 187 FALSE, /* Limb */ 188 FALSE, /* Linb */ 189 FALSE, /* Osma */ 190 FALSE, /* Shaw */ 191 FALSE, /* Tale */ 192 FALSE, /* Ugar */ 193 FALSE, /* Hrkt */ 194 FALSE, /* Bugi */ 195 FALSE, /* Glag */ 196 FALSE, /* Khar */ 197 FALSE, /* Sylo */ 198 FALSE, /* Talu */ 199 FALSE, /* Tfng */ 200 FALSE, /* Xpeo */ 201 FALSE, /* Bali */ 202 FALSE, /* Batk */ 203 FALSE, /* Blis */ 204 FALSE, /* Brah */ 205 FALSE, /* Cham */ 206 FALSE, /* Cirt */ 207 FALSE, /* Cyrs */ 208 FALSE, /* Egyd */ 209 FALSE, /* Egyh */ 210 FALSE, /* Egyp */ 211 FALSE, /* Geok */ 212 FALSE, /* Hans */ 213 FALSE, /* Hant */ 214 FALSE, /* Hmng */ 215 FALSE, /* Hung */ 216 FALSE, /* Inds */ 217 FALSE, /* Java */ 218 FALSE, /* Kali */ 219 FALSE, /* Latf */ 220 FALSE, /* Latg */ 221 FALSE, /* Lepc */ 222 FALSE, /* Lina */ 223 FALSE, /* Mand */ 224 FALSE, /* Maya */ 225 FALSE, /* Mero */ 226 FALSE, /* Nkoo */ 227 FALSE, /* Orkh */ 228 FALSE, /* Perm */ 229 FALSE, /* Phag */ 230 FALSE, /* Phnx */ 231 FALSE, /* Plrd */ 232 FALSE, /* Roro */ 233 FALSE, /* Sara */ 234 FALSE, /* Syre */ 235 FALSE, /* Syrj */ 236 FALSE, /* Syrn */ 237 FALSE, /* Teng */ 238 FALSE, /* Taii */ 239 FALSE, /* Visp */ 240 FALSE, /* Xsux */ 241 FALSE, /* Zxxx */ 242 FALSE, /* Zzzz */ 243 FALSE, /* Cari */ 244 FALSE, /* Jpan */ 245 FALSE, /* Lana */ 246 FALSE, /* Lyci */ 247 FALSE, /* Lydi */ 248 FALSE, /* Olck */ 249 FALSE, /* Rjng */ 250 FALSE, /* Saur */ 251 FALSE, /* Sgnw */ 252 FALSE, /* Sund */ 253 FALSE, /* Moon */ 254 FALSE, /* Mtei */ 255 FALSE, /* Armi */ 256 FALSE, /* Avst */ 257 FALSE, /* Cakm */ 258 FALSE, /* Kore */ 259 FALSE, /* Kthi */ 260 FALSE, /* Mani */ 261 FALSE, /* Phli */ 262 FALSE, /* Phlp */ 263 FALSE, /* Phlv */ 264 FALSE, /* Prti */ 265 FALSE, /* Samr */ 266 FALSE, /* Tavt */ 267 FALSE, /* Zmth */ 268 FALSE, /* Zsym */ 269 FALSE, /* Bamu */ 270 FALSE, /* Lisu */ 271 FALSE, /* Nkgb */ 272 FALSE /* Sarb */ 273}; 274 275 276const char ParagraphLayout::fgClassID = 0; 277 278static void fillMissingCharToGlyphMapValues(le_int32 *charToGlyphMap, 279 le_int32 charCount) { 280 le_int32 lastValidGlyph = -1; 281 le_int32 ch; 282 for (ch = 0; ch <= charCount; ch += 1) { 283 if (charToGlyphMap[ch] == -1) { 284 charToGlyphMap[ch] = lastValidGlyph; 285 } else { 286 lastValidGlyph = charToGlyphMap[ch]; 287 } 288 } 289} 290 291/* 292 * How to deal with composite fonts: 293 * 294 * Don't store the client's FontRuns; we'll need to compute sub-font FontRuns using Doug's 295 * LEFontInstance method. Do that by intersecting the client's FontRuns with fScriptRuns. Use 296 * that to compute fFontRuns, and then intersect fFontRuns, fScriptRuns and fLevelRuns. Doing 297 * it in this order means we do a two-way intersection and a three-way intersection. 298 * 299 * An optimization would be to only do this if there's at least one composite font... 300 * 301 * Other notes: 302 * 303 * * Return the sub-fonts as the run fonts... could keep the mapping back to the client's FontRuns 304 * but that probably makes it more complicated of everyone... 305 * 306 * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API. 307 * 308 * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels? 309 * 310 */ 311ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count, 312 const FontRuns *fontRuns, 313 const ValueRuns *levelRuns, 314 const ValueRuns *scriptRuns, 315 const LocaleRuns *localeRuns, 316 UBiDiLevel paragraphLevel, le_bool vertical, 317 LEErrorCode &status) 318 : fChars(chars), fCharCount(count), 319 fFontRuns(NULL), fLevelRuns(levelRuns), fScriptRuns(scriptRuns), fLocaleRuns(localeRuns), 320 fVertical(vertical), fClientLevels(TRUE), fClientScripts(TRUE), fClientLocales(TRUE), fEmbeddingLevels(NULL), 321 fAscent(0), fDescent(0), fLeading(0), 322 fGlyphToCharMap(NULL), fCharToMinGlyphMap(NULL), fCharToMaxGlyphMap(NULL), fGlyphWidths(NULL), fGlyphCount(0), 323 fParaBidi(NULL), fLineBidi(NULL), 324 fStyleRunLimits(NULL), fStyleIndices(NULL), fStyleRunCount(0), 325 fBreakIterator(NULL), fLineStart(-1), fLineEnd(0), 326 /*fVisualRuns(NULL), fStyleRunInfo(NULL), fVisualRunCount(-1), 327 fFirstVisualRun(-1), fLastVisualRun(-1),*/ fVisualRunLastX(0), fVisualRunLastY(0) 328{ 329 330 if (LE_FAILURE(status)) { 331 fCharCount = -1; 332 return; 333 } 334 335 (void)copyright; // Suppress unused variable warning. 336 (void)fVertical; // Suppress warning for unused field fVertical. 337 338 // FIXME: should check the limit arrays for consistency... 339 340 computeLevels(paragraphLevel); 341 342 if (scriptRuns == NULL) { 343 computeScripts(); 344 } 345 346 if (localeRuns == NULL) { 347 computeLocales(); 348 } 349 350 computeSubFonts(fontRuns, status); 351 352 if (LE_FAILURE(status)) { 353 //other stuff? 354 fCharCount = -1; 355 return; 356 } 357 358 // now intersect the font, direction and script runs... 359 const RunArray *styleRunArrays[] = {fFontRuns, fLevelRuns, fScriptRuns, fLocaleRuns}; 360 le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0]; 361 StyleRuns styleRuns(styleRunArrays, styleCount); 362 LEErrorCode layoutStatus = LE_NO_ERROR; 363 364 fStyleRunCount = styleRuns.getRuns(NULL, NULL); 365 366 fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount); 367 fStyleIndices = LE_NEW_ARRAY(le_int32, fStyleRunCount * styleCount); 368 if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) { 369 status = LE_MEMORY_ALLOCATION_ERROR; 370 return; 371 } 372 373 styleRuns.getRuns(fStyleRunLimits, fStyleIndices); 374 375 // now build a LayoutEngine for each style run... 376 le_int32 *styleIndices = fStyleIndices; 377 le_int32 run, runStart; 378 379 fStyleRunInfo = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount); 380 if (fStyleRunInfo == NULL) { 381 status = LE_MEMORY_ALLOCATION_ERROR; 382 return; 383 } 384 else { 385 // initialize 386 for (run = 0; run < fStyleRunCount; run += 1) { 387 fStyleRunInfo[run].font = NULL; 388 fStyleRunInfo[run].runBase = 0; 389 fStyleRunInfo[run].runLimit = 0; 390 fStyleRunInfo[run].script = (UScriptCode)0; 391 fStyleRunInfo[run].locale = NULL; 392 fStyleRunInfo[run].level = 0; 393 fStyleRunInfo[run].glyphBase = 0; 394 fStyleRunInfo[run].engine = NULL; 395 fStyleRunInfo[run].glyphCount = 0; 396 fStyleRunInfo[run].glyphs = NULL; 397 fStyleRunInfo[run].positions = NULL; 398 } 399 } 400 401 fGlyphCount = 0; 402 for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) { 403 fStyleRunInfo[run].font = fFontRuns->getFont(styleIndices[0]); 404 fStyleRunInfo[run].runBase = runStart; 405 fStyleRunInfo[run].runLimit = fStyleRunLimits[run]; 406 fStyleRunInfo[run].script = (UScriptCode) fScriptRuns->getValue(styleIndices[2]); 407 fStyleRunInfo[run].locale = fLocaleRuns->getLocale(styleIndices[3]); 408 fStyleRunInfo[run].level = (UBiDiLevel) fLevelRuns->getValue(styleIndices[1]); 409 fStyleRunInfo[run].glyphBase = fGlyphCount; 410 411 fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font, 412 fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), layoutStatus); 413 if (LE_FAILURE(layoutStatus)) { 414 status = layoutStatus; 415 return; 416 } 417 418 fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount, 419 fStyleRunInfo[run].level & 1, 0, 0, layoutStatus); 420 if (LE_FAILURE(layoutStatus)) { 421 status = layoutStatus; 422 return; 423 } 424 425 runStart = fStyleRunLimits[run]; 426 styleIndices += styleCount; 427 fGlyphCount += fStyleRunInfo[run].glyphCount; 428 } 429 430 // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps, 431 // in logical order. (Both maps need an extra entry for the end of the text.) 432 // 433 // For each layout get the positions and convert them into glyph widths, in 434 // logical order. Get the glyph-to-char mapping, offset by starting index in the 435 // character array. Swap the glyph width and glyph-to-char arrays into logical order. 436 // Finally, fill in the char-to-glyph mappings. 437 fGlyphWidths = LE_NEW_ARRAY(float, fGlyphCount); 438 fGlyphToCharMap = LE_NEW_ARRAY(le_int32, fGlyphCount + 1); 439 fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1); 440 fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1); 441 if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) || 442 (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) { 443 status = LE_MEMORY_ALLOCATION_ERROR; 444 return; 445 } 446 447 le_int32 glyph; 448 449 for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) { 450 LayoutEngine *engine = fStyleRunInfo[run].engine; 451 le_int32 glyphCount = fStyleRunInfo[run].glyphCount; 452 le_int32 glyphBase = fStyleRunInfo[run].glyphBase; 453 454 fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount); 455 fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2); 456 if ((fStyleRunInfo[run].glyphs == NULL) || 457 (fStyleRunInfo[run].positions == NULL)) { 458 status = LE_MEMORY_ALLOCATION_ERROR; 459 return; 460 } 461 462 engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus); 463 if (LE_FAILURE(layoutStatus)) { 464 status = layoutStatus; 465 return; 466 } 467 468 engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus); 469 if (LE_FAILURE(layoutStatus)) { 470 status = layoutStatus; 471 return; 472 } 473 474 engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus); 475 if (LE_FAILURE(layoutStatus)) { 476 status = layoutStatus; 477 return; 478 } 479 480 for (glyph = 0; glyph < glyphCount; glyph += 1) { 481 fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2]; 482 } 483 484 if ((fStyleRunInfo[run].level & 1) != 0) { 485 LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount); 486 LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount); 487 } 488 489 runStart = fStyleRunLimits[run]; 490 491 delete engine; 492 fStyleRunInfo[run].engine = NULL; 493 } 494 495 fGlyphToCharMap[fGlyphCount] = fCharCount; 496 497 // Initialize the char-to-glyph maps to -1 so that we can later figure out 498 // whether any of the entries in the map aren't filled in below. 499 le_int32 chIndex; 500 for (chIndex = 0; chIndex <= fCharCount; chIndex += 1) { 501 fCharToMinGlyphMap[chIndex] = -1; 502 fCharToMaxGlyphMap[chIndex] = -1; 503 } 504 505 for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) { 506 le_int32 ch = fGlyphToCharMap[glyph]; 507 508 fCharToMinGlyphMap[ch] = glyph; 509 } 510 511 fCharToMinGlyphMap[fCharCount] = fGlyphCount; 512 513 for (glyph = 0; glyph < fGlyphCount; glyph += 1) { 514 le_int32 ch = fGlyphToCharMap[glyph]; 515 516 fCharToMaxGlyphMap[ch] = glyph; 517 } 518 519 fCharToMaxGlyphMap[fCharCount] = fGlyphCount; 520 521 // Now fill in the missing values in the char-to-glyph maps. 522 fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount); 523 fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount); 524} 525 526ParagraphLayout::~ParagraphLayout() 527{ 528 delete (FontRuns *) fFontRuns; 529 530 if (! fClientLevels) { 531 delete (ValueRuns *) fLevelRuns; 532 fLevelRuns = NULL; 533 534 fClientLevels = TRUE; 535 } 536 537 if (! fClientScripts) { 538 delete (ValueRuns *) fScriptRuns; 539 fScriptRuns = NULL; 540 541 fClientScripts = TRUE; 542 } 543 544 if (! fClientLocales) { 545 delete (LocaleRuns *) fLocaleRuns; 546 fLocaleRuns = NULL; 547 548 fClientLocales = TRUE; 549 } 550 551 if (fEmbeddingLevels != NULL) { 552 LE_DELETE_ARRAY(fEmbeddingLevels); 553 fEmbeddingLevels = NULL; 554 } 555 556 if (fGlyphToCharMap != NULL) { 557 LE_DELETE_ARRAY(fGlyphToCharMap); 558 fGlyphToCharMap = NULL; 559 } 560 561 if (fCharToMinGlyphMap != NULL) { 562 LE_DELETE_ARRAY(fCharToMinGlyphMap); 563 fCharToMinGlyphMap = NULL; 564 } 565 566 if (fCharToMaxGlyphMap != NULL) { 567 LE_DELETE_ARRAY(fCharToMaxGlyphMap); 568 fCharToMaxGlyphMap = NULL; 569 } 570 571 if (fGlyphWidths != NULL) { 572 LE_DELETE_ARRAY(fGlyphWidths); 573 fGlyphWidths = NULL; 574 } 575 576 if (fParaBidi != NULL) { 577 ubidi_close(fParaBidi); 578 fParaBidi = NULL; 579 } 580 581 if (fLineBidi != NULL) { 582 ubidi_close(fLineBidi); 583 fLineBidi = NULL; 584 } 585 586 if (fStyleRunCount > 0) { 587 le_int32 run; 588 589 LE_DELETE_ARRAY(fStyleRunLimits); 590 LE_DELETE_ARRAY(fStyleIndices); 591 592 for (run = 0; run < fStyleRunCount; run += 1) { 593 LE_DELETE_ARRAY(fStyleRunInfo[run].glyphs); 594 LE_DELETE_ARRAY(fStyleRunInfo[run].positions); 595 596 fStyleRunInfo[run].glyphs = NULL; 597 fStyleRunInfo[run].positions = NULL; 598 } 599 600 LE_DELETE_ARRAY(fStyleRunInfo); 601 602 fStyleRunLimits = NULL; 603 fStyleIndices = NULL; 604 fStyleRunInfo = NULL; 605 fStyleRunCount = 0; 606 } 607 608 if (fBreakIterator != NULL) { 609 delete fBreakIterator; 610 fBreakIterator = NULL; 611 } 612} 613 614 615le_bool ParagraphLayout::isComplex(const LEUnicode chars[], le_int32 count) 616{ 617 UErrorCode scriptStatus = U_ZERO_ERROR; 618 UScriptCode scriptCode = USCRIPT_INVALID_CODE; 619 UScriptRun *sr = uscript_openRun(chars, count, &scriptStatus); 620 le_bool result = FALSE; 621 622 while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) { 623 if (isComplex(scriptCode)) { 624 result = TRUE; 625 break; 626 } 627 } 628 629 uscript_closeRun(sr); 630 return result; 631} 632 633le_int32 ParagraphLayout::getAscent() const 634{ 635 if (fAscent <= 0 && fCharCount > 0) { 636 ((ParagraphLayout *) this)->computeMetrics(); 637 } 638 639 return fAscent; 640} 641 642le_int32 ParagraphLayout::getDescent() const 643{ 644 if (fAscent <= 0 && fCharCount > 0) { 645 ((ParagraphLayout *) this)->computeMetrics(); 646 } 647 648 return fDescent; 649} 650 651le_int32 ParagraphLayout::getLeading() const 652{ 653 if (fAscent <= 0 && fCharCount > 0) { 654 ((ParagraphLayout *) this)->computeMetrics(); 655 } 656 657 return fLeading; 658} 659 660le_bool ParagraphLayout::isDone() const 661{ 662 return fLineEnd >= fCharCount; 663} 664 665ParagraphLayout::Line *ParagraphLayout::nextLine(float width) 666{ 667 if (isDone()) { 668 return NULL; 669 } 670 671 fLineStart = fLineEnd; 672 673 if (width > 0) { 674 le_int32 glyph = fCharToMinGlyphMap[fLineStart]; 675 float widthSoFar = 0; 676 677 while (glyph < fGlyphCount && widthSoFar + fGlyphWidths[glyph] <= width) { 678 widthSoFar += fGlyphWidths[glyph++]; 679 } 680 681 // If no glyphs fit on the line, force one to fit. 682 // 683 // (There shouldn't be any zero width glyphs at the 684 // start of a line unless the paragraph consists of 685 // only zero width glyphs, because otherwise the zero 686 // width glyphs will have been included on the end of 687 // the previous line...) 688 if (widthSoFar == 0 && glyph < fGlyphCount) { 689 glyph += 1; 690 } 691 692 fLineEnd = previousBreak(fGlyphToCharMap[glyph]); 693 694 // If this break is at or before the last one, 695 // find a glyph, starting at the one which didn't 696 // fit, that produces a break after the last one. 697 while (fLineEnd <= fLineStart) { 698 fLineEnd = fGlyphToCharMap[glyph++]; 699 } 700 } else { 701 fLineEnd = fCharCount; 702 } 703 704 return computeVisualRuns(); 705} 706 707void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel) 708{ 709 UErrorCode bidiStatus = U_ZERO_ERROR; 710 711 if (fLevelRuns != NULL) { 712 le_int32 ch; 713 le_int32 run; 714 715 fEmbeddingLevels = LE_NEW_ARRAY(UBiDiLevel, fCharCount); 716 717 for (ch = 0, run = 0; run < fLevelRuns->getCount(); run += 1) { 718 UBiDiLevel runLevel = (UBiDiLevel) fLevelRuns->getValue(run) | UBIDI_LEVEL_OVERRIDE; 719 le_int32 runLimit = fLevelRuns->getLimit(run); 720 721 while (ch < runLimit) { 722 fEmbeddingLevels[ch++] = runLevel; 723 } 724 } 725 } 726 727 fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus); 728 ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus); 729 730 if (fLevelRuns == NULL) { 731 le_int32 levelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus); 732 ValueRuns *levelRuns = new ValueRuns(levelRunCount); 733 734 le_int32 logicalStart = 0; 735 le_int32 run; 736 le_int32 limit; 737 UBiDiLevel level; 738 739 for (run = 0; run < levelRunCount; run += 1) { 740 ubidi_getLogicalRun(fParaBidi, logicalStart, &limit, &level); 741 levelRuns->add(level, limit); 742 logicalStart = limit; 743 } 744 745 fLevelRuns = levelRuns; 746 fClientLevels = FALSE; 747 } 748} 749 750void ParagraphLayout::computeScripts() 751{ 752 UErrorCode scriptStatus = U_ZERO_ERROR; 753 UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus); 754 ValueRuns *scriptRuns = new ValueRuns(0); 755 le_int32 limit; 756 UScriptCode script; 757 758 while (uscript_nextRun(sr, NULL, &limit, &script)) { 759 scriptRuns->add(script, limit); 760 } 761 762 uscript_closeRun(sr); 763 764 fScriptRuns = scriptRuns; 765 fClientScripts = FALSE; 766} 767 768void ParagraphLayout::computeLocales() 769{ 770 LocaleRuns *localeRuns = new LocaleRuns(0); 771 const Locale *defaultLocale = &Locale::getDefault(); 772 773 localeRuns->add(defaultLocale, fCharCount); 774 775 fLocaleRuns = localeRuns; 776 fClientLocales = FALSE; 777} 778 779void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status) 780{ 781 if (LE_FAILURE(status)) { 782 return; 783 } 784 785 const RunArray *styleRunArrays[] = {fontRuns, fScriptRuns}; 786 le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0]; 787 StyleRuns styleRuns(styleRunArrays, styleCount); 788 le_int32 styleRunCount = styleRuns.getRuns(NULL, NULL); 789 le_int32 *styleRunLimits = LE_NEW_ARRAY(le_int32, styleRunCount); 790 le_int32 *styleIndices = LE_NEW_ARRAY(le_int32, styleRunCount * styleCount); 791 FontRuns *subFontRuns = new FontRuns(0); 792 le_int32 run, offset, *si; 793 794 styleRuns.getRuns(styleRunLimits, styleIndices); 795 796 si = styleIndices; 797 offset = 0; 798 799 for (run = 0; run < styleRunCount; run += 1) { 800 const LEFontInstance *runFont = fontRuns->getFont(si[0]); 801 le_int32 script = fScriptRuns->getValue(si[1]); 802 803 while (offset < styleRunLimits[run]) { 804 const LEFontInstance *subFont = runFont->getSubFont(fChars, &offset, styleRunLimits[run], script, status); 805 806 if (LE_FAILURE(status)) { 807 delete subFontRuns; 808 goto cleanUp; 809 } 810 811 subFontRuns->add(subFont, offset); 812 } 813 814 si += styleCount; 815 } 816 817 fFontRuns = subFontRuns; 818 819cleanUp: 820 LE_DELETE_ARRAY(styleIndices); 821 LE_DELETE_ARRAY(styleRunLimits); 822} 823 824void ParagraphLayout::computeMetrics() 825{ 826 le_int32 i, count = fFontRuns->getCount(); 827 le_int32 maxDL = 0; 828 829 for (i = 0; i < count; i += 1) { 830 const LEFontInstance *font = fFontRuns->getFont(i); 831 le_int32 ascent = font->getAscent(); 832 le_int32 descent = font->getDescent(); 833 le_int32 leading = font->getLeading(); 834 le_int32 dl = descent + leading; 835 836 if (ascent > fAscent) { 837 fAscent = ascent; 838 } 839 840 if (descent > fDescent) { 841 fDescent = descent; 842 } 843 844 if (leading > fLeading) { 845 fLeading = leading; 846 } 847 848 if (dl > maxDL) { 849 maxDL = dl; 850 } 851 } 852 853 fLeading = maxDL - fDescent; 854} 855 856#if 1 857struct LanguageMap 858{ 859 const char *localeCode; 860 le_int32 languageCode; 861}; 862 863static const LanguageMap languageMap[] = 864{ 865 {"afr", afkLanguageCode}, // Afrikaans 866 {"ara", araLanguageCode}, // Arabic 867 {"asm", asmLanguageCode}, // Assamese 868 {"bel", belLanguageCode}, // Belarussian 869 {"ben", benLanguageCode}, // Bengali 870 {"bod", tibLanguageCode}, // Tibetan 871 {"bul", bgrLanguageCode}, // Bulgarian 872 {"cat", catLanguageCode}, // Catalan 873 {"ces", csyLanguageCode}, // Czech 874 {"che", cheLanguageCode}, // Chechen 875 {"cop", copLanguageCode}, // Coptic 876 {"cym", welLanguageCode}, // Welsh 877 {"dan", danLanguageCode}, // Danish 878 {"deu", deuLanguageCode}, // German 879 {"dzo", dznLanguageCode}, // Dzongkha 880 {"ell", ellLanguageCode}, // Greek 881 {"eng", engLanguageCode}, // English 882 {"est", etiLanguageCode}, // Estonian 883 {"eus", euqLanguageCode}, // Basque 884 {"fas", farLanguageCode}, // Farsi 885 {"fin", finLanguageCode}, // Finnish 886 {"fra", fraLanguageCode}, // French 887 {"gle", gaeLanguageCode}, // Irish Gaelic 888 {"guj", gujLanguageCode}, // Gujarati 889 {"hau", hauLanguageCode}, // Hausa 890 {"heb", iwrLanguageCode}, // Hebrew 891 {"hin", hinLanguageCode}, // Hindi 892 {"hrv", hrvLanguageCode}, // Croatian 893 {"hun", hunLanguageCode}, // Hungarian 894 {"hye", hyeLanguageCode}, // Armenian 895 {"ind", indLanguageCode}, // Indonesian 896 {"ita", itaLanguageCode}, // Italian 897 {"jpn", janLanguageCode}, // Japanese 898 {"kan", kanLanguageCode}, // Kannada 899 {"kas", kshLanguageCode}, // Kashmiri 900 {"khm", khmLanguageCode}, // Khmer 901 {"kok", kokLanguageCode}, // Konkani 902 {"kor", korLanguageCode}, // Korean 903// {"mal_XXX", malLanguageCode}, // Malayalam - Traditional 904 {"mal", mlrLanguageCode}, // Malayalam - Reformed 905 {"mar", marLanguageCode}, // Marathi 906 {"mlt", mtsLanguageCode}, // Maltese 907 {"mni", mniLanguageCode}, // Manipuri 908 {"mon", mngLanguageCode}, // Mongolian 909 {"nep", nepLanguageCode}, // Nepali 910 {"ori", oriLanguageCode}, // Oriya 911 {"pol", plkLanguageCode}, // Polish 912 {"por", ptgLanguageCode}, // Portuguese 913 {"pus", pasLanguageCode}, // Pashto 914 {"ron", romLanguageCode}, // Romanian 915 {"rus", rusLanguageCode}, // Russian 916 {"san", sanLanguageCode}, // Sanskrit 917 {"sin", snhLanguageCode}, // Sinhalese 918 {"slk", skyLanguageCode}, // Slovak 919 {"snd", sndLanguageCode}, // Sindhi 920 {"slv", slvLanguageCode}, // Slovenian 921 {"spa", espLanguageCode}, // Spanish 922 {"sqi", sqiLanguageCode}, // Albanian 923 {"srp", srbLanguageCode}, // Serbian 924 {"swe", sveLanguageCode}, // Swedish 925 {"syr", syrLanguageCode}, // Syriac 926 {"tam", tamLanguageCode}, // Tamil 927 {"tel", telLanguageCode}, // Telugu 928 {"tha", thaLanguageCode}, // Thai 929 {"tur", trkLanguageCode}, // Turkish 930 {"urd", urdLanguageCode}, // Urdu 931 {"yid", jiiLanguageCode}, // Yiddish 932// {"zhp", zhpLanguageCode}, // Chinese - Phonetic 933 {"zho", zhsLanguageCode}, // Chinese 934 {"zho_CHN", zhsLanguageCode}, // Chinese - China 935 {"zho_HKG", zhsLanguageCode}, // Chinese - Hong Kong 936 {"zho_MAC", zhtLanguageCode}, // Chinese - Macao 937 {"zho_SGP", zhsLanguageCode}, // Chinese - Singapore 938 {"zho_TWN", zhtLanguageCode} // Chinese - Taiwan 939}; 940 941static const le_int32 languageMapCount = ARRAY_SIZE(languageMap); 942 943le_int32 ParagraphLayout::getLanguageCode(const Locale *locale) 944{ 945 char code[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 946 const char *language = locale->getISO3Language(); 947 const char *country = locale->getISO3Country(); 948 949 uprv_strcat(code, language); 950 951 if ((uprv_strcmp(language, "zho") == 0) && country != NULL) { 952 uprv_strcat(code, "_"); 953 uprv_strcat(code, country); 954 } 955 956 for (le_int32 i = 0; i < languageMapCount; i += 1) { 957 if (uprv_strcmp(code, languageMap[i].localeCode) == 0) { 958 return languageMap[i].languageCode; 959 } 960 } 961 962 return nullLanguageCode; 963} 964#else 965 966// TODO - dummy implementation for right now... 967le_int32 ParagraphLayout::getLanguageCode(const Locale *locale) 968{ 969 return nullLanguageCode; 970} 971#endif 972 973le_bool ParagraphLayout::isComplex(UScriptCode script) 974{ 975 if (script < 0 || script >= (UScriptCode) scriptCodeCount) { 976 return FALSE; 977 } 978 979 return complexTable[script]; 980} 981 982le_int32 ParagraphLayout::previousBreak(le_int32 charIndex) 983{ 984 // skip over any whitespace or control characters, 985 // because they can hang in the margin. 986 while (charIndex < fCharCount && 987 (u_isWhitespace(fChars[charIndex]) || 988 u_iscntrl(fChars[charIndex]))) { 989 charIndex += 1; 990 } 991 992 // Create the BreakIterator if we don't already have one 993 if (fBreakIterator == NULL) { 994 Locale thai("th"); 995 UCharCharacterIterator *iter = new UCharCharacterIterator(fChars, fCharCount); 996 UErrorCode status = U_ZERO_ERROR; 997 998 fBreakIterator = BreakIterator::createLineInstance(thai, status); 999 fBreakIterator->adoptText(iter); 1000 } 1001 1002 // return the break location that's at or before 1003 // the character we stopped on. Note: if we're 1004 // on a break, the "+ 1" will cause preceding to 1005 // back up to it. 1006 return fBreakIterator->preceding(charIndex + 1); 1007} 1008 1009ParagraphLayout::Line *ParagraphLayout::computeVisualRuns() 1010{ 1011 UErrorCode bidiStatus = U_ZERO_ERROR; 1012 le_int32 dirRunCount, visualRun; 1013 1014 fVisualRunLastX = 0; 1015 fVisualRunLastY = 0; 1016 fFirstVisualRun = getCharRun(fLineStart); 1017 fLastVisualRun = getCharRun(fLineEnd - 1); 1018 1019 if (fLineBidi == NULL) { 1020 fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus); 1021 } 1022 1023 ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus); 1024 dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus); 1025 1026 Line *line = new Line(); 1027 1028 for (visualRun = 0; visualRun < dirRunCount; visualRun += 1) { 1029 le_int32 relStart, run, runLength; 1030 UBiDiDirection runDirection = ubidi_getVisualRun(fLineBidi, visualRun, &relStart, &runLength); 1031 le_int32 runStart = fLineStart + relStart; 1032 le_int32 runEnd = runStart + runLength - 1; 1033 le_int32 firstRun = getCharRun(runStart); 1034 le_int32 lastRun = getCharRun(runEnd); 1035 le_int32 startRun = (runDirection == UBIDI_LTR)? firstRun : lastRun; 1036 le_int32 stopRun = (runDirection == UBIDI_LTR)? lastRun + 1 : firstRun - 1; 1037 le_int32 dir = (runDirection == UBIDI_LTR)? 1 : -1; 1038 1039 for (run = startRun; run != stopRun; run += dir) { 1040 le_int32 firstChar = (run == firstRun)? runStart : fStyleRunInfo[run].runBase; 1041 le_int32 lastChar = (run == lastRun)? runEnd : fStyleRunInfo[run].runLimit - 1; 1042 1043 appendRun(line, run, firstChar, lastChar); 1044 } 1045 } 1046 1047 return line; 1048} 1049 1050void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_int32 firstChar, le_int32 lastChar) 1051{ 1052 le_int32 glyphBase = fStyleRunInfo[run].glyphBase; 1053 le_int32 inGlyph, outGlyph; 1054 1055 // Get the glyph indices for all the characters between firstChar and lastChar, 1056 // make the minimum one be leftGlyph and the maximum one be rightGlyph. 1057 // (need to do this to handle local reorderings like Indic left matras) 1058 le_int32 leftGlyph = fGlyphCount; 1059 le_int32 rightGlyph = -1; 1060 le_int32 ch; 1061 1062 for (ch = firstChar; ch <= lastChar; ch += 1) { 1063 le_int32 minGlyph = fCharToMinGlyphMap[ch]; 1064 le_int32 maxGlyph = fCharToMaxGlyphMap[ch]; 1065 1066 if (minGlyph < leftGlyph) { 1067 leftGlyph = minGlyph; 1068 } 1069 1070 if (maxGlyph > rightGlyph) { 1071 rightGlyph = maxGlyph; 1072 } 1073 } 1074 1075 if ((fStyleRunInfo[run].level & 1) != 0) { 1076 le_int32 swap = rightGlyph; 1077 le_int32 last = glyphBase + fStyleRunInfo[run].glyphCount - 1; 1078 1079 // Here, we want to remove the glyphBase bias... 1080 rightGlyph = last - leftGlyph; 1081 leftGlyph = last - swap; 1082 } else { 1083 rightGlyph -= glyphBase; 1084 leftGlyph -= glyphBase; 1085 } 1086 1087 // Set the position bias for the glyphs. If we're at the start of 1088 // a line, we want the first glyph to be at x = 0, even if it comes 1089 // from the middle of a layout. If we've got a right-to-left run, we 1090 // want the left-most glyph to start at the final x position of the 1091 // previous run, even though this glyph may be in the middle of the 1092 // run. 1093 fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2]; 1094 1095 // Make rightGlyph be the glyph just to the right of 1096 // the run's glyphs 1097 rightGlyph += 1; 1098 1099 UBiDiDirection direction = ((fStyleRunInfo[run].level & 1) == 0)? UBIDI_LTR : UBIDI_RTL; 1100 le_int32 glyphCount = rightGlyph - leftGlyph; 1101 LEGlyphID *glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount); 1102 float *positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2); 1103 le_int32 *glyphToCharMap = LE_NEW_ARRAY(le_int32, glyphCount); 1104 1105 LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], glyphCount); 1106 1107 for (outGlyph = 0, inGlyph = leftGlyph * 2; inGlyph <= rightGlyph * 2; inGlyph += 2, outGlyph += 2) { 1108 positions[outGlyph] = fStyleRunInfo[run].positions[inGlyph] + fVisualRunLastX; 1109 positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] + fVisualRunLastY; 1110 } 1111 1112 // Save the ending position of this run 1113 // to use for the start of the next run 1114 fVisualRunLastX = positions[outGlyph - 2]; 1115 fVisualRunLastY = positions[outGlyph - 1]; 1116 1117 if ((fStyleRunInfo[run].level & 1) == 0) { 1118 for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) { 1119 glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph]; 1120 } 1121 } else { 1122 // Because fGlyphToCharMap is stored in logical order to facilitate line breaking, 1123 // we need to map the physical glyph indices to logical indices while we copy the 1124 // character indices. 1125 le_int32 base = glyphBase + fStyleRunInfo[run].glyphCount - 1; 1126 1127 for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) { 1128 glyphToCharMap[outGlyph] = fGlyphToCharMap[base - inGlyph]; 1129 } 1130 } 1131 1132 line->append(fStyleRunInfo[run].font, direction, glyphCount, glyphs, positions, glyphToCharMap); 1133} 1134 1135le_int32 ParagraphLayout::getCharRun(le_int32 charIndex) 1136{ 1137 if (charIndex < 0 || charIndex > fCharCount) { 1138 return -1; 1139 } 1140 1141 le_int32 run; 1142 1143 // NOTE: as long as fStyleRunLimits is well-formed 1144 // the above range check guarantees that we'll never 1145 // fall off the end of the array. 1146 run = 0; 1147 while (charIndex >= fStyleRunLimits[run]) { 1148 run += 1; 1149 } 1150 1151 return run; 1152} 1153 1154 1155const char ParagraphLayout::Line::fgClassID = 0; 1156 1157#define INITIAL_RUN_CAPACITY 4 1158#define RUN_CAPACITY_GROW_LIMIT 16 1159 1160ParagraphLayout::Line::~Line() 1161{ 1162 le_int32 i; 1163 1164 for (i = 0; i < fRunCount; i += 1) { 1165 delete fRuns[i]; 1166 } 1167 1168 LE_DELETE_ARRAY(fRuns); 1169} 1170 1171le_int32 ParagraphLayout::Line::getAscent() const 1172{ 1173 if (fAscent <= 0) { 1174 ((ParagraphLayout::Line *)this)->computeMetrics(); 1175 } 1176 1177 return fAscent; 1178} 1179 1180le_int32 ParagraphLayout::Line::getDescent() const 1181{ 1182 if (fAscent <= 0) { 1183 ((ParagraphLayout::Line *)this)->computeMetrics(); 1184 } 1185 1186 return fDescent; 1187} 1188 1189le_int32 ParagraphLayout::Line::getLeading() const 1190{ 1191 if (fAscent <= 0) { 1192 ((ParagraphLayout::Line *)this)->computeMetrics(); 1193 } 1194 1195 return fLeading; 1196} 1197 1198le_int32 ParagraphLayout::Line::getWidth() const 1199{ 1200 const VisualRun *lastRun = getVisualRun(fRunCount - 1); 1201 1202 if (lastRun == NULL) { 1203 return 0; 1204 } 1205 1206 le_int32 glyphCount = lastRun->getGlyphCount(); 1207 const float *positions = lastRun->getPositions(); 1208 1209 return (le_int32) positions[glyphCount * 2]; 1210} 1211 1212const ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(le_int32 runIndex) const 1213{ 1214 if (runIndex < 0 || runIndex >= fRunCount) { 1215 return NULL; 1216 } 1217 1218 return fRuns[runIndex]; 1219} 1220 1221void ParagraphLayout::Line::append(const LEFontInstance *font, UBiDiDirection direction, le_int32 glyphCount, 1222 const LEGlyphID glyphs[], const float positions[], const le_int32 glyphToCharMap[]) 1223{ 1224 if (fRunCount >= fRunCapacity) { 1225 if (fRunCapacity == 0) { 1226 fRunCapacity = INITIAL_RUN_CAPACITY; 1227 fRuns = LE_NEW_ARRAY(ParagraphLayout::VisualRun *, fRunCapacity); 1228 } else { 1229 fRunCapacity += (fRunCapacity < RUN_CAPACITY_GROW_LIMIT? fRunCapacity : RUN_CAPACITY_GROW_LIMIT); 1230 fRuns = (ParagraphLayout::VisualRun **) LE_GROW_ARRAY(fRuns, fRunCapacity); 1231 } 1232 } 1233 1234 fRuns[fRunCount++] = new ParagraphLayout::VisualRun(font, direction, glyphCount, glyphs, positions, glyphToCharMap); 1235} 1236 1237void ParagraphLayout::Line::computeMetrics() 1238{ 1239 le_int32 maxDL = 0; 1240 1241 for (le_int32 i = 0; i < fRunCount; i += 1) { 1242 le_int32 ascent = fRuns[i]->getAscent(); 1243 le_int32 descent = fRuns[i]->getDescent(); 1244 le_int32 leading = fRuns[i]->getLeading(); 1245 le_int32 dl = descent + leading; 1246 1247 if (ascent > fAscent) { 1248 fAscent = ascent; 1249 } 1250 1251 if (descent > fDescent) { 1252 fDescent = descent; 1253 } 1254 1255 if (leading > fLeading) { 1256 fLeading = leading; 1257 } 1258 1259 if (dl > maxDL) { 1260 maxDL = dl; 1261 } 1262 } 1263 1264 fLeading = maxDL - fDescent; 1265} 1266 1267const char ParagraphLayout::VisualRun::fgClassID = 0; 1268 1269ParagraphLayout::VisualRun::~VisualRun() 1270{ 1271 LE_DELETE_ARRAY(fGlyphToCharMap); 1272 LE_DELETE_ARRAY(fPositions); 1273 LE_DELETE_ARRAY(fGlyphs); 1274} 1275 1276U_NAMESPACE_END 1277 1278#endif 1279 1280