module Data.Text.ParagraphLayout.Internal.Paginable
( PageOptions (..)
, Paginable
, paginate
)
where
import Data.Int (Int32)
import Data.Text.ParagraphLayout.Internal.LinePagination
import Data.Text.ParagraphLayout.Internal.ParagraphLine
import Data.Text.ParagraphLayout.Internal.Plain.ParagraphLayout
data PageOptions = PageOptions
{ pageCurrentHeight :: Int32
-- ^ Amount of vertical space available for the paragraph
-- on the current page.
, pageNextHeight :: Int32
-- ^ Expected amount of vertical space available for the paragraph
-- on the next page.
--
-- If this is greater than `pageCurrentHeight`, the paragraph may be pushed
-- onto the next page in order to better satisfy orphan/widow constraints.
, pageOrphans :: Word
-- ^ If a page break is required inside the paragraph, this will be the
-- minimum number of lines to keep at the bottom of this page, if possible.
, pageWidows :: Word
-- ^ If a page break is required inside the paragraph, this will be the
-- minimum number of lines to keep at the top of the next page, if possible.
}
-- | Typeclass for layouts that can be broken into pages.
class Paginable pl where
-- | Break a chunk of content from the given layout, to be placed together
-- on a page.
--
-- Explanation of return values:
--
-- - @(`Continue`, p, `Nothing`)@
-- means that @p@ is the entire layout and fits best on the current page.
--
-- - @(`Break`, p, `Nothing`)@
-- means that @p@ is the entire layout and fits best on a new page.
-- In other words, @p@ should be preceded by a page break.
--
-- - @(`Continue`, p, `Just` rest)@
-- means that @p@ is a part of the layout that fits best on the current
-- page, and @rest@ should be passed to this function again.
-- In other words, @p@ should be followed by a page break.
--
-- - @(`Break`, p, `Just` rest)@
-- means that @p@ is a part of the layout that fits best on a new page,
-- and @rest@ should be passed to this function again.
-- In other words, @p@ should be surrounded by page breaks
-- on both sides.
paginate :: PageOptions -> pl -> (PageContinuity, pl, Maybe pl)
instance Line a => Paginable [a] where
paginate opts ls = case paginateLines o w h1 h2 ls of
(c, p, []) -> (c, p, Nothing)
(c, p, rest) -> (c, p, Just rest)
where
o = pageOrphans opts
w = pageWidows opts
h1 = pageCurrentHeight opts
h2 = pageNextHeight opts
-- | Break a paragraph in order to fit the given pagination constraints.
instance Paginable ParagraphLayout where
paginate opts pl = case paginate opts (cutLines pl) of
(c, p, Nothing) -> (c, mergeLines p, Nothing)
(c, p, Just rest) -> (c, mergeLines p, Just (mergeLines rest))