~jaro/balkon

0e50058d5a6739068d79b08f6fac7fc29aa39f92 — Jaro 1 year, 7 months ago 7a73994
Add user data to spans.
40 files changed, 479 insertions(+), 280 deletions(-)

M .golden/paginatedParagraphLayout/loremIpsum20em.golden
M .golden/paginatedParagraphLayout/spannedArabicFiller20em.golden
M .golden/paragraphLayout/arabicFiller20em.golden
M .golden/paragraphLayout/czechHello.golden
M .golden/paragraphLayout/czechHelloParagraphNarrow.golden
M .golden/paragraphLayout/czechHelloParagraphUltraNarrow.golden
M .golden/paragraphLayout/devanagari.golden
M .golden/paragraphLayout/devanagariAccent.golden
M .golden/paragraphLayout/devanagariPrefixedAccent.golden
M .golden/paragraphLayout/hardBreaksLTR.golden
M .golden/paragraphLayout/hardBreaksRTL.golden
M .golden/paragraphLayout/ligature.golden
M .golden/paragraphLayout/ligatureParagraphBreak1.golden
M .golden/paragraphLayout/ligatureParagraphBreak2.golden
M .golden/paragraphLayout/lineHeightLarger.golden
M .golden/paragraphLayout/lineHeightNormal.golden
M .golden/paragraphLayout/lineHeightSmaller.golden
M .golden/paragraphLayout/loremIpsum100em.golden
M .golden/paragraphLayout/loremIpsum20em.golden
M .golden/paragraphLayout/manySpaces.golden
M .golden/paragraphLayout/mixedLanguageLTR.golden
M .golden/paragraphLayout/mixedScriptWords.golden
M .golden/paragraphLayout/spannedArabicFiller20em.golden
M .golden/paragraphLayout/spannedLoremIpsum20em.golden
M CHANGELOG.md
M lib/Data/Text/ParagraphLayout/Plain.hs
M src/Data/Text/ParagraphLayout/Internal/Fragment.hs
M src/Data/Text/ParagraphLayout/Internal/Paginable.hs
M src/Data/Text/ParagraphLayout/Internal/ParagraphConstruction.hs
M src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs
M src/Data/Text/ParagraphLayout/Internal/Plain.hs
M src/Data/Text/ParagraphLayout/Internal/Plain/Paragraph.hs
M src/Data/Text/ParagraphLayout/Internal/Plain/ParagraphLayout.hs
M src/Data/Text/ParagraphLayout/Internal/ResolvedSpan.hs
M src/Data/Text/ParagraphLayout/Internal/Run.hs
M src/Data/Text/ParagraphLayout/Internal/Span.hs
M test/Data/Text/ParagraphLayout/Plain/ParagraphData.hs
M test/Data/Text/ParagraphLayout/PlainSpec.hs
M test/Data/Text/ParagraphLayout/PrettyShow.hs
M test/Data/Text/ParagraphLayout/SpanData.hs
M .golden/paginatedParagraphLayout/loremIpsum20em.golden => .golden/paginatedParagraphLayout/loremIpsum20em.golden +22 -11
@@ 3,7 3,8 @@
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 18310, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 49,7 50,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 17443, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 101,7 103,8 @@
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 152,7 155,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 17562, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 196,7 200,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 18769, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 247,7 252,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 19226, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 294,7 300,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 17973, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 342,7 349,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 18971, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 400,7 408,8 @@
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 17239, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 444,7 453,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19199, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 493,7 503,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 12076, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paginatedParagraphLayout/spannedArabicFiller20em.golden => .golden/paginatedParagraphLayout/spannedArabicFiller20em.golden +28 -14
@@ 3,7 3,8 @@
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 12652, y_origin = 0, x_size = 7198, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 31,7 32,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 7060, y_origin = 0, x_size = 5592, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 52,7 54,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 7060, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 81,7 84,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 14850, y_origin = -1500, x_size = 4045, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 101,7 105,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 9168, y_origin = -1500, x_size = 5682, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 127,7 132,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 3213, y_origin = -1500, x_size = 5955, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 153,7 159,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1500, x_size = 3213, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 209,7 216,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 14300, y_origin = 0, x_size = 4862, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 232,7 240,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 10225, y_origin = 0, x_size = 4075, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 251,7 260,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 3898, y_origin = 0, x_size = 6327, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 277,7 287,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 3898, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 292,7 303,8 @@
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 12238, y_origin = -1500, x_size = 2357, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 308,7 320,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 5880, y_origin = -1500, x_size = 6358, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 335,7 348,8 @@
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -1500, x_size = 5880, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =

M .golden/paragraphLayout/arabicFiller20em.golden => .golden/paragraphLayout/arabicFiller20em.golden +8 -4
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 19850, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 58,7 59,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1500, x_size = 18895, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 116,7 118,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -3000, x_size = 19162, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 169,7 172,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -4500, x_size = 14595, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =

M .golden/paragraphLayout/czechHello.golden => .golden/paragraphLayout/czechHello.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 5274, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/czechHelloParagraphNarrow.golden => .golden/paragraphLayout/czechHelloParagraphNarrow.golden +10 -5
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 1234, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 12,7 13,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 1089, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 22,7 24,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 948, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 31,7 34,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 961, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 40,7 44,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 835, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/czechHelloParagraphUltraNarrow.golden => .golden/paragraphLayout/czechHelloParagraphUltraNarrow.golden +22 -11
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 663, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 11,7 12,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 571, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 19,7 21,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 590, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 27,7 30,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 253, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 35,7 39,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 246, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 43,7 48,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 446, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 51,7 57,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 502, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 59,7 66,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 559, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 67,7 75,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 402, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 75,7 84,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 0, y_origin = -10089, x_size = 559, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 83,7 93,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 0, y_origin = -11210, x_size = 276, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/devanagari.golden => .golden/paragraphLayout/devanagari.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 8640, y_size = -1630}
            , fragmentPen = (0, -1171)
            , fragmentGlyphs =

M .golden/paragraphLayout/devanagariAccent.golden => .golden/paragraphLayout/devanagariAccent.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 645, y_size = -1000}
            , fragmentPen = (0, -500)
            , fragmentGlyphs =

M .golden/paragraphLayout/devanagariPrefixedAccent.golden => .golden/paragraphLayout/devanagariPrefixedAccent.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 0, y_size = -1000}
            , fragmentPen = (0, -500)
            , fragmentGlyphs =

M .golden/paragraphLayout/hardBreaksLTR.golden => .golden/paragraphLayout/hardBreaksLTR.golden +18 -9
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 1563, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 16,7 17,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 3357, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 36,7 38,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 1563, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 49,7 52,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 4305, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 61,7 65,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 861, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 69,7 74,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 1563, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 82,14 88,16 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 0, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =
                []
            }
        , Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 3675, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 101,7 109,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 1722, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/hardBreaksRTL.golden => .golden/paragraphLayout/hardBreaksRTL.golden +18 -9
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 2808, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 16,7 17,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1500, x_size = 5852, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 36,7 38,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -3000, x_size = 2808, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 49,7 52,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -4500, x_size = 4884, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 62,7 66,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -6000, x_size = 1211, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 70,7 75,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -7500, x_size = 2808, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 83,14 89,16 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -9000, x_size = 0, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =
                []
            }
        , Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -10500, x_size = 4156, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 102,7 110,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = -12000, x_size = 1960, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =

