From 259fe6a14bb6d13e227635bfe344970b422ace1e Mon Sep 17 00:00:00 2001 From: Jaro Date: Tue, 28 Feb 2023 00:45:06 +0100 Subject: [PATCH] Use ICU to find line break boundaries. --- .golden/loremIpsum100em/golden | 19 +-- .golden/loremIpsum20em/golden | 104 +++++++-------- balkon.cabal | 5 +- src/Data/Text/ParagraphLayout/Break.hs | 39 ++++++ src/Data/Text/ParagraphLayout/Plain.hs | 81 ++++++++++-- src/Data/Text/ParagraphLayout/ResolvedSpan.hs | 2 + test/Data/Text/ParagraphLayout/BreakSpec.hs | 122 ++++++++++++++++++ test/Data/Text/ParagraphLayout/SpanData.hs | 3 + 8 files changed, 302 insertions(+), 73 deletions(-) create mode 100644 src/Data/Text/ParagraphLayout/Break.hs create mode 100644 test/Data/Text/ParagraphLayout/BreakSpec.hs diff --git a/.golden/loremIpsum100em/golden b/.golden/loremIpsum100em/golden index 68b7efd..bfcb7cc 100644 --- a/.golden/loremIpsum100em/golden +++ b/.golden/loremIpsum100em/golden @@ -1,5 +1,5 @@ -ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -2242, x_size = 99586, y_size = 2242}, spanLayouts = [ - SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 99586, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = +ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -3363, x_size = 99185, y_size = 3363}, spanLayouts = [ + SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 95643, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = [(GlyphInfo {codepoint = 47, cluster = 0, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 507, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 1, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 85, cluster = 2, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -212,15 +212,15 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -2242, x_size = (GlyphInfo {codepoint = 3, cluster = 209, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 210, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 211, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 212, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 70, cluster = 213, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 452, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 212, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 99185, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 70, cluster = 213, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 452, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 214, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 80, cluster = 215, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 80, cluster = 216, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 217, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 71, cluster = 218, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 99283, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 82, cluster = 219, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 71, cluster = 218, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 82, cluster = 219, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 220, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 70, cluster = 221, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 452, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 222, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -435,8 +435,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -2242, x_size = (GlyphInfo {codepoint = 72, cluster = 433, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 86, cluster = 434, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 446, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 435, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 436, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 79, cluster = 437, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 436, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 4041, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 79, cluster = 437, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 438, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 69, cluster = 439, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 440, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), diff --git a/.golden/loremIpsum20em/golden b/.golden/loremIpsum20em/golden index e4dbffb..b0a7891 100644 --- a/.golden/loremIpsum20em/golden +++ b/.golden/loremIpsum20em/golden @@ -1,5 +1,5 @@ -ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = 19967, y_size = 12331}, spanLayouts = [ - SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 19905, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = +ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = 19525, y_size = 12331}, spanLayouts = [ + SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 18541, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = [(GlyphInfo {codepoint = 47, cluster = 0, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 507, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 1, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 85, cluster = 2, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -39,12 +39,12 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 87, cluster = 36, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 37, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 85, cluster = 38, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 39, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 68, cluster = 40, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 39, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 17674, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 68, cluster = 40, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 71, cluster = 41, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 76, cluster = 42, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -1121, x_size = 19860, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 83, cluster = 43, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 76, cluster = 42, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 83, cluster = 43, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 44, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 86, cluster = 45, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 446, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 70, cluster = 46, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -79,17 +79,17 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 83, cluster = 75, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 76, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 85, cluster = 77, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 78, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 76, cluster = 79, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 78, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 16812, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 76, cluster = 79, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 81, cluster = 80, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 70, cluster = 81, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 82, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 71, cluster = 83, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 84, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 71, cluster = 85, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 88, cluster = 86, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -2242, x_size = 19179, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 81, cluster = 87, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 88, cluster = 86, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 81, cluster = 87, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 88, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 89, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 90, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -117,8 +117,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 74, cluster = 112, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 578, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 81, cluster = 113, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 114, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 115, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 68, cluster = 116, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 115, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 17211, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 68, cluster = 116, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 117, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 118, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 84, cluster = 119, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -131,9 +132,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 3, cluster = 126, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 127, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 81, cluster = 128, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 76, cluster = 129, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -3363, x_size = 19967, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 80, cluster = 130, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 76, cluster = 129, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 80, cluster = 130, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 131, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 132, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 71, cluster = 133, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -156,8 +156,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 88, cluster = 150, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 151, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 86, cluster = 152, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 446, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 153, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 81, cluster = 154, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 153, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 19508, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 81, cluster = 154, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 155, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 86, cluster = 156, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 446, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 157, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -175,9 +176,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 68, cluster = 169, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 170, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 171, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 82, cluster = 172, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -4484, x_size = 19496, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 81, cluster = 173, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 82, cluster = 172, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 81, cluster = 173, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 174, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 175, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 176, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -202,8 +202,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 3, cluster = 195, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 196, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 197, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 198, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 68, cluster = 199, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 198, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 18074, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 68, cluster = 199, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 200, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 201, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 84, cluster = 202, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -220,9 +221,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 70, cluster = 213, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 452, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 214, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 80, cluster = 215, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 80, cluster = 216, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -5605, x_size = 19717, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 82, cluster = 217, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 80, cluster = 216, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 82, cluster = 217, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 71, cluster = 218, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 219, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 220, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -241,8 +241,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 88, cluster = 233, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 234, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 86, cluster = 235, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 446, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 236, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 68, cluster = 237, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 236, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 16045, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 68, cluster = 237, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 238, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 239, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 392, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 240, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -265,9 +266,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 85, cluster = 257, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 258, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 83, cluster = 259, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 85, cluster = 260, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -6726, x_size = 19780, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 72, cluster = 261, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 85, cluster = 260, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 72, cluster = 261, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 75, cluster = 262, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 571, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 263, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 81, cluster = 264, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -279,8 +279,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 3, cluster = 270, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 271, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 81, cluster = 272, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 273, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 89, cluster = 274, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 488, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 273, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 19525, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 89, cluster = 274, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 488, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 275, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 276, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 277, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -311,9 +312,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 71, cluster = 302, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 303, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 304, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 82, cluster = 305, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -7847, x_size = 19775, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 85, cluster = 306, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 82, cluster = 305, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 85, cluster = 306, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 307, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 308, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 309, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -325,8 +325,9 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 76, cluster = 315, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 316, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 317, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 318, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 81, cluster = 319, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 318, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 17455, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 81, cluster = 319, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 320, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 321, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 322, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -358,16 +359,16 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 87, cluster = 348, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 3, cluster = 349, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 350, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 70, cluster = 351, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -8968, x_size = 19822, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 70, cluster = 352, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 70, cluster = 351, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 70, cluster = 352, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 353, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 72, cluster = 354, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 559, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 70, cluster = 355, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 68, cluster = 356, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 87, cluster = 357, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 402, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 358, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 70, cluster = 359, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 358, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -10089, x_size = 18534, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 70, cluster = 359, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 360, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 83, cluster = 361, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 362, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -403,14 +404,14 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 88, cluster = 392, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 79, cluster = 393, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 273, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 83, cluster = 394, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 68, cluster = 395, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -10089, x_size = 19687, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 3, cluster = 396, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 68, cluster = 395, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 396, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 84, cluster = 397, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 88, cluster = 398, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 399, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 3, cluster = 400, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 82, cluster = 401, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 3, cluster = 400, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 231, y_advance = 0, x_offset = 0, y_offset = 0})] + }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -11210, x_size = 19490, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = + [(GlyphInfo {codepoint = 82, cluster = 401, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 431, cluster = 402, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 1033, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 70, cluster = 405, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 465, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 76, cluster = 406, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 253, y_advance = 0, x_offset = 0, y_offset = 0}), @@ -448,9 +449,8 @@ ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = -12331, x_size = (GlyphInfo {codepoint = 68, cluster = 438, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 69, cluster = 439, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 589, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 82, cluster = 440, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 590, y_advance = 0, x_offset = 0, y_offset = 0}), - (GlyphInfo {codepoint = 85, cluster = 441, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0})] - }, Fragment {fragmentRect = Rect {x_origin = 0, y_origin = -11210, x_size = 1681, y_size = -1121}, fragmentPen = (0,-932), fragmentGlyphs = - [(GlyphInfo {codepoint = 88, cluster = 442, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 85, cluster = 441, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 386, y_advance = 0, x_offset = 0, y_offset = 0}), + (GlyphInfo {codepoint = 88, cluster = 442, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 574, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 80, cluster = 443, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 861, y_advance = 0, x_offset = 0, y_offset = 0}), (GlyphInfo {codepoint = 17, cluster = 444, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 246, y_advance = 0, x_offset = 0, y_offset = 0})] }] diff --git a/balkon.cabal b/balkon.cabal index b8c82dc..a6af70e 100644 --- a/balkon.cabal +++ b/balkon.cabal @@ -97,6 +97,7 @@ library -- Modules exported by the library. exposed-modules: Data.Text.ParagraphLayout, + Data.Text.ParagraphLayout.Break, Data.Text.ParagraphLayout.Fragment, Data.Text.ParagraphLayout.LineHeight, Data.Text.ParagraphLayout.Plain, @@ -133,6 +134,7 @@ test-suite balkon-test other-modules: Data.Text.ParagraphLayoutSpec, + Data.Text.ParagraphLayout.BreakSpec, Data.Text.ParagraphLayout.FontLoader, Data.Text.ParagraphLayout.ParagraphConstruction, Data.Text.ParagraphLayout.ParagraphData, @@ -153,7 +155,8 @@ test-suite balkon-test hspec-discover, hspec-golden ^>=0.2.0.0, harfbuzz-pure ^>=1.0.2.0, - text ^>=2.0.1 + text ^>=2.0.1, + text-icu ^>=0.8.0.2 build-tool-depends: hspec-discover:hspec-discover diff --git a/src/Data/Text/ParagraphLayout/Break.hs b/src/Data/Text/ParagraphLayout/Break.hs new file mode 100644 index 0000000..01fee72 --- /dev/null +++ b/src/Data/Text/ParagraphLayout/Break.hs @@ -0,0 +1,39 @@ +-- | Boundary analysis using `Data.Text.ICU`, but returning numeric offsets +-- instead of text slices. +-- +-- Within this module, each /offset/ refers to the number of `Word8` items +-- (also called UTF-8 code units or bytes) between the start of the input `Text` +-- and the position of the break. The internal offset of the `Text` from the +-- start of its underlying byte array is excluded. +module Data.Text.ParagraphLayout.Break (breaksDesc, subOffsetsDesc) +where + +import Data.Text (Text) +import Data.Text.Foreign (lengthWord8) +import Data.Text.ICU (Break, Breaker, breaksRight, brkPrefix, brkStatus) + +-- | List of all breaks in the given text, with offsets in descending order, +-- including the status of the break if applicable. +-- +-- Includes the start of the text (with offset 0) as the last list item. +-- +-- Excludes the end of the text (with offset equal to the text length). +breaksDesc :: Breaker a -> Text -> [(Int, a)] +breaksDesc breaker input = map brkStartOffsetStatus $ breaksRight breaker input + +brkStartOffsetStatus :: Break a -> (Int, a) +brkStartOffsetStatus brk = (brkStartOffset brk, brkStatus brk) + +-- | The ICU library returns "breaks" as slices of text with two boundaries. +-- This gives the smaller of the two distances from the start of the text +-- to the boundaries of the break. +brkStartOffset :: Break a -> Int +brkStartOffset brk = lengthWord8 (brkPrefix brk) + +-- | Given a list of offsets into a text in descending order, produce a list of +-- corresponding offsets into a slice of the text starting at a given offset. +subOffsetsDesc :: Int -> [(Int, a)] -> [(Int, a)] +subOffsetsDesc d = takeWhile valid . map adjust + where + valid (off, _) = off >= 0 + adjust (off, a) = (off - d, a) diff --git a/src/Data/Text/ParagraphLayout/Plain.hs b/src/Data/Text/ParagraphLayout/Plain.hs index 85ff9f6..7689530 100644 --- a/src/Data/Text/ParagraphLayout/Plain.hs +++ b/src/Data/Text/ParagraphLayout/Plain.hs @@ -22,6 +22,7 @@ where import Data.Int (Int32) import Data.List (mapAccumL) +import Data.Maybe (fromMaybe, listToMaybe) import Data.Text.Array (Array) import Data.Text.Foreign (I8, lengthWord8) import Data.Text.Glyphize @@ -35,9 +36,12 @@ import Data.Text.Glyphize ,fontExtentsForDir ,shape ) +import Data.Text.ICU (LocaleName(Locale), breakLine) +import qualified Data.Text.ICU as BreakStatus (Line) import Data.Text.Internal (Text(Text)) import qualified Data.Text.Lazy as Lazy +import Data.Text.ParagraphLayout.Break import Data.Text.ParagraphLayout.Fragment import Data.Text.ParagraphLayout.LineHeight import qualified Data.Text.ParagraphLayout.ProtoFragment as PF @@ -234,13 +238,40 @@ tryAddSplitRunsH _ currentX runs 0 = do let (runs1, runs2) = splitTextsAt8 1 runs let (_, pfs) = mapAccumL addRunH currentX runs1 (pfs, runs2) -tryAddSplitRunsH maxWidth currentX runs splitPoint = do - let (runs1, runs2) = splitTextsAt8 splitPoint runs +tryAddSplitRunsH maxWidth currentX runs breakPoint = do + -- TODO: Trim spaces around breaks. + let (runs1, runs2) = splitTextsAt8 breakPoint runs let (nextX, pfs) = mapAccumL addRunH currentX runs1 + let next = nextBreakPoint (fromIntegral breakPoint) runs if abs nextX <= maxWidth then (pfs, runs2) - -- TODO: Use ICU breaking library. - else tryAddSplitRunsH maxWidth currentX runs (splitPoint-1) + else tryAddSplitRunsH maxWidth currentX runs (fromIntegral next) + +-- | Find the farthermost break point in one of the given runs, whose offset is +-- less than the given limit, respecting locale rules. +-- +-- The result will be an offset in `Word8` units from the start of the first +-- `Run` from the list. +-- +-- If no breaks are possible, the result will be @0@. +nextBreakPoint :: Int -> [WithSpan Run] -> Int +nextBreakPoint _ [] = 0 +nextBreakPoint limit runs@(headRun:_) = fromMaybe 0 $ listToMaybe points + where + -- TODO: Try to limit deconstruction of texts for offsets. + (Text _ firstRunOffset _) = getText headRun + points = + dropWhile (>= limit) $ breakPoints firstRunOffset $ reverse runs + +breakPoints :: Int -> [WithSpan Run] -> [Int] +breakPoints _ [] = [] +breakPoints firstRunOffset (x:xs) = offsets ++ rest + where + -- TODO: Try to limit deconstruction of texts for offsets. + (Text _ thisRunOffset _) = getText x + offsets = map (correctOffset . fst) (runLineBreaks x) + correctOffset = (+ (thisRunOffset - firstRunOffset)) + rest = breakPoints firstRunOffset xs -- | Calculate layout for multiple runs on the same line and -- arrange them in one horizontal direction starting from the given x_offset. @@ -282,24 +313,52 @@ shapeRun (WithSpan rs run) = shape font buffer features features = [] resolveSpans :: Paragraph -> [RS.ResolvedSpan] -resolveSpans (Paragraph arr off spans opts) = do - let texts = cuts arr off spans +resolveSpans p@(Paragraph arr off spans opts) = do + let (end, texts) = cuts arr off spans let indexes = [0..] + let paragraphStart = fromIntegral off + (s, t, i) <- zip3 spans texts indexes + let lang = spanLanguage s + let breaks = paragraphLineBreaks p end lang + -- TODO: Try to limit deconstruction of texts for offsets. + let (Text _ spanStart _) = t return RS.ResolvedSpan { RS.spanIndex = i , RS.spanText = t , RS.spanFont = paragraphFont opts , RS.spanLineHeight = paragraphLineHeight opts - , RS.spanLanguage = spanLanguage s + , RS.spanLanguage = lang + , RS.spanLineBreaks = subOffsetsDesc (spanStart - paragraphStart) breaks } --- | Produce a list of `Text`s, defined by an initial offset and a list of --- consecutive `Span`s, out of the underlying `Array`. +paragraphLineBreaks :: Paragraph -> I8 -> String -> [(Int, BreakStatus.Line)] +paragraphLineBreaks (Paragraph arr off _ _) end lang = + breaksDesc (breakLine (localeFromLanguage lang)) paragraphText + where + paragraphText = Text arr (fromIntegral off) (fromIntegral (end - off)) + +runLineBreaks :: WithSpan Run -> [(Int, BreakStatus.Line)] +runLineBreaks (WithSpan rs run) = dropWhile (not . valid) $ + subOffsetsDesc (runStart - spanStart) $ RS.spanLineBreaks rs + where + valid (off, _) = off < runLength + runLength = lengthWord8 $ getText run + -- TODO: Try to limit deconstruction of texts for offsets. + (Text _ runStart _) = getText run + (Text _ spanStart _) = getText rs + +-- TODO: Identify and correct for differences between the two. +localeFromLanguage :: String -> LocaleName +localeFromLanguage x = Locale x + +-- | Given an underlying `Array`, an initial offset, and a list of consecutive +-- `Span`s, produce a list of `Text`s corresponding to the given spans, as well +-- as the offset of the end of the last `Text`. -- -- TODO: Consider adding checks for array bounds. -cuts :: Array -> I8 -> [Span] -> [Text] -cuts arr initialOffset spans = snd $ mapAccumL (cut arr) initialOffset spans +cuts :: Array -> I8 -> [Span] -> (I8, [Text]) +cuts arr initialOffset spans = mapAccumL (cut arr) initialOffset spans -- | Produce a `Text`, defined by an initial offset and a `Span`, out of the -- underlying `Array`. diff --git a/src/Data/Text/ParagraphLayout/ResolvedSpan.hs b/src/Data/Text/ParagraphLayout/ResolvedSpan.hs index f9f38b4..445f729 100644 --- a/src/Data/Text/ParagraphLayout/ResolvedSpan.hs +++ b/src/Data/Text/ParagraphLayout/ResolvedSpan.hs @@ -3,6 +3,7 @@ where import Data.Text (Text) import Data.Text.Glyphize (Font) +import qualified Data.Text.ICU as BreakStatus (Line) import Data.Text.ParagraphLayout.LineHeight import Data.Text.ParagraphLayout.TextContainer @@ -15,6 +16,7 @@ data ResolvedSpan = ResolvedSpan , spanFont :: Font , spanLineHeight :: LineHeight , spanLanguage :: String + , spanLineBreaks :: [(Int, BreakStatus.Line)] } deriving (Show) diff --git a/test/Data/Text/ParagraphLayout/BreakSpec.hs b/test/Data/Text/ParagraphLayout/BreakSpec.hs new file mode 100644 index 0000000..91fe7d7 --- /dev/null +++ b/test/Data/Text/ParagraphLayout/BreakSpec.hs @@ -0,0 +1,122 @@ +module Data.Text.ParagraphLayout.BreakSpec (spec) where + +import Data.Text (empty, pack, singleton) +import Data.Text.ICU + (LocaleName(Locale) + ,breakCharacter + ,breakLine + ,breakSentence + ,breakWord + ) +import qualified Data.Text.ICU as BreakStatus (Line(..), Word(..)) + +import Test.Hspec +import Data.Text.ParagraphLayout.Break + +spec :: Spec +spec = do + + describe "breaksDesc" $ do + + -- One of the crucial building blocks of a text layout engine. + describe "breakLine" $ do + let b lang = breaksDesc $ breakLine (Locale lang) + + it "finds no breaks in empty input" $ + b "en" empty `shouldBe` + [] + + it "finds break at offset 0 in non-empty input" $ + b "en" (singleton 'a') `shouldBe` + [(0, BreakStatus.Soft)] + + it "finds hard break after newline" $ + b "en" (pack "hello\nworld") `shouldBe` + [(6, BreakStatus.Hard) + ,(0, BreakStatus.Soft) + ] + + it "finds soft breaks after spaces and tabs" $ + b "en" (pack "a few\twords") `shouldBe` + [(6, BreakStatus.Soft) + ,(2, BreakStatus.Soft) + ,(0, BreakStatus.Soft) + ] + + it "finds soft breaks after spaces and hyphens" $ + b "cs" (pack "následuje stanice Frýdek-Místek") `shouldBe` + [(27, BreakStatus.Soft) + ,(19, BreakStatus.Soft) + ,(11, BreakStatus.Soft) + ,(0, BreakStatus.Soft) + ] + + it "finds soft breaks in Japanese kana" $ + b "ja" (pack "トイレはどこですか?") `shouldBe` + [(24, BreakStatus.Soft) + ,(21, BreakStatus.Soft) + ,(18, BreakStatus.Soft) + ,(15, BreakStatus.Soft) + ,(12, BreakStatus.Soft) + ,(9, BreakStatus.Soft) + ,(6, BreakStatus.Soft) + ,(3, BreakStatus.Soft) + ,(0, BreakStatus.Soft) + ] + + -- Probably not useful for a web browser rendering engine. + describe "breakSentence" $ do + let b lang = breaksDesc $ breakSentence (Locale lang) + + it "finds no breaks in empty input" $ + b "en" empty `shouldBe` + [] + + it "finds break at offset 0 in non-empty input" $ + b "en" (singleton 'a') `shouldBe` + [(0, ())] + + -- Probably not useful for a web browser rendering engine, + -- but may be used for text search and selection. + describe "breakWord" $ do + let b lang = breaksDesc $ breakWord (Locale lang) + + it "finds no breaks in empty input" $ + b "en" empty `shouldBe` + [] + + it "finds break at offset 0 in non-empty input" $ + b "en" (singleton 'a') `shouldBe` + [(0, BreakStatus.Uncategorized)] + + it "finds breaks after runs of letters and spaces" $ + b "en" (pack "a few words") `shouldBe` + [(8, BreakStatus.Uncategorized) + ,(5, BreakStatus.Letter) + ,(2, BreakStatus.Uncategorized) + ,(1, BreakStatus.Letter) + ,(0, BreakStatus.Uncategorized) + ] + + -- Probably not useful for Balkón; + -- HarfBuzz takes care of identifying character clusters for us. + describe "breakCharacter" $ do + let b lang = breaksDesc $ breakCharacter (Locale lang) + + it "finds no breaks in empty input" $ + b "en" empty `shouldBe` + [] + + it "finds break at offset 0 in non-empty input" $ + b "en" (singleton 'a') `shouldBe` + [(0, ())] + + describe "subOffsetsDesc" $ do + + let result = subOffsetsDesc 5 [(11, 'a'), (8, 'b'), (5, 'c'), (2, 'd')] + + it "should reduce offsets" $ + map fst result `shouldBe` [6, 3, 0] + + it "should preserve payload" $ + map snd result `shouldBe` ['a', 'b', 'c'] diff --git a/test/Data/Text/ParagraphLayout/SpanData.hs b/test/Data/Text/ParagraphLayout/SpanData.hs index ed23fe6..fc1a825 100644 --- a/test/Data/Text/ParagraphLayout/SpanData.hs +++ b/test/Data/Text/ParagraphLayout/SpanData.hs @@ -17,6 +17,7 @@ emptySpan font = ResolvedSpan , spanFont = font , spanLineHeight = Normal , spanLanguage = "en" + , spanLineBreaks = [] } czechHello :: Font -> ResolvedSpan @@ -26,6 +27,7 @@ czechHello font = ResolvedSpan , spanFont = font , spanLineHeight = Normal , spanLanguage = "cs" + , spanLineBreaks = [] } serbianMixedScript :: Font -> ResolvedSpan @@ -35,4 +37,5 @@ serbianMixedScript font = ResolvedSpan , spanFont = font , spanLineHeight = Normal , spanLanguage = "sr" + , spanLineBreaks = [] } -- 2.30.2