]> git.saurik.com Git - apple/icu.git/blob - icuSources/layout/OpenTypeLayoutEngine.cpp
ICU-531.48.tar.gz
[apple/icu.git] / icuSources / layout / OpenTypeLayoutEngine.cpp
1
2 /*
3 *
4 * (C) Copyright IBM Corp. and others 1998-2013 - All Rights Reserved
5 *
6 */
7
8 #include "LETypes.h"
9 #include "LEScripts.h"
10 #include "LELanguages.h"
11
12 #include "LayoutEngine.h"
13 #include "CanonShaping.h"
14 #include "OpenTypeLayoutEngine.h"
15 #include "ScriptAndLanguageTags.h"
16 #include "CharSubstitutionFilter.h"
17
18 #include "GlyphSubstitutionTables.h"
19 #include "GlyphDefinitionTables.h"
20 #include "GlyphPositioningTables.h"
21
22 #include "LEGlyphStorage.h"
23 #include "GlyphPositionAdjustments.h"
24
25 #include "GDEFMarkFilter.h"
26
27 #include "KernTable.h"
28
29 U_NAMESPACE_BEGIN
30
31 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine)
32
33 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG
34 #define ligaFeatureTag LE_LIGA_FEATURE_TAG
35 #define cligFeatureTag LE_CLIG_FEATURE_TAG
36 #define kernFeatureTag LE_KERN_FEATURE_TAG
37 #define markFeatureTag LE_MARK_FEATURE_TAG
38 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG
39 #define loclFeatureTag LE_LOCL_FEATURE_TAG
40 #define caltFeatureTag LE_CALT_FEATURE_TAG
41
42 #define dligFeatureTag LE_DLIG_FEATURE_TAG
43 #define rligFeatureTag LE_RLIG_FEATURE_TAG
44 #define paltFeatureTag LE_PALT_FEATURE_TAG
45
46 #define hligFeatureTag LE_HLIG_FEATURE_TAG
47 #define smcpFeatureTag LE_SMCP_FEATURE_TAG
48 #define fracFeatureTag LE_FRAC_FEATURE_TAG
49 #define afrcFeatureTag LE_AFRC_FEATURE_TAG
50 #define zeroFeatureTag LE_ZERO_FEATURE_TAG
51 #define swshFeatureTag LE_SWSH_FEATURE_TAG
52 #define cswhFeatureTag LE_CSWH_FEATURE_TAG
53 #define saltFeatureTag LE_SALT_FEATURE_TAG
54 #define naltFeatureTag LE_NALT_FEATURE_TAG
55 #define rubyFeatureTag LE_RUBY_FEATURE_TAG
56 #define ss01FeatureTag LE_SS01_FEATURE_TAG
57 #define ss02FeatureTag LE_SS02_FEATURE_TAG
58 #define ss03FeatureTag LE_SS03_FEATURE_TAG
59 #define ss04FeatureTag LE_SS04_FEATURE_TAG
60 #define ss05FeatureTag LE_SS05_FEATURE_TAG
61 #define ss06FeatureTag LE_SS06_FEATURE_TAG
62 #define ss07FeatureTag LE_SS07_FEATURE_TAG
63
64 #define ccmpFeatureMask 0x80000000UL
65 #define ligaFeatureMask 0x40000000UL
66 #define cligFeatureMask 0x20000000UL
67 #define kernFeatureMask 0x10000000UL
68 #define paltFeatureMask 0x08000000UL
69 #define markFeatureMask 0x04000000UL
70 #define mkmkFeatureMask 0x02000000UL
71 #define loclFeatureMask 0x01000000UL
72 #define caltFeatureMask 0x00800000UL
73
74 #define dligFeatureMask 0x00400000UL
75 #define rligFeatureMask 0x00200000UL
76 #define hligFeatureMask 0x00100000UL
77 #define smcpFeatureMask 0x00080000UL
78 #define fracFeatureMask 0x00040000UL
79 #define afrcFeatureMask 0x00020000UL
80 #define zeroFeatureMask 0x00010000UL
81 #define swshFeatureMask 0x00008000UL
82 #define cswhFeatureMask 0x00004000UL
83 #define saltFeatureMask 0x00002000UL
84 #define naltFeatureMask 0x00001000UL
85 #define rubyFeatureMask 0x00000800UL
86 #define ss01FeatureMask 0x00000400UL
87 #define ss02FeatureMask 0x00000200UL
88 #define ss03FeatureMask 0x00000100UL
89 #define ss04FeatureMask 0x00000080UL
90 #define ss05FeatureMask 0x00000040UL
91 #define ss06FeatureMask 0x00000020UL
92 #define ss07FeatureMask 0x00000010UL
93
94 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
95
96 static const FeatureMap featureMap[] =
97 {
98 {ccmpFeatureTag, ccmpFeatureMask},
99 {ligaFeatureTag, ligaFeatureMask},
100 {cligFeatureTag, cligFeatureMask},
101 {kernFeatureTag, kernFeatureMask},
102 {paltFeatureTag, paltFeatureMask},
103 {markFeatureTag, markFeatureMask},
104 {mkmkFeatureTag, mkmkFeatureMask},
105 {loclFeatureTag, loclFeatureMask},
106 {caltFeatureTag, caltFeatureMask},
107 {hligFeatureTag, hligFeatureMask},
108 {smcpFeatureTag, smcpFeatureMask},
109 {fracFeatureTag, fracFeatureMask},
110 {afrcFeatureTag, afrcFeatureMask},
111 {zeroFeatureTag, zeroFeatureMask},
112 {swshFeatureTag, swshFeatureMask},
113 {cswhFeatureTag, cswhFeatureMask},
114 {saltFeatureTag, saltFeatureMask},
115 {naltFeatureTag, naltFeatureMask},
116 {rubyFeatureTag, rubyFeatureMask},
117 {ss01FeatureTag, ss01FeatureMask},
118 {ss02FeatureTag, ss02FeatureMask},
119 {ss03FeatureTag, ss03FeatureMask},
120 {ss04FeatureTag, ss04FeatureMask},
121 {ss05FeatureTag, ss05FeatureMask},
122 {ss06FeatureTag, ss06FeatureMask},
123 {ss07FeatureTag, ss07FeatureMask}
124 };
125
126 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
127
128 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
129 le_int32 typoFlags, const LEReferenceTo<GlyphSubstitutionTableHeader> &gsubTable, LEErrorCode &success)
130 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures),
131 fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
132 fGSUBTable(gsubTable),
133 fGDEFTable(fontInstance, LE_GDEF_TABLE_TAG, success),
134 fGPOSTable(fontInstance, LE_GPOS_TABLE_TAG, success), fSubstitutionFilter(NULL)
135 {
136 applyTypoFlags();
137
138 setScriptAndLanguageTags();
139
140 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
141 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
142 if (!fGPOSTable.isEmpty()&& !fGPOSTable->coversScript(fGPOSTable, fScriptTag, success)) {
143 fGPOSTable.clear(); // already loaded
144 }
145 }
146
147 void OpenTypeLayoutEngine::applyTypoFlags() {
148 const le_int32& typoFlags = fTypoFlags;
149 const LEFontInstance *fontInstance = fFontInstance;
150
151 switch (typoFlags & (LE_SS01_FEATURE_FLAG
152 | LE_SS02_FEATURE_FLAG
153 | LE_SS03_FEATURE_FLAG
154 | LE_SS04_FEATURE_FLAG
155 | LE_SS05_FEATURE_FLAG
156 | LE_SS06_FEATURE_FLAG
157 | LE_SS07_FEATURE_FLAG)) {
158 case LE_SS01_FEATURE_FLAG:
159 fFeatureMask |= ss01FeatureMask;
160 break;
161 case LE_SS02_FEATURE_FLAG:
162 fFeatureMask |= ss02FeatureMask;
163 break;
164 case LE_SS03_FEATURE_FLAG:
165 fFeatureMask |= ss03FeatureMask;
166 break;
167 case LE_SS04_FEATURE_FLAG:
168 fFeatureMask |= ss04FeatureMask;
169 break;
170 case LE_SS05_FEATURE_FLAG:
171 fFeatureMask |= ss05FeatureMask;
172 break;
173 case LE_SS06_FEATURE_FLAG:
174 fFeatureMask |= ss06FeatureMask;
175 break;
176 case LE_SS07_FEATURE_FLAG:
177 fFeatureMask |= ss07FeatureMask;
178 break;
179 }
180
181 if (typoFlags & LE_Kerning_FEATURE_FLAG) {
182 fFeatureMask |= (kernFeatureMask | paltFeatureMask);
183 // Convenience.
184 }
185 if (typoFlags & LE_Ligatures_FEATURE_FLAG) {
186 fFeatureMask |= (ligaFeatureMask | cligFeatureMask);
187 // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
188 }
189 if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask;
190 if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask;
191 if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask;
192 if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask;
193 if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask;
194 if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask;
195 if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask;
196 if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask;
197 if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask;
198 if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask;
199 if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask;
200 if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask;
201 if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask;
202 if (typoFlags & LE_NALT_FEATURE_FLAG) {
203 // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
204 fFeatureMask = naltFeatureMask;
205 }
206
207 if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
208 // This isn't a font feature, but requests a Char Substitution Filter
209 fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
210 }
211
212 }
213
214 void OpenTypeLayoutEngine::reset()
215 {
216 // NOTE: if we're called from
217 // the destructor, LayoutEngine;:reset()
218 // will have been called already by
219 // LayoutEngine::~LayoutEngine()
220 LayoutEngine::reset();
221 }
222
223 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
224 le_int32 typoFlags, LEErrorCode &success)
225 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
226 fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL)
227 {
228 applyTypoFlags();
229 setScriptAndLanguageTags();
230 }
231
232 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
233 {
234 if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
235 delete fSubstitutionFilter;
236 fSubstitutionFilter = NULL;
237 }
238
239 reset();
240 }
241
242 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
243 {
244 if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
245 return 0xFFFFFFFF;
246 }
247 return scriptTags[scriptCode];
248 }
249
250 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
251 {
252 switch (scriptCode) {
253 case bengScriptCode : return bng2ScriptTag;
254 case devaScriptCode : return dev2ScriptTag;
255 case gujrScriptCode : return gjr2ScriptTag;
256 case guruScriptCode : return gur2ScriptTag;
257 case kndaScriptCode : return knd2ScriptTag;
258 case mlymScriptCode : return mlm2ScriptTag;
259 case oryaScriptCode : return ory2ScriptTag;
260 case tamlScriptCode : return tml2ScriptTag;
261 case teluScriptCode : return tel2ScriptTag;
262 default: return nullScriptTag;
263 }
264 }
265
266 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
267 {
268 if (languageCode < 0 || languageCode >= languageCodeCount) {
269 return 0xFFFFFFFF;
270 }
271
272 return languageTags[languageCode];
273 }
274
275 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
276 {
277 fScriptTag = getScriptTag(fScriptCode);
278 fScriptTagV2 = getV2ScriptTag(fScriptCode);
279 fLangSysTag = getLangSysTag(fLanguageCode);
280 }
281
282 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
283 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
284 {
285 if (LE_FAILURE(success)) {
286 return 0;
287 }
288
289 if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
290 success = LE_ILLEGAL_ARGUMENT_ERROR;
291 return 0;
292 }
293
294 // This is the cheapest way to get mark reordering only for Hebrew.
295 // We could just do the mark reordering for all scripts, but most
296 // of them probably don't need it... Another option would be to
297 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
298 // would need to do is mark reordering, so that seems like overkill.
299 if (fScriptCode == hebrScriptCode) {
300 outChars = LE_NEW_ARRAY(LEUnicode, count);
301
302 if (outChars == NULL) {
303 success = LE_MEMORY_ALLOCATION_ERROR;
304 return 0;
305 }
306
307 if (LE_FAILURE(success)) {
308 LE_DELETE_ARRAY(outChars);
309 return 0;
310 }
311
312 CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
313 }
314
315 if (LE_FAILURE(success)) {
316 return 0;
317 }
318
319 glyphStorage.allocateGlyphArray(count, rightToLeft, success);
320 glyphStorage.allocateAuxData(success);
321
322 for (le_int32 i = 0; i < count; i += 1) {
323 glyphStorage.setAuxData(i, fFeatureMask, success);
324 }
325
326 return count;
327 }
328
329 // Input: characters, tags
330 // Output: glyphs, char indices
331 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
332 LEGlyphStorage &glyphStorage, LEErrorCode &success)
333 {
334 if (LE_FAILURE(success)) {
335 return 0;
336 }
337
338 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
339 success = LE_ILLEGAL_ARGUMENT_ERROR;
340 return 0;
341 }
342
343 mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
344
345 if (LE_FAILURE(success)) {
346 return 0;
347 }
348
349 if (fGSUBTable.isValid()) {
350 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable, fScriptTagV2, fLangSysTag, success)) {
351 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
352 fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
353
354 } else {
355 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
356 fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
357 }
358 }
359
360 return count;
361 }
362 // Input: characters, tags
363 // Output: glyphs, char indices
364 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
365 LEGlyphStorage &glyphStorage, LEErrorCode &success)
366 {
367 if (LE_FAILURE(success)) {
368 return 0;
369 }
370
371 if ( count < 0 || max < 0 ) {
372 success = LE_ILLEGAL_ARGUMENT_ERROR;
373 return 0;
374 }
375
376 if (fGSUBTable.isValid()) {
377 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable,fScriptTagV2,fLangSysTag,success)) {
378 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
379 fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
380
381 } else {
382 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
383 fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
384 }
385 }
386
387 return count;
388 }
389 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
390 {
391 if (LE_FAILURE(success)) {
392 return 0;
393 }
394
395 glyphStorage.adoptGlyphArray(tempGlyphStorage);
396 glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
397 glyphStorage.adoptAuxDataArray(tempGlyphStorage);
398 glyphStorage.adoptGlyphCount(tempGlyphStorage);
399
400 return glyphStorage.getGlyphCount();
401 }
402
403 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
404 {
405 LEUnicode *outChars = NULL;
406 LEGlyphStorage fakeGlyphStorage;
407 le_int32 outCharCount, outGlyphCount;
408
409 if (LE_FAILURE(success)) {
410 return 0;
411 }
412
413 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
414 success = LE_ILLEGAL_ARGUMENT_ERROR;
415 return 0;
416 }
417
418 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
419
420 if (LE_FAILURE(success)) {
421 return 0;
422 }
423
424 if (outChars != NULL) {
425 // le_int32 fakeGlyphCount =
426 glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
427 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
428 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
429 } else {
430 // le_int32 fakeGlyphCount =
431 glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
432 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
433 }
434
435 if (LE_FAILURE(success)) {
436 return 0;
437 }
438
439 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
440
441 return outGlyphCount;
442 }
443
444 // apply GPOS table, if any
445 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
446 LEGlyphStorage &glyphStorage, LEErrorCode &success)
447 {
448 if (LE_FAILURE(success)) {
449 return;
450 }
451
452 if (chars == NULL || offset < 0 || count < 0) {
453 success = LE_ILLEGAL_ARGUMENT_ERROR;
454 return;
455 }
456
457 le_int32 glyphCount = glyphStorage.getGlyphCount();
458 if (glyphCount == 0) {
459 return;
460 }
461
462 if (!fGPOSTable.isEmpty()) {
463 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
464 le_int32 i;
465
466 if (adjustments == NULL) {
467 success = LE_MEMORY_ALLOCATION_ERROR;
468 return;
469 }
470
471 #if 0
472 // Don't need to do this if we allocate
473 // the adjustments array w/ new...
474 for (i = 0; i < glyphCount; i += 1) {
475 adjustments->setXPlacement(i, 0);
476 adjustments->setYPlacement(i, 0);
477
478 adjustments->setXAdvance(i, 0);
479 adjustments->setYAdvance(i, 0);
480
481 adjustments->setBaseOffset(i, -1);
482 }
483 #endif
484
485 if (!fGPOSTable.isEmpty()) {
486 if (fScriptTagV2 != nullScriptTag &&
487 fGPOSTable->coversScriptAndLanguage(fGPOSTable, fScriptTagV2,fLangSysTag,success)) {
488 fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag,
489 fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
490
491 } else {
492 fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag,
493 fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
494 }
495 } else if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
496 LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success);
497 KernTable kt(kernTable, success);
498 kt.process(glyphStorage, success);
499 }
500
501 float xAdjust = 0, yAdjust = 0;
502
503 for (i = 0; i < glyphCount; i += 1) {
504 float xAdvance = adjustments->getXAdvance(i);
505 float yAdvance = adjustments->getYAdvance(i);
506 float xPlacement = 0;
507 float yPlacement = 0;
508
509
510 #if 0
511 // This is where separate kerning adjustments
512 // should get applied.
513 xAdjust += xKerning;
514 yAdjust += yKerning;
515 #endif
516
517 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
518 xPlacement += adjustments->getXPlacement(base);
519 yPlacement += adjustments->getYPlacement(base);
520 }
521
522 xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
523 yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
524 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
525
526 xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
527 yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
528 }
529
530 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
531
532 delete adjustments;
533 } else {
534 // if there was no GPOS table, maybe there's non-OpenType kerning we can use
535 LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);
536 }
537
538 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C);
539
540 if (zwnj != 0x0000) {
541 for (le_int32 g = 0; g < glyphCount; g += 1) {
542 LEGlyphID glyph = glyphStorage[g];
543
544 if (glyph == zwnj) {
545 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
546 }
547 }
548 }
549
550 #if 0
551 // Don't know why this is here...
552 LE_DELETE_ARRAY(fFeatureTags);
553 fFeatureTags = NULL;
554 #endif
555 }
556
557 U_NAMESPACE_END