protected void CompileSubstitutionAppendingFromMachine(uint featureId, SubstitutionAppendix substitution, GlyphClassesAppendix glyphClasses, StateMachine machine) { Debug.Assert(machine.States[0] == machine.EntryState, "First state is not the entry state."); SubstitutionAppendix.Feature feature = new SubstitutionAppendix.Feature(); checked { for (ushort requiredState = 0; requiredState < machine.States.Count; requiredState++) { var state = machine.States[requiredState]; foreach (var transition in state.Transitions) { SubstitutionAppendix.Rule rule = new SubstitutionAppendix.Rule(); rule.RequiredState = requiredState; rule.NewState = (ushort)machine.States.IndexOf(transition.TargetState); if (transition is AlwaysTransition) { rule.Condition = SubstitutionAppendix.RuleCondition.Unconditional; } else if (transition is SimpleTransition) { rule.Condition = SubstitutionAppendix.RuleCondition.Glyph; rule.ConditionParameter = ((SimpleTransition)transition).GlyphId; } else if (transition is SetTransition) { SetTransition setTransition = (SetTransition)transition; int[] glyphs = setTransition.GlyphIdSet.Select(id => (int)id).ToArray(); GlyphClassesAppendix.Coverage coverage = glyphClasses.FindCoverage(glyphs); if (coverage == null) { coverage = glyphClasses.AppendCoverage(glyphs); } if (!glyphClasses.Coverages.Contains(coverage)) { glyphClasses.Coverages.Add(coverage); } rule.Condition = SubstitutionAppendix.RuleCondition.GlyphClass; rule.ConditionParameter = (ushort)glyphClasses.Coverages.IndexOf(coverage); } else { Debug.Assert(false, "Unknown condition: " + transition.GetType()); continue; } if (transition.Action == null) { rule.Action = SubstitutionAppendix.RuleAction.Nothing; } else { SubstitutionAction action = transition.Action as SubstitutionAction; if (action == null) { Debug.Assert(false, "Unknown action: " + transition.Action.GetType()); continue; } int replacementGlyphCount = action.ReplacementGlyphIds.Count(); if (replacementGlyphCount == 1 && action.ReplacedGlyphCount <= 3) { if (action.ReplacedGlyphCount == 0) { rule.Action = SubstitutionAppendix.RuleAction.GlyphInsertion; } else if (action.ReplacedGlyphCount == 1) { rule.Action = SubstitutionAppendix.RuleAction.GlyphOverwrite; } else if (action.ReplacedGlyphCount == 2) { rule.Action = SubstitutionAppendix.RuleAction.GlyphRewrite_2_1; } else if (action.ReplacedGlyphCount == 3) { rule.Action = SubstitutionAppendix.RuleAction.GlyphRewrite_3_1; } else { Debug.Assert(false, "Unknown action: " + action); continue; } rule.ActionParameter = action.ReplacementGlyphIds.First(); } else if (replacementGlyphCount == 0) { rule.Action = SubstitutionAppendix.RuleAction.GlyphDeletion; rule.ActionParameter = (ushort)action.ReplacedGlyphCount; } else { rule.Action = SubstitutionAppendix.RuleAction.GlyphRewrite_N_M; rule.ActionParameter = substitution.AppendParameters(new SubstitutionAppendix.GlyphRewriteParameters((byte)action.ReplacedGlyphCount, action.ReplacementGlyphIds.Select(g => (int)g).ToArray())); } rule.ActionOffset = (sbyte)(1 - action.SkippedGlyphCount - action.ReplacedGlyphCount); rule.TapeMovement = (sbyte)action.SkippedGlyphCount; } rule.TapeMovement += (sbyte)(transition.HeadShift); feature.Rules.Add(rule); } } } substitution.Features.Add(feature); substitution.FeatureOffsets.Add(new SubstitutionAppendix.FeatureOffset { Tag = featureId }); }
/// <summary> /// Compiles state machine for substitution feature and saves state machine to <paramref name="substitution"/> appendix. /// </summary> /// <param name="typeface">Glyph typeface in which look for feature.</param> /// <param name="scriptId">ID of script in which look for feature.</param> /// <param name="languageId">ID of language in which look for feature.</param> /// <param name="featureId">ID of feature to look up.</param> /// <param name="substitution">Substitution appendix in which will be compiled state machine stored.</param> /// <param name="glyphClasses">Glyph classes appendix for use by substitution appendix.</param> /// <param name="availableGlyphs">Which glyphs should be restricted in state machine.</param> /// <returns>All used glyphs during compilation. Can add additional glyphs to availableGlyphs.</returns> public virtual IEnumerable <ushort> CompileFeature(GlyphTypeface typeface, uint scriptId, uint languageId, uint featureId, SubstitutionAppendix substitution, GlyphClassesAppendix glyphClasses, IEnumerable <ushort> availableGlyphs) { StateMachine machine = GetOrCompile(_gsubParser, new SubstitutionCompiler(), typeface, scriptId, languageId, featureId); machine = new StateMachineOptimizer().Optimize(machine); machine = new StateMachineNormalizer().Normalize(machine, availableGlyphs); this.CompileSubstitutionAppendingFromMachine(featureId, substitution, glyphClasses, machine); return(machine.GetGeneratedGlyphIds()); }