]> git.saurik.com Git - apple/icu.git/blob - icuSources/layout/OpenTypeLayoutEngine.cpp
ICU-461.12.tar.gz
[apple/icu.git] / icuSources / layout / OpenTypeLayoutEngine.cpp
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
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
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
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
129 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
130 {
131 if (fTypoFlags & 0x80000000L) {
132 delete fSubstitutionFilter;
133 }
134
135 reset();
136 }
137
138 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
139 {
140 if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
141 return 0xFFFFFFFF;
142 }
143 return scriptTags[scriptCode];
144 }
145
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
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
171 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
172 {
173 fScriptTag = getScriptTag(fScriptCode);
174 fScriptTagV2 = getV2ScriptTag(fScriptCode);
175 fLangSysTag = getLangSysTag(fLanguageCode);
176 }
177
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
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
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 }
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
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
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