aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Writers
diff options
context:
space:
mode:
authorChristian Despres <50160106+despresc@users.noreply.github.com>2020-09-12 11:50:36 -0400
committerGitHub <noreply@github.com>2020-09-12 08:50:36 -0700
commit22babd5382c21d32e9d73984c5d5ff3d83ba715b (patch)
tree6178f81b414e90158e68b864b227b1eb07a05bb7 /src/Text/Pandoc/Writers
parent6fda8cfa28fa07f94854ca47d27e0415a4499033 (diff)
downloadpandoc-22babd5382c21d32e9d73984c5d5ff3d83ba715b.tar.gz
[API change] Rename Writers.Tables and its contents (#6679)
Writers.Tables is now Writers.AnnotatedTable. All of the types and functions in it have had the "Ann" removed from them. Now it is expected that the module be imported qualified.
Diffstat (limited to 'src/Text/Pandoc/Writers')
-rw-r--r--src/Text/Pandoc/Writers/AnnotatedTable.hs300
-rw-r--r--src/Text/Pandoc/Writers/HTML.hs40
-rw-r--r--src/Text/Pandoc/Writers/Tables.hs291
3 files changed, 320 insertions, 311 deletions
diff --git a/src/Text/Pandoc/Writers/AnnotatedTable.hs b/src/Text/Pandoc/Writers/AnnotatedTable.hs
new file mode 100644
index 000000000..48c9d61f2
--- /dev/null
+++ b/src/Text/Pandoc/Writers/AnnotatedTable.hs
@@ -0,0 +1,300 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE TupleSections #-}
+
+{- |
+ Module : Text.Pandoc.Writers.AnnotatedTable
+ Copyright : Copyright 2020 Christian Despres
+ License : GNU GPL, version 2 or above
+
+ Maintainer : Christian Despres <christian.j.j.despres@gmail.com>
+ Stability : alpha
+ Portability : portable
+
+Definitions and conversion functions for an intermediate 'Table' and
+related types, which annotates the existing Pandoc 'B.Table' types
+with additional inferred information. For use in writers that need to
+know the details of columns that cells span, row numbers, and the
+cells that are in the row head.
+-}
+
+module Text.Pandoc.Writers.AnnotatedTable
+ ( toTable
+ , fromTable
+ , Table(..)
+ , TableHead(..)
+ , TableBody(..)
+ , TableFoot(..)
+ , HeaderRow(..)
+ , BodyRow(..)
+ , RowNumber(..)
+ , RowHead
+ , RowBody
+ , Cell(..)
+ , ColNumber(..)
+ )
+where
+
+import Control.Monad.RWS.Strict
+ hiding ( (<>) )
+import Data.Generics ( Data
+ , Typeable
+ )
+import Data.List.NonEmpty ( NonEmpty(..) )
+import GHC.Generics ( Generic )
+import qualified Text.Pandoc.Builder as B
+
+-- | An annotated table type, corresponding to the Pandoc 'B.Table'
+-- constructor and the HTML @\<table\>@ element. It records the data
+-- of the columns that cells span, the cells in the row head, the row
+-- numbers of rows, and the column numbers of cells, in addition to
+-- the data in a 'B.Table'. The type itself does not enforce any
+-- guarantees about the consistency of this data. Use 'toTable' to
+-- produce a 'Table' from a Pandoc 'B.Table'.
+data Table = Table B.Attr B.Caption [B.ColSpec] TableHead [TableBody] TableFoot
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | An annotated table head, corresponding to a Pandoc 'B.TableHead'
+-- and the HTML @\<thead\>@ element.
+data TableHead = TableHead B.Attr [HeaderRow]
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | An annotated table body, with an intermediate head and body,
+-- corresponding to a Pandoc 'B.TableBody' and the HTML @\<tbody\>@
+-- element.
+data TableBody = TableBody B.Attr B.RowHeadColumns [HeaderRow] [BodyRow]
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | An annotated table foot, corresponding to a Pandoc 'B.TableFoot'
+-- and the HTML @\<tfoot\>@ element.
+data TableFoot = TableFoot B.Attr [HeaderRow]
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | An annotated header row, corresponding to a Pandoc 'B.Row' and
+-- the HTML @\<tr\>@ element, and also recording the row number of the
+-- row. All the cells in a 'HeaderRow' are header (@\<th\>@) cells.
+data HeaderRow = HeaderRow B.Attr RowNumber [Cell]
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | An annotated body row, corresponding to a Pandoc 'B.Row' and the
+-- HTML @\<tr\>@ element, and also recording its row number and
+-- separating the row head cells from the row body cells.
+data BodyRow = BodyRow B.Attr RowNumber RowHead RowBody
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | The row number of a row. Note that rows are numbered continuously
+-- from zero from the start of the table, so the first row in a table
+-- body, for instance, may have a large 'RowNumber'.
+newtype RowNumber = RowNumber Int
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum)
+
+-- | The head of a body row; the portion of the row lying in the stub
+-- of the 'TableBody'. Its cells correspond to HTML @\<th\>@ cells.
+type RowHead = [Cell]
+
+-- | The body of a body row; the portion of the row lying after the
+-- stub of the 'TableBody'. Its cells correspond to HTML @\<td\>@
+-- cells.
+type RowBody = [Cell]
+
+-- | An annotated table cell, wrapping a Pandoc 'B.Cell' with its
+-- 'ColNumber' and the 'B.ColSpec' data for the columns that the cell
+-- spans.
+data Cell = Cell (NonEmpty B.ColSpec) ColNumber B.Cell
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
+
+-- | The column number of a cell, meaning the column number of the
+-- first column that the cell spans, if the table were laid on a
+-- grid. Columns are numbered starting from zero.
+newtype ColNumber = ColNumber Int
+ deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum)
+
+-- | Convert a Pandoc 'B.Table' to an annotated 'Table'. This function
+-- also performs the same normalization that the 'B.table' builder
+-- does (fixing overlapping cells, cells that protrude out of their
+-- table section, and so on). If the input table happens to satisfy
+-- the conditions that 'B.table' guarantees, then the resulting
+-- 'Table' will be identical, save for the addition of the inferred
+-- table information.
+toTable
+ :: B.Attr
+ -> B.Caption
+ -> [B.ColSpec]
+ -> B.TableHead
+ -> [B.TableBody]
+ -> B.TableFoot
+ -> Table
+toTable attr cap cs th tbs tf = Table attr cap cs th' tbs' tf'
+ where
+ (th', tbs', tf') = fst $ evalRWS (annotateTable th tbs tf) (cs, length cs) 0
+
+-- | Internal monad for annotating a table, passing in the 'B.ColSpec'
+-- data for the table, the grid width, and the current 'RowNumber' to
+-- be referenced or updated.
+type AnnM a = RWS ([B.ColSpec], Int) () RowNumber a
+
+incRowNumber :: AnnM RowNumber
+incRowNumber = do
+ rn <- get
+ put $ rn + 1
+ return rn
+
+annotateTable
+ :: B.TableHead
+ -> [B.TableBody]
+ -> B.TableFoot
+ -> AnnM (TableHead, [TableBody], TableFoot)
+annotateTable th tbs tf = do
+ th' <- annotateTableHead th
+ tbs' <- traverse annotateTableBody tbs
+ tf' <- annotateTableFoot tf
+ return (th', tbs', tf')
+
+annotateTableHead :: B.TableHead -> AnnM TableHead
+annotateTableHead (B.TableHead attr rows) =
+ TableHead attr <$> annotateHeaderSection rows
+
+annotateTableBody :: B.TableBody -> AnnM TableBody
+annotateTableBody (B.TableBody attr rhc th tb) = do
+ twidth <- asks snd
+ let rhc' = max 0 $ min (B.RowHeadColumns twidth) rhc
+ th' <- annotateHeaderSection th
+ tb' <- annotateBodySection rhc' tb
+ return $ TableBody attr rhc' th' tb'
+
+annotateTableFoot :: B.TableFoot -> AnnM TableFoot
+annotateTableFoot (B.TableFoot attr rows) =
+ TableFoot attr <$> annotateHeaderSection rows
+
+annotateHeaderSection :: [B.Row] -> AnnM [HeaderRow]
+annotateHeaderSection rows = do
+ colspec <- asks fst
+ let hangcolspec = (1, ) <$> colspec
+ annotateHeaderSection' hangcolspec id $ B.clipRows rows
+ where
+ annotateHeaderSection' oldHang acc (B.Row attr cells : rs) = do
+ let (_, newHang, cells', _) =
+ annotateRowSection 0 oldHang $ cells <> repeat B.emptyCell
+ n <- incRowNumber
+ let annRow = HeaderRow attr n cells'
+ annotateHeaderSection' newHang (acc . (annRow :)) rs
+ annotateHeaderSection' _ acc [] = return $ acc []
+
+annotateBodySection :: B.RowHeadColumns -> [B.Row] -> AnnM [BodyRow]
+annotateBodySection (B.RowHeadColumns rhc) rows = do
+ colspec <- asks fst
+ let colspec' = (1, ) <$> colspec
+ let (stubspec, bodyspec) = splitAt rhc colspec'
+ normalizeBodySection' stubspec bodyspec id $ B.clipRows rows
+ where
+ normalizeBodySection' headHang bodyHang acc (B.Row attr cells : rs) = do
+ let (colnum, headHang', rowStub, cells') =
+ annotateRowSection 0 headHang $ cells <> repeat B.emptyCell
+ let (_, bodyHang', rowBody, _) = annotateRowSection colnum bodyHang cells'
+ n <- incRowNumber
+ let annRow = BodyRow attr n rowStub rowBody
+ normalizeBodySection' headHang' bodyHang' (acc . (annRow :)) rs
+ normalizeBodySection' _ _ acc [] = return $ acc []
+
+-- | Lay out a section of a 'Table' row on a grid row, annotating the
+-- cells with the 'B.ColSpec' data for the columns that they
+-- span. Performs the same normalization as 'B.placeRowSection'.
+annotateRowSection
+ :: ColNumber -- ^ The current column number
+ -> [(B.RowSpan, B.ColSpec)] -- ^ The overhang of the previous grid row,
+ -- with column data
+ -> [B.Cell] -- ^ The cells to annotate
+ -> (ColNumber, [(B.RowSpan, B.ColSpec)], [Cell], [B.Cell]) -- ^ The new
+ -- column
+ -- number,
+ -- overhang,
+ -- annotated
+ -- cells,
+ -- and
+ -- remaining
+ -- cells
+annotateRowSection !colnum oldHang cells
+ -- If the grid has overhang at our position, try to re-lay in
+ -- the next position.
+ | (o, colspec) : os <- oldHang
+ , o > 1
+ = let (colnum', newHang, newCell, cells') =
+ annotateRowSection (colnum + 1) os cells
+ in (colnum', (o - 1, colspec) : newHang, newCell, cells')
+ -- Otherwise if there is any available width, place the cell and
+ -- continue.
+ | c : cells' <- cells
+ , (h, w) <- getDim c
+ , w' <- max 1 w
+ , (w'', cellHang@(chStart : chRest), oldHang') <- splitCellHang h w' oldHang
+ = let c' = setW w'' c
+ annCell = Cell (snd <$> chStart :| chRest) colnum c'
+ colnum' = colnum + ColNumber (getColSpan w'')
+ (colnum'', newHang, newCells, remainCells) =
+ annotateRowSection colnum' oldHang' cells'
+ in (colnum'', cellHang <> newHang, annCell : newCells, remainCells)
+ -- Otherwise there is no room in the section
+ | otherwise
+ = (colnum, [], [], cells)
+ where
+ getColSpan (B.ColSpan x) = x
+ getDim (B.Cell _ _ h w _) = (h, w)
+ setW w (B.Cell a b h _ c) = B.Cell a b h w c
+
+-- | In @'splitCellHang' rs cs coldata@, with @rs@ the height of a
+-- cell that lies at the beginning of @coldata@, and @cs@ its width
+-- (which is not assumed to fit in the available space), return the
+-- actual width of the cell (what will fit in the available space),
+-- the data for the columns that the cell spans (including updating
+-- the overhang to equal @rs@), and the remaining column data.
+splitCellHang
+ :: B.RowSpan
+ -> B.ColSpan
+ -> [(B.RowSpan, B.ColSpec)]
+ -> (B.ColSpan, [(B.RowSpan, B.ColSpec)], [(B.RowSpan, B.ColSpec)])
+splitCellHang h n = go 0
+ where
+ go acc ((1, spec) : ls) | acc < n =
+ let (acc', hang, ls') = go (acc + 1) ls in (acc', (h, spec) : hang, ls')
+ go acc l = (acc, [], l)
+
+-- | Convert an annotated 'Table' to a Pandoc
+-- 'B.Table'. This is the inverse of 'toTable' on
+-- well-formed tables (i.e. tables satisfying the guarantees of
+-- 'B.table').
+fromTable
+ :: Table
+ -> ( B.Attr
+ , B.Caption
+ , [B.ColSpec]
+ , B.TableHead
+ , [B.TableBody]
+ , B.TableFoot
+ )
+fromTable (Table attr cap cs th tbs tf) = (attr, cap, cs, th', tbs', tf')
+ where
+ th' = fromTableHead th
+ tbs' = map fromTableBody tbs
+ tf' = fromTableFoot tf
+
+fromTableHead :: TableHead -> B.TableHead
+fromTableHead (TableHead attr rows) = B.TableHead attr $ fromHeaderRow <$> rows
+
+fromTableBody :: TableBody -> B.TableBody
+fromTableBody (TableBody attr rhc th tb) =
+ B.TableBody attr rhc (fromHeaderRow <$> th) (fromBodyRow <$> tb)
+
+fromTableFoot :: TableFoot -> B.TableFoot
+fromTableFoot (TableFoot attr rows) = B.TableFoot attr $ fromHeaderRow <$> rows
+
+fromHeaderRow :: HeaderRow -> B.Row
+fromHeaderRow (HeaderRow attr _ cells) = B.Row attr $ fromCell <$> cells
+
+fromBodyRow :: BodyRow -> B.Row
+fromBodyRow (BodyRow attr _ rh rb) =
+ B.Row attr ((fromCell <$> rh) <> (fromCell <$> rb))
+
+fromCell :: Cell -> B.Cell
+fromCell (Cell _ _ c) = c
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs
index eaf13b7da..4fd671da8 100644
--- a/src/Text/Pandoc/Writers/HTML.hs
+++ b/src/Text/Pandoc/Writers/HTML.hs
@@ -56,7 +56,7 @@ import Text.Pandoc.Templates (renderTemplate)
import Text.Pandoc.Walk
import Text.Pandoc.Writers.Math
import Text.Pandoc.Writers.Shared
-import Text.Pandoc.Writers.Tables
+import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
import Text.Pandoc.XML (escapeStringForXML, fromEntities, toEntities,
html5Attributes, html4Attributes, rdfaAttributes)
import qualified Text.Blaze.XHtml5 as H5
@@ -904,13 +904,13 @@ blockToHtml opts (DefinitionList lst) = do
intersperse (nl opts) defs') lst
defList opts contents
blockToHtml opts (Table attr caption colspecs thead tbody tfoot) =
- tableToHtml opts (toAnnTable attr caption colspecs thead tbody tfoot)
+ tableToHtml opts (Ann.toTable attr caption colspecs thead tbody tfoot)
tableToHtml :: PandocMonad m
=> WriterOptions
- -> AnnTable
+ -> Ann.Table
-> StateT WriterState m Html
-tableToHtml opts (AnnTable attr caption colspecs thead tbodies _tfoot) = do
+tableToHtml opts (Ann.Table attr caption colspecs thead tbodies _tfoot) = do
captionDoc <- case caption of
Caption _ [] -> return mempty
Caption _ longCapt -> do
@@ -941,16 +941,16 @@ tableToHtml opts (AnnTable attr caption colspecs thead tbodies _tfoot) = do
tableBodyToHtml :: PandocMonad m
=> WriterOptions
- -> AnnTableBody
+ -> Ann.TableBody
-> StateT WriterState m Html
-tableBodyToHtml opts (AnnTableBody _attr _rowHeadCols _intm rows) =
+tableBodyToHtml opts (Ann.TableBody _attr _rowHeadCols _intm rows) =
H.tbody <$> bodyRowsToHtml opts rows
tableHeadToHtml :: PandocMonad m
=> WriterOptions
- -> AnnTableHead
+ -> Ann.TableHead
-> StateT WriterState m Html
-tableHeadToHtml opts (AnnTableHead attr rows) =
+tableHeadToHtml opts (Ann.TableHead attr rows) =
if null rows || all isEmptyRow rows
then return mempty
else do
@@ -960,8 +960,8 @@ tableHeadToHtml opts (AnnTableHead attr rows) =
headElement
nl opts
where
- isEmptyRow (AnnHeaderRow _attr _rownum cells) = all isEmptyCell cells
- isEmptyCell (AnnCell _colspecs _colnum cell) =
+ isEmptyRow (Ann.HeaderRow _attr _rownum cells) = all isEmptyCell cells
+ isEmptyCell (Ann.Cell _colspecs _colnum cell) =
cell == Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) []
@@ -970,26 +970,26 @@ data RowType = HeaderRow | FooterRow | BodyRow
data CellType = HeaderCell | BodyCell
-data TableRow = TableRow RowType Attr RowNumber AnnRowHead AnnRowBody
+data TableRow = TableRow RowType Attr Ann.RowNumber Ann.RowHead Ann.RowBody
headerRowsToHtml :: PandocMonad m
=> WriterOptions
- -> [AnnHeaderRow]
+ -> [Ann.HeaderRow]
-> StateT WriterState m Html
headerRowsToHtml opts =
rowListToHtml opts . map toTableRow
where
- toTableRow (AnnHeaderRow attr rownum rowbody) =
+ toTableRow (Ann.HeaderRow attr rownum rowbody) =
TableRow HeaderRow attr rownum [] rowbody
bodyRowsToHtml :: PandocMonad m
=> WriterOptions
- -> [AnnBodyRow]
+ -> [Ann.BodyRow]
-> StateT WriterState m Html
bodyRowsToHtml opts =
rowListToHtml opts . zipWith toTableRow [1..]
where
- toTableRow rownum (AnnBodyRow attr _rownum rowhead rowbody) =
+ toTableRow rownum (Ann.BodyRow attr _rownum rowhead rowbody) =
TableRow BodyRow attr rownum rowhead rowbody
@@ -1036,9 +1036,9 @@ tableRowToHtml :: PandocMonad m
-> StateT WriterState m Html
tableRowToHtml opts (TableRow rowtype _attr rownum rowhead rowbody) = do
let rowclass = A.class_ $ case rownum of
- RowNumber x | x `rem` 2 == 1 -> "odd"
- _ | rowtype /= HeaderRow -> "even"
- _ -> "header"
+ Ann.RowNumber x | x `rem` 2 == 1 -> "odd"
+ _ | rowtype /= HeaderRow -> "even"
+ _ -> "header"
let celltype = case rowtype of
HeaderRow -> HeaderCell
_ -> BodyCell
@@ -1068,9 +1068,9 @@ rowspanAttrib = \case
cellToHtml :: PandocMonad m
=> WriterOptions
-> CellType
- -> AnnCell
+ -> Ann.Cell
-> StateT WriterState m Html
-cellToHtml opts celltype (AnnCell (colspec :| _) _colNum cell) =
+cellToHtml opts celltype (Ann.Cell (colspec :| _) _colNum cell) =
let align = fst colspec
in tableCellToHtml opts celltype align cell
diff --git a/src/Text/Pandoc/Writers/Tables.hs b/src/Text/Pandoc/Writers/Tables.hs
deleted file mode 100644
index 757ac41c6..000000000
--- a/src/Text/Pandoc/Writers/Tables.hs
+++ /dev/null
@@ -1,291 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE TupleSections #-}
-
-{- |
- Module : Text.Pandoc.Writers.Tables
- Copyright : Copyright 2020 Christian Despres
- License : GNU GPL, version 2 or above
-
- Maintainer : Christian Despres <christian.j.j.despres@gmail.com>
- Stability : alpha
- Portability : portable
-
-Definitions and helper functions for an intermediate 'AnnTable' type,
-which annotates the existing 'Table' types with additional inferred
-information. For use in writers that need to know the details of
-columns that cells span, row numbers, and the cells that are in the
-row head.
--}
-
-module Text.Pandoc.Writers.Tables
- ( toAnnTable
- , fromAnnTable
- , AnnTable(..)
- , AnnTableHead(..)
- , AnnTableBody(..)
- , AnnTableFoot(..)
- , AnnHeaderRow(..)
- , AnnBodyRow(..)
- , RowNumber(..)
- , AnnRowHead
- , AnnRowBody
- , AnnCell(..)
- , ColNumber(..)
- )
-where
-
-import Control.Monad.RWS.Strict
- hiding ( (<>) )
-import Data.Generics ( Data
- , Typeable
- )
-import Data.List.NonEmpty ( NonEmpty(..) )
-import GHC.Generics ( Generic )
-import Text.Pandoc.Builder
-
--- | An annotated table type, corresponding to the 'Table' constructor
--- and the HTML @\<table\>@ element. It records the data of the
--- columns that cells span, the cells in the row head, the row numbers
--- of rows, and the column numbers of cells, in addition to the data
--- in a 'Table'. The type itself does not enforce any guarantees about
--- the consistency of this data. Use 'toAnnTable' to produce an
--- 'AnnTable' from a pandoc 'Table'.
-data AnnTable = AnnTable Attr Caption [ColSpec] AnnTableHead [AnnTableBody] AnnTableFoot
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | An annotated table head, corresponding to 'TableHead' and the
--- HTML @\<thead\>@ element.
-data AnnTableHead = AnnTableHead Attr [AnnHeaderRow]
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | An annotated table body, with an intermediate head and body,
--- corresponding to 'TableBody' and the HTML @\<tbody\>@ element.
-data AnnTableBody = AnnTableBody Attr RowHeadColumns [AnnHeaderRow] [AnnBodyRow]
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | An annotated table foot, corresponding to 'TableFoot' and the
--- HTML @\<tfoot\>@ element.
-data AnnTableFoot = AnnTableFoot Attr [AnnHeaderRow]
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | An annotated header row, corresponding to 'Row' and the HTML
--- @\<tr\>@ element, and also recording the row number of the row. All
--- the cells in an 'AnnHeaderRow' are header (@\<th\>@) cells.
-data AnnHeaderRow = AnnHeaderRow Attr RowNumber [AnnCell]
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | An annotated body row, corresponding to 'Row' and the HTML
--- @\<tr\>@ element, and also recording its row number and separating
--- the row head cells from the row body cells.
-data AnnBodyRow = AnnBodyRow Attr RowNumber AnnRowHead AnnRowBody
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | The row number of a row. Note that rows are numbered continuously
--- from zero from the start of the table, so the first row in a table
--- body, for instance, may have a large 'RowNumber'.
-newtype RowNumber = RowNumber Int
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum)
-
--- | The head of a body row; the portion of the row lying in the stub
--- of the 'TableBody'. Its cells correspond to HTML @\<th\>@ cells.
-type AnnRowHead = [AnnCell]
-
--- | The body of a body row; the portion of the row lying after the
--- stub of the 'TableBody'. Its cells correspond to HTML @\<td\>@
--- cells.
-type AnnRowBody = [AnnCell]
-
--- | An annotated table cell, wrapping a 'Cell' with its 'ColNumber'
--- and the 'ColSpec' data for the columns that the cell spans.
-data AnnCell = AnnCell (NonEmpty ColSpec) ColNumber Cell
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
-
--- | The column number of a cell, meaning the column number of the
--- first column that the cell spans, if the table were laid on a
--- grid. Columns are numbered starting from zero.
-newtype ColNumber = ColNumber Int
- deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum)
-
--- | Convert a 'Table' to an 'AnnTable'. This function also performs
--- the same normalization that the 'table' builder does (fixing
--- overlapping cells, cells that protrude out of their table section,
--- and so on). If the input table happens to satisfy the conditions
--- that 'table' guarantees, then the resulting 'AnnTable' will be
--- identical, save for the addition of the inferred table information.
-toAnnTable
- :: Attr
- -> Caption
- -> [ColSpec]
- -> TableHead
- -> [TableBody]
- -> TableFoot
- -> AnnTable
-toAnnTable attr cap cs th tbs tf = AnnTable attr cap cs th' tbs' tf'
- where
- (th', tbs', tf') = fst $ evalRWS (annotateTable th tbs tf) (cs, length cs) 0
-
--- | Internal monad for annotating a table, passing in the 'ColSpec'
--- data for the table, the grid width, and the current 'RowNumber' to
--- be referenced or updated.
-type AnnM a = RWS ([ColSpec], Int) () RowNumber a
-
-incRowNumber :: AnnM RowNumber
-incRowNumber = do
- rn <- get
- put $ rn + 1
- return rn
-
-annotateTable
- :: TableHead
- -> [TableBody]
- -> TableFoot
- -> AnnM (AnnTableHead, [AnnTableBody], AnnTableFoot)
-annotateTable th tbs tf = do
- th' <- annotateTableHead th
- tbs' <- traverse annotateTableBody tbs
- tf' <- annotateTableFoot tf
- return (th', tbs', tf')
-
-annotateTableHead :: TableHead -> AnnM AnnTableHead
-annotateTableHead (TableHead attr rows) =
- AnnTableHead attr <$> annotateHeaderSection rows
-
-annotateTableBody :: TableBody -> AnnM AnnTableBody
-annotateTableBody (TableBody attr rhc th tb) = do
- twidth <- asks snd
- let rhc' = max 0 $ min (RowHeadColumns twidth) rhc
- th' <- annotateHeaderSection th
- tb' <- annotateBodySection rhc' tb
- return $ AnnTableBody attr rhc' th' tb'
-
-annotateTableFoot :: TableFoot -> AnnM AnnTableFoot
-annotateTableFoot (TableFoot attr rows) =
- AnnTableFoot attr <$> annotateHeaderSection rows
-
-annotateHeaderSection :: [Row] -> AnnM [AnnHeaderRow]
-annotateHeaderSection rows = do
- colspec <- asks fst
- let hangcolspec = (1, ) <$> colspec
- annotateHeaderSection' hangcolspec id $ clipRows rows
- where
- annotateHeaderSection' oldHang acc (Row attr cells : rs) = do
- let (_, newHang, cells', _) =
- annotateRowSection 0 oldHang $ cells <> repeat emptyCell
- n <- incRowNumber
- let annRow = AnnHeaderRow attr n cells'
- annotateHeaderSection' newHang (acc . (annRow :)) rs
- annotateHeaderSection' _ acc [] = return $ acc []
-
-annotateBodySection :: RowHeadColumns -> [Row] -> AnnM [AnnBodyRow]
-annotateBodySection (RowHeadColumns rhc) rows = do
- colspec <- asks fst
- let colspec' = (1, ) <$> colspec
- let (stubspec, bodyspec) = splitAt rhc colspec'
- normalizeBodySection' stubspec bodyspec id $ clipRows rows
- where
- normalizeBodySection' headHang bodyHang acc (Row attr cells : rs) = do
- let (colnum, headHang', rowStub, cells') =
- annotateRowSection 0 headHang $ cells <> repeat emptyCell
- let (_, bodyHang', rowBody, _) = annotateRowSection colnum bodyHang cells'
- n <- incRowNumber
- let annRow = AnnBodyRow attr n rowStub rowBody
- normalizeBodySection' headHang' bodyHang' (acc . (annRow :)) rs
- normalizeBodySection' _ _ acc [] = return $ acc []
-
--- | Lay out a section of a 'Table' row on a grid row, annotating the
--- cells with the 'ColSpec' data for the columns that they
--- span. Performs the same normalization as 'placeRowSection'.
-annotateRowSection
- :: ColNumber -- ^ The current column number
- -> [(RowSpan, ColSpec)] -- ^ The overhang of the previous grid row,
- -- with column data
- -> [Cell] -- ^ The cells to annotate
- -> (ColNumber, [(RowSpan, ColSpec)], [AnnCell], [Cell]) -- ^ The new
- -- column
- -- number,
- -- overhang,
- -- annotated
- -- cells,
- -- and
- -- remaining
- -- cells
-annotateRowSection !colnum oldHang cells
- -- If the grid has overhang at our position, try to re-lay in
- -- the next position.
- | (o, colspec) : os <- oldHang
- , o > 1
- = let (colnum', newHang, newCell, cells') =
- annotateRowSection (colnum + 1) os cells
- in (colnum', (o - 1, colspec) : newHang, newCell, cells')
- -- Otherwise if there is any available width, place the cell and
- -- continue.
- | c : cells' <- cells
- , (h, w) <- getDim c
- , w' <- max 1 w
- , (w'', cellHang@(chStart : chRest), oldHang') <- splitCellHang h w' oldHang
- = let c' = setW w'' c
- annCell = AnnCell (snd <$> chStart :| chRest) colnum c'
- colnum' = colnum + ColNumber (getColSpan w'')
- (colnum'', newHang, newCells, remainCells) =
- annotateRowSection colnum' oldHang' cells'
- in (colnum'', cellHang <> newHang, annCell : newCells, remainCells)
- -- Otherwise there is no room in the section
- | otherwise
- = (colnum, [], [], cells)
- where
- getColSpan (ColSpan x) = x
- getDim (Cell _ _ h w _) = (h, w)
- setW w (Cell a b h _ c) = Cell a b h w c
-
--- | In @'splitCellHang' rs cs coldata@, with @rs@ the height of a
--- cell that lies at the beginning of @coldata@, and @cs@ its width
--- (which is not assumed to fit in the available space), return the
--- actual width of the cell (what will fit in the available space),
--- the data for the columns that the cell spans (including updating
--- the overhang to equal @rs@), and the remaining column data.
-splitCellHang
- :: RowSpan
- -> ColSpan
- -> [(RowSpan, ColSpec)]
- -> (ColSpan, [(RowSpan, ColSpec)], [(RowSpan, ColSpec)])
-splitCellHang h n = go 0
- where
- go acc ((1, spec) : ls) | acc < n =
- let (acc', hang, ls') = go (acc + 1) ls in (acc', (h, spec) : hang, ls')
- go acc l = (acc, [], l)
-
--- | Convert an 'AnnTable' to a 'Table'. This is the inverse of
--- 'toAnnTable' on well-formed tables (i.e. tables satisfying the
--- guarantees of 'table').
-fromAnnTable
- :: AnnTable -> (Attr, Caption, [ColSpec], TableHead, [TableBody], TableFoot)
-fromAnnTable (AnnTable attr cap cs th tbs tf) = (attr, cap, cs, th', tbs', tf')
- where
- th' = fromAnnTableHead th
- tbs' = map fromAnnTableBody tbs
- tf' = fromAnnTableFoot tf
-
-fromAnnTableHead :: AnnTableHead -> TableHead
-fromAnnTableHead (AnnTableHead attr rows) =
- TableHead attr $ fromAnnHeaderRow <$> rows
-
-fromAnnTableBody :: AnnTableBody -> TableBody
-fromAnnTableBody (AnnTableBody attr rhc th tb) =
- TableBody attr rhc (fromAnnHeaderRow <$> th) (fromAnnBodyRow <$> tb)
-
-fromAnnTableFoot :: AnnTableFoot -> TableFoot
-fromAnnTableFoot (AnnTableFoot attr rows) =
- TableFoot attr $ fromAnnHeaderRow <$> rows
-
-fromAnnHeaderRow :: AnnHeaderRow -> Row
-fromAnnHeaderRow (AnnHeaderRow attr _ cells) = Row attr $ fromAnnCell <$> cells
-
-fromAnnBodyRow :: AnnBodyRow -> Row
-fromAnnBodyRow (AnnBodyRow attr _ rh rb) =
- Row attr ((fromAnnCell <$> rh) <> (fromAnnCell <$> rb))
-
-fromAnnCell :: AnnCell -> Cell
-fromAnnCell (AnnCell _ _ c) = c