From 22babd5382c21d32e9d73984c5d5ff3d83ba715b Mon Sep 17 00:00:00 2001 From: Christian Despres <50160106+despresc@users.noreply.github.com> Date: Sat, 12 Sep 2020 11:50:36 -0400 Subject: [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. --- src/Text/Pandoc/Writers/AnnotatedTable.hs | 300 ++++++++++++++++++++++++++++++ src/Text/Pandoc/Writers/HTML.hs | 40 ++-- src/Text/Pandoc/Writers/Tables.hs | 291 ----------------------------- 3 files changed, 320 insertions(+), 311 deletions(-) create mode 100644 src/Text/Pandoc/Writers/AnnotatedTable.hs delete mode 100644 src/Text/Pandoc/Writers/Tables.hs (limited to 'src') 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 + 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 @\@ 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 @\@ 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 @\@ +-- 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 @\@ 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 @\@ element, and also recording the row number of the +-- row. All the cells in a 'HeaderRow' are header (@\@) 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 @\@ 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 @\@ 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 @\@ +-- 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 - 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 @\@ 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 @\@ 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 @\@ 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 @\@ element. -data AnnTableFoot = AnnTableFoot Attr [AnnHeaderRow] - deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) - --- | An annotated header row, corresponding to 'Row' and the HTML --- @\@ element, and also recording the row number of the row. All --- the cells in an 'AnnHeaderRow' are header (@\@) 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 --- @\@ 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 @\@ 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 @\@ --- 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 -- cgit v1.2.3