1 /* 2 ********************************************************************** 3 * Copyright (C) 2002-2010, 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 29 U_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. */ 34 static const char copyright[] = U_COPYRIGHT_STRING; 35 36 class StyleRuns 37 { 38 public: 39 StyleRuns(const RunArray *styleRunArrays[], le_int32 styleCount); 40 41 ~StyleRuns(); 42 43 le_int32 getRuns(le_int32 runLimits[], le_int32 styleIndices[]); 44 45 private: 46 le_int32 fStyleCount; 47 le_int32 fRunCount; 48 49 le_int32 *fRunLimits; 50 le_int32 *fStyleIndices; 51 }; 52 53 StyleRuns::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 108 StyleRuns::~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 119 le_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 */ 138 static 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 276 const char ParagraphLayout::fgClassID = 0; 277 278 static 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 */ 311 ParagraphLayout::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 // FIXME: should check the limit arrays for consistency... 336 337 computeLevels(paragraphLevel); 338 339 if (scriptRuns == NULL) { 340 computeScripts(); 341 } 342 343 if (localeRuns == NULL) { 344 computeLocales(); 345 } 346 347 computeSubFonts(fontRuns, status); 348 349 if (LE_FAILURE(status)) { 350 //other stuff? 351 fCharCount = -1; 352 return; 353 } 354 355 // now intersect the font, direction and script runs... 356 const RunArray *styleRunArrays[] = {fFontRuns, fLevelRuns, fScriptRuns, fLocaleRuns}; 357 le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0]; 358 StyleRuns styleRuns(styleRunArrays, styleCount); 359 LEErrorCode layoutStatus = LE_NO_ERROR; 360 361 fStyleRunCount = styleRuns.getRuns(NULL, NULL); 362 363 fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount); 364 fStyleIndices = LE_NEW_ARRAY(le_int32, fStyleRunCount * styleCount); 365 if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) { 366 status = LE_MEMORY_ALLOCATION_ERROR; 367 return; 368 } 369 370 styleRuns.getRuns(fStyleRunLimits, fStyleIndices); 371 372 // now build a LayoutEngine for each style run... 373 le_int32 *styleIndices = fStyleIndices; 374 le_int32 run, runStart; 375 376 fStyleRunInfo = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount); 377 if (fStyleRunInfo == NULL) { 378 status = LE_MEMORY_ALLOCATION_ERROR; 379 return; 380 } 381 else { 382 // initialize 383 for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) { 384 fStyleRunInfo[run].font = NULL; 385 fStyleRunInfo[run].runBase = 0; 386 fStyleRunInfo[run].runLimit = 0; 387 fStyleRunInfo[run].script = (UScriptCode)0; 388 fStyleRunInfo[run].locale = NULL; 389 fStyleRunInfo[run].level = 0; 390 fStyleRunInfo[run].glyphBase = 0; 391 fStyleRunInfo[run].engine = NULL; 392 fStyleRunInfo[run].glyphCount = 0; 393 fStyleRunInfo[run].glyphs = NULL; 394 fStyleRunInfo[run].positions = NULL; 395 } 396 } 397 398 fGlyphCount = 0; 399 for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) { 400 fStyleRunInfo[run].font = fFontRuns->getFont(styleIndices[0]); 401 fStyleRunInfo[run].runBase = runStart; 402 fStyleRunInfo[run].runLimit = fStyleRunLimits[run]; 403 fStyleRunInfo[run].script = (UScriptCode) fScriptRuns->getValue(styleIndices[2]); 404 fStyleRunInfo[run].locale = fLocaleRuns->getLocale(styleIndices[3]); 405 fStyleRunInfo[run].level = (UBiDiLevel) fLevelRuns->getValue(styleIndices[1]); 406 fStyleRunInfo[run].glyphBase = fGlyphCount; 407 408 fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font, 409 fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), layoutStatus); 410 if (LE_FAILURE(layoutStatus)) { 411 status = layoutStatus; 412 return; 413 } 414 415 fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount, 416 fStyleRunInfo[run].level & 1, 0, 0, layoutStatus); 417 if (LE_FAILURE(layoutStatus)) { 418 status = layoutStatus; 419 return; 420 } 421 422 runStart = fStyleRunLimits[run]; 423 styleIndices += styleCount; 424 fGlyphCount += fStyleRunInfo[run].glyphCount; 425 } 426 427 // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps, 428 // in logical order. (Both maps need an extra entry for the end of the text.) 429 // 430 // For each layout get the positions and convert them into glyph widths, in 431 // logical order. Get the glyph-to-char mapping, offset by starting index in the 432 // character array. Swap the glyph width and glyph-to-char arrays into logical order. 433 // Finally, fill in the char-to-glyph mappings. 434 fGlyphWidths = LE_NEW_ARRAY(float, fGlyphCount); 435 fGlyphToCharMap = LE_NEW_ARRAY(le_int32, fGlyphCount + 1); 436 fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1); 437 fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1); 438 if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) || 439 (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) { 440 status = LE_MEMORY_ALLOCATION_ERROR; 441 return; 442 } 443 444 le_int32 glyph; 445 446 for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) { 447 LayoutEngine *engine = fStyleRunInfo[run].engine; 448 le_int32 glyphCount = fStyleRunInfo[run].glyphCount; 449 le_int32 glyphBase = fStyleRunInfo[run].glyphBase; 450 451 fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount); 452 fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2); 453 if ((fStyleRunInfo[run].glyphs == NULL) || 454 (fStyleRunInfo[run].positions == NULL)) { 455 status = LE_MEMORY_ALLOCATION_ERROR; 456 return; 457 } 458 459 engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus); 460 if (LE_FAILURE(layoutStatus)) { 461 status = layoutStatus; 462 return; 463 } 464 465 engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus); 466 if (LE_FAILURE(layoutStatus)) { 467 status = layoutStatus; 468 return; 469 } 470 471 engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus); 472 if (LE_FAILURE(layoutStatus)) { 473 status = layoutStatus; 474 return; 475 } 476 477 for (glyph = 0; glyph < glyphCount; glyph += 1) { 478 fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2]; 479 } 480 481 if ((fStyleRunInfo[run].level & 1) != 0) { 482 LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount); 483 LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount); 484 } 485 486 runStart = fStyleRunLimits[run]; 487 488 delete engine; 489 fStyleRunInfo[run].engine = NULL; 490 } 491 492 fGlyphToCharMap[fGlyphCount] = fCharCount; 493 494 // Initialize the char-to-glyph maps to -1 so that we can later figure out 495 // whether any of the entries in the map aren't filled in below. 496 le_int32 chIndex; 497 for (chIndex = 0; chIndex <= fCharCount; chIndex += 1) { 498 fCharToMinGlyphMap[chIndex] = -1; 499 fCharToMaxGlyphMap[chIndex] = -1; 500 } 501 502 for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) { 503 le_int32 ch = fGlyphToCharMap[glyph]; 504 505 fCharToMinGlyphMap[ch] = glyph; 506 } 507 508 fCharToMinGlyphMap[fCharCount] = fGlyphCount; 509 510 for (glyph = 0; glyph < fGlyphCount; glyph += 1) { 511 le_int32 ch = fGlyphToCharMap[glyph]; 512 513 fCharToMaxGlyphMap[ch] = glyph; 514 } 515 516 fCharToMaxGlyphMap[fCharCount] = fGlyphCount; 517 518 // Now fill in the missing values in the char-to-glyph maps. 519 fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount); 520 fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount); 521 } 522 523 ParagraphLayout::~ParagraphLayout() 524 { 525 delete (FontRuns *) fFontRuns; 526 527 if (! fClientLevels) { 528 delete (ValueRuns *) fLevelRuns; 529 fLevelRuns = NULL; 530 531 fClientLevels = TRUE; 532 } 533 534 if (! fClientScripts) { 535 delete (ValueRuns *) fScriptRuns; 536 fScriptRuns = NULL; 537 538 fClientScripts = TRUE; 539 } 540 541 if (! fClientLocales) { 542 delete (LocaleRuns *) fLocaleRuns; 543 fLocaleRuns = NULL; 544 545 fClientLocales = TRUE; 546 } 547 548 if (fEmbeddingLevels != NULL) { 549 LE_DELETE_ARRAY(fEmbeddingLevels); 550 fEmbeddingLevels = NULL; 551 } 552 553 if (fGlyphToCharMap != NULL) { 554 LE_DELETE_ARRAY(fGlyphToCharMap); 555 fGlyphToCharMap = NULL; 556 } 557 558 if (fCharToMinGlyphMap != NULL) { 559 LE_DELETE_ARRAY(fCharToMinGlyphMap); 560 fCharToMinGlyphMap = NULL; 561 } 562 563 if (fCharToMaxGlyphMap != NULL) { 564 LE_DELETE_ARRAY(fCharToMaxGlyphMap); 565 fCharToMaxGlyphMap = NULL; 566 } 567 568 if (fGlyphWidths != NULL) { 569 LE_DELETE_ARRAY(fGlyphWidths); 570 fGlyphWidths = NULL; 571 } 572 573 if (fParaBidi != NULL) { 574 ubidi_close(fParaBidi); 575 fParaBidi = NULL; 576 } 577 578 if (fLineBidi != NULL) { 579 ubidi_close(fLineBidi); 580 fLineBidi = NULL; 581 } 582 583 if (fStyleRunCount > 0) { 584 le_int32 run; 585 586 LE_DELETE_ARRAY(fStyleRunLimits); 587 LE_DELETE_ARRAY(fStyleIndices); 588 589 for (run = 0; run < fStyleRunCount; run += 1) { 590 LE_DELETE_ARRAY(fStyleRunInfo[run].glyphs); 591 LE_DELETE_ARRAY(fStyleRunInfo[run].positions); 592 593 fStyleRunInfo[run].glyphs = NULL; 594 fStyleRunInfo[run].positions = NULL; 595 } 596 597 LE_DELETE_ARRAY(fStyleRunInfo); 598 599 fStyleRunLimits = NULL; 600 fStyleIndices = NULL; 601 fStyleRunInfo = NULL; 602 fStyleRunCount = 0; 603 } 604 605 if (fBreakIterator != NULL) { 606 delete fBreakIterator; 607 fBreakIterator = NULL; 608 } 609 } 610 611 612 le_bool ParagraphLayout::isComplex(const LEUnicode chars[], le_int32 count) 613 { 614 UErrorCode scriptStatus = U_ZERO_ERROR; 615 UScriptCode scriptCode = USCRIPT_INVALID_CODE; 616 UScriptRun *sr = uscript_openRun(chars, count, &scriptStatus); 617 le_bool result = FALSE; 618 619 while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) { 620 if (isComplex(scriptCode)) { 621 result = TRUE; 622 break; 623 } 624 } 625 626 uscript_closeRun(sr); 627 return result; 628 } 629 630 le_int32 ParagraphLayout::getAscent() const 631 { 632 if (fAscent <= 0 && fCharCount > 0) { 633 ((ParagraphLayout *) this)->computeMetrics(); 634 } 635 636 return fAscent; 637 } 638 639 le_int32 ParagraphLayout::getDescent() const 640 { 641 if (fAscent <= 0 && fCharCount > 0) { 642 ((ParagraphLayout *) this)->computeMetrics(); 643 } 644 645 return fDescent; 646 } 647 648 le_int32 ParagraphLayout::getLeading() const 649 { 650 if (fAscent <= 0 && fCharCount > 0) { 651 ((ParagraphLayout *) this)->computeMetrics(); 652 } 653 654 return fLeading; 655 } 656 657 le_bool ParagraphLayout::isDone() const 658 { 659 return fLineEnd >= fCharCount; 660 } 661 662 ParagraphLayout::Line *ParagraphLayout::nextLine(float width) 663 { 664 if (isDone()) { 665 return NULL; 666 } 667 668 fLineStart = fLineEnd; 669 670 if (width > 0) { 671 le_int32 glyph = fCharToMinGlyphMap[fLineStart]; 672 float widthSoFar = 0; 673 674 while (glyph < fGlyphCount && widthSoFar + fGlyphWidths[glyph] <= width) { 675 widthSoFar += fGlyphWidths[glyph++]; 676 } 677 678 // If no glyphs fit on the line, force one to fit. 679 // 680 // (There shouldn't be any zero width glyphs at the 681 // start of a line unless the paragraph consists of 682 // only zero width glyphs, because otherwise the zero 683 // width glyphs will have been included on the end of 684 // the previous line...) 685 if (widthSoFar == 0 && glyph < fGlyphCount) { 686 glyph += 1; 687 } 688 689 fLineEnd = previousBreak(fGlyphToCharMap[glyph]); 690 691 // If this break is at or before the last one, 692 // find a glyph, starting at the one which didn't 693 // fit, that produces a break after the last one. 694 while (fLineEnd <= fLineStart) { 695 fLineEnd = fGlyphToCharMap[glyph++]; 696 } 697 } else { 698 fLineEnd = fCharCount; 699 } 700 701 return computeVisualRuns(); 702 } 703 704 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel) 705 { 706 UErrorCode bidiStatus = U_ZERO_ERROR; 707 708 if (fLevelRuns != NULL) { 709 le_int32 ch; 710 le_int32 run; 711 712 fEmbeddingLevels = LE_NEW_ARRAY(UBiDiLevel, fCharCount); 713 714 for (ch = 0, run = 0; run < fLevelRuns->getCount(); run += 1) { 715 UBiDiLevel runLevel = (UBiDiLevel) fLevelRuns->getValue(run) | UBIDI_LEVEL_OVERRIDE; 716 le_int32 runLimit = fLevelRuns->getLimit(run); 717 718 while (ch < runLimit) { 719 fEmbeddingLevels[ch++] = runLevel; 720 } 721 } 722 } 723 724 fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus); 725 ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus); 726 727 if (fLevelRuns == NULL) { 728 le_int32 levelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus); 729 ValueRuns *levelRuns = new ValueRuns(levelRunCount); 730 731 le_int32 logicalStart = 0; 732 le_int32 run; 733 le_int32 limit; 734 UBiDiLevel level; 735 736 for (run = 0; run < levelRunCount; run += 1) { 737 ubidi_getLogicalRun(fParaBidi, logicalStart, &limit, &level); 738 levelRuns->add(level, limit); 739 logicalStart = limit; 740 } 741 742 fLevelRuns = levelRuns; 743 fClientLevels = FALSE; 744 } 745 } 746 747 void ParagraphLayout::computeScripts() 748 { 749 UErrorCode scriptStatus = U_ZERO_ERROR; 750 UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus); 751 ValueRuns *scriptRuns = new ValueRuns(0); 752 le_int32 limit; 753 UScriptCode script; 754 755 while (uscript_nextRun(sr, NULL, &limit, &script)) { 756 scriptRuns->add(script, limit); 757 } 758 759 uscript_closeRun(sr); 760 761 fScriptRuns = scriptRuns; 762 fClientScripts = FALSE; 763 } 764 765 void ParagraphLayout::computeLocales() 766 { 767 LocaleRuns *localeRuns = new LocaleRuns(0); 768 const Locale *defaultLocale = &Locale::getDefault(); 769 770 localeRuns->add(defaultLocale, fCharCount); 771 772 fLocaleRuns = localeRuns; 773 fClientLocales = FALSE; 774 } 775 776 void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status) 777 { 778 if (LE_FAILURE(status)) { 779 return; 780 } 781 782 const RunArray *styleRunArrays[] = {fontRuns, fScriptRuns}; 783 le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0]; 784 StyleRuns styleRuns(styleRunArrays, styleCount); 785 le_int32 styleRunCount = styleRuns.getRuns(NULL, NULL); 786 le_int32 *styleRunLimits = LE_NEW_ARRAY(le_int32, styleRunCount); 787 le_int32 *styleIndices = LE_NEW_ARRAY(le_int32, styleRunCount * styleCount); 788 FontRuns *subFontRuns = new FontRuns(0); 789 le_int32 run, offset, *si; 790 791 styleRuns.getRuns(styleRunLimits, styleIndices); 792 793 si = styleIndices; 794 offset = 0; 795 796 for (run = 0; run < styleRunCount; run += 1) { 797 const LEFontInstance *runFont = fontRuns->getFont(si[0]); 798 le_int32 script = fScriptRuns->getValue(si[1]); 799 800 while (offset < styleRunLimits[run]) { 801 const LEFontInstance *subFont = runFont->getSubFont(fChars, &offset, styleRunLimits[run], script, status); 802 803 if (LE_FAILURE(status)) { 804 delete subFontRuns; 805 goto cleanUp; 806 } 807 808 subFontRuns->add(subFont, offset); 809 } 810 811 si += styleCount; 812 } 813 814 fFontRuns = subFontRuns; 815 816 cleanUp: 817 LE_DELETE_ARRAY(styleIndices); 818 LE_DELETE_ARRAY(styleRunLimits); 819 } 820 821 void ParagraphLayout::computeMetrics() 822 { 823 le_int32 i, count = fFontRuns->getCount(); 824 le_int32 maxDL = 0; 825 826 for (i = 0; i < count; i += 1) { 827 const LEFontInstance *font = fFontRuns->getFont(i); 828 le_int32 ascent = font->getAscent(); 829 le_int32 descent = font->getDescent(); 830 le_int32 leading = font->getLeading(); 831 le_int32 dl = descent + leading; 832 833 if (ascent > fAscent) { 834 fAscent = ascent; 835 } 836 837 if (descent > fDescent) { 838 fDescent = descent; 839 } 840 841 if (leading > fLeading) { 842 fLeading = leading; 843 } 844 845 if (dl > maxDL) { 846 maxDL = dl; 847 } 848 } 849 850 fLeading = maxDL - fDescent; 851 } 852 853 #if 1 854 struct LanguageMap 855 { 856 const char *localeCode; 857 le_int32 languageCode; 858 }; 859 860 static const LanguageMap languageMap[] = 861 { 862 {"afr", afkLanguageCode}, // Afrikaans 863 {"ara", araLanguageCode}, // Arabic 864 {"asm", asmLanguageCode}, // Assamese 865 {"bel", belLanguageCode}, // Belarussian 866 {"ben", benLanguageCode}, // Bengali 867 {"bod", tibLanguageCode}, // Tibetan 868 {"bul", bgrLanguageCode}, // Bulgarian 869 {"cat", catLanguageCode}, // Catalan 870 {"ces", csyLanguageCode}, // Czech 871 {"che", cheLanguageCode}, // Chechen 872 {"cop", copLanguageCode}, // Coptic 873 {"cym", welLanguageCode}, // Welsh 874 {"dan", danLanguageCode}, // Danish 875 {"deu", deuLanguageCode}, // German 876 {"dzo", dznLanguageCode}, // Dzongkha 877 {"ell", ellLanguageCode}, // Greek 878 {"eng", engLanguageCode}, // English 879 {"est", etiLanguageCode}, // Estonian 880 {"eus", euqLanguageCode}, // Basque 881 {"fas", farLanguageCode}, // Farsi 882 {"fin", finLanguageCode}, // Finnish 883 {"fra", fraLanguageCode}, // French 884 {"gle", gaeLanguageCode}, // Irish Gaelic 885 {"guj", gujLanguageCode}, // Gujarati 886 {"hau", hauLanguageCode}, // Hausa 887 {"heb", iwrLanguageCode}, // Hebrew 888 {"hin", hinLanguageCode}, // Hindi 889 {"hrv", hrvLanguageCode}, // Croatian 890 {"hun", hunLanguageCode}, // Hungarian 891 {"hye", hyeLanguageCode}, // Armenian 892 {"ind", indLanguageCode}, // Indonesian 893 {"ita", itaLanguageCode}, // Italian 894 {"jpn", janLanguageCode}, // Japanese 895 {"kan", kanLanguageCode}, // Kannada 896 {"kas", kshLanguageCode}, // Kashmiri 897 {"khm", khmLanguageCode}, // Khmer 898 {"kok", kokLanguageCode}, // Konkani 899 {"kor", korLanguageCode}, // Korean 900 // {"mal_XXX", malLanguageCode}, // Malayalam - Traditional 901 {"mal", mlrLanguageCode}, // Malayalam - Reformed 902 {"mar", marLanguageCode}, // Marathi 903 {"mlt", mtsLanguageCode}, // Maltese 904 {"mni", mniLanguageCode}, // Manipuri 905 {"mon", mngLanguageCode}, // Mongolian 906 {"nep", nepLanguageCode}, // Nepali 907 {"ori", oriLanguageCode}, // Oriya 908 {"pol", plkLanguageCode}, // Polish 909 {"por", ptgLanguageCode}, // Portuguese 910 {"pus", pasLanguageCode}, // Pashto 911 {"ron", romLanguageCode}, // Romanian 912 {"rus", rusLanguageCode}, // Russian 913 {"san", sanLanguageCode}, // Sanskrit 914 {"sin", snhLanguageCode}, // Sinhalese 915 {"slk", skyLanguageCode}, // Slovak 916 {"snd", sndLanguageCode}, // Sindhi 917 {"slv", slvLanguageCode}, // Slovenian 918 {"spa", espLanguageCode}, // Spanish 919 {"sqi", sqiLanguageCode}, // Albanian 920 {"srp", srbLanguageCode}, // Serbian 921 {"swe", sveLanguageCode}, // Swedish 922 {"syr", syrLanguageCode}, // Syriac 923 {"tam", tamLanguageCode}, // Tamil 924 {"tel", telLanguageCode}, // Telugu 925 {"tha", thaLanguageCode}, // Thai 926 {"tur", trkLanguageCode}, // Turkish 927 {"urd", urdLanguageCode}, // Urdu 928 {"yid", jiiLanguageCode}, // Yiddish 929 // {"zhp", zhpLanguageCode}, // Chinese - Phonetic 930 {"zho", zhsLanguageCode}, // Chinese 931 {"zho_CHN", zhsLanguageCode}, // Chinese - China 932 {"zho_HKG", zhsLanguageCode}, // Chinese - Hong Kong 933 {"zho_MAC", zhtLanguageCode}, // Chinese - Macao 934 {"zho_SGP", zhsLanguageCode}, // Chinese - Singapore 935 {"zho_TWN", zhtLanguageCode} // Chinese - Taiwan 936 }; 937 938 static const le_int32 languageMapCount = ARRAY_SIZE(languageMap); 939 940 le_int32 ParagraphLayout::getLanguageCode(const Locale *locale) 941 { 942 char code[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 943 const char *language = locale->getISO3Language(); 944 const char *country = locale->getISO3Country(); 945 946 uprv_strcat(code, language); 947 948 if ((uprv_strcmp(language, "zho") == 0) && country != NULL) { 949 uprv_strcat(code, "_"); 950 uprv_strcat(code, country); 951 } 952 953 for (le_int32 i = 0; i < languageMapCount; i += 1) { 954 if (uprv_strcmp(code, languageMap[i].localeCode) == 0) { 955 return languageMap[i].languageCode; 956 } 957 } 958 959 return nullLanguageCode; 960 } 961 #else 962 963 // TODO - dummy implementation for right now... 964 le_int32 ParagraphLayout::getLanguageCode(const Locale *locale) 965 { 966 return nullLanguageCode; 967 } 968 #endif 969 970 le_bool ParagraphLayout::isComplex(UScriptCode script) 971 { 972 if (script < 0 || script >= (UScriptCode) scriptCodeCount) { 973 return FALSE; 974 } 975 976 return complexTable[script]; 977 } 978 979 le_int32 ParagraphLayout::previousBreak(le_int32 charIndex) 980 { 981 // skip over any whitespace or control characters, 982 // because they can hang in the margin. 983 while (charIndex < fCharCount && 984 (u_isWhitespace(fChars[charIndex]) || 985 u_iscntrl(fChars[charIndex]))) { 986 charIndex += 1; 987 } 988 989 // Create the BreakIterator if we don't already have one 990 if (fBreakIterator == NULL) { 991 Locale thai("th"); 992 UCharCharacterIterator *iter = new UCharCharacterIterator(fChars, fCharCount); 993 UErrorCode status = U_ZERO_ERROR; 994 995 fBreakIterator = BreakIterator::createLineInstance(thai, status); 996 fBreakIterator->adoptText(iter); 997 } 998 999 // return the break location that's at or before 1000 // the character we stopped on. Note: if we're 1001 // on a break, the "+ 1" will cause preceding to 1002 // back up to it. 1003 return fBreakIterator->preceding(charIndex + 1); 1004 } 1005 1006 ParagraphLayout::Line *ParagraphLayout::computeVisualRuns() 1007 { 1008 UErrorCode bidiStatus = U_ZERO_ERROR; 1009 le_int32 dirRunCount, visualRun; 1010 1011 fVisualRunLastX = 0; 1012 fVisualRunLastY = 0; 1013 fFirstVisualRun = getCharRun(fLineStart); 1014 fLastVisualRun = getCharRun(fLineEnd - 1); 1015 1016 if (fLineBidi == NULL) { 1017 fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus); 1018 } 1019 1020 ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus); 1021 dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus); 1022 1023 Line *line = new Line(); 1024 1025 for (visualRun = 0; visualRun < dirRunCount; visualRun += 1) { 1026 le_int32 relStart, run, runLength; 1027 UBiDiDirection runDirection = ubidi_getVisualRun(fLineBidi, visualRun, &relStart, &runLength); 1028 le_int32 runStart = fLineStart + relStart; 1029 le_int32 runEnd = runStart + runLength - 1; 1030 le_int32 firstRun = getCharRun(runStart); 1031 le_int32 lastRun = getCharRun(runEnd); 1032 le_int32 startRun = (runDirection == UBIDI_LTR)? firstRun : lastRun; 1033 le_int32 stopRun = (runDirection == UBIDI_LTR)? lastRun + 1 : firstRun - 1; 1034 le_int32 dir = (runDirection == UBIDI_LTR)? 1 : -1; 1035 1036 for (run = startRun; run != stopRun; run += dir) { 1037 le_int32 firstChar = (run == firstRun)? runStart : fStyleRunInfo[run].runBase; 1038 le_int32 lastChar = (run == lastRun)? runEnd : fStyleRunInfo[run].runLimit - 1; 1039 1040 appendRun(line, run, firstChar, lastChar); 1041 } 1042 } 1043 1044 return line; 1045 } 1046 1047 void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_int32 firstChar, le_int32 lastChar) 1048 { 1049 le_int32 glyphBase = fStyleRunInfo[run].glyphBase; 1050 le_int32 inGlyph, outGlyph; 1051 1052 // Get the glyph indices for all the characters between firstChar and lastChar, 1053 // make the minimum one be leftGlyph and the maximum one be rightGlyph. 1054 // (need to do this to handle local reorderings like Indic left matras) 1055 le_int32 leftGlyph = fGlyphCount; 1056 le_int32 rightGlyph = -1; 1057 le_int32 ch; 1058 1059 for (ch = firstChar; ch <= lastChar; ch += 1) { 1060 le_int32 minGlyph = fCharToMinGlyphMap[ch]; 1061 le_int32 maxGlyph = fCharToMaxGlyphMap[ch]; 1062 1063 if (minGlyph < leftGlyph) { 1064 leftGlyph = minGlyph; 1065 } 1066 1067 if (maxGlyph > rightGlyph) { 1068 rightGlyph = maxGlyph; 1069 } 1070 } 1071 1072 if ((fStyleRunInfo[run].level & 1) != 0) { 1073 le_int32 swap = rightGlyph; 1074 le_int32 last = glyphBase + fStyleRunInfo[run].glyphCount - 1; 1075 1076 // Here, we want to remove the glyphBase bias... 1077 rightGlyph = last - leftGlyph; 1078 leftGlyph = last - swap; 1079 } else { 1080 rightGlyph -= glyphBase; 1081 leftGlyph -= glyphBase; 1082 } 1083 1084 // Set the position bias for the glyphs. If we're at the start of 1085 // a line, we want the first glyph to be at x = 0, even if it comes 1086 // from the middle of a layout. If we've got a right-to-left run, we 1087 // want the left-most glyph to start at the final x position of the 1088 // previous run, even though this glyph may be in the middle of the 1089 // run. 1090 fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2]; 1091 1092 // Make rightGlyph be the glyph just to the right of 1093 // the run's glyphs 1094 rightGlyph += 1; 1095 1096 UBiDiDirection direction = ((fStyleRunInfo[run].level & 1) == 0)? UBIDI_LTR : UBIDI_RTL; 1097 le_int32 glyphCount = rightGlyph - leftGlyph; 1098 LEGlyphID *glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount); 1099 float *positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2); 1100 le_int32 *glyphToCharMap = LE_NEW_ARRAY(le_int32, glyphCount); 1101 1102 LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], glyphCount); 1103 1104 for (outGlyph = 0, inGlyph = leftGlyph * 2; inGlyph <= rightGlyph * 2; inGlyph += 2, outGlyph += 2) { 1105 positions[outGlyph] = fStyleRunInfo[run].positions[inGlyph] + fVisualRunLastX; 1106 positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] + fVisualRunLastY; 1107 } 1108 1109 // Save the ending position of this run 1110 // to use for the start of the next run 1111 fVisualRunLastX = positions[outGlyph - 2]; 1112 fVisualRunLastY = positions[outGlyph - 1]; 1113 1114 if ((fStyleRunInfo[run].level & 1) == 0) { 1115 for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) { 1116 glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph]; 1117 } 1118 } else { 1119 // Because fGlyphToCharMap is stored in logical order to facilitate line breaking, 1120 // we need to map the physical glyph indices to logical indices while we copy the 1121 // character indices. 1122 le_int32 base = glyphBase + fStyleRunInfo[run].glyphCount - 1; 1123 1124 for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) { 1125 glyphToCharMap[outGlyph] = fGlyphToCharMap[base - inGlyph]; 1126 } 1127 } 1128 1129 line->append(fStyleRunInfo[run].font, direction, glyphCount, glyphs, positions, glyphToCharMap); 1130 } 1131 1132 le_int32 ParagraphLayout::getCharRun(le_int32 charIndex) 1133 { 1134 if (charIndex < 0 || charIndex > fCharCount) { 1135 return -1; 1136 } 1137 1138 le_int32 run; 1139 1140 // NOTE: as long as fStyleRunLimits is well-formed 1141 // the above range check guarantees that we'll never 1142 // fall off the end of the array. 1143 run = 0; 1144 while (charIndex >= fStyleRunLimits[run]) { 1145 run += 1; 1146 } 1147 1148 return run; 1149 } 1150 1151 1152 const char ParagraphLayout::Line::fgClassID = 0; 1153 1154 #define INITIAL_RUN_CAPACITY 4 1155 #define RUN_CAPACITY_GROW_LIMIT 16 1156 1157 ParagraphLayout::Line::~Line() 1158 { 1159 le_int32 i; 1160 1161 for (i = 0; i < fRunCount; i += 1) { 1162 delete fRuns[i]; 1163 } 1164 1165 LE_DELETE_ARRAY(fRuns); 1166 } 1167 1168 le_int32 ParagraphLayout::Line::getAscent() const 1169 { 1170 if (fAscent <= 0) { 1171 ((ParagraphLayout::Line *)this)->computeMetrics(); 1172 } 1173 1174 return fAscent; 1175 } 1176 1177 le_int32 ParagraphLayout::Line::getDescent() const 1178 { 1179 if (fAscent <= 0) { 1180 ((ParagraphLayout::Line *)this)->computeMetrics(); 1181 } 1182 1183 return fDescent; 1184 } 1185 1186 le_int32 ParagraphLayout::Line::getLeading() const 1187 { 1188 if (fAscent <= 0) { 1189 ((ParagraphLayout::Line *)this)->computeMetrics(); 1190 } 1191 1192 return fLeading; 1193 } 1194 1195 le_int32 ParagraphLayout::Line::getWidth() const 1196 { 1197 const VisualRun *lastRun = getVisualRun(fRunCount - 1); 1198 1199 if (lastRun == NULL) { 1200 return 0; 1201 } 1202 1203 le_int32 glyphCount = lastRun->getGlyphCount(); 1204 const float *positions = lastRun->getPositions(); 1205 1206 return (le_int32) positions[glyphCount * 2]; 1207 } 1208 1209 const ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(le_int32 runIndex) const 1210 { 1211 if (runIndex < 0 || runIndex >= fRunCount) { 1212 return NULL; 1213 } 1214 1215 return fRuns[runIndex]; 1216 } 1217 1218 void ParagraphLayout::Line::append(const LEFontInstance *font, UBiDiDirection direction, le_int32 glyphCount, 1219 const LEGlyphID glyphs[], const float positions[], const le_int32 glyphToCharMap[]) 1220 { 1221 if (fRunCount >= fRunCapacity) { 1222 if (fRunCapacity == 0) { 1223 fRunCapacity = INITIAL_RUN_CAPACITY; 1224 fRuns = LE_NEW_ARRAY(ParagraphLayout::VisualRun *, fRunCapacity); 1225 } else { 1226 fRunCapacity += (fRunCapacity < RUN_CAPACITY_GROW_LIMIT? fRunCapacity : RUN_CAPACITY_GROW_LIMIT); 1227 fRuns = (ParagraphLayout::VisualRun **) LE_GROW_ARRAY(fRuns, fRunCapacity); 1228 } 1229 } 1230 1231 fRuns[fRunCount++] = new ParagraphLayout::VisualRun(font, direction, glyphCount, glyphs, positions, glyphToCharMap); 1232 } 1233 1234 void ParagraphLayout::Line::computeMetrics() 1235 { 1236 le_int32 maxDL = 0; 1237 1238 for (le_int32 i = 0; i < fRunCount; i += 1) { 1239 le_int32 ascent = fRuns[i]->getAscent(); 1240 le_int32 descent = fRuns[i]->getDescent(); 1241 le_int32 leading = fRuns[i]->getLeading(); 1242 le_int32 dl = descent + leading; 1243 1244 if (ascent > fAscent) { 1245 fAscent = ascent; 1246 } 1247 1248 if (descent > fDescent) { 1249 fDescent = descent; 1250 } 1251 1252 if (leading > fLeading) { 1253 fLeading = leading; 1254 } 1255 1256 if (dl > maxDL) { 1257 maxDL = dl; 1258 } 1259 } 1260 1261 fLeading = maxDL - fDescent; 1262 } 1263 1264 const char ParagraphLayout::VisualRun::fgClassID = 0; 1265 1266 ParagraphLayout::VisualRun::~VisualRun() 1267 { 1268 LE_DELETE_ARRAY(fGlyphToCharMap); 1269 LE_DELETE_ARRAY(fPositions); 1270 LE_DELETE_ARRAY(fGlyphs); 1271 } 1272 1273 U_NAMESPACE_END 1274 1275 #endif 1276 1277