]> git.saurik.com Git - apple/icu.git/blob - icuSources/layout/OpenTypeLayoutEngine.cpp
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / layout / OpenTypeLayoutEngine.cpp
1
2 /*
3 *
4 * (C) Copyright IBM Corp. 1998-2008 - 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 U_NAMESPACE_BEGIN
28
29 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine)
30
31 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG
32 #define ligaFeatureTag LE_LIGA_FEATURE_TAG
33 #define cligFeatureTag LE_CLIG_FEATURE_TAG
34 #define kernFeatureTag LE_KERN_FEATURE_TAG
35 #define markFeatureTag LE_MARK_FEATURE_TAG
36 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG
37 #define loclFeatureTag LE_LOCL_FEATURE_TAG
38
39 // 'dlig' not used at the moment
40 #define dligFeatureTag 0x646C6967
41
42 // 'palt'
43 #define paltFeatureTag 0x70616C74
44
45 #define ccmpFeatureMask 0x80000000UL
46 #define ligaFeatureMask 0x40000000UL
47 #define cligFeatureMask 0x20000000UL
48 #define kernFeatureMask 0x10000000UL
49 #define paltFeatureMask 0x08000000UL
50 #define markFeatureMask 0x04000000UL
51 #define mkmkFeatureMask 0x02000000UL
52 #define loclFeatureMask 0x01000000UL
53
54 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask)
55 #define ligaFeatures (ligaFeatureMask | cligFeatureMask | minimalFeatures)
56 #define kernFeatures (kernFeatureMask | paltFeatureMask | minimalFeatures)
57 #define kernAndLigaFeatures (ligaFeatures | kernFeatures)
58
59 static const FeatureMap featureMap[] =
60 {
61 {ccmpFeatureTag, ccmpFeatureMask},
62 {ligaFeatureTag, ligaFeatureMask},
63 {cligFeatureTag, cligFeatureMask},
64 {kernFeatureTag, kernFeatureMask},
65 {paltFeatureTag, paltFeatureMask},
66 {markFeatureTag, markFeatureMask},
67 {mkmkFeatureTag, mkmkFeatureMask},
68 {loclFeatureTag, loclFeatureMask}
69 };
70
71 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
72
73 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
74 le_int32 typoFlags, const GlyphSubstitutionTableHeader *gsubTable)
75 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags), fFeatureMask(minimalFeatures),
76 fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
77 fGSUBTable(gsubTable), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
78 {
79 static const le_uint32 gdefTableTag = LE_GDEF_TABLE_TAG;
80 static const le_uint32 gposTableTag = LE_GPOS_TABLE_TAG;
81 const GlyphPositioningTableHeader *gposTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag);
82
83 // todo: switch to more flags and bitfield rather than list of feature tags?
84 switch (typoFlags & ~0x80000000L) {
85 case 0: break; // default
86 case 1: fFeatureMask = kernFeatures; break;
87 case 2: fFeatureMask = ligaFeatures; break;
88 case 3: fFeatureMask = kernAndLigaFeatures; break;
89 default: break;
90 }
91
92 if (typoFlags & 0x80000000L) {
93 fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
94 }
95
96 setScriptAndLanguageTags();
97
98 fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
99
100 if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
101 fGPOSTable = gposTable;
102 }
103 }
104
105 void OpenTypeLayoutEngine::reset()
106 {
107 // NOTE: if we're called from
108 // the destructor, LayoutEngine;:reset()
109 // will have been called already by
110 // LayoutEngine::~LayoutEngine()
111 LayoutEngine::reset();
112 }
113
114 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
115 le_int32 typoFlags)
116 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags), fFeatureOrder(FALSE),
117 fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
118 {
119 setScriptAndLanguageTags();
120 }
121
122 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
123 {
124 if (fTypoFlags & 0x80000000L) {
125 delete fSubstitutionFilter;
126 }
127
128 reset();
129 }
130
131 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
132 {
133 if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
134 return 0xFFFFFFFF;
135 }
136
137 return scriptTags[scriptCode];
138 }
139
140 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
141 {
142 if (languageCode < 0 || languageCode >= languageCodeCount) {
143 return 0xFFFFFFFF;
144 }
145
146 return languageTags[languageCode];
147 }
148
149 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
150 {
151 fScriptTag = getScriptTag(fScriptCode);
152 fLangSysTag = getLangSysTag(fLanguageCode);
153 }
154
155 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
156 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
157 {
158 if (LE_FAILURE(success)) {
159 return 0;
160 }
161
162 if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
163 success = LE_ILLEGAL_ARGUMENT_ERROR;
164 return 0;
165 }
166
167 // This is the cheapest way to get mark reordering only for Hebrew.
168 // We could just do the mark reordering for all scripts, but most
169 // of them probably don't need it... Another option would be to
170 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
171 // would need to do is mark reordering, so that seems like overkill.
172 if (fScriptCode == hebrScriptCode) {
173 outChars = LE_NEW_ARRAY(LEUnicode, count);
174
175 if (outChars == NULL) {
176 success = LE_MEMORY_ALLOCATION_ERROR;
177 return 0;
178 }
179
180 CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
181 }
182
183 glyphStorage.allocateGlyphArray(count, rightToLeft, success);
184 glyphStorage.allocateAuxData(success);
185
186 for (le_int32 i = 0; i < count; i += 1) {
187 glyphStorage.setAuxData(i, fFeatureMask, success);
188 }
189
190 return count;
191 }
192
193 // Input: characters, tags
194 // Output: glyphs, char indices
195 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
196 LEGlyphStorage &glyphStorage, LEErrorCode &success)
197 {
198 if (LE_FAILURE(success)) {
199 return 0;
200 }
201
202 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
203 success = LE_ILLEGAL_ARGUMENT_ERROR;
204 return 0;
205 }
206
207 mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
208
209 if (LE_FAILURE(success)) {
210 return 0;
211 }
212
213 if (fGSUBTable != NULL) {
214 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
215 fFeatureMap, fFeatureMapCount, fFeatureOrder);
216 }
217
218 return count;
219 }
220
221 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
222 {
223 if (LE_FAILURE(success)) {
224 return 0;
225 }
226
227 glyphStorage.adoptGlyphArray(tempGlyphStorage);
228 glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
229 glyphStorage.adoptAuxDataArray(tempGlyphStorage);
230 glyphStorage.adoptGlyphCount(tempGlyphStorage);
231
232 return glyphStorage.getGlyphCount();
233 }
234
235 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
236 {
237 LEUnicode *outChars = NULL;
238 LEGlyphStorage fakeGlyphStorage;
239 le_int32 outCharCount, outGlyphCount, fakeGlyphCount;
240
241 if (LE_FAILURE(success)) {
242 return 0;
243 }
244
245 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
246 success = LE_ILLEGAL_ARGUMENT_ERROR;
247 return 0;
248 }
249
250 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
251
252 if (LE_FAILURE(success)) {
253 return 0;
254 }
255
256 if (outChars != NULL) {
257 fakeGlyphCount = glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
258 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
259 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
260 } else {
261 fakeGlyphCount = glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
262 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
263 }
264
265 if (LE_FAILURE(success)) {
266 return 0;
267 }
268
269 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
270
271 return outGlyphCount;
272 }
273
274 // apply GPOS table, if any
275 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
276 LEGlyphStorage &glyphStorage, LEErrorCode &success)
277 {
278 if (LE_FAILURE(success)) {
279 return;
280 }
281
282 if (chars == NULL || offset < 0 || count < 0) {
283 success = LE_ILLEGAL_ARGUMENT_ERROR;
284 return;
285 }
286
287 le_int32 glyphCount = glyphStorage.getGlyphCount();
288
289 if (glyphCount > 0 && fGPOSTable != NULL) {
290 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
291 le_int32 i;
292
293 if (adjustments == NULL) {
294 success = LE_MEMORY_ALLOCATION_ERROR;
295 return;
296 }
297
298 #if 0
299 // Don't need to do this if we allocate
300 // the adjustments array w/ new...
301 for (i = 0; i < glyphCount; i += 1) {
302 adjustments->setXPlacement(i, 0);
303 adjustments->setYPlacement(i, 0);
304
305 adjustments->setXAdvance(i, 0);
306 adjustments->setYAdvance(i, 0);
307
308 adjustments->setBaseOffset(i, -1);
309 }
310 #endif
311
312 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, fFontInstance,
313 fFeatureMap, fFeatureMapCount, fFeatureOrder);
314
315 float xAdjust = 0, yAdjust = 0;
316
317 for (i = 0; i < glyphCount; i += 1) {
318 float xAdvance = adjustments->getXAdvance(i);
319 float yAdvance = adjustments->getYAdvance(i);
320 float xPlacement = 0;
321 float yPlacement = 0;
322
323
324 #if 0
325 // This is where separate kerning adjustments
326 // should get applied.
327 xAdjust += xKerning;
328 yAdjust += yKerning;
329 #endif
330
331 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
332 xPlacement += adjustments->getXPlacement(base);
333 yPlacement += adjustments->getYPlacement(base);
334 }
335
336 xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
337 yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
338 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
339
340 xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
341 yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
342 }
343
344 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
345
346 delete adjustments;
347 }
348
349 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C);
350
351 if (zwnj != 0x0000) {
352 for (le_int32 g = 0; g < glyphCount; g += 1) {
353 LEGlyphID glyph = glyphStorage[g];
354
355 if (glyph == zwnj) {
356 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
357 }
358 }
359 }
360
361 #if 0
362 // Don't know why this is here...
363 LE_DELETE_ARRAY(fFeatureTags);
364 fFeatureTags = NULL;
365 #endif
366 }
367
368 U_NAMESPACE_END