protected void AdvanceClusterPosition(ref ClusterPosition cluster)
        {
            // Looks forward in the cluster map until finding a new cluster,
            // or until we reach the end of a cluster run returned by shaping.
            //
            // Glyph shaping can produce a clustermap where a:
            //  - A single codepoint maps to a single glyph (simple Latin and precomposed CJK)
            //  - A single codepoint to several glyphs (diacritics decomposed into distinct glyphs)
            //  - Multiple codepoints are coalesced into a single glyph.
            //
            int textPosition = cluster.textPosition;
            int clusterId    = glyphClusters_[textPosition];

            for (++textPosition; textPosition < cluster.runEndPosition; ++textPosition)
            {
                if (glyphClusters_[textPosition] != clusterId)
                {
                    // Now pointing to the next cluster.
                    cluster.textPosition = textPosition;
                    return;
                }
            }
            if (textPosition == cluster.runEndPosition)
            {
                // We crossed a text analysis run.
                SetClusterPosition(ref cluster, textPosition);
            }
        }
        protected int GetClusterGlyphStart(ref ClusterPosition cluster)
        {
            // Maps from text position to corresponding starting index in the glyph array.
            // This is needed because there isn't a 1:1 correspondence between text and
            // glyphs produced.

            int glyphStart = runs_[cluster.runIndex].glyphStart;

            return((cluster.textPosition < glyphClusters_.Length)
                ? glyphStart + glyphClusters_[cluster.textPosition]
                : glyphStart + runs_[cluster.runIndex].glyphCount);
        }
        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;
            }
        }
        // Text/cluster navigation.

        protected void SetClusterPosition(ref ClusterPosition cluster, int textPosition)
        {
            // Since layout should never split text clusters, we want to move ahead whole
            // clusters at a time.

            // Updates the current position and seeks its matching text analysis run.
            cluster.textPosition = textPosition;

            // If the new text position is outside the previous analysis run,
            // find the right one.

            if (textPosition >= cluster.runEndPosition || !runs_[cluster.runIndex].ContainsTextPosition(textPosition))
            {
                // If we can resume the search from the previous run index,
                // (meaning the new text position comes after the previously
                // seeked one), we can save some time. Otherwise restart from
                // the beginning.
                int newRunIndex = 0;
                if (textPosition >= runs_[cluster.runIndex].textStart)
                {
                    newRunIndex = cluster.runIndex;
                }

                // Find new run that contains the text position.
                for (int i = 0; i < runs_.Length; i++)
                {
                    if (runs_[i].ContainsTextPosition(textPosition))
                    {
                        newRunIndex = i;
                        break;
                    }
                }

                // Keep run index within the list, rather than pointing off the end.
                if (newRunIndex >= runs_.Length)
                {
                    newRunIndex = runs_.Length - 1;
                }

                // Cache the position of the next analysis run to efficiently
                // move forward in the clustermap.
                cluster.runIndex       = newRunIndex;
                cluster.runEndPosition = runs_[newRunIndex].textStart + runs_[newRunIndex].textLength;
            }
        }
 protected float GetClusterRangeWidth(ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd)
 {
     // Sums the glyph advances between two cluster positions,
     // useful for determining how long a line or word is.
     return(GetClusterRangeWidth(GetClusterGlyphStart(ref clusterStart), GetClusterGlyphStart(ref clusterEnd), glyphAdvances_));
 }
        protected void ProduceJustifiedAdvances(ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd, out List <float> justifiedAdvances)
        {
            // Performs simple inter-word justification
            // using the breakpoint analysis whitespace property.

            // Copy out default, unjustified advances.
            int glyphStart = GetClusterGlyphStart(ref clusterStart);
            int glyphEnd   = GetClusterGlyphStart(ref clusterEnd);

            justifiedAdvances = new List <float>(glyphEnd - glyphStart + 1);
            for (int i = glyphStart; i < glyphEnd; i++)
            {
                justifiedAdvances.Add(glyphAdvances_[i]);
            }

            if (glyphEnd - glyphStart == 0)
            {
                return; // No glyphs to modify.
            }
            float maxWidth = rect.Right - rect.Left;

            if (maxWidth <= 0)
            {
                return; // Text can't fit anyway.
            }
            ////////////////////////////////////////
            // First, count how many spaces there are in the text range.

            ClusterPosition cluster         = clusterStart;
            int             whitespaceCount = 0;

            while (cluster.textPosition < clusterEnd.textPosition)
            {
                if (breakpoints_[cluster.textPosition].IsWhitespace)
                {
                    ++whitespaceCount;
                }
                AdvanceClusterPosition(ref cluster);
            }
            if (whitespaceCount <= 0)
            {
                return; // Can't justify using spaces, since none exist.
            }
            ////////////////////////////////////////
            // Second, determine the needed contribution to each space.

            float lineWidth             = GetClusterRangeWidth(glyphStart, glyphEnd, glyphAdvances_);
            float justificationPerSpace = (maxWidth - lineWidth) / whitespaceCount;

            if (justificationPerSpace <= 0)
            {
                return; // Either already justified or would be negative justification.
            }
            if (justificationPerSpace > maxSpaceWidth_)
            {
                return; // Avoid justification if it would space the line out awkwardly far.
            }
            ////////////////////////////////////////
            // Lastly, adjust the advance widths, adding the difference to each space character.

            cluster = clusterStart;
            while (cluster.textPosition < clusterEnd.textPosition)
            {
                if (breakpoints_[cluster.textPosition].IsWhitespace)
                {
                    justifiedAdvances[GetClusterGlyphStart(ref cluster) - glyphStart] += justificationPerSpace;
                }

                AdvanceClusterPosition(ref cluster);
            }
        }
        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 FitText(ref ClusterPosition clusterStart, int textEnd, float maxWidth, out ClusterPosition clusterEnd)
        {
            // Fits as much text as possible into the given width,
            // using the clusters and advances returned by DWrite.

            ////////////////////////////////////////
            // Set the starting cluster to the starting text position,
            // and continue until we exceed the maximum width or hit
            // a hard break.
            ClusterPosition cluster            = clusterStart;
            ClusterPosition nextCluster        = clusterStart;
            int             validBreakPosition = cluster.textPosition;
            int             bestBreakPosition  = cluster.textPosition;
            float           textWidth          = 0;

            while (cluster.textPosition < textEnd)
            {
                // Use breakpoint information to find where we can safely break words.
                AdvanceClusterPosition(ref nextCluster);
                LineBreakpoint breakpoint = breakpoints_[nextCluster.textPosition - 1];

                // Check whether we exceeded the amount of text that can fit,
                // unless it's whitespace, which we allow to flow beyond the end.

                textWidth += GetClusterRangeWidth(ref cluster, ref nextCluster);
                if (textWidth > maxWidth && !breakpoint.IsWhitespace)
                {
                    // Want a minimum of one cluster.
                    if (validBreakPosition > clusterStart.textPosition)
                    {
                        break;
                    }
                }

                validBreakPosition = nextCluster.textPosition;

                // See if we can break after this character cluster, and if so,
                // mark it as the new potential break point.
                if (breakpoint.BreakConditionAfter != BreakCondition.MayNotBreak)
                {
                    bestBreakPosition = validBreakPosition;
                    if (breakpoint.BreakConditionAfter == BreakCondition.MustBreak)
                    {
                        break; // we have a hard return, so we've fit all we can.
                    }
                }
                cluster = nextCluster;
            }

            ////////////////////////////////////////
            // Want last best position that didn't break a word, but if that's not available,
            // fit at least one cluster (emergency line breaking).
            if (bestBreakPosition == clusterStart.textPosition)
            {
                bestBreakPosition = validBreakPosition;
            }

            SetClusterPosition(ref cluster, bestBreakPosition);

            clusterEnd = cluster;

            return(true);
        }
