/// <summary> /// This method retrieves the geometry for a formatted cluster. /// </summary> /// <param name="clusterIndex"> This parameter indicates the formatted cluster index. </param> /// <param name="bidiLevel"> This parameter indicates the bidi level of the cluster. </param> /// <param name="font"> This parameter references the font for the cluster. </param> /// <param name="input"> This parameter references the formatted text output. </param> /// <returns> This method returns the geometry for the formatted cluster if available; otherwise, this method returns <c>null</c>. </returns> public Geometry Retrieve(int clusterIndex, byte bidiLevel, FontHandle font, FormatterSink input) { Contract.Requires(clusterIndex >= 0); Contract.Requires(font != null); Contract.Requires(input != null); FormattedCluster cluster = input.Clusters[clusterIndex]; ResizeInternalBuffers(cluster.Glyphs.Length); int index = 0; foreach(int itemIndex in cluster.Glyphs) { _GlyphAdvances[index] = cluster.Region.Width; _GlyphIndices[index] = input.Glyphs[itemIndex].Index; _GlyphOffsets[index] = input.Glyphs[itemIndex].Offset; ++index; } TextGeometryKey key; key.Advances = _GlyphAdvances; key.Indices = _GlyphIndices; key.Offsets = _GlyphOffsets; Dictionary<TextGeometryKey, Geometry> cacheForFont; if(_Cache.TryGetValue(font, out cacheForFont)) { Geometry result; if(cacheForFont.TryGetValue(key, out result)) { return result; } } else { cacheForFont = new Dictionary<TextGeometryKey, Geometry>(); _Cache.Add(font, cacheForFont); } Geometry geometry = _Sink.CreateGeometry(key, bidiLevel, font); TextGeometryKey newKey; newKey.Advances = (float[])key.Advances.Clone(); newKey.Indices = (short[])key.Indices.Clone(); newKey.Offsets = (GlyphOffset[])key.Offsets.Clone(); cacheForFont.Add(newKey, geometry); return geometry; }
public TextPipeline(FontDevice fontDevice) { Contract.Requires(fontDevice != null); _FontDevice = fontDevice; _TextAnalyzer = new Analyzer(_FontDevice.Factory); _AggregatorSink = new AggregatorSink(); _Aggregator = new Aggregator(_AggregatorSink); _ShaperSink = new ShaperSink(); _Shaper = new Shaper(_FontDevice, _ShaperSink); _FormatterSink = new FormatterSink(); _Formatter = new Formatter(_FormatterSink); _TypesetterSink = new TypesetterSink(); _Typesetter = new Typesetter(_TypesetterSink); _GeometryCache = new TextGeometryCache(); }
/// <summary> /// This constructor links a new instance of this class to an output sink. /// </summary> /// <param name="outputSink"> This parameter references the output sink. </param> public Formatter(FormatterSink outputSink) { Contract.Requires(outputSink != null); _OutputSink = outputSink; }
internal ParagraphMetrics( Paragraph paragraph, FormatterSink formattedData, TextGeometryCache geometryCache) { Contract.Requires(paragraph != null); Contract.Requires(formattedData != null); Contract.Requires(geometryCache != null); _Paragraph = paragraph; _Leading = formattedData.Leading; _LayoutRegion = formattedData.LayoutRegion; _BaselineOffset = formattedData.BaselineOffset; _Clusters = formattedData.Clusters.ToArray(); _ClusterBidiLevels = new byte[formattedData.Clusters.Count]; _Outlines = CreateOutlineList(formattedData, geometryCache, out _ClusterBidiLevels); // create the text index to cluster index transformation table _TextToCluster = new int[formattedData.FullText.Length]; for(int i = 0; i < _Clusters.Length; ++i) { foreach(int characterIndex in _Clusters[i].Characters) { _TextToCluster[characterIndex] = i; } } // determine the region that encompasses all formatted clusters float left = float.MaxValue; float top = float.MaxValue; float right = float.MinValue; float bottom = float.MinValue; for(int i = 0; i < _Clusters.Length; ++i) { left = Math.Min(left, _Clusters[i].Region.Left); top = Math.Min(top, _Clusters[i].Region.Top); right = Math.Max(right, _Clusters[i].Region.Right); bottom = Math.Max(bottom, _Clusters[i].Region.Bottom); } _TextRegion = Rectangle.FromEdges(left, top, right, bottom); // determine the ranges of each line _LineListBuilder.Clear(); int lastLine = 0; IndexedRange range = IndexedRange.Empty; for(int i = 0; i < formattedData.Runs.Count; ++i) { if(formattedData.Runs[i].LineNumber > lastLine) { _LineListBuilder.Add(ToTextRange(range)); range = new IndexedRange(formattedData.Runs[i].Clusters.StartIndex, 0); lastLine = formattedData.Runs[i].LineNumber; } range = range.Extend(formattedData.Runs[i].Clusters.Length); } if(range.Length > 0) { _LineListBuilder.Add(ToTextRange(range)); } _Lines = new LineCollection(_LineListBuilder); // build a collection of regions mapping to text indexes _RegionListBuilder.Clear(); for(int i = 0; i < paragraph.Text.Length; ++i) { _RegionListBuilder.Add(_Clusters[_TextToCluster[i]].Region); } _Regions = new RegionCollection(_RegionListBuilder); }
/// <summary> /// This method creates an outline collection. /// </summary> /// <param name="formattedData"> This parameter references the formatted input sink. </param> /// <param name="geometryCache"> This parameter references the text geometry cache. </param> /// <param name="clusterBidiLevels"> This parameter references the cluster bidi levels. </param> /// <returns> This method returns a new outline collection containing an outline for each formatted cluster. </returns> private static OutlineCollection CreateOutlineList( FormatterSink formattedData, TextGeometryCache geometryCache, out byte[] clusterBidiLevels) { _OutlineListBuilder.Clear(); clusterBidiLevels = new byte[formattedData.Clusters.Count]; int geometryClusterIndex = 0; for(int i = 0; i < formattedData.Runs.Count; ++i) { FormattedRun run = formattedData.Runs[i]; FontFace face = run.Font.ResolveFace(); float emSize = face.Metrics.Ascent + face.Metrics.Descent; float baseline = face.Metrics.Ascent / emSize; foreach(int clusterIndex in run.Clusters) { Geometry geometry = geometryCache.Retrieve( clusterIndex, run.BidiLevel, run.Font, formattedData); _OutlineListBuilder.Add(new Outline(geometry, run.EmSize, baseline)); clusterBidiLevels[geometryClusterIndex] = run.BidiLevel; geometryClusterIndex++; } } return new OutlineCollection(_OutlineListBuilder); }