]> git.saurik.com Git - apple/icu.git/blame_incremental - icuSources/layoutex/ParagraphLayout.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / layoutex / ParagraphLayout.cpp
... / ...
CommitLineData
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