Example #9
0
 protected float GetClusterRangeWidth(ref ClusterPosition clusterStart, ref  ClusterPosition clusterEnd)
 {
     // Sums the glyph advances between two cluster positions,
     // useful for determining how long a line or word is.
     return GetClusterRangeWidth(GetClusterGlyphStart(ref clusterStart), GetClusterGlyphStart(ref clusterEnd), glyphAdvances_);
 }
Example #10
0
        protected void AdvanceClusterPosition(ref ClusterPosition cluster)
        {
            // Looks forward in the cluster map until finding a new cluster,
            // or until we reach the end of a cluster run returned by shaping.
            //
            // Glyph shaping can produce a clustermap where a:
            //  - A single codepoint maps to a single glyph (simple Latin and precomposed CJK)
            //  - A single codepoint to several glyphs (diacritics decomposed into distinct glyphs)
            //  - Multiple codepoints are coalesced into a single glyph.
            //
            int textPosition = cluster.textPosition;
            int clusterId = glyphClusters_[textPosition];

            for (++textPosition; textPosition < cluster.runEndPosition; ++textPosition)
            {
                if (glyphClusters_[textPosition] != clusterId)
                {
                    // Now pointing to the next cluster.
                    cluster.textPosition = textPosition;
                    return;
                }
            }
            if (textPosition == cluster.runEndPosition)
            {
                // We crossed a text analysis run.
                SetClusterPosition(ref cluster, textPosition);
            }
        }
