1
2 /*
3 *
4 * (C) Copyright IBM Corp. 1998-2012 - 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;
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 // le_int32 fakeGlyphCount =
322 glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
323 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
324 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
325 } else {
326 // le_int32 fakeGlyphCount =
327 glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
328 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
329 }
330
331 if (LE_FAILURE(success)) {
332 return 0;
333 }
334
335 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
336
337 return outGlyphCount;
338 }
339
340 // apply GPOS table, if any
adjustGlyphPositions(const LEUnicode chars[],le_int32 offset,le_int32 count,le_bool reverse,LEGlyphStorage & glyphStorage,LEErrorCode & success)341 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
342 LEGlyphStorage &glyphStorage, LEErrorCode &success)
343 {
344 if (LE_FAILURE(success)) {
345 return;
346 }
347
348 if (chars == NULL || offset < 0 || count < 0) {
349 success = LE_ILLEGAL_ARGUMENT_ERROR;
350 return;
351 }
352
353 le_int32 glyphCount = glyphStorage.getGlyphCount();
354 if (glyphCount == 0) {
355 return;
356 }
357
358 if (fGPOSTable != NULL) {
359 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
360 le_int32 i;
361
362 if (adjustments == NULL) {
363 success = LE_MEMORY_ALLOCATION_ERROR;
364 return;
365 }
366
367 #if 0
368 // Don't need to do this if we allocate
369 // the adjustments array w/ new...
370 for (i = 0; i < glyphCount; i += 1) {
371 adjustments->setXPlacement(i, 0);
372 adjustments->setYPlacement(i, 0);
373
374 adjustments->setXAdvance(i, 0);
375 adjustments->setYAdvance(i, 0);
376
377 adjustments->setBaseOffset(i, -1);
378 }
379 #endif
380
381 if (fGPOSTable != NULL) {
382 if (fScriptTagV2 != nullScriptTag && fGPOSTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) {
383 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, fGDEFTable, success, fFontInstance,
384 fFeatureMap, fFeatureMapCount, fFeatureOrder);
385
386 } else {
387 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance,
388 fFeatureMap, fFeatureMapCount, fFeatureOrder);
389 }
390 } else if ( fTypoFlags & 0x1 ) {
391 static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG;
392 KernTable kt(fFontInstance, getFontTable(kernTableTag));
393 kt.process(glyphStorage);
394 }
395
396 float xAdjust = 0, yAdjust = 0;
397
398 for (i = 0; i < glyphCount; i += 1) {
399 float xAdvance = adjustments->getXAdvance(i);
400 float yAdvance = adjustments->getYAdvance(i);
401 float xPlacement = 0;
402 float yPlacement = 0;
403
404
405 #if 0
406 // This is where separate kerning adjustments
407 // should get applied.
408 xAdjust += xKerning;
409 yAdjust += yKerning;
410 #endif
411
412 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
413 xPlacement += adjustments->getXPlacement(base);
414 yPlacement += adjustments->getYPlacement(base);
415 }
416
417 xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
418 yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
419 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
420
421 xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
422 yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
423 }
424
425 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
426
427 delete adjustments;
428 } else {
429 // if there was no GPOS table, maybe there's non-OpenType kerning we can use
430 LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);
431 }
432
433 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C);
434
435 if (zwnj != 0x0000) {
436 for (le_int32 g = 0; g < glyphCount; g += 1) {
437 LEGlyphID glyph = glyphStorage[g];
438
439 if (glyph == zwnj) {
440 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
441 }
442 }
443 }
444
445 #if 0
446 // Don't know why this is here...
447 LE_DELETE_ARRAY(fFeatureTags);
448 fFeatureTags = NULL;
449 #endif
450 }
451
452 U_NAMESPACE_END
453