~jaro/balkon

409c6d7eddc9c7f96438f7c9c2d51b1c644f1413 — Jaro 1 year, 5 months ago 1e8de66
Add paragraphLines to Rich ParagraphLayout.

BREAKING: The paragraphLines field of ParagraphLayout must be preserved
for pagination.

BREAKING: The width of a rich text ParagraphLayout will now remain
unchanged after pagination.

BREAKING: Empty space at the top of the first line and at the bottom
of the last line in a ParagraphLayout will now be preserved.
38 files changed, 321 insertions(+), 64 deletions(-)

M .golden/paginatedRichParagraphLayout/loremIpsum20em.golden
M .golden/richParagraphLayout/emptyBoxMiddle.golden
M .golden/richParagraphLayout/hardBoxBreakLTR.golden
M .golden/richParagraphLayout/hardBoxBreakRTL.golden
M .golden/richParagraphLayout/loremIpsum20em.golden
M .golden/richParagraphLayout/loremIpsum20emCentre.golden
M .golden/richParagraphLayout/loremIpsum20emRight.golden
M .golden/richParagraphLayout/mixedDirectionComplexLTR.golden
M .golden/richParagraphLayout/mixedDirectionComplexRTL.golden
M .golden/richParagraphLayout/mixedDirectionSimpleLTR.golden
M .golden/richParagraphLayout/mixedDirectionSimpleRTL.golden
M .golden/richParagraphLayout/mixedLineHeightBaseline.golden
M .golden/richParagraphLayout/mixedLineHeightBaseline3.golden
M .golden/richParagraphLayout/mixedLineHeightBottom.golden
M .golden/richParagraphLayout/mixedLineHeightTop.golden
M .golden/richParagraphLayout/mixedScript.golden
M .golden/richParagraphLayout/mixedScriptWrap.golden
M .golden/richParagraphLayout/mixedSizes.golden
M .golden/richParagraphLayout/nestedBoxes.golden
M .golden/richParagraphLayout/neutralDirectionLTR.golden
M .golden/richParagraphLayout/neutralDirectionRTL.golden
M .golden/richParagraphLayout/newline1Paragraph.golden
M .golden/richParagraphLayout/newline1TextParagraph.golden
M .golden/richParagraphLayout/newline2Paragraph.golden
M .golden/richParagraphLayout/newline2TextParagraph.golden
M .golden/richParagraphLayout/spaceBoxCollapsed.golden
M .golden/richParagraphLayout/spaceBoxMiddle.golden
M .golden/richParagraphLayout/spaceBoxPreserved.golden
M CHANGELOG.md
M balkon.cabal
M lib/Data/Text/ParagraphLayout/Rich.hs
M src/Data/Text/ParagraphLayout/Internal/Layout.hs
A src/Data/Text/ParagraphLayout/Internal/Line.hs
M src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs
M src/Data/Text/ParagraphLayout/Internal/Rich.hs
M src/Data/Text/ParagraphLayout/Internal/Rich/ParagraphLayout.hs
M test/Data/Text/ParagraphLayout/PrettyShow.hs
M test/Data/Text/ParagraphLayout/RichSpec.hs
M .golden/paginatedRichParagraphLayout/loremIpsum20em.golden => .golden/paginatedRichParagraphLayout/loremIpsum20em.golden +19 -2
@@ 1,5 1,9 @@
[ (Continue, ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 18310, y_size = -2242}
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -2242}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"


@@ 105,6 109,14 @@
)
, (Continue, ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -6726}
    , paragraphLines =
        [ Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -3363, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 0, y_origin = -4484, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 0, y_origin = -5605, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"


@@ 426,7 438,12 @@
    }
)
, (Continue, ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 19199, y_size = -3363}
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -3363}
    , paragraphLines =
        [ Line {lineNumber = 9, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 10, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 11, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/emptyBoxMiddle.golden => .golden/richParagraphLayout/emptyBoxMiddle.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 5193, y_size = -1121}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 5193, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/hardBoxBreakLTR.golden => .golden/richParagraphLayout/hardBoxBreakLTR.golden +4 -0
@@ 1,5 1,9 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 5855, y_size = -2242}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 5855, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 5855, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/hardBoxBreakRTL.golden => .golden/richParagraphLayout/hardBoxBreakRTL.golden +4 -0
@@ 1,5 1,9 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999996545, y_origin = 0, x_size = 3455, y_size = -3000}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999996545, y_origin = 0, x_size = 3455, y_size = -1500}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 999996545, y_origin = -1500, x_size = 3455, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text2"

