1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module Data.Text.ParagraphLayout.Internal.Rich.ParagraphLayout
( ParagraphLayout (..)
, appendFragments
, emptyParagraphLayout
, filterFragments
, mapFragments
, paragraphLayout
, paragraphOriginX
, paragraphOriginY
, paragraphSafeWidth
, shapedRuns
)
where
import Data.Int (Int32)
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.LineNumbers
import Data.Text.ParagraphLayout.Internal.LinePagination
import Data.Text.ParagraphLayout.Internal.ParagraphExtents
import Data.Text.ParagraphLayout.Internal.Rect
-- | The resulting layout of the whole paragraph.
data ParagraphLayout d = ParagraphLayout
{ paragraphRect :: Rect Int32
-- ^ The containing block (CSS3).
, paragraphFragments :: [Fragment d]
-- ^ The resulting layout of all input text, divided into fragments as
-- required by the input structure, line breaking, text writing direction,
-- and changes of script.
}
deriving (Eq, Read, Show)
instance LineNumbers (ParagraphLayout d) where
lineNumbersWithDuplication pl = map fragmentLine $ paragraphFragments pl
instance LineHeight (ParagraphLayout d) where
lineHeight pl = height $ paragraphRect pl
-- | Wrap the given `Fragment`s and compute their containing rectangle.
paragraphLayout :: [Fragment d] -> ParagraphLayout d
paragraphLayout frags = ParagraphLayout pRect frags
where pRect = containRects $ map fragmentSpacedRect frags
-- | A `ParagraphLayout` with no fragments.
-- Useful as an identity element for `appendFragments`.
emptyParagraphLayout :: ParagraphLayout a
emptyParagraphLayout = ParagraphLayout emptyRect []
-- | Remove fragments that do not match the given predicate.
--
-- The containing rectangle will be recalculated.
filterFragments :: (Fragment d -> Bool) -> ParagraphLayout d ->
ParagraphLayout d
filterFragments predicate (ParagraphLayout _ frags) =
paragraphLayout $ filter predicate frags
-- | Run a mapping function over each fragment inside a `ParagraphLayout`.
--
-- The containing rectangle will be recalculated.
mapFragments :: (Fragment d -> Fragment d) -> ParagraphLayout d ->
ParagraphLayout d
mapFragments mapFunc (ParagraphLayout _ frags) =
paragraphLayout $ map mapFunc frags
-- | Combine fragments from two `ParagraphLayout`s.
--
-- The containing rectangle will be recalculated.
appendFragments :: ParagraphLayout d -> ParagraphLayout d -> ParagraphLayout d
appendFragments (ParagraphLayout _ a) (ParagraphLayout _ b) =
paragraphLayout $ a ++ b
-- | Return all shaped runs in the paragraph.
shapedRuns :: ParagraphLayout d -> [ShapedRun]
shapedRuns pl = map shapedRun $ paragraphFragments pl
-- | Width of the widest line, including spacing.
--
-- This is the smallest `Data.Text.ParagraphLayout.Rich.paragraphMaxWidth`
-- that will not introduce new line breaks.
--
-- When `Data.Text.ParagraphLayout.Rich.paragraphMaxWidth` is set to `maxBound`,
-- `paragraphSafeWidth` can be used to determine the @max-content@ width of the
-- paragraph for CSS.
paragraphSafeWidth :: ParagraphLayout d -> Int32
paragraphSafeWidth pl = maximum $ map (lineWidth pl) $ lineNumbers pl
lineWidth :: ParagraphLayout d -> Int -> Int32
lineWidth pl line = sum $ map fragmentWidth $ lineFragments pl line
fragmentWidth :: Fragment d -> Int32
fragmentWidth f = width $ fragmentSpacedRect f
lineFragments :: ParagraphLayout d -> Int -> [Fragment d]
lineFragments pl line = filter byLine $ paragraphFragments pl
where
byLine frag = line == fragmentLine frag