• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  *
4  * (C) Copyright IBM Corp. 1998-2010 - 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 // 'dlig' not used at the moment
43 #define dligFeatureTag 0x646C6967
44 
45 // 'palt'
46 #define paltFeatureTag 0x70616C74
47 
48 #define ccmpFeatureMask 0x80000000UL
49 #define ligaFeatureMask 0x40000000UL
50 #define cligFeatureMask 0x20000000UL
51 #define kernFeatureMask 0x10000000UL
52 #define paltFeatureMask 0x08000000UL
53 #define markFeatureMask 0x04000000UL
54 #define mkmkFeatureMask 0x02000000UL
55 #define loclFeatureMask 0x01000000UL
56 #define caltFeatureMask 0x00800000UL
57 
58 #define minimalFeatures     (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
59 #define ligaFeatures        (ligaFeatureMask | cligFeatureMask | minimalFeatures)
60 #define kernFeatures        (kernFeatureMask | paltFeatureMask | minimalFeatures)
61 #define kernAndLigaFeatures (ligaFeatures    | kernFeatures)
62 
63 static const FeatureMap featureMap[] =
64 {
65     {ccmpFeatureTag, ccmpFeatureMask},
66     {ligaFeatureTag, ligaFeatureMask},
67     {cligFeatureTag, cligFeatureMask},
68     {kernFeatureTag, kernFeatureMask},
69     {paltFeatureTag, paltFeatureMask},
70     {markFeatureTag, markFeatureMask},
71     {mkmkFeatureTag, mkmkFeatureMask},
72     {loclFeatureTag, loclFeatureMask},
73     {caltFeatureTag, caltFeatureMask}
74 };
75 
76 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
77 
OpenTypeLayoutEngine(const LEFontInstance * fontInstance,le_int32 scriptCode,le_int32 languageCode,le_int32 typoFlags,const GlyphSubstitutionTableHeader * gsubTable,LEErrorCode & success)78 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
79                         le_int32 typoFlags, const GlyphSubstitutionTableHeader *gsubTable, LEErrorCode &success)
80     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures),
81       fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
82       fGSUBTable(gsubTable), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
83 {
84     static const le_uint32 gdefTableTag = LE_GDEF_TABLE_TAG;
85     static const le_uint32 gposTableTag = LE_GPOS_TABLE_TAG;
86     const GlyphPositioningTableHeader *gposTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag);
87 
88     // todo: switch to more flags and bitfield rather than list of feature tags?
89     switch (typoFlags & ~0x80000000L) {
90     case 0: break; // default
91     case 1: fFeatureMask = kernFeatures; break;
92     case 2: fFeatureMask = ligaFeatures; break;
93     case 3: fFeatureMask = kernAndLigaFeatures; break;
94     default: break;
95     }
96 
97     if (typoFlags & 0x80000000L) {
98         fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
99     }
100 
101     setScriptAndLanguageTags();
102 
103     fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
104 
105 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
106 //    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
107     if (gposTable != NULL && gposTable->coversScript(fScriptTag)) {
108         fGPOSTable = gposTable;
109     }
110 }
111 
reset()112 void OpenTypeLayoutEngine::reset()
113 {
114     // NOTE: if we're called from
115     // the destructor, LayoutEngine;:reset()
116     // will have been called already by
117     // LayoutEngine::~LayoutEngine()
118     LayoutEngine::reset();
119 }
120 
OpenTypeLayoutEngine(const LEFontInstance * fontInstance,le_int32 scriptCode,le_int32 languageCode,le_int32 typoFlags,LEErrorCode & success)121 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
122                        le_int32 typoFlags, LEErrorCode &success)
123     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
124       fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
125 {
126     setScriptAndLanguageTags();
127 }
128 
~OpenTypeLayoutEngine()129 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
130 {
131     if (fTypoFlags & 0x80000000L) {
132         delete fSubstitutionFilter;
133     }
134 
135     reset();
136 }
137 
getScriptTag(le_int32 scriptCode)138 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
139 {
140     if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
141         return 0xFFFFFFFF;
142     }
143     return scriptTags[scriptCode];
144 }
145 
getV2ScriptTag(le_int32 scriptCode)146 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
147 {
148 	switch (scriptCode) {
149 		case bengScriptCode :    return bng2ScriptTag;
150 		case devaScriptCode :    return dev2ScriptTag;
151 		case gujrScriptCode :    return gjr2ScriptTag;
152 		case guruScriptCode :    return gur2ScriptTag;
153 		case kndaScriptCode :    return knd2ScriptTag;
154 		case mlymScriptCode :    return mlm2ScriptTag;
155 		case oryaScriptCode :    return ory2ScriptTag;
156 		case tamlScriptCode :    return tml2ScriptTag;
157 		case teluScriptCode :    return tel2ScriptTag;
158 		default:                 return nullScriptTag;
159 	}
160 }
161 
getLangSysTag(le_int32 languageCode)162 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
163 {
164     if (languageCode < 0 || languageCode >= languageCodeCount) {
165         return 0xFFFFFFFF;
166     }
167 
168     return languageTags[languageCode];
169 }
170 
setScriptAndLanguageTags()171 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
172 {
173     fScriptTag  = getScriptTag(fScriptCode);
174     fScriptTagV2 = getV2ScriptTag(fScriptCode);
175     fLangSysTag = getLangSysTag(fLanguageCode);
176 }
177 
characterProcessing(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEUnicode * & outChars,LEGlyphStorage & glyphStorage,LEErrorCode & success)178 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
179                 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
180 {
181     if (LE_FAILURE(success)) {
182         return 0;
183     }
184 
185     if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
186         success = LE_ILLEGAL_ARGUMENT_ERROR;
187         return 0;
188     }
189 
190     // This is the cheapest way to get mark reordering only for Hebrew.
191     // We could just do the mark reordering for all scripts, but most
192     // of them probably don't need it... Another option would be to
193     // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
194     // would need to do is mark reordering, so that seems like overkill.
195     if (fScriptCode == hebrScriptCode) {
196         outChars = LE_NEW_ARRAY(LEUnicode, count);
197 
198         if (outChars == NULL) {
199             success = LE_MEMORY_ALLOCATION_ERROR;
200             return 0;
201         }
202 
203         if (LE_FAILURE(success)) {
204             LE_DELETE_ARRAY(outChars);
205             return 0;
206         }
207 
208         CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
209     }
210 
211     if (LE_FAILURE(success)) {
212         return 0;
213     }
214 
215     glyphStorage.allocateGlyphArray(count, rightToLeft, success);
216     glyphStorage.allocateAuxData(success);
217 
218     for (le_int32 i = 0; i < count; i += 1) {
219         glyphStorage.setAuxData(i, fFeatureMask, success);
220     }
221 
222     return count;
223 }
224 
225 // Input: characters, tags
226 // Output: glyphs, char indices
glyphProcessing(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)227 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
228                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
229 {
230     if (LE_FAILURE(success)) {
231         return 0;
232     }
233 
234     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
235         success = LE_ILLEGAL_ARGUMENT_ERROR;
236         return 0;
237     }
238 
239     mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
240 
241     if (LE_FAILURE(success)) {
242         return 0;
243     }
244 
245     if (fGSUBTable != NULL) {
246         if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) {
247             count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
248                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
249 
250         } else {
251         count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
252                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
253         }
254     }
255 
256     return count;
257 }
258 // Input: characters, tags
259 // Output: glyphs, char indices
glyphSubstitution(le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)260 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
261                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
262 {
263     if (LE_FAILURE(success)) {
264         return 0;
265     }
266 
267     if ( count < 0 || max < 0 ) {
268         success = LE_ILLEGAL_ARGUMENT_ERROR;
269         return 0;
270     }
271 
272     if (fGSUBTable != NULL) {
273         if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) {
274             count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
275                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
276 
277         } else {
278         count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
279                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
280         }
281     }
282 
283     return count;
284 }
glyphPostProcessing(LEGlyphStorage & tempGlyphStorage,LEGlyphStorage & glyphStorage,LEErrorCode & success)285 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
286 {
287     if (LE_FAILURE(success)) {
288         return 0;
289     }
290 
291     glyphStorage.adoptGlyphArray(tempGlyphStorage);
292     glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
293     glyphStorage.adoptAuxDataArray(tempGlyphStorage);
294     glyphStorage.adoptGlyphCount(tempGlyphStorage);
295 
296     return glyphStorage.getGlyphCount();
297 }
298 
computeGlyphs(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)299 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
300 {
301     LEUnicode *outChars = NULL;
302     LEGlyphStorage fakeGlyphStorage;
303     le_int32 outCharCount, outGlyphCount, fakeGlyphCount;
304 
305     if (LE_FAILURE(success)) {
306         return 0;
307     }
308 
309     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
310         success = LE_ILLEGAL_ARGUMENT_ERROR;
311         return 0;
312     }
313 
314     outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
315 
316     if (LE_FAILURE(success)) {
317         return 0;
318     }
319 
320     if (outChars != NULL) {
321         fakeGlyphCount = glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
322         LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
323         //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
324     } else {
325         fakeGlyphCount = glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
326         //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
327     }
328 
329     if (LE_FAILURE(success)) {
330         return 0;
331     }
332 
333     outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
334 
335     return outGlyphCount;
336 }
337 
338 // apply GPOS table, if any
adjustGlyphPositions(const LEUnicode chars[],le_int32 offset,le_int32 count,le_bool reverse,LEGlyphStorage & glyphStorage,LEErrorCode & success)339 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
340                                                 LEGlyphStorage &glyphStorage, LEErrorCode &success)
341 {
342     if (LE_FAILURE(success)) {
343         return;
344     }
345 
346     if (chars == NULL || offset < 0 || count < 0) {
347         success = LE_ILLEGAL_ARGUMENT_ERROR;
348         return;
349     }
350 
351     le_int32 glyphCount = glyphStorage.getGlyphCount();
352     if (glyphCount == 0) {
353         return;
354     }
355 
356     if (fGPOSTable != NULL) {
357         GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
358         le_int32 i;
359 
360         if (adjustments == NULL) {
361             success = LE_MEMORY_ALLOCATION_ERROR;
362             return;
363         }
364 
365 #if 0
366         // Don't need to do this if we allocate
367         // the adjustments array w/ new...
368         for (i = 0; i < glyphCount; i += 1) {
369             adjustments->setXPlacement(i, 0);
370             adjustments->setYPlacement(i, 0);
371 
372             adjustments->setXAdvance(i, 0);
373             adjustments->setYAdvance(i, 0);
374 
375             adjustments->setBaseOffset(i, -1);
376         }
377 #endif
378 
379         if (fGPOSTable != NULL) {
380             if (fScriptTagV2 != nullScriptTag && fGPOSTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) {
381                 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, fGDEFTable, success, fFontInstance,
382                                 fFeatureMap, fFeatureMapCount, fFeatureOrder);
383 
384             } else {
385                 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance,
386                                 fFeatureMap, fFeatureMapCount, fFeatureOrder);
387             }
388         } else if ( fTypoFlags & 0x1 ) {
389             static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG;
390             KernTable kt(fFontInstance, getFontTable(kernTableTag));
391             kt.process(glyphStorage);
392         }
393 
394         float xAdjust = 0, yAdjust = 0;
395 
396         for (i = 0; i < glyphCount; i += 1) {
397             float xAdvance   = adjustments->getXAdvance(i);
398             float yAdvance   = adjustments->getYAdvance(i);
399             float xPlacement = 0;
400             float yPlacement = 0;
401 
402 
403 #if 0
404             // This is where separate kerning adjustments
405             // should get applied.
406             xAdjust += xKerning;
407             yAdjust += yKerning;
408 #endif
409 
410             for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
411                 xPlacement += adjustments->getXPlacement(base);
412                 yPlacement += adjustments->getYPlacement(base);
413             }
414 
415             xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
416             yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
417             glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
418 
419             xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
420             yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
421         }
422 
423         glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
424 
425         delete adjustments;
426     } else {
427         // if there was no GPOS table, maybe there's non-OpenType kerning we can use
428         LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);
429     }
430 
431     LEGlyphID zwnj  = fFontInstance->mapCharToGlyph(0x200C);
432 
433     if (zwnj != 0x0000) {
434         for (le_int32 g = 0; g < glyphCount; g += 1) {
435             LEGlyphID glyph = glyphStorage[g];
436 
437             if (glyph == zwnj) {
438                 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
439             }
440         }
441     }
442 
443 #if 0
444     // Don't know why this is here...
445     LE_DELETE_ARRAY(fFeatureTags);
446     fFeatureTags = NULL;
447 #endif
448 }
449 
450 U_NAMESPACE_END
451