~jaro/balkon

ref: 6cf2c7c1029689145011c1836ec519a485b7f088 balkon/src/Data/Text/ParagraphLayout/Plain.hs -rw-r--r-- 4.5 KiB
6cf2c7c1Jaro Use Rect in "plain" interface. 1 year, 6 months ago
                                                                                
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
-- | Shaping for a paragraph of plain, unidirectional text using a single font.
--
-- The input text must be encoded as UTF-8 in a contiguous byte array.
--
-- Positions and distances are represented as 32-bit integers. Their unit must
-- be defined by the caller, who must calculate the desired dimensions of the
-- EM square of the input font and set them using @hb_font_set_scale()@. For
-- example, if @1em = 20px@, if the output pixels are square, and if the output
-- coordinates are in 1/64ths of a pixel, you should set both the @x_scale@ and
-- the @y_scale@ to @1280@.
module Data.Text.ParagraphLayout.Plain
    (LineHeight(..)
    ,Paragraph(..)
    ,ParagraphLayout(..)
    ,ParagraphOptions(..)
    ,Rect(..)
    ,Span(..)
    ,SpanLayout(..)
    ,exampleParagraph
    ,layoutPlain
    )
where

import Data.Int (Int32)
import Data.Text (pack)
import Data.Text.Array (Array)
import Data.Text.Foreign (I8)
import Data.Text.Glyphize (Font, GlyphInfo, GlyphPos)
import Data.Text.Internal (Text(Text))

import Data.Text.ParagraphLayout.Rect

-- | Text to be laid out as a paragraph.
--
-- May be divided into any number of neighbouring spans, each of which will
-- have its own layout rectangle(s) calculated.
data Paragraph = Paragraph

    Array
    -- ^ A byte array containing the whole text to be laid out, in UTF-8.

    I8
    -- ^ Byte offset of the first span.
    -- Any characters preceding this offset will not be shaped, but may still
    -- be used to influence the shape of neighbouring characters.

    [Span]
    -- ^ Parts of the text to be laid out, in logical order.
    -- The offset plus total length of all spans must not exceed array bounds.
    -- Any characters following the last span will not be shaped, but may still
    -- be used to influence the shape of neighbouring characters.

    ParagraphOptions
    -- ^ Properties applying to the paragraph as a whole.

data ParagraphOptions = ParagraphOptions
    { paragraphFont :: Font
    , paragraphLineHeight :: LineHeight
    , paragraphMaxWidth :: Int32
    }

data LineHeight

    = Absolute Int32
    -- ^ Set line height independently of the font.

    | Relative Float
    -- ^ Set line height as a multiplier of the font's built-in value.

data Span = Span

    { spanLength :: I8
    -- ^ Byte offset to the next span or the end of the paragraph text.

    , spanLanguage :: String
    -- ^ Used for selecting the appropriate glyphs and line breaking rules.

    }

-- | The resulting layout of the whole paragraph.
data ParagraphLayout = ParagraphLayout
    { paragraphRect :: Rect Int32
    , spanLayouts :: [SpanLayout]
    }
    deriving (Eq, Read, Show)

-- | The resulting layout of each span, which may include multiple bounding
-- boxes if broken over multiple lines.
data SpanLayout = SpanLayout [Box]
    deriving (Eq, Read, Show)

type Box =
    ( Rect Int32
    -- ^ Rectangle containing all glyph advances in this box. This is the space
    -- that the glyphs "take up" and is probably what you want to use for
    -- detecting position-based events such as mouse clicks.
    --
    -- Beware that actual glyphs will not be drawn exactly to the borders of
    -- this rectangle -- they may be offset inwards and they can also extend
    -- outwards!
    --
    -- These are not the typographic bounding boxes that you use for determining
    -- the area to draw on -- you need FreeType or a similar library for that.
    --
    -- The origin coordinates are relative to the paragraph.
    --
    -- The sizes can be positive or negative, depending on the text direction.
    --
    -- X coordinates increase from left to right.
    -- Y coordinates increase from bottom to top.
    , [(GlyphInfo, GlyphPos)]
    )

-- | Interface for basic plain text layout.
--
-- The entire paragraph will be assumed to have the same text direction and
-- will be shaped using a single font, aligned to the left for LTR text or to
-- the right for RTL text.
layoutPlain :: Paragraph -> ParagraphLayout
-- Stub implementation to make this a valid Haskell source.
-- Of course, this will eventually be replaced by an actual implementation. :)
layoutPlain (Paragraph _ _ spans _)
    = ParagraphLayout (Rect 0 0 0 0) (map (\_ -> SpanLayout []) spans)

exampleArray :: Array
exampleOffset :: Int
(Text exampleArray exampleOffset _) = pack "Tak jsem tady, 世界!"

exampleParagraph :: Font -> Paragraph
exampleParagraph font = Paragraph
    exampleArray
    (fromIntegral exampleOffset + 4)
    [Span 11 "cs" -- this will contain the text "jsem tady, "
    ,Span 7 "ja" -- this will contain the text "世界!"
    ]
    (ParagraphOptions font (Relative 1.5) 20000)