void SplitJustifiedGlyphsIntoRuns(CanvasTextAnalyzer textAnalyzer, LayoutBox layoutBox, CanvasGlyph[] justifiedGlyphs, bool needsAdditionalJustificationCharacters) { int glyphIndex = 0; float xPosition = (float)layoutBox.Rectangle.Right; for (int i = 0; i < layoutBox.GlyphRuns.Count; i++) { if (layoutBox.GlyphRuns[i].Glyphs.Count == 0) { continue; } int originalGlyphCountForThisRun = layoutBox.GlyphRuns[i].Glyphs.Count; if (needsAdditionalJustificationCharacters) { // Replace the glyph data, since justification can modify glyphs CanvasGlyph[] justifiedGlyphsForThisGlyphRun = new CanvasGlyph[layoutBox.GlyphRuns[i].Glyphs.Count]; for (int j = 0; j < layoutBox.GlyphRuns[i].Glyphs.Count; j++) { justifiedGlyphsForThisGlyphRun[j] = justifiedGlyphs[glyphIndex + j]; } CanvasCharacterRange range = layoutBox.GlyphRuns[i].GetRange(); var glyphRunClusterMap = layoutBox.GlyphRuns[i].GetClusterMap(range); var glyphRunShaping = layoutBox.GlyphRuns[i].GetShaping(); CanvasGlyph[] newSetOfGlyphs = textAnalyzer.AddGlyphsAfterJustification( layoutBox.GlyphRuns[i].FormattingSpan.FontFace, layoutBox.GlyphRuns[i].FormattingSpan.FontSize, layoutBox.GlyphRuns[i].FormattingSpan.Script, glyphRunClusterMap, layoutBox.GlyphRuns[i].Glyphs.ToArray(), justifiedGlyphsForThisGlyphRun, glyphRunShaping); layoutBox.GlyphRuns[i].Glyphs = new List <CanvasGlyph>(newSetOfGlyphs); } else { for (int j = 0; j < layoutBox.GlyphRuns[i].Glyphs.Count; j++) { layoutBox.GlyphRuns[i].Glyphs[j] = justifiedGlyphs[glyphIndex + j]; } } glyphIndex += originalGlyphCountForThisRun; } }
public int[] GetClusterMap(CanvasCharacterRange range) { // // Create a cluster map for this character range. Because the cluster map // should reflect only the text positions in the range, we need to re-normalize // it (so that it starts at 0). // int[] clusterMap = new int[range.CharacterCount]; int formattingSpanStartIndex = FormattingSpan.Range.CharacterIndex; int firstClusterMapValue = FormattingSpan.ClusterMap[range.CharacterIndex - formattingSpanStartIndex]; for (int i = 0; i < range.CharacterCount; ++i) { int indexWithinFormattingSpan = range.CharacterIndex - formattingSpanStartIndex + i; // Cluster maps are per formatting span. clusterMap[i] = FormattingSpan.ClusterMap[indexWithinFormattingSpan] - firstClusterMapValue; } return(clusterMap); }
CanvasJustificationOpportunity[] GetJustificationOpportunities(CanvasTextAnalyzer textAnalyzer, LayoutBox layoutBox, out CanvasGlyph[] allGlyphs) { int layoutBoxGlyphCount = layoutBox.GetGlyphCount(); CanvasJustificationOpportunity[] justificationOpportunities = new CanvasJustificationOpportunity[layoutBoxGlyphCount]; allGlyphs = new CanvasGlyph[layoutBoxGlyphCount]; int glyphIndex = 0; for (int i = 0; i < layoutBox.GlyphRuns.Count; i++) { if (layoutBox.GlyphRuns[i].Glyphs.Count == 0) { continue; } CanvasCharacterRange range = layoutBox.GlyphRuns[i].GetRange(); var glyphRunClusterMap = layoutBox.GlyphRuns[i].GetClusterMap(range); var glyphRunShaping = layoutBox.GlyphRuns[i].GetShaping(); var justificationOpportunitiesThisGlyphRun = textAnalyzer.GetJustificationOpportunities( range, layoutBox.GlyphRuns[i].FormattingSpan.FontFace, layoutBox.GlyphRuns[i].FormattingSpan.FontSize, layoutBox.GlyphRuns[i].FormattingSpan.Script, glyphRunClusterMap, glyphRunShaping); for (int j = 0; j < layoutBox.GlyphRuns[i].Glyphs.Count; ++j) { justificationOpportunities[glyphIndex + j] = justificationOpportunitiesThisGlyphRun[j]; allGlyphs[glyphIndex + j] = layoutBox.GlyphRuns[i].Glyphs[j]; } glyphIndex += layoutBox.GlyphRuns[i].Glyphs.Count; } return(justificationOpportunities); }
CanvasJustificationOpportunity[] GetJustificationOpportunities(CanvasTextAnalyzer textAnalyzer, int startingGlyphRunIndex, int endingGlyphRunIndex, int glyphCount, out CanvasGlyph[] allGlyphs) { CanvasJustificationOpportunity[] justificationOpportunities = new CanvasJustificationOpportunity[glyphCount]; allGlyphs = new CanvasGlyph[glyphCount]; int glyphIndex = 0; for (int i = startingGlyphRunIndex; i < endingGlyphRunIndex; ++i) { if (glyphRuns[i].Glyphs.Count == 0) { continue; } CanvasCharacterRange range = glyphRuns[i].GetRange(); var glyphRunClusterMap = glyphRuns[i].GetClusterMap(range); var glyphRunShaping = glyphRuns[i].GetShaping(); var justificationOpportunitiesThisGlyphRun = textAnalyzer.GetJustificationOpportunities( range, glyphRuns[i].FormattingSpan.FontFace, glyphRuns[i].FormattingSpan.FontSize, glyphRuns[i].FormattingSpan.Script, glyphRunClusterMap, glyphRunShaping); for (int j = 0; j < glyphRuns[i].Glyphs.Count; ++j) { justificationOpportunities[glyphIndex + j] = justificationOpportunitiesThisGlyphRun[j]; allGlyphs[glyphIndex + j] = glyphRuns[i].Glyphs[j]; } glyphIndex += glyphRuns[i].Glyphs.Count; } return(justificationOpportunities); }
int GetBoundary(CanvasCharacterRange range) { return(range.CharacterIndex + range.CharacterCount); }
void SplitJustifiedGlyphsIntoRuns(CanvasTextAnalyzer textAnalyzer, int startingGlyphRunIndex, int endingGlyphRunIndex, CanvasGlyph[] justifiedGlyphs, bool needsAdditionalJustificationCharacters) { int glyphIndex = 0; float xPosition = glyphRuns[startingGlyphRunIndex].Position.X; for (int i = startingGlyphRunIndex; i < endingGlyphRunIndex; ++i) { if (glyphRuns[i].Glyphs.Count == 0) { continue; } // Adjust glyph run positioning based on justification glyphRuns[i].Position = new Vector2() { X = xPosition, Y = glyphRuns[i].Position.Y }; // Update running total glyph run advance for (int j = 0; j < glyphRuns[i].Glyphs.Count; j++) { xPosition += glyphRuns[i].Glyphs[j].Advance; } if (needsAdditionalJustificationCharacters) { // Replace the glyph data, since justification can modify glyphs CanvasGlyph[] justifiedGlyphsForThisGlyphRun = new CanvasGlyph[glyphRuns[i].Glyphs.Count]; for (int j = 0; j < glyphRuns[i].Glyphs.Count; j++) { justifiedGlyphsForThisGlyphRun[j] = justifiedGlyphs[glyphIndex + j]; } CanvasCharacterRange range = glyphRuns[i].GetRange(); var glyphRunClusterMap = glyphRuns[i].GetClusterMap(range); var glyphRunShaping = glyphRuns[i].GetShaping(); CanvasGlyph[] newSetOfGlyphs = textAnalyzer.AddGlyphsAfterJustification( glyphRuns[i].FormattingSpan.FontFace, glyphRuns[i].FormattingSpan.FontSize, glyphRuns[i].FormattingSpan.Script, glyphRunClusterMap, glyphRuns[i].Glyphs.ToArray(), justifiedGlyphsForThisGlyphRun, glyphRunShaping); glyphRuns[i].Glyphs = new List <CanvasGlyph>(newSetOfGlyphs); } else { for (int j = 0; j < glyphRuns[i].Glyphs.Count; j++) { glyphRuns[i].Glyphs[j] = justifiedGlyphs[glyphIndex + j]; } } glyphIndex += glyphRuns[i].Glyphs.Count; } }
List <FormattingSpan> EvaluateFormattingSpans( CanvasTextAnalyzer textAnalyzer, IReadOnlyList <KeyValuePair <CanvasCharacterRange, CanvasScaledFont> > fontRuns, IReadOnlyList <KeyValuePair <CanvasCharacterRange, CanvasAnalyzedScript> > scriptRuns, out float maxLineSpacing) { maxLineSpacing = 0; List <FormattingSpan> formattingSpans = new List <FormattingSpan>(); // // Divide up our text space into spans of uniform font face and uniform script. // foreach (var scriptRun in scriptRuns) { var scriptProperties = textAnalyzer.GetScriptProperties(scriptRun.Value); bool isRightToLeft = IsRightToLeft(scriptProperties.IsoScriptNumber); foreach (var fontRun in fontRuns) { int fontMatchEnd = fontRun.Key.CharacterIndex + fontRun.Key.CharacterCount; int scriptEnd = scriptRun.Key.CharacterIndex + scriptRun.Key.CharacterCount; if (fontRun.Key.CharacterIndex > scriptEnd) { continue; } if (fontMatchEnd < scriptRun.Key.CharacterIndex) { continue; } int rangeStart = System.Math.Max(fontRun.Key.CharacterIndex, scriptRun.Key.CharacterIndex); int rangeEnd = System.Math.Min(fontMatchEnd, scriptEnd); int length = rangeEnd - rangeStart; float fontSize = desiredFontSize * fontRun.Value.ScaleFactor; FormattingSpan formattingSpan = new FormattingSpan(); formattingSpan.IsRightToLeft = isRightToLeft; int[] clusterMap; bool[] isShapedAlone; CanvasGlyphShaping[] glyphShaping; CanvasCharacterRange range = new CanvasCharacterRange { CharacterIndex = rangeStart, CharacterCount = length }; // Evaluate which glyphs comprise the text. formattingSpan.Glyphs = textAnalyzer.GetGlyphs( range, fontRun.Value.FontFace, fontSize, false, // isSideways formattingSpan.IsRightToLeft, scriptRun.Value, "", null, null, out clusterMap, out isShapedAlone, out glyphShaping); formattingSpan.FontFace = fontRun.Value.FontFace; formattingSpan.FontSize = fontSize; formattingSpan.Script = scriptRun.Value; formattingSpan.ClusterMap = clusterMap; formattingSpan.GlyphShaping = glyphShaping; formattingSpan.Range = range; formattingSpan.NeedsAdditionalJustificationCharacters = scriptProperties.JustificationCharacter != null; formattingSpans.Add(formattingSpan); // // For text which contains non-uniform font faces, CanvasTextLayout takes the maximum of // all of line spacings and applies it as the overall line spacing. We do the same thing, here. // maxLineSpacing = System.Math.Max(maxLineSpacing, GetLineSpacing(formattingSpan.FontFace, fontSize)); } } return(formattingSpans); }