M .golden/paragraphLayout/ligature.golden => .golden/paragraphLayout/ligature.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 4672, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/ligatureParagraphBreak1.golden => .golden/paragraphLayout/ligatureParagraphBreak1.golden +6 -3
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 2162, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 14,7 15,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 2104, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 26,7 28,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 402, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/ligatureParagraphBreak2.golden => .golden/paragraphLayout/ligatureParagraphBreak2.golden +6 -3
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 1772, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 14,7 15,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 1361, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 24,7 26,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 1535, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/lineHeightLarger.golden => .golden/paragraphLayout/lineHeightLarger.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = -1600}
            , fragmentPen = (0, -1172)
            , fragmentGlyphs =

M .golden/paragraphLayout/lineHeightNormal.golden => .golden/paragraphLayout/lineHeightNormal.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/lineHeightSmaller.golden => .golden/paragraphLayout/lineHeightSmaller.golden +2 -1
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = -599}
            , fragmentPen = (0, -671)
            , fragmentGlyphs =

M .golden/paragraphLayout/loremIpsum100em.golden => .golden/paragraphLayout/loremIpsum100em.golden +6 -3
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 95412, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 222,7 223,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 98954, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 450,7 452,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 4041, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/loremIpsum20em.golden => .golden/paragraphLayout/loremIpsum20em.golden +22 -11
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 18310, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 49,7 50,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 17443, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 94,7 96,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 19791, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 145,7 148,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 17562, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 189,7 193,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 18769, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 240,7 245,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 19226, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 287,7 293,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 17973, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 335,7 342,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 18971, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 386,7 394,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 17239, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 430,7 439,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 0, y_origin = -10089, x_size = 19199, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 479,7 489,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 0, y_origin = -11210, x_size = 12076, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/manySpaces.golden => .golden/paragraphLayout/manySpaces.golden +8 -4
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 3132, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 16,7 17,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 3132, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 29,7 31,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 3132, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 42,7 45,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 3132, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/mixedLanguageLTR.golden => .golden/paragraphLayout/mixedLanguageLTR.golden +4 -2
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 4837, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 24,7 25,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 4837, y_origin = 0, x_size = 1276, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/mixedScriptWords.golden => .golden/paragraphLayout/mixedScriptWords.golden +46 -23
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 13,7 14,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 777, y_origin = 0, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 23,7 25,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 3112, y_origin = 0, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 33,7 36,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 43,7 47,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 777, y_origin = -1121, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 53,7 58,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 3112, y_origin = -1121, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 63,7 69,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 73,7 80,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 2335, y_origin = -2242, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 83,7 91,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 3112, y_origin = -2242, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 93,7 102,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 103,7 113,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 2335, y_origin = -3363, x_size = 777, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 113,7 124,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 3112, y_origin = -3363, x_size = 2335, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 123,7 135,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 253, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 131,7 144,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 253, y_origin = -4484, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 139,7 153,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 1016, y_origin = -4484, x_size = 737, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 149,7 164,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 1753, y_origin = -4484, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 157,7 173,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 2516, y_origin = -4484, x_size = 484, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 166,7 183,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 3000, y_origin = -4484, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 174,7 192,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 3763, y_origin = -4484, x_size = 253, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 182,7 201,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 4016, y_origin = -4484, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 190,7 210,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 198,7 219,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 763, y_origin = -5605, x_size = 253, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 206,7 228,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 1016, y_origin = -5605, x_size = 763, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M .golden/paragraphLayout/spannedArabicFiller20em.golden => .golden/paragraphLayout/spannedArabicFiller20em.golden +28 -14
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 12652, y_origin = 0, x_size = 7198, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 31,7 32,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 7060, y_origin = 0, x_size = 5592, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 52,7 54,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 7060, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 81,7 84,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 14850, y_origin = -1500, x_size = 4045, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 101,7 105,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 9168, y_origin = -1500, x_size = 5682, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 127,7 132,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 3213, y_origin = -1500, x_size = 5955, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 153,7 159,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1500, x_size = 3213, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 169,7 176,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 14300, y_origin = -3000, x_size = 4862, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 192,7 200,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 10225, y_origin = -3000, x_size = 4075, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 211,7 220,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 3898, y_origin = -3000, x_size = 6327, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 237,7 247,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -3000, x_size = 3898, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 252,7 263,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 12238, y_origin = -4500, x_size = 2357, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 268,7 280,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 5880, y_origin = -4500, x_size = 6358, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =


@@ 295,7 308,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -4500, x_size = 5880, y_size = -1500}
            , fragmentPen = (0, -1085)
            , fragmentGlyphs =

