~jaro/balkon

810e30b3c54804be1608cdc347f79da42211783e — Jaro 10 months ago a084e33
Add manual test for mixed vertical alignment.
A .golden/shapedRuns/mixedVerticalAlignment.fontInfo => .golden/shapedRuns/mixedVerticalAlignment.fontInfo +1 -0
@@ 0,0 1,1 @@
assets/fonts/ubuntu/Ubuntu-R.ttf 0 32,32 32,32

A .golden/shapedRuns/mixedVerticalAlignment.golden => .golden/shapedRuns/mixedVerticalAlignment.golden +117 -0
@@ 0,0 1,117 @@
[ (0, -57, 
    [ (GlyphInfo {codepoint = 88, cluster = 6, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 83, cluster = 7, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 8, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (44, -48, 
    [ (GlyphInfo {codepoint = 75, cluster = 9, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 76, cluster = 10, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 8, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 74, cluster = 11, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 75, cluster = 12, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 13, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (113, -42, 
    [ (GlyphInfo {codepoint = 68, cluster = 14, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 17, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 81, cluster = 15, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 71, cluster = 16, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 17, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (174, -40, 
    [ (GlyphInfo {codepoint = 75, cluster = 18, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 76, cluster = 19, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 8, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 74, cluster = 20, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 75, cluster = 21, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 22, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 85, cluster = 23, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 12, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 24, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (273, -57, 
    [ (GlyphInfo {codepoint = 71, cluster = 25, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 82, cluster = 26, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 90, cluster = 27, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 25, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 81, cluster = 28, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 29, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (361, -66, 
    [ (GlyphInfo {codepoint = 79, cluster = 30, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 9, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 82, cluster = 31, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 90, cluster = 32, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 25, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 33, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (421, -72, 
    [ (GlyphInfo {codepoint = 68, cluster = 34, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 17, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 81, cluster = 35, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 71, cluster = 36, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 37, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (482, -74, 
    [ (GlyphInfo {codepoint = 79, cluster = 38, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 9, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 82, cluster = 39, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 90, cluster = 40, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 25, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 41, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 85, cluster = 42, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 12, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 43, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (572, -12, 
    [ (GlyphInfo {codepoint = 59, cluster = 44, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 20, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 45, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (599, -92, 
    [ (GlyphInfo {codepoint = 59, cluster = 46, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 20, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 47, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (626, -57, 
    [ (GlyphInfo {codepoint = 86, cluster = 48, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 14, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 87, cluster = 49, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 13, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 85, cluster = 50, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 12, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 51, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 87, cluster = 52, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 13, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 70, cluster = 53, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 15, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 75, cluster = 54, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 76, cluster = 55, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 8, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 81, cluster = 56, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 74, cluster = 57, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 58, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (780, -48, 
    [ (GlyphInfo {codepoint = 87, cluster = 59, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 13, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 68, cluster = 60, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 17, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 79, cluster = 61, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 9, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 79, cluster = 62, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 9, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 63, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (835, -30, 
    [ (GlyphInfo {codepoint = 70, cluster = 64, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 15, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 79, cluster = 65, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 9, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 76, cluster = 66, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 8, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 80, cluster = 67, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 28, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 68, cluster = 68, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 17, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 91, cluster = 69, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 16, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 70, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (935, -42, 
    [ (GlyphInfo {codepoint = 83, cluster = 71, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 85, cluster = 72, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 12, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 73, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 70, cluster = 74, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 15, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 75, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 71, cluster = 76, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 19, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 76, cluster = 77, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 8, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 81, cluster = 78, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 74, cluster = 79, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 80, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (1087, -30, 
    [ (GlyphInfo {codepoint = 87, cluster = 81, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 13, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 75, cluster = 82, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 72, cluster = 83, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 3, cluster = 84, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 7, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
, (1143, -74, 
    [ (GlyphInfo {codepoint = 70, cluster = 85, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 15, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 85, cluster = 86, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 12, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 68, cluster = 87, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 17, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 86, cluster = 88, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 14, y_advance = 0, x_offset = 0, y_offset = 0})
    , (GlyphInfo {codepoint = 75, cluster = 89, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False}, GlyphPos {x_advance = 18, y_advance = 0, x_offset = 0, y_offset = 0})
    ])
]
\ No newline at end of file

M TESTING.md => TESTING.md +68 -0
@@ 150,3 150,71 @@ Steps:
         of blank space below the top of the window that is not large enough to
         insert another line of text.

## Script 4: Verify visual output for vertical alignment

Goals:

  - To verify the correct effect of vertical alignment of inline boxes.

Prerequisites:

  - Access to files from the Git repository of Balkón.

  - Successfully built Typograffiti.
    See previous script.

  - Successfully verified visual output for a whole paragraph.
    See previous script.

  - A horizontal on-screen ruler to check baseline alignment.
    In a stacking window manager, the edge of another window can
    fulfil this role.

Steps:

  1. Run Typograffiti with the font path, face index, pixel size,
     and scale set according to the contents of the file
     `.golden/shapedRuns/mixedVerticalAlignment.fontInfo` from Balkón,
     and with input set to be read from the file
     `.golden/shapedRuns/mixedVerticalAlignment.golden` from Balkón.

  2. Verify that text is displayed in the output window.

  3. If necessary, resize the window so that the whole line can be seen.
     Note that there will be one instance of a letter "X" partially overflowing
     the top edge of the window, which is intentional.

  4. Verify all of the following:

      a. There is a line of text displayed, whose (vertically displaced) words
         read, left to right: "up high and higher down low and lower X X
         stretching tall climax preceding the crash"

      b. The baselines of the words "up", "down", and "stretching" are aligned
         to each other.

      c. The baselines of the words "lower" and "crash" are aligned
         to each other.

      d. The baselines of the words "climax" and "the" are aligned
         to each other.

      e. In the sequence of words "up high and higher", each word is placed
         higher than its preceding word.

      f. In the sequence of words "down low and lower", each word is placed
         lower than its preceding word.

      g. The words "climax" and "the" are placed at the top of the window,
         with only a small amount of blank space below the top of the window.

      h. The word "higher" is placed lower than the words "climax" and "the".

      i. The first letter "X" is placed higher than any of the words,
         and the centre of the letter's shape approximately intersects the top
         edge of the window, making only approximately half of it visible.

      j. The second letter "X" is placed lower than any of the words,
         and the letter's shape intersects the baseline of the words
         "lower" and "crash".


M test/Data/Text/ParagraphLayout/Rich/ParagraphData.hs => test/Data/Text/ParagraphLayout/Rich/ParagraphData.hs +75 -0
@@ 9,6 9,7 @@ module Data.Text.ParagraphLayout.Rich.ParagraphData
    , mixedLineHeightParagraph
    , mixedScriptParagraph
    , mixedSizesParagraph
    , mixedVerticalAlignmentParagraph
    , nestedBoxesParagraph
    , neutralDirectionParagraph
    , newline1Paragraph


@@ 261,6 262,80 @@ mixedSizesParagraph (bigFont, smallFont) = constructParagraph
        tBig = tLTR { textFont = bigFont, textLanguage = "en" }
        tSmall = tLTR { textFont = smallFont, textLanguage = "en" }

mixedVerticalAlignmentParagraph :: Font -> ParagraphOptions -> Paragraph String
mixedVerticalAlignmentParagraph font = constructParagraph
    (pack "prefix")
    (rootBox t
        -- Testing raised baselines.
        [ text "base" "up "
        , box "+9" (bRaise 9) t
            [ text "base+9" "high "
            , box "+6" (bRaise 6) t
                [ text "base+9+6" "and "
                , box "+2" (bRaise 2) t
                    [ text "base+9+6+2" "higher "
                    ]
                ]
            ]
        -- Testing lowered baselines.
        , text "base" "down "
        , box "-9" (bLower 9) t
            [ text "base-9" "low "
            , box "-6" (bLower 6) t
                [ text "base-9-6" "and "
                , box "-2" (bLower 2) t
                    [ text "base-9-6-2" "lower "
                    ]
                ]
            ]
        -- Line-aligned boxes should ignore their baseline-aligned parent.
        --
        -- Setting line height to zero should make approximately half
        -- of the glyph (at the midpoint between ascender and descender)
        -- overflow the line.
        --
        -- The parent should not affect layout unless it happens to be tall
        -- enough to stretch the line.
        , box "redundant" bBaseline (t { textLineHeight = Absolute 70 })
            [ box "(h0)top" bTop (t { textLineHeight = Absolute 0 })
                [ text "(h0)top" "X "
                ]
            , box "(h0)bottom" bBottom (t { textLineHeight = Absolute 0 })
                [ text "(h0)bottom" "X "
                ]
            ]
        -- Stretch the line more upwards than downwards, so that
        -- top-aligned boxes will be placed higher than any other boxes.
        , text "base" "stretching "
        , box "(h72)+9" (bRaise 9) (t { textLineHeight = Absolute 72 })
            [ text "(h72)base+9" "tall "
            ]
        -- Test line-top alignment with a subtree.
        , box "top" bTop t
            [ box "+12" (bRaise 12) t
                [ text "top+12" "climax "
                ]
            , text "top" "preceding "
            ]
        -- Test line-bottom alignment, which should exclude other line-relative
        -- aligned boxes from its subtree.
        , box "bottom" bBottom t
            [ box "top" bTop t
                [ text "bottom+top" "the "
                ]
            , text "bottom" "crash "
            ]
        ]
    )
    (pack "suffix")
    where
        bTop = b_ { boxVerticalAlignment = AlignLineTop }
        bRaise x = b_ { boxVerticalAlignment = AlignBaseline x }
        bBaseline = b_ { boxVerticalAlignment = AlignBaseline 0 }
        bLower x = b_ { boxVerticalAlignment = AlignBaseline (-x) }
        bBottom = b_ { boxVerticalAlignment = AlignLineBottom }
        t = tLTR { textFont = font, textLanguage = "en" }

nestedBoxesParagraph :: Font -> ParagraphOptions -> Paragraph String
nestedBoxesParagraph font = constructParagraph
    (pack "prefix")

M test/Data/Text/ParagraphLayout/RichSpec.hs => test/Data/Text/ParagraphLayout/RichSpec.hs +16 -0
@@ 489,3 489,19 @@ spec = do
                    { paragraphAlignment = AlignStart
                    , paragraphMaxWidth = 144
                    }

        describe "with Latin font" $ do
            let fontPath = latinFont
            font <- runIO $ loadFont latinFont 0 demoOptions

            let shapedRunsSpec = shapedRunsSpecWithFont fontPath font

            shapedRunsSpec
                "handles complex vertical alignment"
                "mixedVerticalAlignment" $
                layoutRich $
                mixedVerticalAlignmentParagraph font $
                defaultParagraphOptions
                    { paragraphAlignment = AlignStart
                    , paragraphMaxWidth = largeWidth
                    }