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