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