@@ 246,12 246,22 @@ lastSpanBoxes xs = case reverse xs of
-- output list will contain a run of zero characters. This can be used to
-- correctly size an empty line.
--
+-- If there is a hard line break in the input, the run containing it will have
+-- its `runHardBreak` set to `True`.
+--
-- If there is no hard line break in the input, the first output list will
-- contain the whole input, and the second output list will be empty.
hardSplit :: NonEmpty (WithSpan d Run) -> ([WithSpan d Run], [WithSpan d Run])
-hardSplit runs = allowFstEmpty $ trimFst $ NonEmpty.last $ splits
+hardSplit runs = case reverse hSplits of
+ [] -> noSplit
+ (splitRuns : _) -> forcedSplit splitRuns
where
- trimFst (runs1, runs2) = (trim runs1, runs2)
+ noSplit =
+ (NonEmpty.toList (trim runs), [])
+ forcedSplit (runs1, runs2) =
+ (NonEmpty.toList $ markHard $ trim runs1, runs2)
+ markHard = mapLast markHard'
+ markHard' (WithSpan rs x) = WithSpan rs x { runHardBreak = True }
trim
= trimTextsStartPreserve isStartSpace
. trimTextsEndPreserve isEndSpace
@@ 259,13 269,17 @@ hardSplit runs = allowFstEmpty $ trimFst $ NonEmpty.last $ splits
-- TODO: Consider optimising.
-- We do not need to look for any line breaks further than the
-- shortest hard break.
- splits = noSplit :| hSplits
- noSplit = (runs, [])
hSplits = nonEmptyFsts $
-- from longest to shortest
splitTextsBy (map fst . filter isHard . runLineBreaks) runs
isHard (_, status) = status == BreakStatus.Hard
+-- | Apply a function to the last element of the non-empty list.
+mapLast :: (a -> a) -> NonEmpty a -> NonEmpty a
+mapLast f xs = case NonEmpty.uncons xs of
+ (x, Nothing) -> f x :| []
+ (x, Just rest) -> NonEmpty.cons x $ mapLast f rest
+
-- | Treat a list of runs as a contiguous sequence,
-- and find all possible ways to split them into two non-empty lists,
-- using soft line break opportunities (typically after words) and then
@@ 312,10 326,11 @@ layoutRunsH runs = map layoutRunH runs
layoutRunH :: WithSpan d Run -> ProtoFragmentWithSpan d
layoutRunH (WithSpan rs run) = WithSpan rs pf
where
- pf = PF.protoFragmentH dir lvl glyphs
+ pf = PF.protoFragmentH dir lvl glyphs hard
glyphs = shapeRun (WithSpan rs run)
dir = runDirection run
lvl = runLevel run
+ hard = runHardBreak run
-- | Calculate layout for the given run independently of its position.
shapeRun :: WithSpan d Run -> [(GlyphInfo, GlyphPos)]
@@ 356,7 371,7 @@ runBreaksFromSpan :: Run -> [(Int, a)] -> [(Int, a)]
runBreaksFromSpan run spanBreaks =
dropWhile (not . valid) $ subOffsetsDesc (runOffsetInSpan run) spanBreaks
where
- valid (off, _) = off < runLength
+ valid (off, _) = off <= runLength
runLength = lengthWord8 $ getText run
-- | Predicate for characters that can be potentially removed from the
@@ 60,6 60,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
]
it "handles Arabic hello" $ do
@@ 72,6 73,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
]
it "handles Serbian with mixed script" $ do
@@ 85,6 87,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 12
@@ 92,6 95,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Cyrl"
+ , runHardBreak = False
}
]
it "handles mixed direction with base LTR" $ do
@@ 105,6 109,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 7
@@ 112,6 117,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 13
@@ 119,6 125,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
]
it "handles mixed direction with base RTL" $ do
@@ 132,6 139,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 7
@@ 139,6 147,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 13
@@ 146,6 155,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
]
it "handles Arabic text with English inside" $ do
@@ 159,6 169,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 5
@@ 166,6 177,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 14
@@ 173,6 185,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 79
@@ 180,6 193,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 82
@@ 187,6 201,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Zyyy"
+ , runHardBreak = False
}
]
it "handles English text with Arabic inside" $ do
@@ 200,6 215,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 13
@@ 207,6 223,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Arab"
+ , runHardBreak = False
}
, Run
{ runOffsetInSpan = 47
@@ 214,6 231,7 @@ spec = do
, runLevel = 0
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
]
-- Unrealistic example where text changes direction
@@ 229,6 247,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Latn"
+ , runHardBreak = False
}
-- direction change
, Run
@@ 237,6 256,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Latn"
+ , runHardBreak = False
}
-- direction change
, Run
@@ 245,6 265,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Latn"
+ , runHardBreak = False
}
-- script change
, Run
@@ 253,6 274,7 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Cyrl"
+ , runHardBreak = False
}
-- direction change
, Run
@@ 261,6 283,7 @@ spec = do
, runLevel = 2
, runDirection = DirLTR
, runScript = Just "Cyrl"
+ , runHardBreak = False
}
-- direction change
, Run
@@ 269,5 292,6 @@ spec = do
, runLevel = 1
, runDirection = DirRTL
, runScript = Just "Cyrl"
+ , runHardBreak = False
}
]