module Data.Text.ParagraphLayout.Internal.Paragraph (Paragraph(..) ,ParagraphLayout(..) ,ParagraphOptions(..) ,paragraphLayout ,paragraphOriginX ,paragraphOriginY ,paragraphSpanBounds ) where import Data.Int (Int32) import Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as NonEmpty import Data.Text.Array (Array) import Data.Text.Glyphize (Font) import Data.Text.ParagraphLayout.Internal.LineHeight import Data.Text.ParagraphLayout.Internal.Rect import Data.Text.ParagraphLayout.Internal.Span -- | Text to be laid out as a single paragraph. -- -- May be divided into any number of neighbouring spans, each of which will -- be represented as a separate `SpanLayout` in the resulting layout. -- -- The input text must be encoded as UTF-8 in a contiguous byte array. -- -- You may need to use "Data.Text.Internal" in order to determine the byte -- array and the necessary offsets to construct the paragraph without copying -- data. -- -- For simple use cases, it may be sufficient to construct paragraphs using -- [ParagraphConstruction]("Data.Text.ParagraphLayout.ParagraphConstruction"). data Paragraph = Paragraph Array -- ^ A byte array containing the whole text to be laid out, in UTF-8. Int -- ^ Byte offset of the first span from the start of the byte array. -- 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 initial offset plus total length of all spans must not exceed -- the bounds of the byte array. -- Any characters following the last span will not be shaped, but may still -- be used to influence the shape of neighbouring characters. ParagraphOptions -- ^ Options applying to the paragraph as a whole. data ParagraphOptions = ParagraphOptions { paragraphFont :: Font -- ^ Font to be used for shaping and measurement. -- Make sure to set its scale (see `Data.Text.Glyphize.optionScale`) using -- the same units that you want in the output. , paragraphLineHeight :: LineHeight -- ^ Preferred line height of the resulting box fragments. , paragraphMaxWidth :: Int32 -- ^ Line width at which line breaking should occur. -- Lines will be broken at language-appropriate boundaries. -- If a line still exceeds this limit then, it will be broken at character -- boundaries, and if it already consists of a single cluster that cannot -- be further broken down, it will overflow. } deriving (Eq, Show) -- | The resulting layout of the whole paragraph. data ParagraphLayout = ParagraphLayout { paragraphRect :: Rect Int32 -- ^ The containing block (CSS3). , spanLayouts :: [SpanLayout] } deriving (Eq, Read, Show) -- | Calculate the offsets into the `Paragraph`'s underlying `Array` where each -- span starts and ends, in ascending order. The resulting list will be one -- larger than the list of input spans. paragraphSpanBounds :: Paragraph -> NonEmpty Int paragraphSpanBounds (Paragraph _ initialOffset spans _) = NonEmpty.scanl (+) initialOffset (map spanLength spans) paragraphOriginX :: (Num a) => a paragraphOriginX = 0 paragraphOriginY :: (Num a) => a paragraphOriginY = 0 empty :: (Num a) => Rect a empty = Rect { x_origin = paragraphOriginX , y_origin = paragraphOriginY , x_size = 0 , y_size = 0 } containRects :: (Ord a, Num a) => [Rect a] -> Rect a containRects = foldr union empty -- | Wrap the given `SpanLayout`s and compute their containing rectangle. paragraphLayout :: [SpanLayout] -> ParagraphLayout paragraphLayout sls = ParagraphLayout pRect sls where pRect = containRects $ concat $ map spanRects sls