Example #11
0
        protected int GetClusterGlyphStart(ref ClusterPosition cluster)
        {
            // Maps from text position to corresponding starting index in the glyph array.
            // This is needed because there isn't a 1:1 correspondence between text and
            // glyphs produced.

            int glyphStart = runs_[cluster.runIndex].glyphStart;

            return (cluster.textPosition < glyphClusters_.Length)
                ? glyphStart + glyphClusters_[cluster.textPosition]
                : glyphStart + runs_[cluster.runIndex].glyphCount;
        }
Example #12
0
        // Text/cluster navigation.

        protected void SetClusterPosition(ref ClusterPosition cluster, int textPosition)
        {
            // Since layout should never split text clusters, we want to move ahead whole
            // clusters at a time.

            // Updates the current position and seeks its matching text analysis run.
            cluster.textPosition = textPosition;

            // If the new text position is outside the previous analysis run,
            // find the right one.

            if (textPosition >= cluster.runEndPosition || !runs_[cluster.runIndex].ContainsTextPosition(textPosition))
            {
                // If we can resume the search from the previous run index,
                // (meaning the new text position comes after the previously
                // seeked one), we can save some time. Otherwise restart from
                // the beginning.
                int newRunIndex = 0;
                if (textPosition >= runs_[cluster.runIndex].textStart)
                {
                    newRunIndex = cluster.runIndex;
                }

                // Find new run that contains the text position.
                for (int i = 0; i < runs_.Length; i++)
                    if (runs_[i].ContainsTextPosition(textPosition))
                    {
                        newRunIndex = i;
                        break;
                    }

                // Keep run index within the list, rather than pointing off the end.
                if (newRunIndex >= runs_.Length)
                {
                    newRunIndex = runs_.Length - 1;
                }

                // Cache the position of the next analysis run to efficiently
                // move forward in the clustermap.
                cluster.runIndex = newRunIndex;
                cluster.runEndPosition = runs_[newRunIndex].textStart + runs_[newRunIndex].textLength;
            }
        }
