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;

                    // Evaluate which glyphs comprise the text.
                    formattingSpan.Glyphs = textAnalyzer.GetGlyphs(
                        new CanvasCharacterRange {
                        CharacterIndex = rangeStart, CharacterCount = length
                    },
                        fontRun.Value.FontFace,
                        fontSize,
                        false, // isSideways
                        formattingSpan.IsRightToLeft,
                        scriptRun.Value);

                    formattingSpan.FontFace = fontRun.Value.FontFace;
                    formattingSpan.FontSize = fontSize;

                    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);
        }
        List <FormattingSpan> EvaluateFormattingSpans(
            CanvasTextAnalyzer textAnalyzer,
            IReadOnlyList <KeyValuePair <CanvasCharacterRange, CanvasScaledFont> > fontRuns,
            IReadOnlyList <KeyValuePair <CanvasCharacterRange, CanvasAnalyzedScript> > scriptRuns,
            IReadOnlyList <KeyValuePair <CanvasCharacterRange, CanvasAnalyzedBidi> > bidiRuns,
            out float maxLineSpacing)
        {
            maxLineSpacing = 0;

            //
            // Divide up our text space into spans of uniform font face, script and bidi.
            //

            List <FormattingSpan> formattingSpans = new List <FormattingSpan>();

            FormattingSpanHelper formattingSpanHelper = new FormattingSpanHelper(fontRuns, scriptRuns, bidiRuns);

            while (!formattingSpanHelper.IsDone())
            {
                var scriptProperties = textAnalyzer.GetScriptProperties(formattingSpanHelper.Script);

                float fontSize = desiredFontSize * formattingSpanHelper.ScaledFont.ScaleFactor;

                FormattingSpan formattingSpan = new FormattingSpan();

                int[]  clusterMap;
                bool[] isShapedAlone;
                CanvasGlyphShaping[] glyphShaping;

                // Evaluate which glyphs comprise the text.
                formattingSpan.Glyphs = textAnalyzer.GetGlyphs(
                    formattingSpanHelper.Range,
                    formattingSpanHelper.ScaledFont.FontFace,
                    fontSize,
                    false, // isSideways
                    formattingSpanHelper.Bidi.ResolvedLevel % 2 == 1,
                    formattingSpanHelper.Script,
                    "",
                    null,
                    null,
                    out clusterMap,
                    out isShapedAlone,
                    out glyphShaping);

                formattingSpan.FontFace     = formattingSpanHelper.ScaledFont.FontFace;
                formattingSpan.FontSize     = fontSize;
                formattingSpan.Script       = formattingSpanHelper.Script;
                formattingSpan.ClusterMap   = clusterMap;
                formattingSpan.GlyphShaping = glyphShaping;
                formattingSpan.Range        = formattingSpanHelper.Range;
                formattingSpan.BidiLevel    = formattingSpanHelper.Bidi.ResolvedLevel;
                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));

                formattingSpanHelper.MoveNext();
            }

            return(formattingSpans);
        }