M .golden/richParagraphLayout/loremIpsum20em.golden => .golden/richParagraphLayout/loremIpsum20em.golden +13 -0
@@ 1,5 1,18 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -12331}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -3363, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -4484, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -5605, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 0, y_origin = -6726, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 0, y_origin = -7847, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 9, lineRect = Rect {x_origin = 0, y_origin = -8968, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 10, lineRect = Rect {x_origin = 0, y_origin = -10089, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 11, lineRect = Rect {x_origin = 0, y_origin = -11210, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/loremIpsum20emCentre.golden => .golden/richParagraphLayout/loremIpsum20emCentre.golden +13 -0
@@ 1,5 1,18 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 104, y_origin = 0, x_size = 19791, y_size = -12331}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 104, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 104, y_origin = -1121, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 104, y_origin = -2242, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 104, y_origin = -3363, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 104, y_origin = -4484, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 104, y_origin = -5605, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 104, y_origin = -6726, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 104, y_origin = -7847, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 9, lineRect = Rect {x_origin = 104, y_origin = -8968, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 10, lineRect = Rect {x_origin = 104, y_origin = -10089, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 11, lineRect = Rect {x_origin = 104, y_origin = -11210, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/loremIpsum20emRight.golden => .golden/richParagraphLayout/loremIpsum20emRight.golden +13 -0
@@ 1,5 1,18 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 209, y_origin = 0, x_size = 19791, y_size = -12331}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 209, y_origin = 0, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 209, y_origin = -1121, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 209, y_origin = -2242, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 209, y_origin = -3363, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 209, y_origin = -4484, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 209, y_origin = -5605, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 209, y_origin = -6726, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 209, y_origin = -7847, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 9, lineRect = Rect {x_origin = 209, y_origin = -8968, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 10, lineRect = Rect {x_origin = 209, y_origin = -10089, x_size = 19791, y_size = -1121}}
        , Line {lineNumber = 11, lineRect = Rect {x_origin = 209, y_origin = -11210, x_size = 19791, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/mixedDirectionComplexLTR.golden => .golden/richParagraphLayout/mixedDirectionComplexLTR.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 24557, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 24557, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/mixedDirectionComplexRTL.golden => .golden/richParagraphLayout/mixedDirectionComplexRTL.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999975443, y_origin = 0, x_size = 24557, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999975443, y_origin = 0, x_size = 24557, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text3"

M .golden/richParagraphLayout/mixedDirectionSimpleLTR.golden => .golden/richParagraphLayout/mixedDirectionSimpleLTR.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 7954, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 7954, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/mixedDirectionSimpleRTL.golden => .golden/richParagraphLayout/mixedDirectionSimpleRTL.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999992046, y_origin = 0, x_size = 7954, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999992046, y_origin = 0, x_size = 7954, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/mixedLineHeightBaseline.golden => .golden/richParagraphLayout/mixedLineHeightBaseline.golden +8 -0
@@ 1,5 1,13 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -9400}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -3400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -4700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -6400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -7700, x_size = 16708, y_size = -1700}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "mediumText"

M .golden/richParagraphLayout/mixedLineHeightBaseline3.golden => .golden/richParagraphLayout/mixedLineHeightBaseline3.golden +8 -0
@@ 1,5 1,13 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -9400}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -3400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -4700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -6400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -7700, x_size = 16708, y_size = -1700}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "mediumText"

M .golden/richParagraphLayout/mixedLineHeightBottom.golden => .golden/richParagraphLayout/mixedLineHeightBottom.golden +8 -0
@@ 1,5 1,13 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -9400}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -3400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -4700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -6400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -7700, x_size = 16708, y_size = -1700}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "mediumText"

