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