| |
| /* |
| * %W% %E% |
| * |
| * (C) Copyright IBM Corp. 1998, 1999, 2000, 2001 - All Rights Reserved |
| * |
| */ |
| |
| #include "LETypes.h" |
| #include "LayoutEngine.h" |
| #include "ArabicLayoutEngine.h" |
| //#include "HebrewLayoutEngine.h" |
| #include "IndicLayoutEngine.h" |
| #include "ThaiLayoutEngine.h" |
| #include "GXLayoutEngine.h" |
| #include "ScriptAndLanguageTags.h" |
| |
| #include "OpenTypeUtilities.h" |
| #include "GlyphSubstitutionTables.h" |
| #include "MorphTables.h" |
| |
| #include "DefaultCharMapper.h" |
| |
| U_NAMESPACE_BEGIN |
| |
| #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) |
| |
| LEUnicode32 DefaultCharMapper::controlChars[] = { |
| 0x0009, 0x000A, 0x000D, |
| /*0x200C, 0x200D,*/ 0x200E, 0x200F, |
| 0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, |
| 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F |
| }; |
| |
| const le_int32 DefaultCharMapper::controlCharsCount = ARRAY_SIZE(controlChars); |
| |
| LEUnicode32 DefaultCharMapper::mirroredChars[] = { |
| 0x0028, 0x0029, // ascii paired punctuation |
| 0x003c, 0x003e, |
| 0x005b, 0x005d, |
| 0x007b, 0x007d, |
| 0x2045, 0x2046, // math symbols (not complete) |
| 0x207d, 0x207e, |
| 0x208d, 0x208e, |
| 0x2264, 0x2265, |
| 0x3008, 0x3009, // chinese paired punctuation |
| 0x300a, 0x300b, |
| 0x300c, 0x300d, |
| 0x300e, 0x300f, |
| 0x3010, 0x3011, |
| 0x3014, 0x3015, |
| 0x3016, 0x3017, |
| 0x3018, 0x3019, |
| 0x301a, 0x301b |
| }; |
| |
| const le_int32 DefaultCharMapper::mirroredCharsCount = ARRAY_SIZE(mirroredChars); |
| |
| LEUnicode32 DefaultCharMapper::mapChar(LEUnicode32 ch) const |
| { |
| if (fFilterControls) { |
| le_int32 index = OpenTypeUtilities::search((le_uint32)ch, (le_uint32 *)controlChars, controlCharsCount); |
| |
| if (controlChars[index] == ch) { |
| return 0xFFFF; |
| } |
| } |
| |
| if (fMirror) { |
| le_int32 index = OpenTypeUtilities::search((le_uint32) ch, (le_uint32 *)mirroredChars, mirroredCharsCount); |
| |
| if (mirroredChars[index] == ch) { |
| le_int32 mirrorOffset = ((index & 1) == 0) ? 1 : -1; |
| |
| return mirroredChars[index + mirrorOffset]; |
| } |
| } |
| |
| return ch; |
| } |
| |
| LayoutEngine::LayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode) |
| : fGlyphCount(0), fGlyphs(NULL), fCharIndices(NULL), fPositions(NULL), |
| fFontInstance(fontInstance), fScriptCode(scriptCode), fLanguageCode(languageCode) |
| { |
| // nothing else to do? |
| } |
| |
| void LayoutEngine::getCharIndices(le_int32 charIndices[], le_int32 indexBase, LEErrorCode &success) const |
| { |
| le_int32 i; |
| |
| if LE_FAILURE(success) { |
| return; |
| } |
| |
| if (charIndices == NULL) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| |
| if (fCharIndices == NULL) { |
| success = LE_NO_LAYOUT_ERROR; |
| return; |
| } |
| |
| for (i = 0; i < fGlyphCount; i += 1) { |
| charIndices[i] = fCharIndices[i] + indexBase; |
| } |
| } |
| |
| // Copy the glyphs into caller's (32-bit) glyph array, OR in extraBits |
| void LayoutEngine::getGlyphs(le_uint32 glyphs[], le_uint32 extraBits, LEErrorCode &success) const |
| { |
| le_int32 i; |
| |
| if (LE_FAILURE(success)) { |
| return; |
| } |
| |
| if (glyphs == NULL) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| |
| if (fGlyphs == NULL) { |
| success = LE_NO_LAYOUT_ERROR; |
| return; |
| } |
| |
| for (i = 0; i < fGlyphCount; i += 1) { |
| glyphs[i] = fGlyphs[i] | extraBits; |
| } |
| }; |
| |
| le_int32 LayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, |
| LEGlyphID *&glyphs, le_int32 *&charIndices, LEErrorCode &success) |
| { |
| if (LE_FAILURE(success)) { |
| return 0; |
| } |
| |
| if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| |
| mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphs, charIndices, success); |
| |
| return count; |
| } |
| |
| // Input: glyphs |
| // Output: positions |
| void LayoutEngine::positionGlyphs(const LEGlyphID glyphs[], le_int32 glyphCount, float x, float y, float *&positions, LEErrorCode &success) |
| { |
| if (LE_FAILURE(success)) { |
| return; |
| } |
| |
| if (glyphCount < 0) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| |
| if (positions == NULL) { |
| positions = new float[2 * (glyphCount + 1)]; |
| |
| if (positions == NULL) { |
| success = LE_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| } |
| |
| le_int32 i; |
| |
| for (i = 0; i < glyphCount; i += 1) { |
| LEPoint advance; |
| |
| positions[i * 2] = x; |
| positions[i * 2 + 1] = y; |
| |
| fFontInstance->getGlyphAdvance(glyphs[i], advance); |
| x += advance.fX; |
| y += advance.fY; |
| } |
| |
| positions[glyphCount * 2] = x; |
| positions[glyphCount * 2 + 1] = y; |
| } |
| |
| void LayoutEngine::adjustMarkGlyphs(const LEGlyphID glyphs[], le_int32 glyphCount, le_bool reverse, LEGlyphFilter *markFilter, |
| float positions[], LEErrorCode &success) |
| { |
| float xAdjust = 0; |
| le_int32 g = 0, direction = 1; |
| le_int32 p; |
| |
| if (LE_FAILURE(success)) { |
| return; |
| } |
| |
| if (positions == NULL || markFilter == NULL) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| |
| if (reverse) { |
| g = glyphCount - 1; |
| direction = -1; |
| } |
| |
| for (p = 0; p < glyphCount; p += 1, g += direction) { |
| float xAdvance = positions[(p + 1) * 2] - positions[p * 2]; |
| |
| positions[p * 2] += xAdjust; |
| |
| if (markFilter->accept(glyphs[g])) { |
| xAdjust -= xAdvance; |
| } |
| } |
| |
| positions[glyphCount * 2] += xAdjust; |
| } |
| |
| const void *LayoutEngine::getFontTable(LETag tableTag) const |
| { |
| return fFontInstance->getFontTable(tableTag); |
| } |
| |
| void LayoutEngine::mapCharsToGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, le_bool mirror, |
| LEGlyphID *&glyphs, le_int32 *&charIndices, LEErrorCode &success) |
| { |
| if (LE_FAILURE(success)) { |
| return; |
| } |
| |
| if (chars == NULL || offset < 0 || count < 0) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| |
| if (glyphs == NULL) { |
| glyphs = new LEGlyphID[count]; |
| |
| if (glyphs == NULL) { |
| success = LE_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| } |
| |
| if (charIndices == NULL) { |
| le_int32 i, dir = 1, out = 0; |
| |
| if (reverse) { |
| out = count - 1; |
| dir = -1; |
| } |
| |
| charIndices = new le_int32[count]; |
| |
| if (charIndices == NULL) { |
| success = LE_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| |
| for (i = 0; i < count; i += 1, out += dir) { |
| charIndices[out] = i; |
| } |
| } |
| |
| DefaultCharMapper charMapper(true, mirror); |
| |
| fFontInstance->mapCharsToGlyphs(chars, offset, count, reverse, &charMapper, glyphs); |
| } |
| |
| // Input: characters, font? |
| // Output: glyphs, positions, char indices |
| // Returns: number of glyphs |
| le_int32 LayoutEngine::layoutChars(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, |
| float x, float y, LEErrorCode &success) |
| { |
| if (LE_FAILURE(success)) { |
| return 0; |
| } |
| |
| if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { |
| success = LE_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| |
| fGlyphCount = computeGlyphs(chars, offset, count, max, rightToLeft, fGlyphs, fCharIndices, success); |
| positionGlyphs(fGlyphs, fGlyphCount, x, y, fPositions, success); |
| adjustGlyphPositions(chars, offset, count, rightToLeft, fGlyphs, fGlyphCount, fPositions, success); |
| |
| return fGlyphCount; |
| } |
| |
| void LayoutEngine::reset() |
| { |
| fGlyphCount = 0; |
| |
| if (fGlyphs != NULL) { |
| delete[] fGlyphs; |
| fGlyphs = NULL; |
| } |
| |
| if (fCharIndices != NULL) { |
| delete[] fCharIndices; |
| fCharIndices = NULL; |
| } |
| |
| if (fPositions != NULL) { |
| delete[] fPositions; |
| fPositions = NULL; |
| } |
| } |
| |
| LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, LEErrorCode &success) |
| { |
| static le_uint32 gsubTableTag = 0x47535542; // "GSUB" |
| static le_uint32 mortTableTag = 0x6D6F7274; // 'mort' |
| |
| if (LE_FAILURE(success)) { |
| return NULL; |
| } |
| |
| const GlyphSubstitutionTableHeader *gsubTable = (const GlyphSubstitutionTableHeader *) fontInstance->getFontTable(gsubTableTag); |
| LayoutEngine *result = NULL; |
| |
| if (gsubTable != NULL && gsubTable->coversScript(OpenTypeLayoutEngine::getScriptTag(scriptCode))) { |
| switch (scriptCode) { |
| case bengScriptCode: |
| case devaScriptCode: |
| case gujrScriptCode: |
| case kndaScriptCode: |
| case mlymScriptCode: |
| case oryaScriptCode: |
| case guruScriptCode: |
| case tamlScriptCode: |
| case teluScriptCode: |
| result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, gsubTable); |
| break; |
| |
| case arabScriptCode: |
| result = new ArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, gsubTable); |
| break; |
| |
| default: |
| result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, gsubTable); |
| break; |
| } |
| } else { |
| const MorphTableHeader *morphTable = (MorphTableHeader *) fontInstance->getFontTable(mortTableTag); |
| |
| if (morphTable != NULL) { |
| result = new GXLayoutEngine(fontInstance, scriptCode, languageCode, morphTable); |
| } else { |
| switch (scriptCode) { |
| case bengScriptCode: |
| case devaScriptCode: |
| case gujrScriptCode: |
| case kndaScriptCode: |
| case mlymScriptCode: |
| case oryaScriptCode: |
| case guruScriptCode: |
| case tamlScriptCode: |
| case teluScriptCode: |
| { |
| result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode); |
| break; |
| } |
| |
| case arabScriptCode: |
| case hebrScriptCode: |
| result = new UnicodeArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode); |
| break; |
| |
| //case hebrScriptCode: |
| // return new HebrewOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode); |
| |
| case thaiScriptCode: |
| result = new ThaiLayoutEngine(fontInstance, scriptCode, languageCode); |
| break; |
| |
| default: |
| result = new LayoutEngine(fontInstance, scriptCode, languageCode); |
| break; |
| } |
| } |
| } |
| |
| if (result == NULL) { |
| success = LE_MEMORY_ALLOCATION_ERROR; |
| } |
| |
| return result; |
| } |
| |
| LayoutEngine::~LayoutEngine() { |
| reset(); |
| } |
| |
| U_NAMESPACE_END |