-- | 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)