M .golden/richParagraphLayout/mixedLineHeightTop.golden => .golden/richParagraphLayout/mixedLineHeightTop.golden +8 -0
@@ 1,5 1,13 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -9400}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -3400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -4700, x_size = 16708, y_size = -1700}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -6400, x_size = 16708, y_size = -1300}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -7700, x_size = 16708, y_size = -1700}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "mediumText"

M .golden/richParagraphLayout/mixedScript.golden => .golden/richParagraphLayout/mixedScript.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 15283, y_size = -1121}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 15283, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/mixedScriptWrap.golden => .golden/richParagraphLayout/mixedScriptWrap.golden +6 -0
@@ 1,5 1,11 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 4897, y_size = -4484}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 4897, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 4897, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 4897, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -3363, x_size = 4897, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/mixedSizes.golden => .golden/richParagraphLayout/mixedSizes.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 8634, y_size = -1121}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 8634, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "bigText1"

M .golden/richParagraphLayout/nestedBoxes.golden => .golden/richParagraphLayout/nestedBoxes.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 14576, y_size = -1121}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 14576, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/neutralDirectionLTR.golden => .golden/richParagraphLayout/neutralDirectionLTR.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 2003, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 2003, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/neutralDirectionRTL.golden => .golden/richParagraphLayout/neutralDirectionRTL.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999997997, y_origin = 0, x_size = 2003, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999997997, y_origin = 0, x_size = 2003, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/newline1Paragraph.golden => .golden/richParagraphLayout/newline1Paragraph.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 1000000000, y_origin = 0, x_size = 0, y_size = -1500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 1000000000, y_origin = 0, x_size = 0, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/newline1TextParagraph.golden => .golden/richParagraphLayout/newline1TextParagraph.golden +4 -0
@@ 1,5 1,9 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999999649, y_origin = 0, x_size = 351, y_size = -3000}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999999649, y_origin = 0, x_size = 351, y_size = -1500}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 999999649, y_origin = -1500, x_size = 351, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/newline2Paragraph.golden => .golden/richParagraphLayout/newline2Paragraph.golden +4 -0
@@ 1,5 1,9 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 1000000000, y_origin = 0, x_size = 0, y_size = -3000}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 1000000000, y_origin = 0, x_size = 0, y_size = -1500}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 1000000000, y_origin = -1500, x_size = 0, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/newline2TextParagraph.golden => .golden/richParagraphLayout/newline2TextParagraph.golden +5 -0
@@ 1,5 1,10 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 999999649, y_origin = 0, x_size = 351, y_size = -4500}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 999999649, y_origin = 0, x_size = 351, y_size = -1500}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 999999649, y_origin = -1500, x_size = 351, y_size = -1500}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 999999649, y_origin = -3000, x_size = 351, y_size = -1500}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text"

