public void FlowText(FlowLayoutSource flowSource, FlowLayoutSink flowSink) { // Reflow all the text, from source to sink. if (!isTextAnalysisComplete_) { throw new Exception("FlowLayout: Text analysis in not complete"); } // Determine the font line height, needed by the flow source. FontMetrics fontMetrics = fontFace_.Metrics; float fontHeight = (fontMetrics.Ascent + fontMetrics.Descent + fontMetrics.LineGap) * fontEmSize_ / fontMetrics.DesignUnitsPerEm; // Set initial cluster position to beginning of text. ClusterPosition cluster = new ClusterPosition(); SetClusterPosition(ref cluster, 0); RectangleF rect; ClusterPosition nextCluster; // Iteratively pull rect's from the source, // and push as much text will fit to the sink. while (cluster.textPosition < text_.Length) { // Pull the next rect from the source. if (!flowSource.GetNextRect(fontHeight, out rect)) { break; } if (rect.Right - rect.Left <= 0) { break; // Stop upon reaching zero sized rects. } // Fit as many clusters between breakpoints that will go in. if (!FitText(ref cluster, text_.Length, rect.Right - rect.Left, out nextCluster)) { break; } // Push the glyph runs to the sink. if (!ProduceGlyphRuns(flowSink, ref rect, ref cluster, ref nextCluster)) { break; } cluster = nextCluster; } }
/// <summary> /// Initializes a new instance of the <see cref="CustomLayoutForm"/> class. /// </summary> public CustomLayoutForm() { InitializeComponent(); try { InitDirect2DAndDirectWrite(); } catch (Exception ex) { LogException(ex); Environment.Exit(1); } flowLayoutSource = new FlowLayoutSource(); flowLayoutSink = new FlowLayoutSink(); flowLayout = new FlowLayout(FactoryDWrite); SetLayoutText(LayoutText.Latin); SetLayoutShape(FlowShape.Circle); panel1.Paint += RenderControlPaint; panel1.Resize += RenderControlResize; }
protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd) { // Produce a series of glyph runs from the given range // and send them to the sink. If the entire text fit // into the rect, then we'll only pass on a single glyph // run. //////////////////////////////////////// // Figure out how many runs we cross, because this is the number // of distinct glyph runs we'll need to reorder visually. int runIndexEnd = clusterEnd.runIndex; if (clusterEnd.textPosition > runs_[runIndexEnd].textStart) { ++runIndexEnd; // Only partially cover the run, so round up. } int[] bidiOrdering = new int[100]; int totalRuns = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex); ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering); //////////////////////////////////////// // Ignore any trailing whitespace // Look backward from end until we find non-space. int trailingWsPosition; for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition) { if (!breakpoints_[trailingWsPosition - 1].IsWhitespace) { break; // Encountered last significant character. } } // Set the glyph run's ending cluster to the last whitespace. ClusterPosition clusterWsEnd = clusterStart; SetClusterPosition(ref clusterWsEnd, trailingWsPosition); //////////////////////////////////////// // Produce justified advances to reduce the jagged edge. List <float> justifiedAdvances; ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances); int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart); //////////////////////////////////////// // Determine starting point for alignment. float x = rect.Left; float y = rect.Bottom; FontMetrics fontMetrics = fontFace_.Metrics; float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm); y -= descent; if (readingDirection_ == ReadingDirection.RightToLeft) { // For RTL, we neeed the run width to adjust the origin // so it starts on the right side. int glyphStart = GetClusterGlyphStart(ref clusterStart); int glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); if (glyphStart < glyphEnd) { float lineWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); x = rect.Right - lineWidth; } } //////////////////////////////////////// // Send each glyph run to the sink. for (int i = 0; i < totalRuns; ++i) { Run run = runs_[bidiOrdering[i]]; int glyphStart = run.glyphStart; int glyphEnd = glyphStart + run.glyphCount; // If the run is only partially covered, we'll need to find // the subsection of glyphs that were fit. if (clusterStart.textPosition > run.textStart) { glyphStart = GetClusterGlyphStart(ref clusterStart); } if (clusterWsEnd.textPosition < run.textStart + run.textLength) { glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); } if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual)) { // The itemizer told us not to draw this character, // either because it was a formatting, control, or other hidden character. continue; } // The run width is needed to know how far to move forward, // and to flip the origin for right-to-left text. float runWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); // Flush this glyph run. //int glyphCount = glyphEnd - glyphStart; int glyphCount = justifiedAdvances.Count; short[] call_glyphIndices_ = new short[glyphCount]; Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length); float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)]; justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length); GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount]; Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length); flowSink.SetGlyphRun( (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL y, glyphCount, call_glyphIndices_, call_justifiedAdvances, call_glyphOffsets_, fontFace_, fontEmSize_, run.bidiLevel, run.isSideways ); x += runWidth; } return(true); }
protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd) { // Produce a series of glyph runs from the given range // and send them to the sink. If the entire text fit // into the rect, then we'll only pass on a single glyph // run. //////////////////////////////////////// // Figure out how many runs we cross, because this is the number // of distinct glyph runs we'll need to reorder visually. int runIndexEnd = clusterEnd.runIndex; if (clusterEnd.textPosition > runs_[runIndexEnd].textStart) ++runIndexEnd; // Only partially cover the run, so round up. int[] bidiOrdering = new int[100]; int totalRuns = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex); ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering); //////////////////////////////////////// // Ignore any trailing whitespace // Look backward from end until we find non-space. int trailingWsPosition; for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition) { if (!breakpoints_[trailingWsPosition - 1].IsWhitespace) break; // Encountered last significant character. } // Set the glyph run's ending cluster to the last whitespace. ClusterPosition clusterWsEnd = clusterStart; SetClusterPosition(ref clusterWsEnd, trailingWsPosition); //////////////////////////////////////// // Produce justified advances to reduce the jagged edge. List<float> justifiedAdvances; ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances); int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart); //////////////////////////////////////// // Determine starting point for alignment. float x = rect.Left; float y = rect.Bottom; FontMetrics fontMetrics = fontFace_.Metrics; float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm); y -= descent; if (readingDirection_ == ReadingDirection.RightToLeft) { // For RTL, we neeed the run width to adjust the origin // so it starts on the right side. int glyphStart = GetClusterGlyphStart(ref clusterStart); int glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); if (glyphStart < glyphEnd) { float lineWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); x = rect.Right - lineWidth; } } //////////////////////////////////////// // Send each glyph run to the sink. for (int i = 0; i < totalRuns; ++i) { Run run = runs_[bidiOrdering[i]]; int glyphStart = run.glyphStart; int glyphEnd = glyphStart + run.glyphCount; // If the run is only partially covered, we'll need to find // the subsection of glyphs that were fit. if (clusterStart.textPosition > run.textStart) { glyphStart = GetClusterGlyphStart(ref clusterStart); } if (clusterWsEnd.textPosition < run.textStart + run.textLength) { glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); } if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual)) { // The itemizer told us not to draw this character, // either because it was a formatting, control, or other hidden character. continue; } // The run width is needed to know how far to move forward, // and to flip the origin for right-to-left text. float runWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); // Flush this glyph run. //int glyphCount = glyphEnd - glyphStart; int glyphCount = justifiedAdvances.Count; short[] call_glyphIndices_ = new short[glyphCount]; Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length); float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)]; justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length); GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount]; Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length); flowSink.SetGlyphRun( (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL y, glyphCount, call_glyphIndices_, call_justifiedAdvances, call_glyphOffsets_, fontFace_, fontEmSize_, run.bidiLevel, run.isSideways ); x += runWidth; } return true; }
public void FlowText(FlowLayoutSource flowSource, FlowLayoutSink flowSink) { // Reflow all the text, from source to sink. if (!isTextAnalysisComplete_) throw new Exception("FlowLayout: Text analysis in not complete"); // Determine the font line height, needed by the flow source. FontMetrics fontMetrics = fontFace_.Metrics; float fontHeight = (fontMetrics.Ascent + fontMetrics.Descent + fontMetrics.LineGap) * fontEmSize_ / fontMetrics.DesignUnitsPerEm; // Set initial cluster position to beginning of text. ClusterPosition cluster = new ClusterPosition(); SetClusterPosition(ref cluster, 0); RectangleF rect; ClusterPosition nextCluster; // Iteratively pull rect's from the source, // and push as much text will fit to the sink. while (cluster.textPosition < text_.Length) { // Pull the next rect from the source. if (!flowSource.GetNextRect(fontHeight, out rect)) break; if (rect.Right - rect.Left <= 0) break; // Stop upon reaching zero sized rects. // Fit as many clusters between breakpoints that will go in. if (!FitText(ref cluster, text_.Length, rect.Right - rect.Left, out nextCluster)) break; // Push the glyph runs to the sink. if (!ProduceGlyphRuns(flowSink, ref rect, ref cluster, ref nextCluster)) break; cluster = nextCluster; } }