{-# LANGUAGE OverloadedStrings #-}
module Text.Pandoc.Readers.Xlsx.Sheets
( xlsxToOutput
) where
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import Data.List (sort, dropWhileEnd)
import Data.Char (isSpace)
import Text.Pandoc.Definition
import Text.Pandoc.Options (ReaderOptions)
import Text.Pandoc.Readers.Xlsx.Parse
import Text.Pandoc.Readers.Xlsx.Cells
import qualified Text.Pandoc.Builder as B
xlsxToOutput :: ReaderOptions -> Xlsx -> (Meta, [Block])
xlsxToOutput :: ReaderOptions -> Xlsx -> (Meta, [Block])
xlsxToOutput ReaderOptions
_opts Xlsx
xlsx =
let sheets :: [XlsxSheet]
sheets = Xlsx -> [XlsxSheet]
xlsxSheets Xlsx
xlsx
sheetBlocks :: [Block]
sheetBlocks = (XlsxSheet -> [Block]) -> [XlsxSheet] -> [Block]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap XlsxSheet -> [Block]
sheetToBlocks [XlsxSheet]
sheets
in (Meta
forall a. Monoid a => a
mempty, [Block]
sheetBlocks)
sheetToBlocks :: XlsxSheet -> [Block]
sheetToBlocks :: XlsxSheet -> [Block]
sheetToBlocks XlsxSheet
sheet =
let SheetId Int
n = XlsxSheet -> SheetId
sheetId XlsxSheet
sheet
name :: Text
name = XlsxSheet -> Text
sheetName XlsxSheet
sheet
sheetIdent :: Text
sheetIdent = Text
"sheet-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show Int
n)
header :: Block
header = Int -> Attr -> [Inline] -> Block
Header Int
2 (Text
sheetIdent, [], []) (Many Inline -> [Inline]
forall a. Many a -> [a]
B.toList (Text -> Many Inline
B.text Text
name))
tableBlock :: [Block]
tableBlock = case XlsxSheet -> Maybe Block
cellsToTable XlsxSheet
sheet of
Just Block
tbl -> [Block
tbl]
Maybe Block
Nothing -> []
in Block
header Block -> [Block] -> [Block]
forall a. a -> [a] -> [a]
: [Block]
tableBlock
cellsToTable :: XlsxSheet -> Maybe Block
cellsToTable :: XlsxSheet -> Maybe Block
cellsToTable XlsxSheet
sheet
| Map CellRef XlsxCell -> Bool
forall k a. Map k a -> Bool
M.null (XlsxSheet -> Map CellRef XlsxCell
sheetCells XlsxSheet
sheet) = Maybe Block
forall a. Maybe a
Nothing
| Bool
otherwise =
let cells :: Map CellRef XlsxCell
cells = XlsxSheet -> Map CellRef XlsxCell
sheetCells XlsxSheet
sheet
refs :: [CellRef]
refs = [CellRef] -> [CellRef]
forall a. Ord a => [a] -> [a]
sort ([CellRef] -> [CellRef]) -> [CellRef] -> [CellRef]
forall a b. (a -> b) -> a -> b
$ Map CellRef XlsxCell -> [CellRef]
forall k a. Map k a -> [k]
M.keys Map CellRef XlsxCell
cells
minCol :: Int
minCol = [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (CellRef -> Int) -> [CellRef] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map CellRef -> Int
cellRefCol [CellRef]
refs
maxCol :: Int
maxCol = [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (CellRef -> Int) -> [CellRef] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map CellRef -> Int
cellRefCol [CellRef]
refs
minRow :: Int
minRow = [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (CellRef -> Int) -> [CellRef] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map CellRef -> Int
cellRefRow [CellRef]
refs
maxRow :: Int
maxRow = [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (CellRef -> Int) -> [CellRef] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map CellRef -> Int
cellRefRow [CellRef]
refs
grid :: [[Maybe XlsxCell]]
grid = [ [ CellRef -> Map CellRef XlsxCell -> Maybe XlsxCell
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup (Int -> Int -> CellRef
CellRef Int
col Int
row) Map CellRef XlsxCell
cells
| Int
col <- [Int
minCol..Int
maxCol]
]
| Int
row <- [Int
minRow..Int
maxRow]
]
([Maybe XlsxCell]
headerRow, [[Maybe XlsxCell]]
bodyRows) = case [[Maybe XlsxCell]]
grid of
([Maybe XlsxCell]
h:[[Maybe XlsxCell]]
bs) -> ([Maybe XlsxCell]
h, [[Maybe XlsxCell]]
bs)
[] -> ([], [])
filteredBodyRows :: [[Maybe XlsxCell]]
filteredBodyRows = ([Maybe XlsxCell] -> Bool)
-> [[Maybe XlsxCell]] -> [[Maybe XlsxCell]]
forall a. (a -> Bool) -> [a] -> [a]
dropWhileEnd [Maybe XlsxCell] -> Bool
isEmptyRow [[Maybe XlsxCell]]
bodyRows
makeCell :: Maybe XlsxCell -> Cell
makeCell Maybe XlsxCell
mcell = case Maybe XlsxCell
mcell of
Just XlsxCell
cell ->
let inlines :: [Inline]
inlines = XlsxCell -> [Inline]
cellToInlines XlsxCell
cell
in Attr -> Alignment -> RowSpan -> ColSpan -> [Block] -> Cell
Cell Attr
nullAttr Alignment
AlignDefault (Int -> RowSpan
RowSpan Int
1) (Int -> ColSpan
ColSpan Int
1) [[Inline] -> Block
Plain [Inline]
inlines]
Maybe XlsxCell
Nothing ->
Attr -> Alignment -> RowSpan -> ColSpan -> [Block] -> Cell
Cell Attr
nullAttr Alignment
AlignDefault (Int -> RowSpan
RowSpan Int
1) (Int -> ColSpan
ColSpan Int
1) [[Inline] -> Block
Plain []]
numCols :: Int
numCols = [Maybe XlsxCell] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Maybe XlsxCell]
headerRow
colSpec :: [(Alignment, ColWidth)]
colSpec = Int -> (Alignment, ColWidth) -> [(Alignment, ColWidth)]
forall a. Int -> a -> [a]
replicate Int
numCols (Alignment
AlignDefault, ColWidth
ColWidthDefault)
thead :: TableHead
thead = Attr -> [Row] -> TableHead
TableHead Attr
nullAttr [Attr -> [Cell] -> Row
Row Attr
nullAttr ([Cell] -> Row) -> [Cell] -> Row
forall a b. (a -> b) -> a -> b
$ (Maybe XlsxCell -> Cell) -> [Maybe XlsxCell] -> [Cell]
forall a b. (a -> b) -> [a] -> [b]
map Maybe XlsxCell -> Cell
makeCell [Maybe XlsxCell]
headerRow]
tbody :: [TableBody]
tbody = [Attr -> RowHeadColumns -> [Row] -> [Row] -> TableBody
TableBody Attr
nullAttr RowHeadColumns
0 [] ([Row] -> TableBody) -> [Row] -> TableBody
forall a b. (a -> b) -> a -> b
$ ([Maybe XlsxCell] -> Row) -> [[Maybe XlsxCell]] -> [Row]
forall a b. (a -> b) -> [a] -> [b]
map (Attr -> [Cell] -> Row
Row Attr
nullAttr ([Cell] -> Row)
-> ([Maybe XlsxCell] -> [Cell]) -> [Maybe XlsxCell] -> Row
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe XlsxCell -> Cell) -> [Maybe XlsxCell] -> [Cell]
forall a b. (a -> b) -> [a] -> [b]
map Maybe XlsxCell -> Cell
makeCell) [[Maybe XlsxCell]]
filteredBodyRows]
tfoot :: TableFoot
tfoot = Attr -> [Row] -> TableFoot
TableFoot Attr
nullAttr []
in Block -> Maybe Block
forall a. a -> Maybe a
Just (Block -> Maybe Block) -> Block -> Maybe Block
forall a b. (a -> b) -> a -> b
$ Attr
-> Caption
-> [(Alignment, ColWidth)]
-> TableHead
-> [TableBody]
-> TableFoot
-> Block
Table Attr
nullAttr (Maybe [Inline] -> [Block] -> Caption
Caption Maybe [Inline]
forall a. Maybe a
Nothing []) [(Alignment, ColWidth)]
colSpec TableHead
thead [TableBody]
tbody TableFoot
tfoot
isEmptyRow :: [Maybe XlsxCell] -> Bool
isEmptyRow :: [Maybe XlsxCell] -> Bool
isEmptyRow = (Maybe XlsxCell -> Bool) -> [Maybe XlsxCell] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Maybe XlsxCell -> Bool
isEmptyCell
where
isEmptyCell :: Maybe XlsxCell -> Bool
isEmptyCell Maybe XlsxCell
Nothing = Bool
True
isEmptyCell (Just XlsxCell
cell) = case XlsxCell -> CellValue
cellValue XlsxCell
cell of
CellValue
EmptyValue -> Bool
True
TextValue Text
t -> (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isSpace Text
t
NumberValue Double
_ -> Bool
False
cellToInlines :: XlsxCell -> [Inline]
cellToInlines :: XlsxCell -> [Inline]
cellToInlines XlsxCell
cell =
let base :: [Inline]
base = case XlsxCell -> CellValue
cellValue XlsxCell
cell of
TextValue Text
t -> Many Inline -> [Inline]
forall a. Many a -> [a]
B.toList (Many Inline -> [Inline]) -> Many Inline -> [Inline]
forall a b. (a -> b) -> a -> b
$ Text -> Many Inline
B.text Text
t
NumberValue Double
n -> [Text -> Inline
Str (Text -> Inline) -> Text -> Inline
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Double -> String
forall a. Show a => a -> String
show Double
n]
CellValue
EmptyValue -> []
applyBold :: [Inline] -> [Inline]
applyBold [Inline]
inls = if XlsxCell -> Bool
cellBold XlsxCell
cell then [[Inline] -> Inline
Strong [Inline]
inls] else [Inline]
inls
applyItalic :: [Inline] -> [Inline]
applyItalic [Inline]
inls = if XlsxCell -> Bool
cellItalic XlsxCell
cell then [[Inline] -> Inline
Emph [Inline]
inls] else [Inline]
inls
in [Inline] -> [Inline]
applyItalic ([Inline] -> [Inline]) -> [Inline] -> [Inline]
forall a b. (a -> b) -> a -> b
$ [Inline] -> [Inline]
applyBold [Inline]
base