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