M .golden/richParagraphLayout/spaceBoxCollapsed.golden => .golden/richParagraphLayout/spaceBoxCollapsed.golden +10 -0
@@ 1,5 1,15 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 590, y_size = -8968}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -3363, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -4484, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -5605, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 0, y_origin = -6726, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 0, y_origin = -7847, x_size = 590, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/spaceBoxMiddle.golden => .golden/richParagraphLayout/spaceBoxMiddle.golden +3 -0
@@ 1,5 1,8 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 4635, y_size = -1121}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 4635, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M .golden/richParagraphLayout/spaceBoxPreserved.golden => .golden/richParagraphLayout/spaceBoxPreserved.golden +11 -0
@@ 1,5 1,16 @@
ParagraphLayout
    { paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 590, y_size = -10089}
    , paragraphLines =
        [ Line {lineNumber = 1, lineRect = Rect {x_origin = 0, y_origin = 0, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 2, lineRect = Rect {x_origin = 0, y_origin = -1121, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 3, lineRect = Rect {x_origin = 0, y_origin = -2242, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 4, lineRect = Rect {x_origin = 0, y_origin = -3363, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 5, lineRect = Rect {x_origin = 0, y_origin = -4484, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 6, lineRect = Rect {x_origin = 0, y_origin = -5605, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 7, lineRect = Rect {x_origin = 0, y_origin = -6726, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 8, lineRect = Rect {x_origin = 0, y_origin = -7847, x_size = 590, y_size = -1121}}
        , Line {lineNumber = 9, lineRect = Rect {x_origin = 0, y_origin = -8968, x_size = 590, y_size = -1121}}
        ]
    , paragraphFragments =
        [ Fragment
            { fragmentUserData = "text1"

M CHANGELOG.md => CHANGELOG.md +9 -0
@@ 8,6 8,15 @@
    * For backwards compatibility, line top alignment remains the default.
      Consider updating your code to use baseline alignment instead.

* Added `paragraphLines` to the rich text `ParagraphLayout`.
  This describes the dimensions of each line of wrapped text,
  including situations when the line contains more space than
  what is necessary to contain its fragments.
  This new field must be passed to pagination.

* The width of a rich text `ParagraphLayout` will now remain unchanged
  after pagination.

* Lines will now be sized to fit all ancestor boxes of all fragments
  on the line, as well as the root box, even if some of those boxes
  do not directly contain any text.

M balkon.cabal => balkon.cabal +1 -0
@@ 111,6 111,7 @@ library balkon-internal
        Data.Text.ParagraphLayout.Internal.BoxOptions,
        Data.Text.ParagraphLayout.Internal.Break,
        Data.Text.ParagraphLayout.Internal.Fragment,
        Data.Text.ParagraphLayout.Internal.Line,
        Data.Text.ParagraphLayout.Internal.LineHeight,
        Data.Text.ParagraphLayout.Internal.LinePagination,
        Data.Text.ParagraphLayout.Internal.Paginable,

M lib/Data/Text/ParagraphLayout/Rich.hs => lib/Data/Text/ParagraphLayout/Rich.hs +12 -1
@@ 61,8 61,18 @@ module Data.Text.ParagraphLayout.Rich
    , paragraphText
    -- * Output layout
    , layoutRich
    , ParagraphLayout (ParagraphLayout, paragraphRect, paragraphFragments)
    , ParagraphLayout
        ( ParagraphLayout
        , paragraphRect
        , paragraphLines
        , paragraphFragments
        )
    , paragraphSafeWidth
    , Line
        ( Line
        , lineNumber
        , lineRect
        )
    , Fragment
        ( Fragment
        , fragmentUserData


@@ 89,6 99,7 @@ where
import Data.Text.ParagraphLayout.Internal.AncestorBox
import Data.Text.ParagraphLayout.Internal.BoxOptions
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.Line
import Data.Text.ParagraphLayout.Internal.LineHeight
import Data.Text.ParagraphLayout.Internal.ParagraphAlignment
import Data.Text.ParagraphLayout.Internal.ParagraphOptions

M src/Data/Text/ParagraphLayout/Internal/Layout.hs => src/Data/Text/ParagraphLayout/Internal/Layout.hs +22 -11
@@ 33,6 33,7 @@ import Data.Text.ParagraphLayout.Internal.BiDiReorder
import Data.Text.ParagraphLayout.Internal.BoxOptions
import Data.Text.ParagraphLayout.Internal.Break
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.Line
import Data.Text.ParagraphLayout.Internal.LineHeight
import Data.Text.ParagraphLayout.Internal.ParagraphAlignment
import Data.Text.ParagraphLayout.Internal.ParagraphExtents


@@ 58,17 59,22 @@ type ProtoFragmentWithBoxes d = WithBoxes d (ProtoFragmentWithSpan d)
-- | 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.
-- The first output component is a flat list of fragments positioned
-- in both dimensions.
--
-- The second output component is a list of lines containing the
-- positioned content.
layoutAndAlignLines
    :: Direction
    -> ParagraphAlignment
    -> Int32
    -> NonEmpty (WithSpan d Run)
    -> [FragmentWithSpan d]
layoutAndAlignLines dir align maxWidth runs = frags
    -> ([FragmentWithSpan d], [Line])
layoutAndAlignLines dir align maxWidth runs = (frags, ls)
    where
        frags = concatMap toList fragsInLines
        (_, fragsInLines) = mapAccumL positionLine originY numberedLines
        (fragsInLines, ls) = unzip fragsAndLines
        (_, fragsAndLines) = mapAccumL positionLine originY numberedLines
        positionLine = positionLineH dir align maxWidth
        numberedLines = zip [1 ..] canonicalLines
        canonicalLines = fmap reorderProtoFragments visibleLines


@@ 100,27 106,32 @@ layoutLines maxWidth openBoxes runs = case nonEmpty rest of

-- | Position all the given horizontal fragments on the same line,
-- using @originY@ as its top edge, and return the bottom edge for continuation.
--
-- Also return a `Line` structure with a defined vertical dimensions
-- but undefined horizontal dimensions.
positionLineH
    :: Direction
    -> ParagraphAlignment
    -> Int32
    -> Int32
    -> (Int, PL.ProtoLine NonEmpty d)
    -> (Int32, NonEmpty (FragmentWithSpan d))
positionLineH dir align maxWidth originY (num, pl) = (nextY, frags)
    -> (Int32, (NonEmpty (FragmentWithSpan d), Line))
positionLineH dir align maxWidth originY (num, pl) =
    (nextY, (frags, line))
    where
        nextY = minimum $ fmap y_min rects
        rects = fmap (\ (WithSpan _ r) -> fragmentRect r) frags
        line = Line { lineNumber = num, lineRect = Rect 0 originY 0 sizeY }
        sizeY = nextY - originY
        (_, frags) = mapAccumL (positionFragmentH num) originX wpfs
        wpfs = PL.applyBoxes $ verticalAlignment originY pl
        wpfs = PL.applyBoxes alignedLine
        (nextY, alignedLine) = verticalAlignment originY pl
        originX = paragraphOriginX + if lineWidth > maxWidth
            then overflowingLineOffset dir (lineWidth - maxWidth)
            else fittingLineOffset align dir (maxWidth - lineWidth)
        lineWidth = PL.width pl

verticalAlignment :: Int32 -> PL.ProtoLine NonEmpty d ->
    PL.ProtoLine NonEmpty d
verticalAlignment originY pl = PL.mapFragments setOrigin pl
    (Int32, PL.ProtoLine NonEmpty d)
verticalAlignment originY pl = (bottomY, PL.mapFragments setOrigin pl)
    where
        bottomY = originY - finalLineHeight
        finalLineHeight = fittingTop - fittingBottom

A src/Data/Text/ParagraphLayout/Internal/Line.hs => src/Data/Text/ParagraphLayout/Internal/Line.hs +25 -0
@@ 0,0 1,25 @@
module Data.Text.ParagraphLayout.Internal.Line (Line (..), shiftLine)
where

import Data.Int (Int32)

import Data.Text.ParagraphLayout.Internal.Rect

-- | Information about a line in a laid out paragraph.
data Line = Line
    { lineNumber :: Int
    -- ^ Logical number of the line box, starting at 1 for a given paragraph.
    , lineRect :: Rect Int32
    -- ^ Dimensions of the line box (CSS), containing all fragments on a given
    -- line, and matching the width of the whole paragraph.
    }
    deriving (Eq, Read, Show)

-- | Add @dx@ and @dy@ to the line's `x_origin` and `y_origin`,
-- respectively.
shiftLine :: Int32 -> Int32 -> Line -> Line
shiftLine dx dy l = l'
    where
        l' = l { lineRect = r' }
        r' = r { x_origin = x_origin r + dx, y_origin = y_origin r + dy }
        r = lineRect l

M src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs => src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs +13 -14
@@ 34,13 34,12 @@ class GenericLayout pl where
    -- | Keep only fragments with the given line number.
    limitFragments :: Int -> pl -> pl

    -- | Add @dx@ and @dy@ to each fragment's `x_origin` and `y_origin`,
    -- respectively.
    shiftFragments :: Int32 -> Int32 -> pl -> pl
    -- | Shift all contents of the paragraph by @dx, dy@.
    shiftContents :: Int32 -> Int32 -> pl -> pl

    -- | Combine fragments from two layouts into one,
    -- | Combine contents from two layouts into one,
    -- without any adjustment of coordinates.
    appendFragments :: pl -> pl -> pl
    appendContents :: pl -> pl -> pl

instance GenericLayout (P.ParagraphLayout d) where
    empty = P.emptyParagraphLayout


@@ 48,17 47,17 @@ instance GenericLayout (P.ParagraphLayout d) where
    topDistance pl = topFragmentOrigin $ P.paragraphFragments pl
    leftDistance pl = leftmostFragmentOrigin $ P.paragraphFragments pl
    limitFragments n = P.filterFragments (fragmentIsOnLine n)
    shiftFragments dx dy = P.mapFragments (shiftFragment dx dy)
    appendFragments = P.appendFragments
    shiftContents dx dy = P.mapFragments (shiftFragment dx dy)
    appendContents = P.appendFragments

instance GenericLayout (R.ParagraphLayout d) where
    empty = R.emptyParagraphLayout
    rect = R.paragraphRect
    topDistance pl = topFragmentOrigin $ R.paragraphFragments pl
    topDistance = R.topDistance
    leftDistance pl = leftmostFragmentOrigin $ R.paragraphFragments pl
    limitFragments n = R.filterFragments (fragmentIsOnLine n)
    shiftFragments dx dy = R.mapFragments (shiftFragment dx dy)
    appendFragments = R.appendFragments
    limitFragments n = R.filterLine n
    shiftContents dx dy = R.shiftContents dx dy
    appendContents = R.appendContents

-- | Split the given paragraph layout into single-line layouts.
cutLines :: (GenericLayout pl, LineNumbers pl) => pl -> [pl]


@@ 70,11 69,11 @@ cutLine n pl = trimTop $ limitFragments n pl

-- | Add a constant to each fragment's `y_origin` so that their maximum is zero.
trimTop :: GenericLayout pl => pl -> pl
trimTop pl = shiftFragments 0 (-topDistance pl) pl
trimTop pl = shiftContents 0 (-topDistance pl) pl

-- | Add a constant to each fragment's `x_origin` so that their minimum is zero.
trimLeft :: GenericLayout pl => pl -> pl
trimLeft pl = shiftFragments (-leftDistance pl) 0 pl
trimLeft pl = shiftContents (-leftDistance pl) 0 pl

topFragmentOrigin :: [Fragment d] -> Int32
topFragmentOrigin frags = maximum $ map (y_origin . fragmentRect) frags


@@ 91,7 90,7 @@ mergeLine :: GenericLayout pl => pl -> pl -> pl
mergeLine pl nextLine = pl'
    where
        -- Quadratic time complexity. TODO: Consider optimising.
        pl' = appendFragments pl $ shiftFragments 0 y nextLine
        pl' = appendContents pl $ shiftContents 0 y nextLine
        y = y_terminus $ rect pl

fragmentIsOnLine :: Int -> Fragment d -> Bool

M src/Data/Text/ParagraphLayout/Internal/Rich.hs => src/Data/Text/ParagraphLayout/Internal/Rich.hs +15 -3
@@ 12,7 12,10 @@ import Data.Text.ParagraphLayout.Internal.BiDiLevels
import Data.Text.ParagraphLayout.Internal.Break
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.Layout
import Data.Text.ParagraphLayout.Internal.Line
import Data.Text.ParagraphLayout.Internal.ParagraphExtents
import Data.Text.ParagraphLayout.Internal.ParagraphOptions
import Data.Text.ParagraphLayout.Internal.Rect
import qualified Data.Text.ParagraphLayout.Internal.ResolvedSpan as RS
import Data.Text.ParagraphLayout.Internal.Rich.Paragraph
import Data.Text.ParagraphLayout.Internal.Rich.ParagraphLayout


@@ 23,15 26,24 @@ import Data.Text.ParagraphLayout.Internal.WithSpan

-- | Lay out a rich text paragraph.
layoutRich :: Paragraph d -> ParagraphLayout d
layoutRich p = paragraphLayout $ map unwrap frags
layoutRich p = ParagraphLayout pRect stretchedLines unwrappedFrags
    where
        Paragraph _ _ root opts = p
        RootBox (Box _ rootTextOpts) = root
        pRect = containRects $ map lineRect stretchedLines
        stretchedLines = map stretchLine ls
        stretchLine l = l { lineRect = stretchRect (lineRect l) }
        stretchRect r = r
            { x_origin = x_origin containingRect
            , x_size = x_size containingRect
            }
        containingRect = containRects $ map fragmentSpacedRect unwrappedFrags
        unwrappedFrags = map unwrap frags
        unwrap (WithSpan rs frag) =
            frag { fragmentUserData = RS.spanUserData rs }
        frags = case nonEmpty wrappedRuns of
        (frags, ls) = case nonEmpty wrappedRuns of
            Just xs -> layoutAndAlignLines dir align maxWidth xs
            Nothing -> []
            Nothing -> ([], [])
        wrappedRuns = spansToRunsWrapped spans
        -- TODO: To support @unicode-bidi: plaintext@ as in CSS, allow ignoring
        --       the text direction of the root box, and instead use the BiDi

M src/Data/Text/ParagraphLayout/Internal/Rich/ParagraphLayout.hs => src/Data/Text/ParagraphLayout/Internal/Rich/ParagraphLayout.hs +42 -31
@@ 1,20 1,21 @@
module Data.Text.ParagraphLayout.Internal.Rich.ParagraphLayout
    ( ParagraphLayout (..)
    , appendFragments
    , appendContents
    , emptyParagraphLayout
    , filterFragments
    , mapFragments
    , paragraphLayout
    , filterLine
    , paragraphOriginX
    , paragraphOriginY
    , paragraphSafeWidth
    , shapedRuns
    , shiftContents
    , topDistance
    )
where

import Data.Int (Int32)

import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.Line
import Data.Text.ParagraphLayout.Internal.LineNumbers
import Data.Text.ParagraphLayout.Internal.LinePagination
import Data.Text.ParagraphLayout.Internal.ParagraphExtents


@@ 24,6 25,9 @@ import Data.Text.ParagraphLayout.Internal.Rect
data ParagraphLayout d = ParagraphLayout
    { paragraphRect :: Rect Int32
    -- ^ The containing block (CSS3).
    , paragraphLines :: [Line]
    -- ^ Information about line boxes (CSS).
    -- May describe additional empty space around text fragments.
    , paragraphFragments :: [Fragment d]
    -- ^ The resulting layout of all input text, divided into fragments as
    -- required by the input structure, line breaking, text writing direction,


@@ 37,38 41,45 @@ instance LineNumbers (ParagraphLayout d) where
instance LineHeight (ParagraphLayout d) where
    lineHeight pl = height $ paragraphRect pl

-- | Wrap the given `Fragment`s and compute their containing rectangle.
paragraphLayout :: [Fragment d] -> ParagraphLayout d
paragraphLayout frags = ParagraphLayout pRect frags
    where pRect = containRects $ map fragmentSpacedRect frags

-- | A `ParagraphLayout` with no fragments.
-- Useful as an identity element for `appendFragments`.
-- | A `ParagraphLayout` with no fragments and no lines.
-- Useful as an identity element for `appendContents`.
emptyParagraphLayout :: ParagraphLayout a
emptyParagraphLayout = ParagraphLayout emptyRect []
emptyParagraphLayout = ParagraphLayout emptyRect [] []

-- | Remove fragments that do not match the given predicate.
--
-- The containing rectangle will be recalculated.
filterFragments :: (Fragment d -> Bool) -> ParagraphLayout d ->
    ParagraphLayout d
filterFragments predicate (ParagraphLayout _ frags) =
    paragraphLayout $ filter predicate frags
-- | Distance from the paragraph origin to its topmost line.
topDistance :: ParagraphLayout d -> Int32
topDistance pl = case paragraphLines pl of
    [] -> 0
    ls -> maximum $ map (y_origin . lineRect) ls

-- | Run a mapping function over each fragment inside a `ParagraphLayout`.
--
-- The containing rectangle will be recalculated.
mapFragments :: (Fragment d -> Fragment d) -> ParagraphLayout d ->
    ParagraphLayout d
mapFragments mapFunc (ParagraphLayout _ frags) =
    paragraphLayout $ map mapFunc frags
-- | Keep the line with the given number and its fragments,
-- and remove everything else.
filterLine :: Int -> ParagraphLayout d -> ParagraphLayout d
filterLine num (ParagraphLayout _ ls frags) =
    ParagraphLayout pRect' ls' frags'
    where
        pRect' = containRects $ map lineRect ls
        ls' = filter ((== num) . lineNumber) ls
        frags' = filter ((== num) . fragmentLine) frags

-- | Add @dx@ and @dy@ to the origins of each line and fragment,
-- effectively shifting the whole paragraph by the given amount.
shiftContents :: Int32 -> Int32 -> ParagraphLayout d -> ParagraphLayout d
shiftContents dx dy (ParagraphLayout _ ls frags) =
    ParagraphLayout pRect' ls' frags'
    where
        pRect' = containRects $ map lineRect ls
        ls' = map (shiftLine dx dy) ls
        frags' = map (shiftFragment dx dy) frags

-- | Combine fragments from two `ParagraphLayout`s.
--
-- The containing rectangle will be recalculated.
appendFragments :: ParagraphLayout d -> ParagraphLayout d -> ParagraphLayout d
appendFragments (ParagraphLayout _ a) (ParagraphLayout _ b) =
    paragraphLayout $ a ++ b
appendContents :: ParagraphLayout d -> ParagraphLayout d -> ParagraphLayout d
appendContents (ParagraphLayout _ ls1 frags1) (ParagraphLayout _ ls2 frags2) =
    ParagraphLayout pRect ls frags
    where
        pRect = containRects $ map lineRect ls
        ls = ls1 ++ ls2
        frags = frags1 ++ frags2

-- | Return all shaped runs in the paragraph.
shapedRuns :: ParagraphLayout d -> [ShapedRun]

M test/Data/Text/ParagraphLayout/PrettyShow.hs => test/Data/Text/ParagraphLayout/PrettyShow.hs +6 -1
@@ 147,7 147,7 @@ instance Show d => PrettyShow (Fragment d) where
        ]

instance Show d => PrettyShow (Rich.ParagraphLayout d) where
    prettyShow (Rich.ParagraphLayout pr frags) = concat
    prettyShow (Rich.ParagraphLayout pr ls frags) = concat
        [ "ParagraphLayout"
        , newline
        , indent1


@@ 155,6 155,11 @@ instance Show d => PrettyShow (Rich.ParagraphLayout d) where
        , show pr
        , newline
        , indent1
        , ", paragraphLines ="
        , newline
        , concat $ commaFirstList indent2 $ map show ls
        , newline
        , indent1
        , ", paragraphFragments ="
        , newline
        , concat $ commaFirstList indent2 $ map prettyShow frags

M test/Data/Text/ParagraphLayout/RichSpec.hs => test/Data/Text/ParagraphLayout/RichSpec.hs +2 -1
@@ 109,7 109,8 @@ spec = do
                let input w = loremIpsumParagraph font $
                        opts { paragraphMaxWidth = w }
                let result20em = layoutRich $ input commonWrapWidth
                let resultSafe = layoutRich $ input expectedSafeWidth
                -- Adding + 1 to work around rounding errors.
                let resultSafe = layoutRich $ input (expectedSafeWidth + 1)
                let resultUnsafe = layoutRich $ input (expectedSafeWidth - 1)
                let resultUnsafe2 = layoutRich $ input (expectedSafeWidth - 2)