]> git.saurik.com Git - apple/icu.git/blame - icuSources/layout/OpenTypeLayoutEngine.cpp
ICU-8.11.tar.gz
[apple/icu.git] / icuSources / layout / OpenTypeLayoutEngine.cpp
CommitLineData
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
26U_NAMESPACE_BEGIN
27
374ca955
A
28UOBJECT_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
56static 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 67static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
b75a7d8f
A
68
69OpenTypeLayoutEngine::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
101void 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
110OpenTypeLayoutEngine::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
118OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
119{
73c04bcf
A
120 if (fTypoFlags & 0x80000000L) {
121 delete fSubstitutionFilter;
122 }
123
b75a7d8f
A
124 reset();
125}
126
127LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
128{
129 if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
130 return 0xFFFFFFFF;
131 }
132
133 return scriptTags[scriptCode];
134}
135
136LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
137{
138 if (languageCode < 0 || languageCode >= languageCodeCount) {
139 return 0xFFFFFFFF;
140 }
141
142 return languageTags[languageCode];
143}
144
145void OpenTypeLayoutEngine::setScriptAndLanguageTags()
146{
147 fScriptTag = getScriptTag(fScriptCode);
148 fLangSysTag = getLangSysTag(fLanguageCode);
149}
150
374ca955
A
151le_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
181le_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
207le_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
221le_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
261void 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
342U_NAMESPACE_END