void CanonShaping::reorderMarks(const LEUnicode *inChars, le_int32 charCount, le_bool rightToLeft, LEUnicode *outChars, LEGlyphStorage &glyphStorage) { LEErrorCode success = LE_NO_ERROR; LEReferenceTo<GlyphDefinitionTableHeader> gdefTable(LETableReference::kStaticData, CanonShaping::glyphDefinitionTable, CanonShaping::glyphDefinitionTableLen); LEReferenceTo<ClassDefinitionTable> classTable = gdefTable->getMarkAttachClassDefinitionTable(gdefTable, success); le_int32 *combiningClasses = LE_NEW_ARRAY(le_int32, charCount); le_int32 *indices = LE_NEW_ARRAY(le_int32, charCount); le_int32 i; if (combiningClasses == NULL || indices == NULL) { if (combiningClasses != NULL) { LE_DELETE_ARRAY(combiningClasses); } if (indices != NULL) { LE_DELETE_ARRAY(indices); } return; } for (i = 0; i < charCount; i += 1) { combiningClasses[i] = classTable->getGlyphClass(classTable, (LEGlyphID) inChars[i], success); indices[i] = i; } for (i = 0; i < charCount; i += 1) { if (combiningClasses[i] != 0) { le_int32 mark; for (mark = i; mark < charCount; mark += 1) { if (combiningClasses[mark] == 0) { break; } } sortMarks(indices, combiningClasses, i, mark); } } le_int32 out = 0, dir = 1; if (rightToLeft) { out = charCount - 1; dir = -1; } for (i = 0; i < charCount; i += 1, out += dir) { le_int32 index = indices[i]; outChars[i] = inChars[index]; glyphStorage.setCharIndex(out, index, success); } LE_DELETE_ARRAY(indices); LE_DELETE_ARRAY(combiningClasses); }
Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, const FontRuns *fontRuns, LEErrorCode &status) : fParagraphLayout(NULL), fLineCount(0), fLinesMax(0), fLinesGrow(LINE_GROW), fLines(NULL), fChars(NULL), fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1) { if (LE_FAILURE(status)) { return; } LocaleRuns *locales = NULL; fChars = LE_NEW_ARRAY(LEUnicode, charCount); LE_ARRAY_COPY(fChars, chars, charCount); fParagraphLayout = new ParagraphLayout(fChars, charCount, fontRuns, NULL, NULL, locales, UBIDI_DEFAULT_LTR, FALSE, status); if (LE_FAILURE(status)) { return; } le_int32 ascent = fParagraphLayout->getAscent(); le_int32 descent = fParagraphLayout->getDescent(); le_int32 leading = fParagraphLayout->getLeading(); fLineHeight = ascent + descent + leading; fAscent = ascent; }
// "glyphs", "indices" -> glyphs, indices le_int32 UnicodeArabicOpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success) { if (LE_FAILURE(success)) { return 0; } // FIXME: we could avoid the memory allocation and copy if we // made a clone of mapCharsToGlyphs which took the fake glyphs // directly. le_int32 tempGlyphCount = tempGlyphStorage.getGlyphCount(); LEUnicode *tempChars = LE_NEW_ARRAY(LEUnicode, tempGlyphCount); if (tempChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } for (le_int32 i = 0; i < tempGlyphCount; i += 1) { tempChars[i] = (LEUnicode) LE_GET_GLYPH(tempGlyphStorage[i]); } glyphStorage.adoptCharIndicesArray(tempGlyphStorage); ArabicOpenTypeLayoutEngine::mapCharsToGlyphs(tempChars, 0, tempGlyphCount, FALSE, TRUE, glyphStorage, success); LE_DELETE_ARRAY(tempChars); return tempGlyphCount; }
void CanonShaping::reorderMarks(const LEUnicode *inChars, le_int32 charCount, le_bool rightToLeft, LEUnicode *outChars, LEGlyphStorage &glyphStorage) { const GlyphDefinitionTableHeader *gdefTable = (const GlyphDefinitionTableHeader *) glyphDefinitionTable; const ClassDefinitionTable *classTable = gdefTable->getMarkAttachClassDefinitionTable(); le_int32 *combiningClasses = LE_NEW_ARRAY(le_int32, charCount); le_int32 *indices = LE_NEW_ARRAY(le_int32, charCount); LEErrorCode status = LE_NO_ERROR; le_int32 i; for (i = 0; i < charCount; i += 1) { combiningClasses[i] = classTable->getGlyphClass((LEGlyphID) inChars[i]); indices[i] = i; } for (i = 0; i < charCount; i += 1) { if (combiningClasses[i] != 0) { le_int32 mark; for (mark = i; mark < charCount; mark += 1) { if (combiningClasses[mark] == 0) { break; } } sortMarks(indices, combiningClasses, i, mark); } } le_int32 out = 0, dir = 1; if (rightToLeft) { out = charCount - 1; dir = -1; } for (i = 0; i < charCount; i += 1, out += dir) { le_int32 index = indices[i]; outChars[i] = inChars[index]; glyphStorage.setCharIndex(out, index, status); } LE_DELETE_ARRAY(indices); LE_DELETE_ARRAY(combiningClasses); }
void FontTableCache::initialize() { fTableCache = LE_NEW_ARRAY(FontTableCacheEntry, fTableCacheSize); if (fTableCache == NULL) { fTableCacheSize = 0; return; } }
le_int32 LayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) { if (LE_FAILURE(success)) { return 0; } if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { success = LE_ILLEGAL_ARGUMENT_ERROR; return 0; } const GlyphSubstitutionTableHeader *canonGSUBTable = (GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable; LETag scriptTag = OpenTypeLayoutEngine::getScriptTag(fScriptCode); LETag langSysTag = OpenTypeLayoutEngine::getLangSysTag(fLanguageCode); le_int32 i, dir = 1, out = 0, outCharCount = count; if (rightToLeft) { out = count - 1; dir = -1; } if (canonGSUBTable->coversScript(scriptTag)) { CharSubstitutionFilter *substitutionFilter = new CharSubstitutionFilter(fFontInstance); glyphStorage.allocateGlyphArray(count, rightToLeft, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { return 0; } for (i = 0; i < count; i += 1, out += dir) { glyphStorage[i] = (LEGlyphID) chars[offset + i]; glyphStorage.setAuxData(i, (void *) canonFeatures, success); } outCharCount = canonGSUBTable->process(glyphStorage, rightToLeft, scriptTag, langSysTag, NULL, substitutionFilter, NULL); outChars = LE_NEW_ARRAY(LEUnicode, outCharCount); for (i = 0; i < outCharCount; i += 1) { outChars[i] = (LEUnicode) LE_GET_GLYPH(glyphStorage[i]); } delete substitutionFilter; } return outCharCount; }
FontTableCache::FontTableCache() : fTableCacheCurr(0), fTableCacheSize(TABLE_CACHE_INIT) { fTableCache = LE_NEW_ARRAY(FontTableCacheEntry, fTableCacheSize); if (fTableCache == NULL) { fTableCacheSize = 0; return; } for (int i = 0; i < fTableCacheSize; i += 1) { fTableCache[i].tag = 0; fTableCache[i].table = NULL; } }
// Input: characters // Output: characters, char indices, tags // Returns: output character count le_int32 IndicOpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, 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; } le_int32 worstCase = count * IndicReordering::getWorstCaseExpansion(fScriptCode); outChars = LE_NEW_ARRAY(LEUnicode, worstCase); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphStorage.allocateGlyphArray(worstCase, rightToLeft, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } // NOTE: assumes this allocates featureTags... // (probably better than doing the worst case stuff here...) le_int32 outCharCount; if (fVersion2) { outCharCount = IndicReordering::v2process(&chars[offset], count, fScriptCode, outChars, glyphStorage); } else { outCharCount = IndicReordering::reorder(&chars[offset], count, fScriptCode, outChars, glyphStorage, &fMPreFixups, success); } if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } glyphStorage.adoptGlyphCount(outCharCount); return outCharCount; }
le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) { if (LE_FAILURE(success)) { return 0; } if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { success = LE_ILLEGAL_ARGUMENT_ERROR; return 0; } // This is the cheapest way to get mark reordering only for Hebrew. // We could just do the mark reordering for all scripts, but most // of them probably don't need it... Another option would be to // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it // would need to do is mark reordering, so that seems like overkill. if (fScriptCode == hebrScriptCode) { outChars = LE_NEW_ARRAY(LEUnicode, count); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage); } if (LE_FAILURE(success)) { return 0; } glyphStorage.allocateGlyphArray(count, rightToLeft, success); glyphStorage.allocateAuxData(success); for (le_int32 i = 0; i < count; i += 1) { glyphStorage.setAuxData(i, fFeatureMask, success); } return count; }
// Input: characters (0..max provided for context) // Output: glyphs, char indices // Returns: the glyph count // NOTE: this assumes that ThaiShaping::compose will allocate the outChars array... le_int32 ThaiLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool /*rightToLeft*/, LEGlyphStorage &glyphStorage, 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; } LEUnicode *outChars; le_int32 glyphCount; // This is enough room for the worst-case expansion // (it says here...) outChars = LE_NEW_ARRAY(LEUnicode, count * 2); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphStorage.allocateGlyphArray(count * 2, FALSE, success); if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphCount = ThaiShaping::compose(chars, offset, count, fGlyphSet, fErrorChar, outChars, glyphStorage); mapCharsToGlyphs(outChars, 0, glyphCount, FALSE, FALSE, glyphStorage, success); LE_DELETE_ARRAY(outChars); glyphStorage.adoptGlyphCount(glyphCount); return glyphCount; }
// Input: characters // Output: characters, char indices, tags // Returns: output character count le_int32 ArabicOpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, 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; } outChars = LE_NEW_ARRAY(LEUnicode, count); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphStorage.allocateGlyphArray(count, rightToLeft, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage); // Note: This processes the *original* character array so we can get context // for the first and last characters. This is OK because only the marks // will have been reordered, and they don't contribute to shaping. ArabicShaping::shape(chars, offset, count, max, rightToLeft, glyphStorage); return count; }
// Input: characters // Output: characters, char indices, tags // Returns: output character count le_int32 TibetanOpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, 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; } le_int32 worstCase = count * 3; // worst case is 3 for Khmer TODO check if 2 is enough outChars = LE_NEW_ARRAY(LEUnicode, worstCase); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphStorage.allocateGlyphArray(worstCase, rightToLeft, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } // NOTE: assumes this allocates featureTags... // (probably better than doing the worst case stuff here...) le_int32 outCharCount = TibetanReordering::reorder(&chars[offset], count, fScriptCode, outChars, glyphStorage); glyphStorage.adoptGlyphCount(outCharCount); return outCharCount; }
le_int32 LayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &/*glyphStorage*/, LEErrorCode &success) { if (LE_FAILURE(success)) { return 0; } if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { success = LE_ILLEGAL_ARGUMENT_ERROR; return 0; } const GlyphSubstitutionTableHeader *canonGSUBTable = (GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable; LETag scriptTag = OpenTypeLayoutEngine::getScriptTag(fScriptCode); LETag langSysTag = OpenTypeLayoutEngine::getLangSysTag(fLanguageCode); le_int32 i, dir = 1, out = 0, outCharCount = count; if (canonGSUBTable->coversScript(scriptTag)) { CharSubstitutionFilter *substitutionFilter = new CharSubstitutionFilter(fFontInstance); const LEUnicode *inChars = &chars[offset]; LEUnicode *reordered = NULL; LEGlyphStorage fakeGlyphStorage; fakeGlyphStorage.allocateGlyphArray(count, rightToLeft, success); if (LE_FAILURE(success)) { return 0; } // This is the cheapest way to get mark reordering only for Hebrew. // We could just do the mark reordering for all scripts, but most // of them probably don't need it... if (fScriptCode == hebrScriptCode) { reordered = LE_NEW_ARRAY(LEUnicode, count); if (reordered == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, reordered, fakeGlyphStorage); inChars = reordered; } fakeGlyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { return 0; } if (rightToLeft) { out = count - 1; dir = -1; } for (i = 0; i < count; i += 1, out += dir) { fakeGlyphStorage[out] = (LEGlyphID) inChars[i]; fakeGlyphStorage.setAuxData(out, canonFeatures, success); } if (reordered != NULL) { LE_DELETE_ARRAY(reordered); } outCharCount = canonGSUBTable->process(fakeGlyphStorage, rightToLeft, scriptTag, langSysTag, NULL, substitutionFilter, canonFeatureMap, canonFeatureMapCount, FALSE); out = (rightToLeft? outCharCount - 1 : 0); outChars = LE_NEW_ARRAY(LEUnicode, outCharCount); for (i = 0; i < outCharCount; i += 1, out += dir) { outChars[out] = (LEUnicode) LE_GET_GLYPH(fakeGlyphStorage[i]); } delete substitutionFilter; } return outCharCount; }
le_int32 LayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) { if (LE_FAILURE(success)) { return 0; } if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { success = LE_ILLEGAL_ARGUMENT_ERROR; return 0; } LEReferenceTo<GlyphSubstitutionTableHeader> canonGSUBTable((GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable); LETag scriptTag = OpenTypeLayoutEngine::getScriptTag(fScriptCode); LETag langSysTag = OpenTypeLayoutEngine::getLangSysTag(fLanguageCode); le_int32 i, dir = 1, out = 0, outCharCount = count; if (canonGSUBTable->coversScript(canonGSUBTable,scriptTag, success) || LE_SUCCESS(success)) { CharSubstitutionFilter *substitutionFilter = new CharSubstitutionFilter(fFontInstance); if (substitutionFilter == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } const LEUnicode *inChars = &chars[offset]; LEUnicode *reordered = NULL; LEGlyphStorage fakeGlyphStorage; fakeGlyphStorage.allocateGlyphArray(count, rightToLeft, success); if (LE_FAILURE(success)) { delete substitutionFilter; return 0; } // This is the cheapest way to get mark reordering only for Hebrew. // We could just do the mark reordering for all scripts, but most // of them probably don't need it... if (fScriptCode == hebrScriptCode) { reordered = LE_NEW_ARRAY(LEUnicode, count); if (reordered == NULL) { delete substitutionFilter; success = LE_MEMORY_ALLOCATION_ERROR; return 0; } CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, reordered, fakeGlyphStorage); inChars = reordered; } fakeGlyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { delete substitutionFilter; return 0; } if (rightToLeft) { out = count - 1; dir = -1; } for (i = 0; i < count; i += 1, out += dir) { fakeGlyphStorage[out] = (LEGlyphID) inChars[i]; fakeGlyphStorage.setAuxData(out, canonFeatures, success); } if (reordered != NULL) { LE_DELETE_ARRAY(reordered); } outCharCount = canonGSUBTable->process(canonGSUBTable, fakeGlyphStorage, rightToLeft, scriptTag, langSysTag, (const GlyphDefinitionTableHeader*)NULL, substitutionFilter, canonFeatureMap, canonFeatureMapCount, FALSE, success); if (LE_FAILURE(success)) { delete substitutionFilter; return 0; } out = (rightToLeft? outCharCount - 1 : 0); /* * The char indices array in fakeGlyphStorage has the correct mapping * back to the original input characters. Save it in glyphStorage. The * subsequent call to glyphStoratge.allocateGlyphArray will keep this * array rather than allocating and initializing a new one. */ glyphStorage.adoptCharIndicesArray(fakeGlyphStorage); outChars = LE_NEW_ARRAY(LEUnicode, outCharCount); if (outChars == NULL) { delete substitutionFilter; success = LE_MEMORY_ALLOCATION_ERROR; return 0; } for (i = 0; i < outCharCount; i += 1, out += dir) { outChars[out] = (LEUnicode) LE_GET_GLYPH(fakeGlyphStorage[i]); } delete substitutionFilter; } return outCharCount; }
LookupProcessor::LookupProcessor(const char *baseAddress, Offset scriptListOffset, Offset featureListOffset, Offset lookupListOffset, LETag scriptTag, LETag languageTag, const FeatureMap *featureMap, le_int32 featureMapCount, le_bool orderFeatures) : lookupListTable(NULL), featureListTable(NULL), lookupSelectArray(NULL), lookupOrderArray(NULL), lookupOrderCount(0) { const ScriptListTable *scriptListTable = NULL; const LangSysTable *langSysTable = NULL; le_uint16 featureCount = 0; le_uint16 lookupListCount = 0; le_uint16 requiredFeatureIndex; if (scriptListOffset != 0) { scriptListTable = (const ScriptListTable *) (baseAddress + scriptListOffset); langSysTable = scriptListTable->findLanguage(scriptTag, languageTag); if (langSysTable != 0) { featureCount = SWAPW(langSysTable->featureCount); } } if (featureListOffset != 0) { featureListTable = (const FeatureListTable *) (baseAddress + featureListOffset); } if (lookupListOffset != 0) { lookupListTable = (const LookupListTable *) (baseAddress + lookupListOffset); lookupListCount = SWAPW(lookupListTable->lookupCount); } if (langSysTable == NULL || featureListTable == NULL || lookupListTable == NULL || featureCount == 0 || lookupListCount == 0) { return; } requiredFeatureIndex = SWAPW(langSysTable->reqFeatureIndex); lookupSelectArray = LE_NEW_ARRAY(FeatureMask, lookupListCount); for (int i = 0; i < lookupListCount; i += 1) { lookupSelectArray[i] = 0; } le_int32 count, order = 0; le_int32 featureReferences = 0; const FeatureTable *featureTable = NULL; LETag featureTag; const FeatureTable *requiredFeatureTable = NULL; LETag requiredFeatureTag = 0x00000000U; // Count the total number of lookups referenced by all features. This will // be the maximum number of entries in the lookupOrderArray. We can't use // lookupListCount because some lookups might be referenced by more than // one feature. for (le_int32 feature = 0; feature < featureCount; feature += 1) { le_uint16 featureIndex = SWAPW(langSysTable->featureIndexArray[feature]); featureTable = featureListTable->getFeatureTable(featureIndex, &featureTag); featureReferences += SWAPW(featureTable->lookupCount); } if (requiredFeatureIndex != 0xFFFF) { requiredFeatureTable = featureListTable->getFeatureTable(requiredFeatureIndex, &requiredFeatureTag); featureReferences += SWAPW(featureTable->lookupCount); } lookupOrderArray = LE_NEW_ARRAY(le_uint16, featureReferences); for (le_int32 f = 0; f < featureMapCount; f += 1) { FeatureMap fm = featureMap[f]; count = 0; // If this is the required feature, add its lookups if (requiredFeatureTag == fm.tag) { count += selectLookups(requiredFeatureTable, fm.mask, order); } if (orderFeatures) { // If we added lookups from the required feature, sort them if (count > 1) { OpenTypeUtilities::sort(lookupOrderArray, order); } for (le_uint16 feature = 0; feature < featureCount; feature += 1) { le_uint16 featureIndex = SWAPW(langSysTable->featureIndexArray[feature]); // don't add the required feature to the list more than once... // TODO: Do we need this check? (Spec. says required feature won't be in feature list...) if (featureIndex == requiredFeatureIndex) { continue; } featureTable = featureListTable->getFeatureTable(featureIndex, &featureTag); if (featureTag == fm.tag) { count += selectLookups(featureTable, fm.mask, order + count); } } if (count > 1) { OpenTypeUtilities::sort(&lookupOrderArray[order], count); } order += count; } else { for (le_uint16 feature = 0; feature < featureCount; feature += 1) { le_uint16 featureIndex = SWAPW(langSysTable->featureIndexArray[feature]); // don't add the required feature to the list more than once... // NOTE: This check is commented out because the spec. says that // the required feature won't be in the feature list, and because // any duplicate entries will be removed below. #if 0 if (featureIndex == requiredFeatureIndex) { continue; } #endif featureTable = featureListTable->getFeatureTable(featureIndex, &featureTag); if (featureTag == fm.tag) { order += selectLookups(featureTable, fm.mask, order); } } } } if (!orderFeatures && (order > 1)) { OpenTypeUtilities::sort(lookupOrderArray, order); // If there's no specified feature order, // we will apply the lookups in the order // that they're in the font. If a particular // lookup may be referenced by more than one feature, // it will apprear in the lookupOrderArray more than // once, so remove any duplicate entries in the sorted array. le_int32 out = 1; for (le_int32 in = 1; in < order; in += 1) { if (lookupOrderArray[out - 1] != lookupOrderArray[in]) { if (out != in) { lookupOrderArray[out] = lookupOrderArray[in]; } out += 1; } } order = out; } lookupOrderCount = order; }
void MPreFixups::apply(LEGlyphStorage &glyphStorage, LEErrorCode& leSuccess) { if (LE_FAILURE(leSuccess)) { return; } for (le_int32 fixup = 0; fixup < fFixupCount; fixup += 1) { le_int32 baseIndex = fFixupData[fixup].fBaseIndex; le_int32 mpreIndex = fFixupData[fixup].fMPreIndex; le_int32 mpreLimit = mpreIndex + 1; while (glyphStorage[baseIndex] == 0xFFFF || glyphStorage[baseIndex] == 0xFFFE) { baseIndex -= 1; } while (glyphStorage[mpreLimit] == 0xFFFF || glyphStorage[mpreLimit] == 0xFFFE) { mpreLimit += 1; } if (mpreLimit == baseIndex) { continue; } LEErrorCode success = LE_NO_ERROR; le_int32 mpreCount = mpreLimit - mpreIndex; le_int32 moveCount = baseIndex - mpreLimit; le_int32 mpreDest = baseIndex - mpreCount; LEGlyphID *mpreSave = LE_NEW_ARRAY(LEGlyphID, mpreCount); le_int32 *indexSave = LE_NEW_ARRAY(le_int32, mpreCount); if (mpreSave == NULL || indexSave == NULL) { LE_DELETE_ARRAY(mpreSave); LE_DELETE_ARRAY(indexSave); success = LE_MEMORY_ALLOCATION_ERROR; return; } le_int32 i; for (i = 0; i < mpreCount; i += 1) { mpreSave[i] = glyphStorage[mpreIndex + i]; indexSave[i] = glyphStorage.getCharIndex(mpreIndex + i, success); //charIndices[mpreIndex + i]; } for (i = 0; i < moveCount; i += 1) { LEGlyphID glyph = glyphStorage[mpreLimit + i]; le_int32 charIndex = glyphStorage.getCharIndex(mpreLimit + i, success); glyphStorage[mpreIndex + i] = glyph; glyphStorage.setCharIndex(mpreIndex + i, charIndex, success); } for (i = 0; i < mpreCount; i += 1) { glyphStorage[mpreDest + i] = mpreSave[i]; glyphStorage.setCharIndex(mpreDest, indexSave[i], success); } LE_DELETE_ARRAY(indexSave); LE_DELETE_ARRAY(mpreSave); } }
MPreFixups::MPreFixups(le_int32 charCount) : fFixupData(NULL), fFixupCount(0) { fFixupData = LE_NEW_ARRAY(FixupData, charCount); }
le_int32 HangulOpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEUnicode *&outChars, LEGlyphStorage &glyphStorage, 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; } le_int32 worstCase = count * 3; outChars = LE_NEW_ARRAY(LEUnicode, worstCase); if (outChars == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return 0; } glyphStorage.allocateGlyphArray(worstCase, rightToLeft, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { LE_DELETE_ARRAY(outChars); return 0; } le_int32 outCharCount = 0; le_int32 limit = offset + count; le_int32 i = offset; while (i < limit) { le_int32 state = 0; le_int32 inStart = i; le_int32 outStart = outCharCount; while( i < limit) { LEUnicode lead = 0; LEUnicode vowel = 0; LEUnicode trail = 0; int32_t chClass = getCharClass(chars[i], lead, vowel, trail); const StateTransition transition = stateTable[state][chClass]; if (chClass == CC_X) { /* Any character of type X will be stored as a trail jamo */ if ((transition.actionFlags & AF_T) != 0) { outChars[outCharCount] = trail; glyphStorage.setCharIndex(outCharCount, i-offset, success); glyphStorage.setAuxData(outCharCount++, nullFeatures, success); } } else { /* Any Hangul will be fully decomposed. Output the decomposed characters. */ if ((transition.actionFlags & AF_L) != 0) { outChars[outCharCount] = lead; glyphStorage.setCharIndex(outCharCount, i-offset, success); glyphStorage.setAuxData(outCharCount++, ljmoFeatures, success); } if ((transition.actionFlags & AF_V) != 0) { outChars[outCharCount] = vowel; glyphStorage.setCharIndex(outCharCount, i-offset, success); glyphStorage.setAuxData(outCharCount++, vjmoFeatures, success); } if ((transition.actionFlags & AF_T) != 0) { outChars[outCharCount] = trail; glyphStorage.setCharIndex(outCharCount, i-offset, success); glyphStorage.setAuxData(outCharCount++, tjmoFeatures, success); } } state = transition.newState; /* Negative next state means stop. */ if (state < 0) { break; } i += 1; } le_int32 inLength = i - inStart; le_int32 outLength = outCharCount - outStart; /* * See if the syllable can be composed into a single character. There are 5 * possible cases: * * Input Decomposed to Compose to * LV L, V LV * LVT L, V, T LVT * L, V L, V LV, DEL * LV, T L, V, T LVT, DEL * L, V, T L, V, T LVT, DEL, DEL */ if ((inLength >= 1 && inLength <= 3) && (outLength == 2 || outLength == 3)) { LEUnicode syllable = 0x0000; LEUnicode lead = outChars[outStart]; LEUnicode vowel = outChars[outStart + 1]; LEUnicode trail = outLength == 3? outChars[outStart + 2] : TJMO_FIRST; /* * If the composition consumes the whole decomposed syllable, * we can use it. */ if (compose(lead, vowel, trail, syllable) == outLength) { outCharCount = outStart; outChars[outCharCount] = syllable; glyphStorage.setCharIndex(outCharCount, inStart-offset, success); glyphStorage.setAuxData(outCharCount++, nullFeatures, success); /* * Replace the rest of the input characters with DEL. */ for(le_int32 d = inStart + 1; d < i; d += 1) { outChars[outCharCount] = 0xFFFF; glyphStorage.setCharIndex(outCharCount, d - offset, success); glyphStorage.setAuxData(outCharCount++, nullFeatures, success); } } } } glyphStorage.adoptGlyphCount(outCharCount); return outCharCount; }
Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, const FontRuns *fontRuns, LEErrorCode &status) : fParagraphLayout(NULL), fParagraphCount(0), fParagraphMax(PARA_GROW), fParagraphGrow(PARA_GROW), fLineCount(0), fLinesMax(LINE_GROW), fLinesGrow(LINE_GROW), fLines(NULL), fChars(NULL), fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1), fParagraphLevel(UBIDI_DEFAULT_LTR) { static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000}; if (LE_FAILURE(status)) { return; } le_int32 ascent = 0; le_int32 descent = 0; le_int32 leading = 0; LocaleRuns *locales = NULL; FontRuns fr(0); fLines = LE_NEW_ARRAY(const ParagraphLayout::Line *, fLinesMax); fParagraphLayout = LE_NEW_ARRAY(ParagraphLayout *, fParagraphMax); fChars = LE_NEW_ARRAY(LEUnicode, charCount + 1); LE_ARRAY_COPY(fChars, chars, charCount); fChars[charCount] = 0; LEUnicode *pStart = &fChars[0]; while (*pStart != 0) { LEUnicode *pEnd = u_strpbrk(pStart, separators); le_int32 pAscent, pDescent, pLeading; ParagraphLayout *paragraphLayout = NULL; if (pEnd == NULL) { pEnd = &fChars[charCount]; } if (pEnd != pStart) { subsetFontRuns(fontRuns, pStart - fChars, pEnd - fChars, &fr); paragraphLayout = new ParagraphLayout(pStart, pEnd - pStart, &fr, NULL, NULL, locales, fParagraphLevel, FALSE, status); if (LE_FAILURE(status)) { delete paragraphLayout; break; // return? something else? } if (fParagraphLevel == UBIDI_DEFAULT_LTR) { fParagraphLevel = paragraphLayout->getParagraphLevel(); } pAscent = paragraphLayout->getAscent(); pDescent = paragraphLayout->getDescent(); pLeading = paragraphLayout->getLeading(); if (pAscent > ascent) { ascent = pAscent; } if (pDescent > descent) { descent = pDescent; } if (pLeading > leading) { leading = pLeading; } } if (fParagraphCount >= fParagraphMax) { fParagraphLayout = (ParagraphLayout **) LE_GROW_ARRAY(fParagraphLayout, fParagraphMax + fParagraphGrow); fParagraphMax += fParagraphGrow; } fParagraphLayout[fParagraphCount++] = paragraphLayout; if (*pEnd == 0) { break; } pStart = skipLineEnd(pEnd); } fLineHeight = ascent + descent + leading; fAscent = ascent; }
LookupProcessor::LookupProcessor(const LETableReference &baseAddress, Offset scriptListOffset, Offset featureListOffset, Offset lookupListOffset, LETag scriptTag, LETag languageTag, const FeatureMap *featureMap, le_int32 featureMapCount, le_bool orderFeatures, LEErrorCode& success) : lookupListTable(), featureListTable(), lookupSelectArray(NULL), lookupSelectCount(0), lookupOrderArray(NULL), lookupOrderCount(0), fReference(baseAddress) { LEReferenceTo<ScriptListTable> scriptListTable; LEReferenceTo<LangSysTable> langSysTable; le_uint16 featureCount = 0; le_uint16 lookupListCount = 0; le_uint16 requiredFeatureIndex = 0xFFFF; if (LE_FAILURE(success)) { return; } if (scriptListOffset != 0) { scriptListTable = LEReferenceTo<ScriptListTable>(baseAddress, success, scriptListOffset); langSysTable = scriptListTable->findLanguage(scriptListTable, scriptTag, languageTag, success); if (langSysTable.isValid() && LE_SUCCESS(success)) { featureCount = SWAPW(langSysTable->featureCount); } } if (featureListOffset != 0) { featureListTable = LEReferenceTo<FeatureListTable>(baseAddress, success, featureListOffset); } if (lookupListOffset != 0) { lookupListTable = LEReferenceTo<LookupListTable>(baseAddress,success, lookupListOffset); if(LE_SUCCESS(success) && lookupListTable.isValid()) { lookupListCount = SWAPW(lookupListTable->lookupCount); } } if (langSysTable.isEmpty() || featureListTable.isEmpty() || lookupListTable.isEmpty() || featureCount == 0 || lookupListCount == 0) { return; } if(langSysTable.isValid()) { requiredFeatureIndex = SWAPW(langSysTable->reqFeatureIndex); } lookupSelectArray = LE_NEW_ARRAY(FeatureMask, lookupListCount); if (lookupSelectArray == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return; } for (int i = 0; i < lookupListCount; i += 1) { lookupSelectArray[i] = 0; } lookupSelectCount = lookupListCount; le_int32 count, order = 0; le_uint32 featureReferences = 0; LEReferenceTo<FeatureTable> featureTable; LETag featureTag; LEReferenceTo<FeatureTable> requiredFeatureTable; LETag requiredFeatureTag = 0x00000000U; // Count the total number of lookups referenced by all features. This will // be the maximum number of entries in the lookupOrderArray. We can't use // lookupListCount because some lookups might be referenced by more than // one feature. if(featureListTable.isValid() && LE_SUCCESS(success)) { LEReferenceToArrayOf<le_uint16> featureIndexArray(langSysTable, success, langSysTable->featureIndexArray, featureCount); for (le_uint32 feature = 0; LE_SUCCESS(success)&&(feature < featureCount); feature += 1) { le_uint16 featureIndex = SWAPW(featureIndexArray.getObject(feature, success)); featureTable = featureListTable->getFeatureTable(featureListTable, featureIndex, &featureTag, success); if (!featureTable.isValid() || LE_FAILURE(success)) { continue; } featureReferences += SWAPW(featureTable->lookupCount); } } if (!featureTable.isValid() || LE_FAILURE(success)) { success = LE_INTERNAL_ERROR; return; } if (requiredFeatureIndex != 0xFFFF) { requiredFeatureTable = featureListTable->getFeatureTable(featureListTable, requiredFeatureIndex, &requiredFeatureTag, success); if (LE_FAILURE(success)) return; featureReferences += SWAPW(requiredFeatureTable->lookupCount); } lookupOrderArray = LE_NEW_ARRAY(le_uint16, featureReferences); if (lookupOrderArray == NULL) { success = LE_MEMORY_ALLOCATION_ERROR; return; } lookupOrderCount = featureReferences; for (le_int32 f = 0; f < featureMapCount; f += 1) { FeatureMap fm = featureMap[f]; count = 0; // If this is the required feature, add its lookups if (requiredFeatureTag == fm.tag) { count += selectLookups(requiredFeatureTable, fm.mask, order, success); } if (orderFeatures) { // If we added lookups from the required feature, sort them if (count > 1) { OpenTypeUtilities::sort(lookupOrderArray, order); } for (le_uint16 feature = 0; feature < featureCount; feature += 1) { LEReferenceToArrayOf<le_uint16> featureIndexArray(langSysTable, success, langSysTable->featureIndexArray, featureCount); if (LE_FAILURE(success)) { continue; } le_uint16 featureIndex = SWAPW(featureIndexArray.getObject(feature,success)); // don't add the required feature to the list more than once... // TODO: Do we need this check? (Spec. says required feature won't be in feature list...) if (featureIndex == requiredFeatureIndex) { continue; } featureTable = featureListTable->getFeatureTable(featureListTable, featureIndex, &featureTag, success); if (LE_FAILURE(success)) continue; if (featureTag == fm.tag) { count += selectLookups(featureTable, fm.mask, order + count, success); } } if (count > 1) { OpenTypeUtilities::sort(&lookupOrderArray[order], count); } order += count; } else if(langSysTable.isValid()) { LEReferenceToArrayOf<le_uint16> featureIndexArray(langSysTable, success, langSysTable->featureIndexArray, featureCount); for (le_uint16 feature = 0; LE_SUCCESS(success)&& (feature < featureCount); feature += 1) { le_uint16 featureIndex = SWAPW(featureIndexArray.getObject(feature,success)); // don't add the required feature to the list more than once... // NOTE: This check is commented out because the spec. says that // the required feature won't be in the feature list, and because // any duplicate entries will be removed below. #if 0 if (featureIndex == requiredFeatureIndex) { continue; } #endif featureTable = featureListTable->getFeatureTable(featureListTable, featureIndex, &featureTag, success); if (LE_FAILURE(success)) continue; if (featureTag == fm.tag) { order += selectLookups(featureTable, fm.mask, order, success); } } } } if (!orderFeatures && (order > 1)) { OpenTypeUtilities::sort(lookupOrderArray, order); // If there's no specified feature order, // we will apply the lookups in the order // that they're in the font. If a particular // lookup may be referenced by more than one feature, // it will apprear in the lookupOrderArray more than // once, so remove any duplicate entries in the sorted array. le_int32 out = 1; for (le_int32 in = 1; in < order; in += 1) { if (lookupOrderArray[out - 1] != lookupOrderArray[in]) { if (out != in) { lookupOrderArray[out] = lookupOrderArray[in]; } out += 1; } } order = out; } lookupOrderCount = order; }
const UChar *UnicodeReader::readFile(const char *fileName, GUISupport *guiSupport, int32_t &charCount) { FILE *f; int32_t fileSize; UChar *charBuffer; char *byteBuffer; char startBytes[4] = {'\xA5', '\xA5', '\xA5', '\xA5'}; char errorMessage[128]; const char *cp = ""; int32_t signatureLength = 0; f = fopen(fileName, "rb"); if( f == NULL ) { sprintf(errorMessage,"Couldn't open %s: %s \n", fileName, strerror(errno)); guiSupport->postErrorMessage(errorMessage, "Text File Error"); return 0; } fseek(f, 0, SEEK_END); fileSize = ftell(f); fseek(f, 0, SEEK_SET); fread(startBytes, sizeof(char), 4, f); if (startBytes[0] == '\xFE' && startBytes[1] == '\xFF') { cp = "UTF-16BE"; signatureLength = 2; } else if (startBytes[0] == '\xFF' && startBytes[1] == '\xFE') { if (startBytes[2] == '\x00' && startBytes[3] == '\x00') { cp = "UTF-32LE"; signatureLength = 4; } else { cp = "UTF-16LE"; signatureLength = 2; } } else if (startBytes[0] == '\xEF' && startBytes[1] == '\xBB' && startBytes[2] == '\xBF') { cp = "UTF-8"; signatureLength = 3; } else if (startBytes[0] == '\x0E' && startBytes[1] == '\xFE' && startBytes[2] == '\xFF') { cp = "SCSU"; signatureLength = 3; } else if (startBytes[0] == '\x00' && startBytes[1] == '\x00' && startBytes[2] == '\xFE' && startBytes[3] == '\xFF') { cp = "UTF-32BE"; signatureLength = 4; } else { sprintf(errorMessage, "Couldn't detect the encoding of %s: (%2.2X, %2.2X, %2.2X, %2.2X)\n", fileName, BYTE(startBytes[0]), BYTE(startBytes[1]), BYTE(startBytes[2]), BYTE(startBytes[3])); guiSupport->postErrorMessage(errorMessage, "Text File Error"); fclose(f); return 0; } fileSize -= signatureLength; fseek(f, signatureLength, SEEK_SET); byteBuffer = new char[fileSize]; if(byteBuffer == 0) { sprintf(errorMessage,"Couldn't get memory for reading %s: %s \n", fileName, strerror(errno)); guiSupport->postErrorMessage(errorMessage, "Text File Error"); fclose(f); return 0; } fread(byteBuffer, sizeof(char), fileSize, f); if( ferror(f) ) { sprintf(errorMessage,"Couldn't read %s: %s \n", fileName, strerror(errno)); guiSupport->postErrorMessage(errorMessage, "Text File Error"); fclose(f); delete[] byteBuffer; return 0; } fclose(f); UnicodeString myText(byteBuffer, fileSize, cp); delete[] byteBuffer; charCount = myText.length(); charBuffer = LE_NEW_ARRAY(UChar, charCount + 1); if(charBuffer == 0) { sprintf(errorMessage,"Couldn't get memory for reading %s: %s \n", fileName, strerror(errno)); guiSupport->postErrorMessage(errorMessage, "Text File Error"); return 0; } myText.extract(0, myText.length(), charBuffer); charBuffer[charCount] = 0; // NULL terminate for easier reading in the debugger return charBuffer; }