Example #13
0
        protected void ProduceJustifiedAdvances(ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd, out List<float> justifiedAdvances)
        {
            // Performs simple inter-word justification
            // using the breakpoint analysis whitespace property.

            // Copy out default, unjustified advances.
            int glyphStart = GetClusterGlyphStart(ref clusterStart);
            int glyphEnd = GetClusterGlyphStart(ref clusterEnd);

            justifiedAdvances = new List<float>(glyphEnd - glyphStart + 1);
            for (int i = glyphStart; i < glyphEnd; i++)
                justifiedAdvances.Add(glyphAdvances_[i]);

            if (glyphEnd - glyphStart == 0)
                return; // No glyphs to modify.

            float maxWidth = rect.Right - rect.Left;
            if (maxWidth <= 0)
                return; // Text can't fit anyway.

            ////////////////////////////////////////
            // First, count how many spaces there are in the text range.

            ClusterPosition cluster = clusterStart;
            int whitespaceCount = 0;

            while (cluster.textPosition < clusterEnd.textPosition)
            {
                if (breakpoints_[cluster.textPosition].IsWhitespace)
                    ++whitespaceCount;
                AdvanceClusterPosition(ref cluster);
            }
            if (whitespaceCount <= 0)
                return; // Can't justify using spaces, since none exist.

            ////////////////////////////////////////
            // Second, determine the needed contribution to each space.

            float lineWidth = GetClusterRangeWidth(glyphStart, glyphEnd, glyphAdvances_);
            float justificationPerSpace = (maxWidth - lineWidth) / whitespaceCount;

            if (justificationPerSpace <= 0)
                return; // Either already justified or would be negative justification.

            if (justificationPerSpace > maxSpaceWidth_)
                return; // Avoid justification if it would space the line out awkwardly far.

            ////////////////////////////////////////
            // Lastly, adjust the advance widths, adding the difference to each space character.

            cluster = clusterStart;
            while (cluster.textPosition < clusterEnd.textPosition)
            {
                if (breakpoints_[cluster.textPosition].IsWhitespace)
                    justifiedAdvances[GetClusterGlyphStart(ref cluster) - glyphStart] += justificationPerSpace;

                AdvanceClusterPosition(ref cluster);
            }
        }
Example #14
0
        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;
        }
Example #15
0
        protected bool FitText(ref ClusterPosition clusterStart, int textEnd, float maxWidth, out ClusterPosition clusterEnd)
        {
            // Fits as much text as possible into the given width,
            // using the clusters and advances returned by DWrite.

            ////////////////////////////////////////
            // Set the starting cluster to the starting text position,
            // and continue until we exceed the maximum width or hit
            // a hard break.
            ClusterPosition cluster = clusterStart;
            ClusterPosition nextCluster = clusterStart;
            int validBreakPosition = cluster.textPosition;
            int bestBreakPosition = cluster.textPosition;
            float textWidth = 0;

            while (cluster.textPosition < textEnd)
            {
                // Use breakpoint information to find where we can safely break words.
                AdvanceClusterPosition(ref nextCluster);
                LineBreakpoint breakpoint = breakpoints_[nextCluster.textPosition - 1];

                // Check whether we exceeded the amount of text that can fit,
                // unless it's whitespace, which we allow to flow beyond the end.

                textWidth += GetClusterRangeWidth(ref cluster, ref nextCluster);
                if (textWidth > maxWidth && !breakpoint.IsWhitespace)
                {
                    // Want a minimum of one cluster.
                    if (validBreakPosition > clusterStart.textPosition)
                        break;
                }

                validBreakPosition = nextCluster.textPosition;

                // See if we can break after this character cluster, and if so,
                // mark it as the new potential break point.
                if (breakpoint.BreakConditionAfter != BreakCondition.MayNotBreak)
                {
                    bestBreakPosition = validBreakPosition;
                    if (breakpoint.BreakConditionAfter == BreakCondition.MustBreak)
                        break; // we have a hard return, so we've fit all we can.
                }
                cluster = nextCluster;
            }

            ////////////////////////////////////////
            // Want last best position that didn't break a word, but if that's not available,
            // fit at least one cluster (emergency line breaking).
            if (bestBreakPosition == clusterStart.textPosition)
                bestBreakPosition = validBreakPosition;

            SetClusterPosition(ref cluster, bestBreakPosition);

            clusterEnd = cluster;

            return true;
        }
Example #16
0
        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;
            }
        }