le_int32 HanOpenTypeLayoutEngine::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; } glyphStorage.allocateGlyphArray(count, FALSE, success); glyphStorage.allocateAuxData(success); if (LE_FAILURE(success)) { return 0; } // FIXME: do we want to add the 'trad' feature for 'ZHT' and the // 'smpl' feature for 'ZHS'? If we do this, we can remove the exact // flag from the language tag lookups, so we can use these features // with the default LangSys... for (le_int32 i = 0; i < count; i += 1) { glyphStorage.setAuxData(i, features, success); } return count; }
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; } le_int32 outCharCount = LayoutEngine::characterProcessing(chars, offset, count, max, rightToLeft, outChars, glyphStorage, success); if (LE_FAILURE(success)) { return 0; } glyphStorage.allocateGlyphArray(outCharCount, rightToLeft, success); glyphStorage.allocateAuxData(success); for (le_int32 i = 0; i < outCharCount; i += 1) { glyphStorage.setAuxData(i, (void *) fFeatureList, success); } return outCharCount; }
void ArabicShaping::adjustTags(le_int32 outIndex, le_int32 shapeOffset, LEGlyphStorage &glyphStorage) { LEErrorCode success = LE_NO_ERROR; FeatureMask featureMask = (FeatureMask) glyphStorage.getAuxData(outIndex, success); FeatureMask shape = featureMask & SHAPE_MASK; shape >>= shapeOffset; glyphStorage.setAuxData(outIndex, ((featureMask & ~SHAPE_MASK) | shape), success); }
void IndicReordering::applyPresentationForms(LEGlyphStorage &glyphStorage, le_int32 count) { LEErrorCode success = LE_NO_ERROR; // This sets us up for 2nd pass of glyph substitution as well as setting the feature masks for the // GPOS table lookups for ( le_int32 i = 0 ; i < count ; i++ ) { glyphStorage.setAuxData(i, ( presentationFormsMask | positioningFormsMask ), success); } }
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; }
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; }
void ArabicShaping::shape(const LEUnicode *chars, le_int32 offset, le_int32 charCount, le_int32 charMax, le_bool rightToLeft, LEGlyphStorage &glyphStorage) { // iterate in logical order, store tags in visible order // // the effective right char is the most recently encountered // non-transparent char // // four boolean states: // the effective right char shapes // the effective right char causes left shaping // the current char shapes // the current char causes right shaping // // if both cause shaping, then // shaper.shape(errout, 2) (isolate to initial, or final to medial) // shaper.shape(out, 1) (isolate to final) ShapeType rightType = ST_NOSHAPE_NONE, leftType = ST_NOSHAPE_NONE; LEErrorCode success = LE_NO_ERROR; le_int32 i; for (i = offset - 1; i >= 0; i -= 1) { rightType = getShapeType(chars[i]); if (rightType != ST_TRANSPARENT) { break; } } for (i = offset + charCount; i < charMax; i += 1) { leftType = getShapeType(chars[i]); if (leftType != ST_TRANSPARENT) { break; } } // erout is effective right logical index le_int32 erout = -1; le_bool rightShapes = FALSE; le_bool rightCauses = (rightType & MASK_SHAPE_LEFT) != 0; le_int32 in, e, out = 0, dir = 1; if (rightToLeft) { out = charCount - 1; erout = charCount; dir = -1; } for (in = offset, e = offset + charCount; in < e; in += 1, out += dir) { LEUnicode c = chars[in]; ShapeType t = getShapeType(c); if (t == ST_NOSHAPE_NONE) { glyphStorage.setAuxData(out, NO_FEATURES, success); } else { glyphStorage.setAuxData(out, ISOL_FEATURES, success); } if ((t & MASK_TRANSPARENT) != 0) { continue; } le_bool curShapes = (t & MASK_NOSHAPE) == 0; le_bool curCauses = (t & MASK_SHAPE_RIGHT) != 0; if (rightCauses && curCauses) { if (rightShapes) { adjustTags(erout, 2, glyphStorage); } if (curShapes) { adjustTags(out, 1, glyphStorage); } } rightShapes = curShapes; rightCauses = (t & MASK_SHAPE_LEFT) != 0; erout = out; } if (rightShapes && rightCauses && (leftType & MASK_SHAPE_RIGHT) != 0) { adjustTags(erout, 2, glyphStorage); } }
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; }
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; }