@@ 6,15 6,16 @@
-- to as pages within this module.
--
-- Assumptions:
--
-- * Lines are laid out from top to bottom.
-- * Each page has the same size.
-- (Preceding context may limit the space available on the given page, but it
-- is assumed that the space on every following page can be used in full.)
module Data.Text.ParagraphLayout.Internal.LinePagination
(Line
,lineHeight
,PageContinuity(Break, Continue)
,bestSplit
,lineHeight
,paginateLines
)
where
@@ 38,27 39,30 @@ class Line a where
instance Line Int32 where
lineHeight = id
-- | Represents the best place, as determined by the `paginateLines` function,
-- to place the first line of paginated text.
-- | Represents the best place to place a chunk of paginated content.
data PageContinuity
= Continue
-- ^ The lines are split so that the prefix can continue on the same page
-- as its preceding context.
-- ^ The content is split so that a given chunk can continue
-- on the same page as its preceding context.
--
-- This may be because all constraints were met, or because adding a page
-- break would have no benefit.
-- This may be because all constraints were met, or because
-- adding a page break would have no benefit.
| Break
-- ^ The lines are split so that the prefix should begin on a new page.
-- ^ The content is split so that a given chunk should begin
-- on a new page.
--
-- This may be because the current page does not have enough space
-- to preserve orphan/widow constrains, or because it does not have space
-- for any lines at all.
-- This may be because the current page does not have enough
-- space to preserve orphan/widow constrains, or because it
-- does not have space for any content at all.
deriving (Eq, Show, Read, Enum, Bounded)
-- | Split a list of lines in order to fit the given pagination constraints.
-- | Split a list of lines in order to meet the given pagination constraints.
--
-- This is a high-level function that produces the best usable result,
-- even if some constraints have to be violated.
--
-- The first component of the output determines whether a page break should
-- be inserted before any of the given lines.
@@ 129,10 133,22 @@ split1 :: [a] -> ([a], [a])
split1 xs = (take 1 xs, drop 1 xs)
-- | Split a list of lines so that the prefix contains as many lines
-- as possible while the total of line heights in the prefix does
-- not exceed @h@, the first @o@ lines ("orphans") are kept together,
-- and the last @w@ lines ("widows") are kept together.
bestSplit :: Line a => Word -> Word -> Int32 -> [a] -> ([a], [a])
-- as possible while satisfying the given constraints.
--
-- This is a low-level function that makes no compromises.
bestSplit :: Line a
=> Word
-- ^ Number of lines at the beginning ("orphans") to keep together.
-> Word
-- ^ Number of lines at the end ("widows") to keep together.
-> Int32
-- ^ Maximum total height of lines in the prefix.
-> [a]
-- ^ Lines to split.
-> ([a], [a])
-- ^ Two lists of lines that yield the original list when concatenated,
-- where the prefix, if non-empty, matches the given orphan, widow, and
-- maximum height constraints.
bestSplit o w h ls = NonEmpty.last $ constrainedSplits o w h ls
-- | Split a list of lines in every possible way, from shortest prefix
@@ 35,18 35,29 @@ data PageOptions = PageOptions
}
-- | Typeclass for layouts that can be broken into pages.
---
--- The first component of the output determines whether a page break should
--- be inserted before the paragraph.
---
--- The second component of the output contains the portion of the paragraph
--- that fits on the page and satisfies the given constraints as much as
--- possible.
---
--- The third component of the output will be `Just` the remainder of the
--- paragraph that can be passed to this function again, or `Nothing` if there
--- is nothing left to put on further 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