]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | |
2 | /* | |
b75a7d8f | 3 | * |
729e4ab9 | 4 | * (C) Copyright IBM Corp. 1998-2009 - 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 "ArabicLayoutEngine.h" | |
374ca955 | 14 | #include "CanonShaping.h" |
b75a7d8f | 15 | #include "HanLayoutEngine.h" |
73c04bcf | 16 | #include "HangulLayoutEngine.h" |
b75a7d8f | 17 | #include "IndicLayoutEngine.h" |
73c04bcf | 18 | #include "KhmerLayoutEngine.h" |
b75a7d8f | 19 | #include "ThaiLayoutEngine.h" |
73c04bcf | 20 | #include "TibetanLayoutEngine.h" |
b75a7d8f A |
21 | #include "GXLayoutEngine.h" |
22 | #include "ScriptAndLanguageTags.h" | |
374ca955 A |
23 | #include "CharSubstitutionFilter.h" |
24 | ||
25 | #include "LEGlyphStorage.h" | |
b75a7d8f A |
26 | |
27 | #include "OpenTypeUtilities.h" | |
28 | #include "GlyphSubstitutionTables.h" | |
46f4442e | 29 | #include "GlyphDefinitionTables.h" |
b75a7d8f A |
30 | #include "MorphTables.h" |
31 | ||
32 | #include "DefaultCharMapper.h" | |
33 | ||
73c04bcf | 34 | #include "KernTable.h" |
b75a7d8f | 35 | |
73c04bcf | 36 | U_NAMESPACE_BEGIN |
b75a7d8f | 37 | |
46f4442e A |
38 | /* Leave this copyright notice here! It needs to go somewhere in this library. */ |
39 | static const char copyright[] = U_COPYRIGHT_STRING; | |
40 | ||
b75a7d8f A |
41 | const LEUnicode32 DefaultCharMapper::controlChars[] = { |
42 | 0x0009, 0x000A, 0x000D, | |
43 | /*0x200C, 0x200D,*/ 0x200E, 0x200F, | |
44 | 0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, | |
45 | 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F | |
46 | }; | |
47 | ||
73c04bcf | 48 | const le_int32 DefaultCharMapper::controlCharsCount = LE_ARRAY_SIZE(controlChars); |
b75a7d8f A |
49 | |
50 | LEUnicode32 DefaultCharMapper::mapChar(LEUnicode32 ch) const | |
51 | { | |
52 | if (fFilterControls) { | |
53 | le_int32 index = OpenTypeUtilities::search((le_uint32)ch, (le_uint32 *)controlChars, controlCharsCount); | |
54 | ||
55 | if (controlChars[index] == ch) { | |
56 | return 0xFFFF; | |
57 | } | |
58 | } | |
59 | ||
60 | if (fMirror) { | |
73c04bcf | 61 | le_int32 index = OpenTypeUtilities::search((le_uint32) ch, (le_uint32 *)DefaultCharMapper::mirroredChars, DefaultCharMapper::mirroredCharsCount); |
b75a7d8f A |
62 | |
63 | if (mirroredChars[index] == ch) { | |
73c04bcf | 64 | return DefaultCharMapper::srahCderorrim[index]; |
b75a7d8f A |
65 | } |
66 | } | |
67 | ||
68 | return ch; | |
69 | } | |
70 | ||
374ca955 A |
71 | // This is here to get it out of LEGlyphFilter.h. |
72 | // No particular reason to put it here, other than | |
73 | // this is a good central location... | |
74 | LEGlyphFilter::~LEGlyphFilter() | |
75 | { | |
76 | // nothing to do | |
77 | } | |
b75a7d8f | 78 | |
374ca955 A |
79 | CharSubstitutionFilter::CharSubstitutionFilter(const LEFontInstance *fontInstance) |
80 | : fFontInstance(fontInstance) | |
b75a7d8f | 81 | { |
374ca955 | 82 | // nothing to do |
b75a7d8f A |
83 | } |
84 | ||
374ca955 | 85 | CharSubstitutionFilter::~CharSubstitutionFilter() |
b75a7d8f | 86 | { |
374ca955 A |
87 | // nothing to do |
88 | } | |
b75a7d8f | 89 | |
46f4442e A |
90 | class CanonMarkFilter : public UMemory, public LEGlyphFilter |
91 | { | |
92 | private: | |
93 | const GlyphClassDefinitionTable *classDefTable; | |
94 | ||
95 | CanonMarkFilter(const CanonMarkFilter &other); // forbid copying of this class | |
96 | CanonMarkFilter &operator=(const CanonMarkFilter &other); // forbid copying of this class | |
97 | ||
98 | public: | |
99 | CanonMarkFilter(const GlyphDefinitionTableHeader *gdefTable); | |
100 | virtual ~CanonMarkFilter(); | |
101 | ||
102 | virtual le_bool accept(LEGlyphID glyph) const; | |
103 | }; | |
104 | ||
105 | CanonMarkFilter::CanonMarkFilter(const GlyphDefinitionTableHeader *gdefTable) | |
106 | { | |
107 | classDefTable = gdefTable->getMarkAttachClassDefinitionTable(); | |
108 | } | |
109 | ||
110 | CanonMarkFilter::~CanonMarkFilter() | |
111 | { | |
112 | // nothing to do? | |
113 | } | |
114 | ||
115 | le_bool CanonMarkFilter::accept(LEGlyphID glyph) const | |
116 | { | |
117 | le_int32 glyphClass = classDefTable->getGlyphClass(glyph); | |
118 | ||
119 | return glyphClass != 0; | |
120 | } | |
b75a7d8f | 121 | |
374ca955 | 122 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LayoutEngine) |
b75a7d8f | 123 | |
73c04bcf | 124 | #define ccmpFeatureTag LE_CCMP_FEATURE_TAG |
b75a7d8f | 125 | |
73c04bcf A |
126 | #define ccmpFeatureMask 0x80000000UL |
127 | ||
128 | #define canonFeatures (ccmpFeatureMask) | |
129 | ||
130 | static const FeatureMap canonFeatureMap[] = | |
131 | { | |
132 | {ccmpFeatureTag, ccmpFeatureMask} | |
133 | }; | |
374ca955 | 134 | |
73c04bcf | 135 | static const le_int32 canonFeatureMapCount = LE_ARRAY_SIZE(canonFeatureMap); |
374ca955 | 136 | |
729e4ab9 A |
137 | LayoutEngine::LayoutEngine(const LEFontInstance *fontInstance, |
138 | le_int32 scriptCode, | |
139 | le_int32 languageCode, | |
140 | le_int32 typoFlags, | |
141 | LEErrorCode &success) | |
73c04bcf | 142 | : fGlyphStorage(NULL), fFontInstance(fontInstance), fScriptCode(scriptCode), fLanguageCode(languageCode), |
46f4442e | 143 | fTypoFlags(typoFlags), fFilterZeroWidth(TRUE) |
374ca955 | 144 | { |
729e4ab9 A |
145 | if (LE_FAILURE(success)) { |
146 | return; | |
147 | } | |
148 | ||
374ca955 | 149 | fGlyphStorage = new LEGlyphStorage(); |
729e4ab9 A |
150 | if (fGlyphStorage == NULL) { |
151 | success = LE_MEMORY_ALLOCATION_ERROR; | |
152 | } | |
374ca955 A |
153 | } |
154 | ||
155 | le_int32 LayoutEngine::getGlyphCount() const | |
156 | { | |
157 | return fGlyphStorage->getGlyphCount(); | |
73c04bcf | 158 | } |
374ca955 A |
159 | |
160 | void LayoutEngine::getCharIndices(le_int32 charIndices[], le_int32 indexBase, LEErrorCode &success) const | |
161 | { | |
162 | fGlyphStorage->getCharIndices(charIndices, indexBase, success); | |
b75a7d8f A |
163 | } |
164 | ||
165 | void LayoutEngine::getCharIndices(le_int32 charIndices[], LEErrorCode &success) const | |
166 | { | |
374ca955 | 167 | fGlyphStorage->getCharIndices(charIndices, success); |
b75a7d8f A |
168 | } |
169 | ||
170 | // Copy the glyphs into caller's (32-bit) glyph array, OR in extraBits | |
171 | void LayoutEngine::getGlyphs(le_uint32 glyphs[], le_uint32 extraBits, LEErrorCode &success) const | |
172 | { | |
374ca955 | 173 | fGlyphStorage->getGlyphs(glyphs, extraBits, success); |
b75a7d8f A |
174 | } |
175 | ||
176 | void LayoutEngine::getGlyphs(LEGlyphID glyphs[], LEErrorCode &success) const | |
177 | { | |
374ca955 | 178 | fGlyphStorage->getGlyphs(glyphs, success); |
b75a7d8f A |
179 | } |
180 | ||
181 | ||
182 | void LayoutEngine::getGlyphPositions(float positions[], LEErrorCode &success) const | |
183 | { | |
374ca955 | 184 | fGlyphStorage->getGlyphPositions(positions, success); |
b75a7d8f A |
185 | } |
186 | ||
187 | void LayoutEngine::getGlyphPosition(le_int32 glyphIndex, float &x, float &y, LEErrorCode &success) const | |
374ca955 A |
188 | { |
189 | fGlyphStorage->getGlyphPosition(glyphIndex, x, y, success); | |
190 | } | |
191 | ||
192 | le_int32 LayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, | |
46f4442e | 193 | LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) |
b75a7d8f A |
194 | { |
195 | if (LE_FAILURE(success)) { | |
374ca955 | 196 | return 0; |
b75a7d8f | 197 | } |
374ca955 A |
198 | |
199 | if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { | |
200 | success = LE_ILLEGAL_ARGUMENT_ERROR; | |
201 | return 0; | |
b75a7d8f | 202 | } |
374ca955 A |
203 | |
204 | const GlyphSubstitutionTableHeader *canonGSUBTable = (GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable; | |
205 | LETag scriptTag = OpenTypeLayoutEngine::getScriptTag(fScriptCode); | |
206 | LETag langSysTag = OpenTypeLayoutEngine::getLangSysTag(fLanguageCode); | |
207 | le_int32 i, dir = 1, out = 0, outCharCount = count; | |
208 | ||
209 | if (canonGSUBTable->coversScript(scriptTag)) { | |
210 | CharSubstitutionFilter *substitutionFilter = new CharSubstitutionFilter(fFontInstance); | |
729e4ab9 A |
211 | if (substitutionFilter == NULL) { |
212 | success = LE_MEMORY_ALLOCATION_ERROR; | |
213 | return 0; | |
214 | } | |
215 | ||
73c04bcf A |
216 | const LEUnicode *inChars = &chars[offset]; |
217 | LEUnicode *reordered = NULL; | |
218 | LEGlyphStorage fakeGlyphStorage; | |
374ca955 | 219 | |
73c04bcf A |
220 | fakeGlyphStorage.allocateGlyphArray(count, rightToLeft, success); |
221 | ||
222 | if (LE_FAILURE(success)) { | |
729e4ab9 | 223 | delete substitutionFilter; |
73c04bcf A |
224 | return 0; |
225 | } | |
226 | ||
227 | // This is the cheapest way to get mark reordering only for Hebrew. | |
228 | // We could just do the mark reordering for all scripts, but most | |
229 | // of them probably don't need it... | |
230 | if (fScriptCode == hebrScriptCode) { | |
231 | reordered = LE_NEW_ARRAY(LEUnicode, count); | |
232 | ||
233 | if (reordered == NULL) { | |
729e4ab9 | 234 | delete substitutionFilter; |
73c04bcf A |
235 | success = LE_MEMORY_ALLOCATION_ERROR; |
236 | return 0; | |
237 | } | |
238 | ||
239 | CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, reordered, fakeGlyphStorage); | |
240 | inChars = reordered; | |
241 | } | |
242 | ||
243 | fakeGlyphStorage.allocateAuxData(success); | |
374ca955 A |
244 | |
245 | if (LE_FAILURE(success)) { | |
729e4ab9 | 246 | delete substitutionFilter; |
374ca955 A |
247 | return 0; |
248 | } | |
249 | ||
250 | if (rightToLeft) { | |
251 | out = count - 1; | |
252 | dir = -1; | |
253 | } | |
254 | ||
255 | for (i = 0; i < count; i += 1, out += dir) { | |
73c04bcf A |
256 | fakeGlyphStorage[out] = (LEGlyphID) inChars[i]; |
257 | fakeGlyphStorage.setAuxData(out, canonFeatures, success); | |
374ca955 A |
258 | } |
259 | ||
73c04bcf A |
260 | if (reordered != NULL) { |
261 | LE_DELETE_ARRAY(reordered); | |
262 | } | |
263 | ||
729e4ab9 A |
264 | outCharCount = canonGSUBTable->process(fakeGlyphStorage, rightToLeft, scriptTag, langSysTag, NULL, substitutionFilter, canonFeatureMap, canonFeatureMapCount, FALSE, success); |
265 | ||
266 | if (LE_FAILURE(success)) { | |
267 | delete substitutionFilter; | |
268 | return 0; | |
269 | } | |
374ca955 | 270 | |
73c04bcf | 271 | out = (rightToLeft? outCharCount - 1 : 0); |
374ca955 | 272 | |
46f4442e A |
273 | /* |
274 | * The char indices array in fakeGlyphStorage has the correct mapping | |
275 | * back to the original input characters. Save it in glyphStorage. The | |
276 | * subsequent call to glyphStoratge.allocateGlyphArray will keep this | |
277 | * array rather than allocating and initializing a new one. | |
278 | */ | |
279 | glyphStorage.adoptCharIndicesArray(fakeGlyphStorage); | |
280 | ||
374ca955 | 281 | outChars = LE_NEW_ARRAY(LEUnicode, outCharCount); |
729e4ab9 A |
282 | |
283 | if (outChars == NULL) { | |
284 | delete substitutionFilter; | |
285 | success = LE_MEMORY_ALLOCATION_ERROR; | |
286 | return 0; | |
287 | } | |
288 | ||
374ca955 | 289 | for (i = 0; i < outCharCount; i += 1, out += dir) { |
73c04bcf | 290 | outChars[out] = (LEUnicode) LE_GET_GLYPH(fakeGlyphStorage[i]); |
374ca955 A |
291 | } |
292 | ||
293 | delete substitutionFilter; | |
b75a7d8f | 294 | } |
b75a7d8f | 295 | |
374ca955 A |
296 | return outCharCount; |
297 | } | |
b75a7d8f A |
298 | |
299 | le_int32 LayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, | |
374ca955 | 300 | LEGlyphStorage &glyphStorage, LEErrorCode &success) |
b75a7d8f A |
301 | { |
302 | if (LE_FAILURE(success)) { | |
303 | return 0; | |
304 | } | |
305 | ||
306 | if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { | |
307 | success = LE_ILLEGAL_ARGUMENT_ERROR; | |
308 | return 0; | |
309 | } | |
310 | ||
374ca955 A |
311 | LEUnicode *outChars = NULL; |
312 | le_int32 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, glyphStorage, success); | |
b75a7d8f | 313 | |
374ca955 | 314 | if (outChars != NULL) { |
46f4442e | 315 | mapCharsToGlyphs(outChars, 0, outCharCount, rightToLeft, rightToLeft, glyphStorage, success); |
374ca955 A |
316 | LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work... |
317 | } else { | |
46f4442e | 318 | mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success); |
374ca955 A |
319 | } |
320 | ||
321 | return glyphStorage.getGlyphCount(); | |
b75a7d8f A |
322 | } |
323 | ||
324 | // Input: glyphs | |
325 | // Output: positions | |
374ca955 | 326 | void LayoutEngine::positionGlyphs(LEGlyphStorage &glyphStorage, float x, float y, LEErrorCode &success) |
b75a7d8f A |
327 | { |
328 | if (LE_FAILURE(success)) { | |
329 | return; | |
330 | } | |
331 | ||
374ca955 | 332 | glyphStorage.allocatePositions(success); |
b75a7d8f | 333 | |
374ca955 A |
334 | if (LE_FAILURE(success)) { |
335 | return; | |
b75a7d8f A |
336 | } |
337 | ||
374ca955 | 338 | le_int32 i, glyphCount = glyphStorage.getGlyphCount(); |
b75a7d8f A |
339 | |
340 | for (i = 0; i < glyphCount; i += 1) { | |
341 | LEPoint advance; | |
342 | ||
374ca955 | 343 | glyphStorage.setPosition(i, x, y, success); |
b75a7d8f | 344 | |
374ca955 | 345 | fFontInstance->getGlyphAdvance(glyphStorage[i], advance); |
b75a7d8f A |
346 | x += advance.fX; |
347 | y += advance.fY; | |
348 | } | |
349 | ||
374ca955 | 350 | glyphStorage.setPosition(glyphCount, x, y, success); |
b75a7d8f A |
351 | } |
352 | ||
46f4442e | 353 | void LayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, |
73c04bcf | 354 | LEGlyphStorage &glyphStorage, LEErrorCode &success) |
374ca955 A |
355 | { |
356 | if (LE_FAILURE(success)) { | |
357 | return; | |
358 | } | |
359 | ||
360 | if (chars == NULL || offset < 0 || count < 0) { | |
361 | success = LE_ILLEGAL_ARGUMENT_ERROR; | |
362 | return; | |
363 | } | |
364 | ||
46f4442e A |
365 | GlyphDefinitionTableHeader *gdefTable = (GlyphDefinitionTableHeader *) CanonShaping::glyphDefinitionTable; |
366 | CanonMarkFilter filter(gdefTable); | |
367 | ||
368 | adjustMarkGlyphs(&chars[offset], count, reverse, glyphStorage, &filter, success); | |
369 | ||
73c04bcf A |
370 | if (fTypoFlags & 0x1) { /* kerning enabled */ |
371 | static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG; | |
372 | ||
373 | KernTable kt(fFontInstance, getFontTable(kernTableTag)); | |
374 | kt.process(glyphStorage); | |
375 | } | |
376 | ||
374ca955 A |
377 | // default is no adjustments |
378 | return; | |
379 | } | |
380 | ||
381 | void LayoutEngine::adjustMarkGlyphs(LEGlyphStorage &glyphStorage, LEGlyphFilter *markFilter, LEErrorCode &success) | |
b75a7d8f A |
382 | { |
383 | float xAdjust = 0; | |
374ca955 | 384 | le_int32 p, glyphCount = glyphStorage.getGlyphCount(); |
b75a7d8f A |
385 | |
386 | if (LE_FAILURE(success)) { | |
387 | return; | |
388 | } | |
389 | ||
374ca955 | 390 | if (markFilter == NULL) { |
b75a7d8f A |
391 | success = LE_ILLEGAL_ARGUMENT_ERROR; |
392 | return; | |
393 | } | |
394 | ||
374ca955 | 395 | float ignore, prev; |
b75a7d8f | 396 | |
374ca955 | 397 | glyphStorage.getGlyphPosition(0, prev, ignore, success); |
b75a7d8f | 398 | |
374ca955 A |
399 | for (p = 0; p < glyphCount; p += 1) { |
400 | float next, xAdvance; | |
401 | ||
402 | glyphStorage.getGlyphPosition(p + 1, next, ignore, success); | |
b75a7d8f | 403 | |
374ca955 A |
404 | xAdvance = next - prev; |
405 | glyphStorage.adjustPosition(p, xAdjust, 0, success); | |
406 | ||
407 | if (markFilter->accept(glyphStorage[p])) { | |
b75a7d8f A |
408 | xAdjust -= xAdvance; |
409 | } | |
374ca955 A |
410 | |
411 | prev = next; | |
b75a7d8f A |
412 | } |
413 | ||
374ca955 | 414 | glyphStorage.adjustPosition(glyphCount, xAdjust, 0, success); |
b75a7d8f A |
415 | } |
416 | ||
374ca955 | 417 | void LayoutEngine::adjustMarkGlyphs(const LEUnicode chars[], le_int32 charCount, le_bool reverse, LEGlyphStorage &glyphStorage, LEGlyphFilter *markFilter, LEErrorCode &success) |
b75a7d8f | 418 | { |
374ca955 A |
419 | float xAdjust = 0; |
420 | le_int32 c = 0, direction = 1, p; | |
421 | le_int32 glyphCount = glyphStorage.getGlyphCount(); | |
b75a7d8f | 422 | |
b75a7d8f A |
423 | if (LE_FAILURE(success)) { |
424 | return; | |
425 | } | |
426 | ||
374ca955 | 427 | if (markFilter == NULL) { |
b75a7d8f A |
428 | success = LE_ILLEGAL_ARGUMENT_ERROR; |
429 | return; | |
430 | } | |
431 | ||
374ca955 A |
432 | if (reverse) { |
433 | c = glyphCount - 1; | |
434 | direction = -1; | |
b75a7d8f A |
435 | } |
436 | ||
374ca955 | 437 | float ignore, prev; |
b75a7d8f | 438 | |
374ca955 | 439 | glyphStorage.getGlyphPosition(0, prev, ignore, success); |
b75a7d8f | 440 | |
374ca955 A |
441 | for (p = 0; p < charCount; p += 1, c += direction) { |
442 | float next, xAdvance; | |
443 | ||
444 | glyphStorage.getGlyphPosition(p + 1, next, ignore, success); | |
b75a7d8f | 445 | |
374ca955 A |
446 | xAdvance = next - prev; |
447 | glyphStorage.adjustPosition(p, xAdjust, 0, success); | |
b75a7d8f | 448 | |
374ca955 A |
449 | if (markFilter->accept(chars[c])) { |
450 | xAdjust -= xAdvance; | |
b75a7d8f | 451 | } |
374ca955 A |
452 | |
453 | prev = next; | |
454 | } | |
455 | ||
456 | glyphStorage.adjustPosition(glyphCount, xAdjust, 0, success); | |
457 | } | |
458 | ||
459 | const void *LayoutEngine::getFontTable(LETag tableTag) const | |
460 | { | |
461 | return fFontInstance->getFontTable(tableTag); | |
462 | } | |
463 | ||
46f4442e | 464 | void LayoutEngine::mapCharsToGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, le_bool mirror, |
374ca955 A |
465 | LEGlyphStorage &glyphStorage, LEErrorCode &success) |
466 | { | |
467 | if (LE_FAILURE(success)) { | |
468 | return; | |
b75a7d8f A |
469 | } |
470 | ||
374ca955 | 471 | glyphStorage.allocateGlyphArray(count, reverse, success); |
b75a7d8f | 472 | |
374ca955 A |
473 | DefaultCharMapper charMapper(TRUE, mirror); |
474 | ||
46f4442e | 475 | fFontInstance->mapCharsToGlyphs(chars, offset, count, reverse, &charMapper, fFilterZeroWidth, glyphStorage); |
b75a7d8f A |
476 | } |
477 | ||
478 | // Input: characters, font? | |
479 | // Output: glyphs, positions, char indices | |
480 | // Returns: number of glyphs | |
481 | le_int32 LayoutEngine::layoutChars(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, | |
482 | float x, float y, LEErrorCode &success) | |
483 | { | |
484 | if (LE_FAILURE(success)) { | |
485 | return 0; | |
486 | } | |
487 | ||
488 | if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { | |
489 | success = LE_ILLEGAL_ARGUMENT_ERROR; | |
490 | return 0; | |
491 | } | |
492 | ||
374ca955 | 493 | le_int32 glyphCount; |
73c04bcf A |
494 | |
495 | if (fGlyphStorage->getGlyphCount() > 0) { | |
496 | fGlyphStorage->reset(); | |
497 | } | |
374ca955 A |
498 | |
499 | glyphCount = computeGlyphs(chars, offset, count, max, rightToLeft, *fGlyphStorage, success); | |
500 | positionGlyphs(*fGlyphStorage, x, y, success); | |
501 | adjustGlyphPositions(chars, offset, count, rightToLeft, *fGlyphStorage, success); | |
b75a7d8f | 502 | |
374ca955 | 503 | return glyphCount; |
b75a7d8f A |
504 | } |
505 | ||
506 | void LayoutEngine::reset() | |
507 | { | |
374ca955 | 508 | fGlyphStorage->reset(); |
b75a7d8f A |
509 | } |
510 | ||
511 | LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, LEErrorCode &success) | |
73c04bcf A |
512 | { |
513 | // 3 -> kerning and ligatures | |
514 | return LayoutEngine::layoutEngineFactory(fontInstance, scriptCode, languageCode, 3, success); | |
515 | } | |
516 | ||
517 | LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, le_int32 typoFlags, LEErrorCode &success) | |
b75a7d8f | 518 | { |
374ca955 A |
519 | static const le_uint32 gsubTableTag = LE_GSUB_TABLE_TAG; |
520 | static const le_uint32 mortTableTag = LE_MORT_TABLE_TAG; | |
b75a7d8f A |
521 | |
522 | if (LE_FAILURE(success)) { | |
523 | return NULL; | |
524 | } | |
525 | ||
526 | const GlyphSubstitutionTableHeader *gsubTable = (const GlyphSubstitutionTableHeader *) fontInstance->getFontTable(gsubTableTag); | |
527 | LayoutEngine *result = NULL; | |
528 | LETag scriptTag = 0x00000000; | |
529 | LETag languageTag = 0x00000000; | |
729e4ab9 | 530 | LETag v2ScriptTag = OpenTypeLayoutEngine::getV2ScriptTag(scriptCode); |
b75a7d8f | 531 | |
729e4ab9 A |
532 | // Right now, only invoke V2 processing for Devanagari. TODO: Allow more V2 scripts as they are |
533 | // properly tested. | |
534 | ||
535 | if ( v2ScriptTag == dev2ScriptTag && gsubTable != NULL && gsubTable->coversScript( v2ScriptTag )) { | |
536 | result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, TRUE, gsubTable, success); | |
537 | } | |
538 | else if (gsubTable != NULL && gsubTable->coversScript(scriptTag = OpenTypeLayoutEngine::getScriptTag(scriptCode))) { | |
b75a7d8f A |
539 | switch (scriptCode) { |
540 | case bengScriptCode: | |
541 | case devaScriptCode: | |
542 | case gujrScriptCode: | |
543 | case kndaScriptCode: | |
544 | case mlymScriptCode: | |
545 | case oryaScriptCode: | |
546 | case guruScriptCode: | |
547 | case tamlScriptCode: | |
548 | case teluScriptCode: | |
73c04bcf | 549 | case sinhScriptCode: |
729e4ab9 | 550 | result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, FALSE, gsubTable, success); |
b75a7d8f A |
551 | break; |
552 | ||
553 | case arabScriptCode: | |
729e4ab9 | 554 | result = new ArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
73c04bcf A |
555 | break; |
556 | ||
557 | case hangScriptCode: | |
729e4ab9 | 558 | result = new HangulOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
b75a7d8f A |
559 | break; |
560 | ||
561 | case haniScriptCode: | |
562 | languageTag = OpenTypeLayoutEngine::getLangSysTag(languageCode); | |
563 | ||
564 | switch (languageCode) { | |
565 | case korLanguageCode: | |
566 | case janLanguageCode: | |
567 | case zhtLanguageCode: | |
568 | case zhsLanguageCode: | |
374ca955 | 569 | if (gsubTable->coversScriptAndLanguage(scriptTag, languageTag, TRUE)) { |
729e4ab9 | 570 | result = new HanOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
b75a7d8f A |
571 | break; |
572 | } | |
573 | ||
574 | // note: falling through to default case. | |
575 | default: | |
729e4ab9 | 576 | result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
b75a7d8f A |
577 | break; |
578 | } | |
579 | ||
580 | break; | |
581 | ||
73c04bcf | 582 | case tibtScriptCode: |
729e4ab9 | 583 | result = new TibetanOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
73c04bcf A |
584 | break; |
585 | ||
586 | case khmrScriptCode: | |
729e4ab9 | 587 | result = new KhmerOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
73c04bcf A |
588 | break; |
589 | ||
b75a7d8f | 590 | default: |
729e4ab9 | 591 | result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success); |
b75a7d8f A |
592 | break; |
593 | } | |
594 | } else { | |
595 | const MorphTableHeader *morphTable = (MorphTableHeader *) fontInstance->getFontTable(mortTableTag); | |
596 | ||
597 | if (morphTable != NULL) { | |
729e4ab9 | 598 | result = new GXLayoutEngine(fontInstance, scriptCode, languageCode, morphTable, success); |
b75a7d8f A |
599 | } else { |
600 | switch (scriptCode) { | |
601 | case bengScriptCode: | |
602 | case devaScriptCode: | |
603 | case gujrScriptCode: | |
604 | case kndaScriptCode: | |
605 | case mlymScriptCode: | |
606 | case oryaScriptCode: | |
607 | case guruScriptCode: | |
608 | case tamlScriptCode: | |
609 | case teluScriptCode: | |
73c04bcf | 610 | case sinhScriptCode: |
b75a7d8f | 611 | { |
729e4ab9 | 612 | result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success); |
b75a7d8f A |
613 | break; |
614 | } | |
615 | ||
616 | case arabScriptCode: | |
374ca955 | 617 | //case hebrScriptCode: |
729e4ab9 | 618 | result = new UnicodeArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success); |
b75a7d8f A |
619 | break; |
620 | ||
621 | //case hebrScriptCode: | |
73c04bcf | 622 | // return new HebrewOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags); |
b75a7d8f A |
623 | |
624 | case thaiScriptCode: | |
729e4ab9 | 625 | result = new ThaiLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success); |
b75a7d8f A |
626 | break; |
627 | ||
46f4442e | 628 | case hangScriptCode: |
729e4ab9 | 629 | result = new HangulOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success); |
46f4442e A |
630 | break; |
631 | ||
b75a7d8f | 632 | default: |
729e4ab9 | 633 | result = new LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success); |
b75a7d8f A |
634 | break; |
635 | } | |
636 | } | |
637 | } | |
638 | ||
729e4ab9 A |
639 | if (result && LE_FAILURE(success)) { |
640 | delete result; | |
641 | result = NULL; | |
642 | } | |
643 | ||
b75a7d8f A |
644 | if (result == NULL) { |
645 | success = LE_MEMORY_ALLOCATION_ERROR; | |
646 | } | |
647 | ||
648 | return result; | |
649 | } | |
650 | ||
651 | LayoutEngine::~LayoutEngine() { | |
73c04bcf | 652 | delete fGlyphStorage; |
b75a7d8f A |
653 | } |
654 | ||
655 | U_NAMESPACE_END |