M .golden/paragraphLayout/spannedLoremIpsum20em.golden => .golden/paragraphLayout/spannedLoremIpsum20em.golden +60 -30
@@ 3,7 3,8 @@ ParagraphLayout
    , spanLayouts = [
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 8747, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 31,7 32,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 1
            { fragmentUserData = ()
            , fragmentLine = 1
            , fragmentRect = Rect {x_origin = 8747, y_origin = 0, x_size = 9563, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 62,7 64,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 8553, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 93,7 96,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 2
            { fragmentUserData = ()
            , fragmentLine = 2
            , fragmentRect = Rect {x_origin = 8553, y_origin = -1121, x_size = 8890, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 120,7 124,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 9114, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 151,7 156,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 9114, y_origin = -2242, x_size = 7467, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 176,7 182,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 3
            { fragmentUserData = ()
            , fragmentLine = 3
            , fragmentRect = Rect {x_origin = 16581, y_origin = -2242, x_size = 3210, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 191,7 198,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 3799, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 209,7 217,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 3799, y_origin = -3363, x_size = 7878, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 235,7 244,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 4
            { fragmentUserData = ()
            , fragmentLine = 4
            , fragmentRect = Rect {x_origin = 11677, y_origin = -3363, x_size = 5885, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 255,7 265,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 5686, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 278,7 289,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 5686, y_origin = -4484, x_size = 8592, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 308,7 320,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 5
            { fragmentUserData = ()
            , fragmentLine = 5
            , fragmentRect = Rect {x_origin = 14278, y_origin = -4484, x_size = 4491, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 326,7 339,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 1301, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 339,7 353,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 1301, y_origin = -5605, x_size = 11041, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 370,7 385,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 6
            { fragmentUserData = ()
            , fragmentLine = 6
            , fragmentRect = Rect {x_origin = 12342, y_origin = -5605, x_size = 6884, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 396,7 412,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 10089, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 428,7 445,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 7
            { fragmentUserData = ()
            , fragmentLine = 7
            , fragmentRect = Rect {x_origin = 10089, y_origin = -6726, x_size = 7884, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 457,7 475,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 8158, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 485,7 504,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 8158, y_origin = -7847, x_size = 6988, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 512,7 532,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 8
            { fragmentUserData = ()
            , fragmentLine = 8
            , fragmentRect = Rect {x_origin = 15146, y_origin = -7847, x_size = 3825, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 528,7 549,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 4594, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 547,7 569,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 4594, y_origin = -8968, x_size = 10907, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 582,7 605,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 9
            { fragmentUserData = ()
            , fragmentLine = 9
            , fragmentRect = Rect {x_origin = 15501, y_origin = -8968, x_size = 1738, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 592,7 616,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 0, y_origin = -10089, x_size = 6426, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 616,7 641,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 6426, y_origin = -10089, x_size = 5590, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 640,7 666,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 10
            { fragmentUserData = ()
            , fragmentLine = 10
            , fragmentRect = Rect {x_origin = 12016, y_origin = -10089, x_size = 7183, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 661,7 688,8 @@ ParagraphLayout
                ]
            }
        , Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 0, y_origin = -11210, x_size = 2652, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 677,7 705,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 2652, y_origin = -11210, x_size = 5383, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =


@@ 700,7 729,8 @@ ParagraphLayout
        ,
        SpanLayout
        [ Fragment
            { fragmentLine = 11
            { fragmentUserData = ()
            , fragmentLine = 11
            , fragmentRect = Rect {x_origin = 8035, y_origin = -11210, x_size = 4041, y_size = -1121}
            , fragmentPen = (0, -932)
            , fragmentGlyphs =

M CHANGELOG.md => CHANGELOG.md +4 -0
@@ 5,6 5,10 @@
* Separated plain text layout interface into `Data.Text.ParagraphLayout.Plain`
  submodule, in anticipation of a rich text interface.

* Input text spans can now have arbitrary user data attached to them.
  All related data types (`Paragraph`, `Span`, `Fragment`, `SpanLayout`,
  `ParagraphLayout`) have been extended with a type variable.

* Future-proofed `ParagraphOptions` and `SpanOptions` by hiding their
  constructors. Use `defaultParagraphOptions` and `defaultSpanOptions` instead.


M lib/Data/Text/ParagraphLayout/Plain.hs => lib/Data/Text/ParagraphLayout/Plain.hs +2 -1
@@ 15,7 15,7 @@ module Data.Text.ParagraphLayout.Plain
    , paragraphLineHeight
    , paragraphMaxWidth
    -- ** Text spans
    , Span (Span, spanLength, spanOptions)
    , Span (Span, spanUserData, spanLength, spanOptions)
    , SpanOptions
    , defaultSpanOptions
    -- ** Span options


@@ 32,6 32,7 @@ module Data.Text.ParagraphLayout.Plain
    , SpanLayout (SpanLayout)
    , Fragment
        ( Fragment
        , fragmentUserData
        , fragmentLine
        , fragmentRect
        , fragmentPen

M src/Data/Text/ParagraphLayout/Internal/Fragment.hs => src/Data/Text/ParagraphLayout/Internal/Fragment.hs +7 -3
@@ 17,9 17,13 @@ import Data.Text.ParagraphLayout.Internal.Rect
-- An input span (or /text sequence/ in CSS terms) can be broken into multiple
-- fragments because of line breaking, because of bidirectional ordering,
-- or because it contains glyphs from multiple scripts.
data Fragment = Fragment
data Fragment d = Fragment

    { fragmentLine :: Int
    { fragmentUserData :: d
    -- ^ User-defined data associated with the input text span that produced
    -- this fragment.

    , fragmentLine :: Int
    -- ^ Logical number of the line box holding the fragment, starting at 1.
    -- Fragments with the same line number are on the same line and will not be
    -- separated by page breaks.


@@ 57,7 61,7 @@ data Fragment = Fragment
type ShapedRun = (Int32, Int32, [(GlyphInfo, GlyphPos)])

-- | Convert a `Fragment` to a `ShapedRun`.
shapedRun :: Fragment -> ShapedRun
shapedRun :: Fragment d -> ShapedRun
shapedRun f = (x, y, g)
    where
        x = x_origin r + px

M src/Data/Text/ParagraphLayout/Internal/Paginable.hs => src/Data/Text/ParagraphLayout/Internal/Paginable.hs +1 -1
@@ 74,7 74,7 @@ instance Line a => Paginable [a] where

-- | Implementation of paginating a plain text paragraph layout.
-- Breaks the layout on page boundaries and automatically adjusts coordinates.
instance Paginable ParagraphLayout where
instance Paginable (ParagraphLayout d) where
    paginate opts pl = case paginate opts (cutLines pl) of
        (c, p, Nothing) -> (c, mergeLines p, Nothing)
        (c, p, Just rest) -> (c, mergeLines p, Just (mergeLines rest))

M src/Data/Text/ParagraphLayout/Internal/ParagraphConstruction.hs => src/Data/Text/ParagraphLayout/Internal/ParagraphConstruction.hs +9 -7
@@ 19,28 19,30 @@ import Data.Text.ParagraphLayout.Internal.Span (Span (Span), SpanOptions)

-- | Create first span with optional ignored suffix.
infixr 5 >|
(>|) :: (SpanOptions, String) -> String -> (Lazy.Text, [Span])
(>|) :: (SpanOptions, String) -> String -> (Lazy.Text, [Span ()])
(spanLanguage, spanText) >| suffix = (newText, newSpans)
    where
        newSpans = [newSpan]
        newSpan = Span (lengthWord8 packedSpanText) spanLanguage
        newSpan = Span () (lengthWord8 packedSpanText) spanLanguage
        newText = chunk packedSpanText (chunk packedSuffix empty)
        packedSpanText = pack spanText
        packedSuffix = pack suffix

-- | Create next span.
infixr 5 >|<
(>|<) :: (SpanOptions, String) -> (Lazy.Text, [Span]) -> (Lazy.Text, [Span])
(spanLanguage, spanText) >|< (oldText, oldSpans) = (newText, newSpans)
(>|<) :: (SpanOptions, String) -> (Lazy.Text, [Span ()]) ->
    (Lazy.Text, [Span ()])
(spanLanguage, spanText) >|< (oldText, oldSpans) =
    (newText, newSpans)
    where
        newSpans = newSpan : oldSpans
        newSpan = Span (lengthWord8 packedSpanText) spanLanguage
        newSpan = Span () (lengthWord8 packedSpanText) spanLanguage
        newText = chunk packedSpanText oldText
        packedSpanText = pack spanText

-- | Add optional ignored prefix and wrap in a `Paragraph`.
infixr 5 |<
(|<) :: String -> (Lazy.Text, [Span]) -> ParagraphOptions -> Paragraph
(|<) :: String -> (Lazy.Text, [Span ()]) -> ParagraphOptions -> Paragraph ()
prefix |< (oldText, spans) = Paragraph arr afterPrefix spans
    where
        (Text arr beforePrefix _) = toStrict $ chunk packedPrefix oldText


@@ 49,5 51,5 @@ prefix |< (oldText, spans) = Paragraph arr afterPrefix spans

-- | Create a `Paragraph` with no spans, just two ignored texts.
infixr 5 |<>|
(|<>|) :: String -> String -> ParagraphOptions -> Paragraph
(|<>|) :: String -> String -> ParagraphOptions -> Paragraph ()
prefix |<>| suffix = prefix |< (chunk (pack suffix) empty, [])

M src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs => src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs +11 -11
@@ 14,26 14,26 @@ import Data.Text.ParagraphLayout.Internal.Plain.ParagraphLayout
import Data.Text.ParagraphLayout.Internal.Rect

-- | Represents one line of a `ParagraphLayout`.
newtype ParagraphLine = ParagraphLine ParagraphLayout
newtype ParagraphLine d = ParagraphLine (ParagraphLayout d)

instance Line ParagraphLine where
instance Line (ParagraphLine d) where
    lineHeight (ParagraphLine pl) = height $ paragraphRect pl

-- | Split the given `ParagraphLayout` into individual lines.
cutLines :: ParagraphLayout -> [ParagraphLine]
cutLines :: ParagraphLayout d -> [ParagraphLine d]
cutLines pl = map (\ n -> cutLine n pl) (lineNumbers pl)

-- | Reduce the given `ParagraphLayout` to fragments with the given line number.
cutLine :: Int -> ParagraphLayout -> ParagraphLine
cutLine :: Int -> ParagraphLayout d -> ParagraphLine d
cutLine n pl = ParagraphLine $ trimTop $ limitFragments n pl

-- | Add a constant to each fragment's `y_origin` so that their maximum is zero.
trimTop :: ParagraphLayout -> ParagraphLayout
trimTop :: ParagraphLayout d -> ParagraphLayout d
trimTop pl = shiftFragments (-top) pl
    where
        top = maximum $ map (y_origin . fragmentRect) $ paragraphFragments pl

lineNumbers :: ParagraphLayout -> [Int]
lineNumbers :: ParagraphLayout d -> [Int]
lineNumbers pl = dedupe $ map fragmentLine $ paragraphFragments pl

-- | Remove duplicates from a sorted list.


@@ 42,10 42,10 @@ dedupe xs = map NonEmpty.head $ NonEmpty.group xs

-- | Combine the given `ParagraphLine`s into a `ParagraphLayout` by merging
-- their fragments.
mergeLines :: [ParagraphLine] -> ParagraphLayout
mergeLines :: [ParagraphLine d] -> ParagraphLayout d
mergeLines lls = foldl mergeLine emptyParagraphLayout lls

mergeLine :: ParagraphLayout -> ParagraphLine -> ParagraphLayout
mergeLine :: ParagraphLayout d -> ParagraphLine d -> ParagraphLayout d
mergeLine pl (ParagraphLine nextLine) = pl'
    where
        -- Quadratic time complexity. TODO: Consider optimising.


@@ 53,10 53,10 @@ mergeLine pl (ParagraphLine nextLine) = pl'
        y = y_terminus $ paragraphRect pl

-- | Add @dy@ to each fragment's `y_origin`.
shiftFragments :: Int32 -> ParagraphLayout -> ParagraphLayout
shiftFragments :: Int32 -> ParagraphLayout d -> ParagraphLayout d
shiftFragments dy = mapFragments (shiftFragment dy)

shiftFragment :: Int32 -> Fragment -> Fragment
shiftFragment :: Int32 -> Fragment d -> Fragment d
shiftFragment dy f = f'
    where
        f' = f { fragmentRect = r' }


@@ 64,5 64,5 @@ shiftFragment dy f = f'
        r = fragmentRect f

-- | Keep only fragments with the given line number.
limitFragments :: Int -> ParagraphLayout -> ParagraphLayout
limitFragments :: Int -> ParagraphLayout d -> ParagraphLayout d
limitFragments n = filterFragments ((== n) . fragmentLine)

M src/Data/Text/ParagraphLayout/Internal/Plain.hs => src/Data/Text/ParagraphLayout/Internal/Plain.hs +31 -22
@@ 39,8 39,13 @@ import Data.Text.ParagraphLayout.Internal.Run
import Data.Text.ParagraphLayout.Internal.Span
import Data.Text.ParagraphLayout.Internal.TextContainer

-- This is redundant.
-- TODO: Consider using `ResolvedSpan` as `fragmentUserData`, then swapping it
--       for the actual `spanUserData` before returning it to the user.
type FragmentWithSpan d = WithSpan d (Fragment d)

-- | Lay out a paragraph of plain, unidirectional text using a single font.
layoutPlain :: Paragraph -> ParagraphLayout
layoutPlain :: Paragraph d -> ParagraphLayout d
layoutPlain p@(Paragraph _ _ _ opts) = paragraphLayout sls
    where
        sls = map SpanLayout fragsBySpan


@@ 54,19 59,20 @@ layoutPlain p@(Paragraph _ _ _ opts) = paragraphLayout sls

-- | Split a number of spans into a flat array of runs and add a wrapper
-- so that each run can be traced back to its originating span.
spansToRunsWrapped :: [RS.ResolvedSpan] -> [WithSpan Run]
spansToRunsWrapped :: [RS.ResolvedSpan d] -> [WithSpan d Run]
spansToRunsWrapped ss = concat $ map spanToRunsWrapped ss

-- | Split a span into runs and add a wrapper
-- so that each run can be traced back to its originating span.
spanToRunsWrapped :: RS.ResolvedSpan -> [WithSpan Run]
spanToRunsWrapped :: RS.ResolvedSpan d -> [WithSpan d Run]
spanToRunsWrapped s = map (WithSpan s) (spanToRuns s)

-- | Create a multi-line layout from the given runs, splitting them as
-- necessary to fit within the requested line width.
--
-- The output is a flat list of fragments positioned in both dimensions.
layoutAndAlignLines :: Int32 -> NonEmpty (WithSpan Run) -> [WithSpan Fragment]
layoutAndAlignLines :: Int32 -> NonEmpty (WithSpan d Run) ->
    [FragmentWithSpan d]
layoutAndAlignLines maxWidth runs = frags
    where
        frags = concatMap NonEmpty.toList fragsInLines


@@ 85,7 91,7 @@ nonEmptyItems = catMaybes . map nonEmpty . toList
-- The output is a two-dimensional list of fragments positioned along the
-- horizontal axis.
layoutLines ::
    Int32 -> NonEmpty (WithSpan Run) -> NonEmpty [WithSpan PF.ProtoFragment]
    Int32 -> NonEmpty (WithSpan d Run) -> NonEmpty [WithSpan d PF.ProtoFragment]
layoutLines maxWidth runs = case nonEmpty rest of
        -- Everything fits. We are done.
        Nothing -> fitting :| []


@@ 103,8 109,8 @@ layoutLines maxWidth runs = case nonEmpty rest of
-- @vertical-align: top@ in CSS.
--
-- TODO: For rich text, allow other types of vertical alignment.
positionLineH :: Int32 -> (Int, NonEmpty (WithSpan PF.ProtoFragment)) ->
    (Int32, NonEmpty (WithSpan Fragment))
positionLineH :: Int32 -> (Int, NonEmpty (WithSpan d PF.ProtoFragment)) ->
    (Int32, NonEmpty (FragmentWithSpan d))
positionLineH originY (line, pfs) = (nextY, frags)
    where
        nextY = maximum $ fmap y_min rects


@@ 115,13 121,14 @@ positionLineH originY (line, pfs) = (nextY, frags)
-- | Position the given horizontal fragment on a line,
-- using @originY@ as its top edge and @originX@ as its left edge,
-- returning the X coordinate of its right edge for continuation.
positionFragmentH :: Int -> Int32 -> Int32 -> WithSpan PF.ProtoFragment ->
    (Int32, WithSpan Fragment)
positionFragmentH :: Int -> Int32 -> Int32 -> WithSpan d PF.ProtoFragment ->
    (Int32, FragmentWithSpan d)
positionFragmentH line originY originX (WithSpan rs pf) =
    (nextX, WithSpan rs frag)
    where
        nextX = originX + PF.advance pf
        frag = Fragment line rect (penX, penY) (PF.glyphs pf)
        frag = Fragment userData line rect (penX, penY) (PF.glyphs pf)
        userData = RS.spanUserData rs
        rect = Rect originX originY (PF.advance pf) (-lineHeight)
        penX = 0
        penY = descent + leading `div` 2 - lineHeight


@@ 138,8 145,8 @@ positionFragmentH line originY originX (WithSpan rs pf) =
-- | Calculate layout for multiple horizontal runs, breaking them as necessary
-- to fit as much content as possible without exceeding the maximum line width,
-- and return the remaining runs to be placed on other lines.
layoutAndWrapRunsH :: Int32 -> NonEmpty (WithSpan Run) ->
    ([WithSpan PF.ProtoFragment], [WithSpan Run])
layoutAndWrapRunsH :: Int32 -> NonEmpty (WithSpan d Run) ->
    ([WithSpan d PF.ProtoFragment], [WithSpan d Run])
layoutAndWrapRunsH maxWidth runs = NonEmpty.head $ validLayouts
    where
        validLayouts = dropWhile1 tooLong layouts


@@ 162,7 169,7 @@ layoutAndWrapRunsH maxWidth runs = NonEmpty.head $ validLayouts
--
-- If there is no hard line break in the input, the first output list will
-- contain the whole input, and the second output list will be empty.
hardSplit :: NonEmpty (WithSpan Run) -> ([WithSpan Run], [WithSpan Run])
hardSplit :: NonEmpty (WithSpan d Run) -> ([WithSpan d Run], [WithSpan d Run])
hardSplit runs = allowFstEmpty $ trimFst $ NonEmpty.last $ splits
    where
        trimFst (runs1, runs2) = (trim runs1, runs2)


@@ 191,7 198,8 @@ hardSplit runs = allowFstEmpty $ trimFst $ NonEmpty.last $ splits
-- The results in the form (prefix, suffix) will be ordered so that items
-- closer to the start of the list are preferred for line breaking, but without
-- considering overflows.
softSplits :: NonEmpty (WithSpan Run) -> [([WithSpan Run], [WithSpan Run])]
softSplits :: NonEmpty (WithSpan d Run) ->
    [([WithSpan d Run], [WithSpan d Run])]
softSplits runs = map (allowSndEmpty . trimFst) splits
    where
        trimFst (runs1, runs2) = (trim runs1, runs2)


@@ 221,15 229,15 @@ dropWhile1 p list = case NonEmpty.uncons list of

-- | Calculate layout for multiple horizontal runs on the same line, without
-- any breaking.
layoutRunsH :: [WithSpan Run] -> [WithSpan PF.ProtoFragment]
layoutRunsH :: [WithSpan d Run] -> [WithSpan d PF.ProtoFragment]
layoutRunsH runs = map layoutRunH runs

-- | Sum of all advances within the given fragments.
totalAdvances :: [WithSpan PF.ProtoFragment] -> Int32
totalAdvances :: [WithSpan d PF.ProtoFragment] -> Int32
totalAdvances pfs = sum $ map (\ (WithSpan _ pf) -> PF.advance pf) pfs

-- | Calculate layout for the given horizontal run and attach extra information.
layoutRunH :: WithSpan Run -> WithSpan PF.ProtoFragment
layoutRunH :: WithSpan d Run -> WithSpan d PF.ProtoFragment
layoutRunH (WithSpan rs run) = WithSpan rs pf
    where
        pf = PF.protoFragmentH dir glyphs


@@ 237,7 245,7 @@ layoutRunH (WithSpan rs run) = WithSpan rs pf
        dir = runDirection run

-- | Calculate layout for the given run independently of its position.
shapeRun :: WithSpan Run -> [(GlyphInfo, GlyphPos)]
shapeRun :: WithSpan d Run -> [(GlyphInfo, GlyphPos)]
shapeRun (WithSpan rs run) = shape font buffer features
    where
        font = RS.spanFont rs


@@ 261,7 269,7 @@ shapeRun (WithSpan rs run) = shape font buffer features
            }
        features = []

resolveSpans :: Paragraph -> [RS.ResolvedSpan]
resolveSpans :: Paragraph d -> [RS.ResolvedSpan d]
resolveSpans p@(Paragraph _ pStart spans pOpts) = do
    let sBounds = paragraphSpanBounds p
    let sTexts = paragraphSpanTexts p


@@ 277,7 285,8 @@ resolveSpans p@(Paragraph _ pStart spans pOpts) = do
    let lBreaks = paragraphBreaks breakLine pText lang
    let cBreaks = paragraphBreaks breakCharacter pText lang
    return RS.ResolvedSpan
        { RS.spanIndex = i
        { RS.spanUserData = spanUserData s
        , RS.spanIndex = i
        , RS.spanOffsetInParagraph = sStart - pStart
        , RS.spanText = sText
        , RS.spanFont = paragraphFont pOpts


@@ 291,11 300,11 @@ paragraphBreaks :: (LocaleName -> Breaker a) -> Text -> String -> [(Int, a)]
paragraphBreaks breakFunc txt lang =
    breaksDesc (breakFunc (locale lang LBAuto)) txt

runLineBreaks :: WithSpan Run -> [(Int, BreakStatus.Line)]
runLineBreaks :: WithSpan d Run -> [(Int, BreakStatus.Line)]
runLineBreaks (WithSpan rs run) =
    runBreaksFromSpan run $ RS.spanLineBreaks rs

runCharacterBreaks :: WithSpan Run -> [(Int, ())]
runCharacterBreaks :: WithSpan d Run -> [(Int, ())]
runCharacterBreaks (WithSpan rs run) =
    runBreaksFromSpan run $ RS.spanCharacterBreaks rs


M src/Data/Text/ParagraphLayout/Internal/Plain/Paragraph.hs => src/Data/Text/ParagraphLayout/Internal/Plain/Paragraph.hs +5 -5
@@ 27,7 27,7 @@ import Data.Text.ParagraphLayout.Internal.Span
--
-- For simple use cases, it may be sufficient to construct paragraphs using
-- [ParagraphConstruction]("Data.Text.ParagraphLayout.ParagraphConstruction").
data Paragraph = Paragraph
data Paragraph d = Paragraph

    Array
    -- ^ A byte array containing the whole text to be laid out, in UTF-8.


@@ 44,7 44,7 @@ data Paragraph = Paragraph
    -- Any characters preceding this offset will not be shaped, but may still
    -- be used to influence the shape of neighbouring characters.

    [Span]
    [Span d]
    -- ^ Parts of the text to be laid out, in logical order.
    -- The initial offset plus total length of all spans must not exceed
    -- the bounds of the byte array.


@@ 60,7 60,7 @@ data Paragraph = Paragraph
--
-- You can use this function to verify that Balkón will slice the input text
-- correctly.
paragraphSpanBounds :: Paragraph -> NonEmpty Int
paragraphSpanBounds :: Paragraph d -> NonEmpty Int
paragraphSpanBounds (Paragraph _ initialOffset spans _) =
    -- TODO: Consider adding checks for array bounds.
    NonEmpty.scanl (+) initialOffset (map spanLength spans)


@@ 69,7 69,7 @@ paragraphSpanBounds (Paragraph _ initialOffset spans _) =
--
-- You can use this function to verify that Balkón will slice the input text
-- correctly.
paragraphSpanTexts :: Paragraph -> [Text]
paragraphSpanTexts :: Paragraph d -> [Text]
paragraphSpanTexts p@(Paragraph arr _ _ _) = zipWith toText sStarts sEnds
    where
        toText start end = Text arr start (end - start)


@@ 81,7 81,7 @@ paragraphSpanTexts p@(Paragraph arr _ _ _) = zipWith toText sStarts sEnds
--
-- You can use this function to verify that Balkón will slice the input text
-- correctly.
paragraphText :: Paragraph -> Text
paragraphText :: Paragraph d -> Text
paragraphText p@(Paragraph arr _ _ _) = Text arr start (end - start)
    where
        start = NonEmpty.head sBounds

M src/Data/Text/ParagraphLayout/Internal/Plain/ParagraphLayout.hs => src/Data/Text/ParagraphLayout/Internal/Plain/ParagraphLayout.hs +15 -11
@@ 19,10 19,10 @@ import Data.Text.ParagraphLayout.Internal.Rect
import Data.Text.ParagraphLayout.Internal.Span

-- | The resulting layout of the whole paragraph.
data ParagraphLayout = ParagraphLayout
data ParagraphLayout d = ParagraphLayout
    { paragraphRect :: Rect Int32
    -- ^ The containing block (CSS3).
    , spanLayouts :: [SpanLayout]
    , spanLayouts :: [SpanLayout d]
    }
    deriving (Eq, Read, Show)



@@ 44,20 44,22 @@ containRects :: (Ord a, Num a) => [Rect a] -> Rect a
containRects = foldr union empty

-- | Wrap the given `SpanLayout`s and compute their containing rectangle.
paragraphLayout :: [SpanLayout] -> ParagraphLayout
paragraphLayout :: [SpanLayout d] -> ParagraphLayout d
paragraphLayout sls = ParagraphLayout pRect sls
    where pRect = containRects $ concat $ map spanRects sls

-- | A `ParagraphLayout` with an infinite number of empty spans.
-- Useful as an identity element for `appendFragments`.
emptyParagraphLayout :: ParagraphLayout
emptyParagraphLayout :: ParagraphLayout d
emptyParagraphLayout = ParagraphLayout empty $ repeat (SpanLayout [])

-- | Remove fragments that do not match the given predicate.
--
-- The containing rectangle will be recalculated.
filterFragments :: (Fragment -> Bool) -> ParagraphLayout -> ParagraphLayout
filterFragments fragPred (ParagraphLayout _ sls) = paragraphLayout sls'
filterFragments :: (Fragment d -> Bool) -> ParagraphLayout d ->
    ParagraphLayout d
filterFragments fragPred (ParagraphLayout _ sls) =
    paragraphLayout sls'
    where
        sls' = map slMapFunc sls
        slMapFunc (SpanLayout frags) = SpanLayout (filter fragPred frags)


@@ 65,8 67,10 @@ filterFragments fragPred (ParagraphLayout _ sls) = paragraphLayout sls'
-- | Run a mapping function over each fragment inside a `ParagraphLayout`.
--
-- The containing rectangle will be recalculated.
mapFragments :: (Fragment -> Fragment) -> ParagraphLayout -> ParagraphLayout
mapFragments fragMapFunc (ParagraphLayout _ sls) = paragraphLayout sls'
mapFragments :: (Fragment d -> Fragment d) -> ParagraphLayout d ->
    ParagraphLayout d
mapFragments fragMapFunc (ParagraphLayout _ sls) =
    paragraphLayout sls'
    where
        sls' = map slMapFunc sls
        slMapFunc (SpanLayout frags) = SpanLayout (map fragMapFunc frags)


@@ 74,7 78,7 @@ mapFragments fragMapFunc (ParagraphLayout _ sls) = paragraphLayout sls'
-- | Combine fragments from two `ParagraphLayout`s.
--
-- The containing rectangle will be recalculated.
appendFragments :: ParagraphLayout -> ParagraphLayout -> ParagraphLayout
appendFragments :: ParagraphLayout d -> ParagraphLayout d -> ParagraphLayout d
appendFragments pla plb = paragraphLayout sls'
    where
        sls' = zipWith zipFunc slsa slsb


@@ 84,9 88,9 @@ appendFragments pla plb = paragraphLayout sls'

-- | Return all fragments of shaped text in one flat list,
-- discarding information about their associated spans.
paragraphFragments :: ParagraphLayout -> [Fragment]
paragraphFragments :: ParagraphLayout d -> [Fragment d]
paragraphFragments pl = concat $ map spanFragments $ spanLayouts pl

-- | Return all shaped runs in the paragraph.
shapedRuns :: ParagraphLayout -> [ShapedRun]
shapedRuns :: ParagraphLayout d -> [ShapedRun]
shapedRuns pl = map shapedRun $ paragraphFragments pl

M src/Data/Text/ParagraphLayout/Internal/ResolvedSpan.hs => src/Data/Text/ParagraphLayout/Internal/ResolvedSpan.hs +12 -11
@@ 15,8 15,9 @@ import Data.Text.ParagraphLayout.Internal.TextContainer

-- | Internal structure containing resolved values that may be shared with
-- other spans across the paragraph.
data ResolvedSpan = ResolvedSpan
    { spanIndex :: Int
data ResolvedSpan d = ResolvedSpan
    { spanUserData :: d
    , spanIndex :: Int
    , spanOffsetInParagraph :: Int
    , spanText :: Text
    , spanFont :: Font


@@ 28,34 29,34 @@ data ResolvedSpan = ResolvedSpan
    }
    deriving (Show)

instance Eq ResolvedSpan where
instance Eq (ResolvedSpan d) where
    a == b = spanIndex a == spanIndex b

instance TextContainer ResolvedSpan where
instance TextContainer (ResolvedSpan d) where
    getText = spanText

-- | Wrapper for temporarily mapping the relationship to a `ResolvedSpan`.
data WithSpan a = WithSpan ResolvedSpan a
data WithSpan d a = WithSpan (ResolvedSpan d) a

instance Functor WithSpan where
instance Functor (WithSpan d) where
    fmap f (WithSpan s a) = WithSpan s (f a)

instance TextContainer a => TextContainer (WithSpan a) where
instance TextContainer a => TextContainer (WithSpan d a) where
    getText (WithSpan _ c) = getText c

instance SeparableTextContainer a => SeparableTextContainer (WithSpan a) where
instance SeparableTextContainer a => SeparableTextContainer (WithSpan d a) where
    splitTextAt8 n (WithSpan rs c) = (WithSpan rs c1, WithSpan rs c2)
        where (c1, c2) = splitTextAt8 n c
    dropWhileStart p (WithSpan rs c) = WithSpan rs (dropWhileStart p c)
    dropWhileEnd p (WithSpan rs c) = WithSpan rs (dropWhileEnd p c)

instance WithLevel a => WithLevel (WithSpan a) where
instance WithLevel a => WithLevel (WithSpan d a) where
    level (WithSpan _ x) = level x

splitBySpanIndex :: [WithSpan a] -> [[a]]
splitBySpanIndex :: [WithSpan d a] -> [[a]]
splitBySpanIndex xs = [getBySpanIndex i xs | i <- [0 ..]]

getBySpanIndex :: Int -> [WithSpan a] -> [a]
getBySpanIndex :: Int -> [WithSpan d a] -> [a]
getBySpanIndex idx xs = map contents $ filter matchingIndex $ xs
    where
        matchingIndex (WithSpan rs _) = (spanIndex rs) == idx

M src/Data/Text/ParagraphLayout/Internal/Run.hs => src/Data/Text/ParagraphLayout/Internal/Run.hs +1 -1
@@ 74,7 74,7 @@ considerNext z = case next z of

data Merged a = Incompatible | Merged a

spanToRuns :: ResolvedSpan -> [Run]
spanToRuns :: ResolvedSpan d -> [Run]
spanToRuns s = snd $ mapAccumL run 0 $ protoRuns zipper
    where
        zipper = start $ spanText s

M src/Data/Text/ParagraphLayout/Internal/Span.hs => src/Data/Text/ParagraphLayout/Internal/Span.hs +8 -5
@@ 17,9 17,12 @@ import Data.Text.ParagraphLayout.Internal.Rect
--
-- Each span could have a different font family, size, style, text decoration,
-- colour, language, etc.
data Span = Span
data Span d = Span

    { spanLength :: Int
    { spanUserData :: d
    -- ^ User-defined data associated with the span.

    , spanLength :: Int
    -- ^ Byte offset to the next span or the end of the paragraph text.

    , spanOptions :: SpanOptions


@@ 55,13 58,13 @@ defaultSpanOptions = SpanOptions

-- | The resulting layout of each span, which may include multiple fragments
-- as required by line breaking, text writing direction, and changes of script.
data SpanLayout = SpanLayout [Fragment]
data SpanLayout d = SpanLayout [Fragment d]
    -- TODO: Consider merging fragments created by script changes.
    deriving (Eq, Read, Show)

-- | Return all fragments of shaped text from the given span.
spanFragments :: SpanLayout -> [Fragment]
spanFragments :: SpanLayout d -> [Fragment d]
spanFragments (SpanLayout frags) = frags

spanRects :: SpanLayout -> [Rect Int32]
spanRects :: SpanLayout d -> [Rect Int32]
spanRects sl = map fragmentRect $ spanFragments sl

M test/Data/Text/ParagraphLayout/Plain/ParagraphData.hs => test/Data/Text/ParagraphLayout/Plain/ParagraphData.hs +18 -18
@@ 48,59 48,59 @@ sr = (,) defaultSpanOptions { spanLanguage = "sr" }
zxx :: String -> (SpanOptions, String)
zxx = (,) defaultSpanOptions { spanLanguage = "zxx" }

emptyParagraph :: ParagraphOptions -> Paragraph
emptyParagraph :: ParagraphOptions -> Paragraph ()
emptyParagraph = "x" |<>| "zzzzzzz"

emptySpanParagraph :: ParagraphOptions -> Paragraph
emptySpanParagraph :: ParagraphOptions -> Paragraph ()
emptySpanParagraph = "xx" |< en "" >| "zzzzz"

trivialParagraph :: ParagraphOptions -> Paragraph
trivialParagraph :: ParagraphOptions -> Paragraph ()
trivialParagraph = "xxx" |< en "a" >| "zzz"

ligatureParagraph :: ParagraphOptions -> Paragraph
ligatureParagraph :: ParagraphOptions -> Paragraph ()
ligatureParagraph = "" |< en "inefficient" >| ""

manySpacesParagraph :: ParagraphOptions -> Paragraph
manySpacesParagraph :: ParagraphOptions -> Paragraph ()
manySpacesParagraph = "  " |< zxx " aaaaaa aaaaaa  aaaaaa   aaaaaa    " >| "  "

-- | Filler text using the Arabic script.
-- Source: <https://generator.lorem-ipsum.info/_arabic>
arabicFillerParagraph :: ParagraphOptions -> Paragraph
arabicFillerParagraph :: ParagraphOptions -> Paragraph ()
arabicFillerParagraph = "xxxx" |< zxx "إعلان بأيدي وبغطاء هذه من. عرض غينيا يتمكن واعتلاء في. و فرنسا الثانية وفي, أسر إذ السبب ارتكبها مليارات. فكان الشتاء، ما حتى, غير أن وصغار الأخذ. في الصفحة لهيمنة وتتحمّل وتم, أن أما وبداية الغالي." >| "z"

spannedArabicFillerParagraph :: ParagraphOptions -> Paragraph
spannedArabicFillerParagraph :: ParagraphOptions -> Paragraph ()
spannedArabicFillerParagraph = "xxxx" |< zxx "إعلان بأيدي وبغطاء " >|< zxx "هذه من. عرض" >|< zxx " غينيا يتمكن واعتلاء " >|< zxx "في. و فرنسا" >|< zxx " الثانية وفي, أسر " >|< zxx "إذ السبب ارتكبها" >|< zxx " مليارات. فكان الشتاء، " >|< zxx "ما حتى, غير" >|< zxx " أن وصغار الأخذ. " >|< zxx "في الصفحة لهيمنة" >|< zxx " وتتحمّل وتم, أن " >|< zxx "أما وبداية الغالي." >| "z"

-- | Filler text using the Latin script.
-- Source: <https://www.lipsum.com/>
loremIpsumParagraph :: ParagraphOptions -> Paragraph
loremIpsumParagraph :: ParagraphOptions -> Paragraph ()
loremIpsumParagraph = "xxxx" |< zxx "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." >| "z"

spannedLoremIpsumParagraph :: ParagraphOptions -> Paragraph
spannedLoremIpsumParagraph :: ParagraphOptions -> Paragraph ()
spannedLoremIpsumParagraph = "xxxx" |< zxx "Lorem ipsum dolor " >|< zxx "sit amet, consectetur" >|< zxx " adipiscing elit, sed " >|< zxx "do eiusmod tempor" >|< zxx " incididunt ut labore " >|< zxx "et dolore magna" >|< zxx " aliqua. Ut enim " >|< zxx "ad minim veniam," >|< zxx " quis nostrud exercitation " >|< zxx "ullamco laboris nisi" >|< zxx " ut aliquip ex " >|< zxx "ea commodo consequat." >|< zxx " Duis aute irure " >|< zxx "dolor in reprehenderit" >|< zxx " in voluptate velit " >|< zxx "esse cillum dolore" >|< zxx " eu fugiat nulla " >|< zxx "pariatur. Excepteur" >|< zxx " sint occaecat cupidatat " >|< zxx "non proident, sunt" >|< zxx " in culpa qui " >|< zxx "officia deserunt mollit" >|< zxx " anim id est " >|< zxx "laborum." >| "z"

czechHelloParagraph :: ParagraphOptions -> Paragraph
czechHelloParagraph :: ParagraphOptions -> Paragraph ()
czechHelloParagraph = "xxxxx" |< cs "Ahoj, světe!" >| "zz"

mixedScriptSerbianParagraph :: ParagraphOptions -> Paragraph
mixedScriptSerbianParagraph :: ParagraphOptions -> Paragraph ()
mixedScriptSerbianParagraph = "xxxxxx" |< sr "Vikipedija (Википедија)" >| "zzzz"

-- | For testing line breaking on boundaries that are different from script
-- boundaries.
mixedScriptWordsParagraph :: ParagraphOptions -> Paragraph
mixedScriptWordsParagraph :: ParagraphOptions -> Paragraph ()
mixedScriptWordsParagraph = "xxxxxxx" |< zxx "jjjжжжjjj jjjжжжjjj жжжjjjжжж жжжjjjжжж jжj jжj жjж жjж" >| "zzzzzzzz"

mixedLanguageLTRParagraph :: ParagraphOptions -> Paragraph
mixedLanguageLTRParagraph :: ParagraphOptions -> Paragraph ()
mixedLanguageLTRParagraph = "Tak " |< cs "jsem tady, " >|< ja "世界!" >| "zzzzzz"

-- | Source: <https://faultlore.com/blah/text-hates-you/>
devanagariParagraph :: ParagraphOptions -> Paragraph
devanagariParagraph :: ParagraphOptions -> Paragraph ()
devanagariParagraph = "xxxxxxxx" |< zxx "पन्ह पन्ह त्र र्च कृकृ ड्ड न्हृे" >| "zzzzzzzz"

devanagariAccentParagraph :: ParagraphOptions -> Paragraph
devanagariAccentParagraph :: ParagraphOptions -> Paragraph ()
devanagariAccentParagraph = "" |< zxx "\x954" >| ""

devanagariPrefixedAccentParagraph :: ParagraphOptions -> Paragraph
devanagariPrefixedAccentParagraph :: ParagraphOptions -> Paragraph ()
devanagariPrefixedAccentParagraph = "#" |< zxx "\x954" >| ""

-- | Test hard line breaks with Latin characters:


@@ 110,10 110,10 @@ devanagariPrefixedAccentParagraph = "#" |< zxx "\x954" >| ""
-- - after a long word (line break in the middle of a word needed),
-- - after spaces,
-- - after other line breaks.
hardBreaksLTRParagraph :: ParagraphOptions -> Paragraph
hardBreaksLTRParagraph :: ParagraphOptions -> Paragraph ()
hardBreaksLTRParagraph = "x" |< zxx "jjjjjj\njjjjjj jjjjjj jjjjjj\nmmmmmm \njjjjjj\n\nmm mm mm" >| ""

-- | Test hard line breaks like `hardBreaksLTRParagraph`,
-- but with Arabic characters.
hardBreaksRTLParagraph :: ParagraphOptions -> Paragraph
hardBreaksRTLParagraph :: ParagraphOptions -> Paragraph ()
hardBreaksRTLParagraph = "x" |< zxx "دددددد\nدددددد دددددد دددددد\nسسسسسسس \nدددددد\n\nسس سس سس" >| ""

M test/Data/Text/ParagraphLayout/PlainSpec.hs => test/Data/Text/ParagraphLayout/PlainSpec.hs +4 -4
@@ 11,15 11,15 @@ import Data.Text.ParagraphLayout.PrettyShow
import Data.Text.ParagraphLayout.PrettyShow.Golden
import Data.Text.ParagraphLayout.Rect

type Page = (PageContinuity, ParagraphLayout)
type Page d = (PageContinuity, ParagraphLayout d)

emptyLayout :: ParagraphLayout
emptyLayout :: ParagraphLayout d
emptyLayout = ParagraphLayout (Rect 0 0 0 0) []

emptySpanLayout :: ParagraphLayout
emptySpanLayout :: ParagraphLayout d
emptySpanLayout = ParagraphLayout (Rect 0 0 0 0) [SpanLayout []]

paginateAll :: PageOptions -> ParagraphLayout -> [Page]
paginateAll :: PageOptions -> ParagraphLayout d -> [Page d]
paginateAll opts pl = case paginate opts pl of
    (c, pl1, next) -> (c, pl1) : case next of
        Just pl2 -> paginateAll opts' pl2

M test/Data/Text/ParagraphLayout/PrettyShow.hs => test/Data/Text/ParagraphLayout/PrettyShow.hs +14 -10
@@ 35,19 35,19 @@ instance PrettyShow ShapedRun' where
        , ")"
        ]

type Page = (PageContinuity, Plain.ParagraphLayout)
type Page d = (PageContinuity, Plain.ParagraphLayout d)

newtype Pages = Pages { getPages :: [Page] }
newtype Pages d = Pages { getPages :: [Page d] }
    deriving (Eq)

instance PrettyShow Pages where
instance Show d => PrettyShow (Pages d) where
    prettyShow (Pages ps) =
        concat (commaFirstList indent0 $ map (prettyShow . Page') ps)
        ++ newline

newtype Page' = Page' Page
newtype Page' d = Page' (Page d)

instance PrettyShow Page' where
instance Show d => PrettyShow (Page' d) where
    prettyShow (Page' (c, pl)) = concat
        [ "("
        , show c


@@ 56,7 56,7 @@ instance PrettyShow Page' where
        , ")"
        ]

instance PrettyShow Plain.ParagraphLayout where
instance Show d => PrettyShow (Plain.ParagraphLayout d) where
    prettyShow (Plain.ParagraphLayout pr sls) = concat
        [ "ParagraphLayout"
        , newline


@@ 74,19 74,23 @@ instance PrettyShow Plain.ParagraphLayout where
        , newline
        ]

instance PrettyShow Plain.SpanLayout where
instance Show d => PrettyShow (Plain.SpanLayout d) where
    prettyShow (Plain.SpanLayout frags) = concat
        [ "SpanLayout"
        , newline
        , concat $ commaFirstList indent2 $ map prettyShow frags
        ]

instance PrettyShow Plain.Fragment where
    prettyShow (Plain.Fragment l r pen glyphs) = concat
instance Show d => PrettyShow (Plain.Fragment d) where
    prettyShow (Plain.Fragment d l r pen glyphs) = concat
        [ "Fragment"
        , newline
        , indent3
        , "{ fragmentLine = "
        , "{ fragmentUserData = "
        , show d
        , newline
        , indent3
        , ", fragmentLine = "
        , show l
        , newline
        , indent3

M test/Data/Text/ParagraphLayout/SpanData.hs => test/Data/Text/ParagraphLayout/SpanData.hs +9 -6
@@ 11,9 11,10 @@ import Data.Text.Glyphize (Font)
import Data.Text.ParagraphLayout.Internal.LineHeight
import Data.Text.ParagraphLayout.Internal.ResolvedSpan

emptySpan :: Font -> ResolvedSpan
emptySpan :: Font -> ResolvedSpan ()
emptySpan font = ResolvedSpan
    { spanIndex = 0
    { spanUserData = ()
    , spanIndex = 0
    , spanOffsetInParagraph = 0
    , spanText = pack ""
    , spanFont = font


@@ 23,9 24,10 @@ emptySpan font = ResolvedSpan
    , spanCharacterBreaks = []
    }

czechHello :: Font -> ResolvedSpan
czechHello :: Font -> ResolvedSpan ()
czechHello font = ResolvedSpan
    { spanIndex = 0
    { spanUserData = ()
    , spanIndex = 0
    , spanOffsetInParagraph = 0
    , spanText = pack "Ahoj, světe!"
    , spanFont = font


@@ 35,9 37,10 @@ czechHello font = ResolvedSpan
    , spanCharacterBreaks = []
    }

serbianMixedScript :: Font -> ResolvedSpan
serbianMixedScript :: Font -> ResolvedSpan ()
serbianMixedScript font = ResolvedSpan
    { spanIndex = 0
    { spanUserData = ()
    , spanIndex = 0
    , spanOffsetInParagraph = 0
    , spanText = pack "Vikipedija (Википедија)"
    , spanFont = font