/// <summary> /// ShaperFeateruList.AddFeature - add a feature to the array /// </summary> /// <param name="feature">new feature to add</param> /// <remarks> /// This is aimed at allowing shaping engines to add features /// to the array (generally used for required, all character /// features added at the start of shaping) /// </remarks> internal void AddFeature (Feature feature) { if ( _featuresCount == _features.Length ) { // need more space, so resize the array if (!Resize((ushort)(_featuresCount + 1),_featuresCount)) { return; // can't resize array, fail quietly (not going // to apply this feature!) } } _features[_featuresCount] = feature; ++_featuresCount; }
/// <summary> /// ShaperFeateruList.AddFeature - add a feature to the array /// </summary> /// <remarks> /// An alternative to adding an already created feature. /// </remarks> internal void AddFeature (ushort startIndex, ushort length, uint featureTag, uint parameter) { if ( _featuresCount == _features.Length ) { // need more space if (!Resize((ushort)(_featuresCount + 1),_featuresCount)) { return; } } if (_features[_featuresCount] != null) { _features[_featuresCount].Tag = featureTag; _features[_featuresCount].StartIndex = startIndex; _features[_featuresCount].Length = length; _features[_featuresCount].Parameter = parameter; } else { _features[_featuresCount] = new Feature(startIndex,length,featureTag,parameter); } ++_featuresCount; }
/// <summary> /// ShaperFeateruList.Initialize - initializer for GetGlyphs. /// </summary> /// <remarks> /// Called by pertinent shaper's GetGlyph method (indirectly - /// this function is actually called by the /// ShaperBuffers.InitializeFeatureList method which is /// called by those shapers that need to create a text dependent /// list of features (e.g. Arabic, Mongolian). /// </remarks> internal bool Initialize (ushort newSize) { if (_features == null || newSize > _features.Length || newSize == 0) { Feature[] newArray = new Feature[newSize]; if (newArray != null) { _features = newArray; } } _featuresCount = 0; _minimumAddCount = 3; // add space for init,med,final whenever we need to actually resize array return _features != null; }
/// <summary> /// ShaperFeateruList.Resize - used to change the size of the features array /// </summary> /// <param name="newSize">Requested new array size</param> /// <param name="keepCount">number of features to copy into new array</param> /// <remarks> /// Used locally for each AddFeature, and by ShaperBuffers.InitializeFeatureList. /// May be called from a shaping engine. /// The "keepCount" count takes priority over "newSize" if there's already an /// array, so if size is less than keep, the resized array has at least keep elements. /// </remarks> internal bool Resize (ushort newSize, ushort keepCount) { _featuresCount = keepCount; // if (_features != null && _features.Length != 0 && keepCount > 0 && _features.Length >= keepCount) { // make sure keep count is no bigger than current // array size ushort currentLength = (ushort)_features.Length; // make sure new size is at least as big as keep count if (newSize < keepCount) { newSize = keepCount; } // if new size is bigger than the current array, create // a new array if (newSize > currentLength) { // always use minimum leap for adding to array if (newSize < (currentLength + _minimumAddCount)) { newSize = (ushort)(currentLength + _minimumAddCount); } Feature[] newArray = new Feature[newSize]; if (newArray == null) { // can't create new array, leave (at least we still // have our current array) return false; } // Our client wants us to keep the first "keepCount" entries. // so copy them now. for (int i = 0; i < keepCount; ++i) { newArray[i] = _features[i]; } _features = newArray; } } else { // nothing to keep, or currently no array so initialize return Initialize(newSize); } return true; }
private static void GetNextEnabledGlyphRange( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual nubmer of features in FeatureSet int featureSetOffset, // In: offset of input chars inside feature set FontTable Table, // Layout table (GSUB or GPOS) OpenTypeLayoutWorkspace workspace, // workspace with compiled feature set LangSysTable LangSys, // Language system FeatureList Features, // List of Features in layout table ushort lookupIndex, // List of lokups definitions in layout table int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Character to glyph mapping int StartChar, int StartGlyph, int GlyphRunLength, out int FirstChar, // First char in enabled range out int AfterLastChar, // next char after enabled range out int FirstGlyph, // First char in enabled range out int AfterLastGlyph, // next char after enabled range out uint Parameter // applied feature parameter ) { FirstChar = int.MaxValue; AfterLastChar = int.MaxValue; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; Parameter = 0; if (workspace.IsRequiredFeatureFlagSet(lookupIndex)) { FirstChar = StartChar; AfterLastChar = CharCount; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; return; } for(int feature=0; feature < featureCount; feature++) { if (!workspace.IsFeatureFlagSet(lookupIndex,feature)) { continue; } Feature featureDescription = FeatureSet[feature]; // Shift values from the feature by specified offset and // work with these values from here int featureStart = featureDescription.StartIndex - featureSetOffset; if (featureStart < 0) { featureStart = 0; } int featureAfterEnd = featureDescription.StartIndex + featureDescription.Length - featureSetOffset; if (featureAfterEnd > CharCount) { featureAfterEnd = CharCount; } //If feature is disabled there should not be any flag set Debug.Assert(featureDescription.Parameter != 0); if (featureAfterEnd <= StartChar) { continue; } if (featureStart < FirstChar || ( featureStart == FirstChar && featureAfterEnd >= AfterLastChar ) ) { FirstChar = featureStart; AfterLastChar = featureAfterEnd; Parameter = featureDescription.Parameter; continue; } } //No ranges found if (FirstChar == int.MaxValue) { FirstGlyph = GlyphRunLength; AfterLastGlyph = GlyphRunLength; } else { if (StartGlyph > Charmap[FirstChar]) FirstGlyph = StartGlyph; else FirstGlyph = Charmap[FirstChar]; if (AfterLastChar < CharCount) AfterLastGlyph = Charmap[AfterLastChar]; else AfterLastGlyph = GlyphRunLength; } }
private static void CompileFeatureSet( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual number of features in FeatureSet int featureSetOffset, //In: Offset of character input sequence inside feature set int charCount, // In: number of characters in the input string FontTable Table, // In: Layout table (GSUB or GPOS) LangSysTable LangSys, // In: Language system FeatureList Features, // In: List of Features in layout table int lookupCount, // In: number of lookup in layout table OpenTypeLayoutWorkspace workspace // In: workspace with compiled feature set ) { workspace.InitLookupUsageFlags(lookupCount, featureCount); //Set lookup uasge flags for required feature FeatureTable requiredFeatureTable = LangSys.RequiredFeature(Table, Features); if (!requiredFeatureTable.IsNull) { int featureLookupCount = requiredFeatureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetRequiredFeatureFlag(requiredFeatureTable.LookupIndex(Table,lookup)); } } //Set lookup usage flags for each feature in the FeatureSet for(int feature = 0; feature < featureCount; feature++) { Feature featureDescription = FeatureSet[feature]; //Filter out features which: // Not enabled or applied completely before or after input characters if (featureDescription.Parameter == 0 || featureDescription.StartIndex >= (featureSetOffset + charCount) || (featureDescription.StartIndex+featureDescription.Length) <= featureSetOffset ) { continue; } FeatureTable featureTable = LangSys.FindFeature( Table, Features, featureDescription.Tag); if (featureTable.IsNull) { continue; } int featureLookupCount = featureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetFeatureFlag(featureTable.LookupIndex(Table,lookup), feature); } } }
public static void ApplyFeatures( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, OpenTypeTags TableTag, FontTable Table, LayoutMetrics Metrics, LangSysTable LangSys, FeatureList Features, LookupList Lookups, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList GlyphInfo, int* Advances, LayoutOffset* Offsets ) { UpdateGlyphFlags(Font, GlyphInfo, 0, GlyphInfo.Length, false, GlyphFlags.NotChanged); // if client did not supply us with workspace // we will create our own (temporarily) if (workspace == null) { workspace = new OpenTypeLayoutWorkspace(); } ushort lookupCount=Lookups.LookupCount(Table); //Compile feature set CompileFeatureSet( FeatureSet, featureCount, featureSetOffset, CharCount, Table, LangSys, Features, lookupCount, workspace ); OpenTypeLayoutCache.InitCache(Font, TableTag, GlyphInfo, workspace); for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } int firstChar=0, afterLastChar=0, firstGlyph=0, afterLastGlyph=0; OpenTypeLayoutCache.FindNextLookup(workspace, GlyphInfo, lookupIndex, out lookupIndex, out firstGlyph); // We need to check this again, because FindNextLookup will change lookupIndex if (lookupIndex >= lookupCount) { break; } if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } LookupTable lookup = Lookups.Lookup(Table, lookupIndex); uint parameter=0; bool isLookupReversal = IsLookupReversal(TableTag, lookup.LookupType()); while(firstGlyph < GlyphInfo.Length) // While we have ranges to work on { if (!OpenTypeLayoutCache.FindNextGlyphInLookup(workspace, lookupIndex, isLookupReversal, ref firstGlyph, ref afterLastGlyph)) { firstGlyph = afterLastGlyph; } if (firstGlyph < afterLastGlyph) // Apply lookup while in one range { int nextGlyph; int oldLength = GlyphInfo.Length; int glyphsAfterLastChar = oldLength - afterLastGlyph; bool match = ApplyLookup( Font, // In: Font access interface TableTag, // Layout table tag (GSUB or GPOS) Table, // Layout table (GSUB or GPOS) Metrics, // In: LayoutMetrics lookup, // Lookup definition structure CharCount, Charmap, // In: Char to glyph mapping GlyphInfo, // In/out: List of GlyphInfo structs Advances, // In/out: Glyph adv.widths Offsets, // In/out: Glyph offsets firstGlyph, // where to apply it afterLastGlyph, // how long is a context we can use parameter, // lookup parameter 0, // Nesting level for contextual lookups out nextGlyph // out: next glyph index // !!!: for reversal lookup, should // return new afterLastGlyph ); if (match) { //Adjust range end if length changed, // for reversal changes happens beyond afterLast, no change needed if (!isLookupReversal) { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, firstGlyph, nextGlyph); afterLastGlyph = GlyphInfo.Length - glyphsAfterLastChar; firstGlyph = nextGlyph; } else { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, nextGlyph, afterLastGlyph); afterLastGlyph = nextGlyph; } } else { if (isLookupReversal) afterLastGlyph = nextGlyph; else firstGlyph = nextGlyph; } } else // End of range. Get next { GetNextEnabledGlyphRange( FeatureSet, featureCount, featureSetOffset, Table, workspace, LangSys, Features, lookupIndex, CharCount, Charmap, afterLastChar, afterLastGlyph, GlyphInfo.Length, out firstChar, out afterLastChar, out firstGlyph, out afterLastGlyph, out parameter); } } } }
internal static OpenTypeLayoutResult SubstituteGlyphs( IOpenTypeFont Font, // In: Font access interface OpenTypeLayoutWorkspace workspace, // In: Workspace for layout engine uint ScriptTag, // In: Script tag uint LangSysTag, // In: LangSys tag Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual number of features in FeatureSet int featureSetOffset, int CharCount, // In: Characters count (i.e. Charmap.Length); UshortList Charmap, // In/out: Char to glyph mapping GlyphInfoList Glyphs // In/out: List of GlyphInfo structs ) { try { FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); if (!GsubTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;} GSUBHeader GsubHeader = new GSUBHeader(0); ScriptList ScriptList = GsubHeader.GetScriptList(GsubTable); ScriptTable Script = ScriptList.FindScript(GsubTable,ScriptTag); if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;} LangSysTable LangSys = Script.FindLangSys(GsubTable,LangSysTag); if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;} FeatureList FeatureList = GsubHeader.GetFeatureList(GsubTable); LookupList LookupList = GsubHeader.GetLookupList(GsubTable); LayoutEngine.ApplyFeatures( Font, workspace, OpenTypeTags.GSUB, GsubTable, new LayoutMetrics(), //it is not needed for substitution LangSys, FeatureList, LookupList, FeatureSet, featureCount, featureSetOffset, CharCount, Charmap, Glyphs, null, null ); } catch (FileFormatException) { return OpenTypeLayoutResult.BadFontTable; } return OpenTypeLayoutResult.Success; }
internal static OpenTypeLayoutResult PositionGlyphs( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, uint ScriptTag, uint LangSysTag, LayoutMetrics Metrics, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList Glyphs, int* Advances, LayoutOffset* Offsets ) { try { FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (!GposTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;} GPOSHeader GposHeader = new GPOSHeader(0); ScriptList ScriptList = GposHeader.GetScriptList(GposTable); ScriptTable Script = ScriptList.FindScript(GposTable,ScriptTag); if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;} LangSysTable LangSys = Script.FindLangSys(GposTable,LangSysTag); if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;} FeatureList FeatureList = GposHeader.GetFeatureList(GposTable); LookupList LookupList = GposHeader.GetLookupList(GposTable); LayoutEngine.ApplyFeatures( Font, workspace, OpenTypeTags.GPOS, GposTable, Metrics, LangSys, FeatureList, LookupList, FeatureSet, featureCount, featureSetOffset, CharCount, Charmap, Glyphs, Advances, Offsets ); } catch (FileFormatException) { return OpenTypeLayoutResult.BadFontTable; } return OpenTypeLayoutResult.Success; }