OpenDocument writer: New table cell support with row and column spans (#6682)

Unit tests only verify column spans at this point.

Co-authored-by: Nils Carlson <nils.carlson@ludd.ltu.se>
This commit is contained in:
Nils Carlson 2020-09-24 16:31:47 +00:00 committed by GitHub
parent 1f707da40f
commit 4f13c0e25e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 213 additions and 26 deletions

View file

@ -32,11 +32,12 @@ import qualified Text.Pandoc.Builder as B
import Text.Pandoc.Logging import Text.Pandoc.Logging
import Text.Pandoc.Options import Text.Pandoc.Options
import Text.DocLayout import Text.DocLayout
import Text.Pandoc.Shared (linesToPara, tshow) import Text.Pandoc.Shared (linesToPara, tshow, blocksToInlines)
import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Templates (renderTemplate)
import qualified Text.Pandoc.Translations as Term (Term(Figure, Table)) import qualified Text.Pandoc.Translations as Term (Term(Figure, Table))
import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Math
import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Shared
import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
import Text.Pandoc.XML import Text.Pandoc.XML
import Text.Printf (printf) import Text.Printf (printf)
@ -371,9 +372,7 @@ blockToOpenDocument o bs
| BulletList b <- bs = setFirstPara >> bulletListToOpenDocument o b | BulletList b <- bs = setFirstPara >> bulletListToOpenDocument o b
| OrderedList a b <- bs = setFirstPara >> orderedList a b | OrderedList a b <- bs = setFirstPara >> orderedList a b
| CodeBlock _ s <- bs = setFirstPara >> preformatted s | CodeBlock _ s <- bs = setFirstPara >> preformatted s
| Table _ bc s th tb tf | Table a bc s th tb tf <- bs = setFirstPara >> table (Ann.toTable a bc s th tb tf)
<- bs = let (c, a, w, h, r) = toLegacyTable bc s th tb tf
in setFirstPara >> table c a w h r
| HorizontalRule <- bs = setFirstPara >> return (selfClosingTag "text:p" | HorizontalRule <- bs = setFirstPara >> return (selfClosingTag "text:p"
[ ("text:style-name", "Horizontal_20_Line") ]) [ ("text:style-name", "Horizontal_20_Line") ])
| RawBlock f s <- bs = if f == Format "opendocument" | RawBlock f s <- bs = if f == Format "opendocument"
@ -396,29 +395,32 @@ blockToOpenDocument o bs
orderedList a b = do (ln,pn) <- newOrderedListStyle (isTightList b) a orderedList a b = do (ln,pn) <- newOrderedListStyle (isTightList b) a
inTags True "text:list" [ ("text:style-name", "L" <> tshow ln)] inTags True "text:list" [ ("text:style-name", "L" <> tshow ln)]
<$> orderedListToOpenDocument o pn b <$> orderedListToOpenDocument o pn b
table c a w h r = do table :: PandocMonad m => Ann.Table -> OD m (Doc Text)
table (Ann.Table _ (Caption _ c) colspecs thead tbodies _) = do
tn <- length <$> gets stTableStyles tn <- length <$> gets stTableStyles
pn <- length <$> gets stParaStyles pn <- length <$> gets stParaStyles
let genIds = map chr [65..] let genIds = map chr [65..]
name = "Table" <> tshow (tn + 1) name = "Table" <> tshow (tn + 1)
columnIds = zip genIds w (aligns, mwidths) = unzip colspecs
fromWidth (ColWidth w) | w > 0 = w
fromWidth _ = 0
widths = map fromWidth mwidths
columnIds = zip genIds widths
mkColumn n = selfClosingTag "table:table-column" [("table:style-name", name <> "." <> T.singleton (fst n))] mkColumn n = selfClosingTag "table:table-column" [("table:style-name", name <> "." <> T.singleton (fst n))]
columns = map mkColumn columnIds columns = map mkColumn columnIds
paraHStyles = paraTableStyles "Heading" pn a paraHStyles = paraTableStyles "Heading" pn aligns
paraStyles = paraTableStyles "Contents" (pn + length (newPara paraHStyles)) a paraStyles = paraTableStyles "Contents" (pn + length (newPara paraHStyles)) aligns
newPara = map snd . filter (not . isEmpty . snd) newPara = map snd . filter (not . isEmpty . snd)
addTableStyle $ tableStyle tn columnIds addTableStyle $ tableStyle tn columnIds
mapM_ addParaStyle . newPara $ paraHStyles ++ paraStyles mapM_ addParaStyle . newPara $ paraHStyles ++ paraStyles
captionDoc <- if null c captionDoc <- if null c
then return empty then return empty
else inlinesToOpenDocument o c >>= else inlinesToOpenDocument o (blocksToInlines c) >>=
if isEnabled Ext_native_numbering o if isEnabled Ext_native_numbering o
then numberedTableCaption then numberedTableCaption
else unNumberedCaption "TableCaption" else unNumberedCaption "TableCaption"
th <- if all null h th <- colHeadsToOpenDocument o (map fst paraHStyles) thead
then return empty tr <- mapM (tableBodyToOpenDocument o (map fst paraStyles)) tbodies
else colHeadsToOpenDocument o (map fst paraHStyles) h
tr <- mapM (tableRowToOpenDocument o (map fst paraStyles)) r
let tableDoc = inTags True "table:table" [ let tableDoc = inTags True "table:table" [
("table:name" , name) ("table:name" , name)
, ("table:style-name", name) , ("table:style-name", name)
@ -464,26 +466,54 @@ unNumberedCaption :: Monad m => Text -> Doc Text -> OD m (Doc Text)
unNumberedCaption style caption = return $ inParagraphTagsWithStyle style caption unNumberedCaption style caption = return $ inParagraphTagsWithStyle style caption
colHeadsToOpenDocument :: PandocMonad m colHeadsToOpenDocument :: PandocMonad m
=> WriterOptions -> [Text] -> [[Block]] => WriterOptions -> [Text] -> Ann.TableHead
-> OD m (Doc Text) -> OD m (Doc Text)
colHeadsToOpenDocument o ns hs = colHeadsToOpenDocument o ns (Ann.TableHead _ hs) =
inTagsIndented "table:table-header-rows" . inTagsIndented "table:table-row" . vcat <$> case hs of
mapM (tableItemToOpenDocument o "TableHeaderRowCell") (zip ns hs) [] -> return empty
(x:_) ->
let (Ann.HeaderRow _ _ c) = x
in inTagsIndented "table:table-header-rows" .
inTagsIndented "table:table-row" .
vcat <$> mapM (tableItemToOpenDocument o "TableHeaderRowCell") (zip ns c)
tableBodyToOpenDocument:: PandocMonad m
=> WriterOptions -> [Text] -> Ann.TableBody
-> OD m (Doc Text)
tableBodyToOpenDocument o ns tb =
let (Ann.TableBody _ _ _ r) = tb
in vcat <$> mapM (tableRowToOpenDocument o ns) r
tableRowToOpenDocument :: PandocMonad m tableRowToOpenDocument :: PandocMonad m
=> WriterOptions -> [Text] -> [[Block]] => WriterOptions -> [Text] -> Ann.BodyRow
-> OD m (Doc Text) -> OD m (Doc Text)
tableRowToOpenDocument o ns cs = tableRowToOpenDocument o ns r =
inTagsIndented "table:table-row" . vcat <$> let (Ann.BodyRow _ _ _ c ) = r
mapM (tableItemToOpenDocument o "TableRowCell") (zip ns cs) in inTagsIndented "table:table-row" . vcat <$>
mapM (tableItemToOpenDocument o "TableRowCell") (zip ns c)
colspanAttrib :: ColSpan -> [(Text, Text)]
colspanAttrib cs =
case cs of
ColSpan 1 -> mempty
ColSpan n -> [("table:number-columns-spanned", tshow n)]
rowspanAttrib :: RowSpan -> [(Text, Text)]
rowspanAttrib rs =
case rs of
RowSpan 1 -> mempty
RowSpan n -> [("table:number-rows-spanned", tshow n)]
tableItemToOpenDocument :: PandocMonad m tableItemToOpenDocument :: PandocMonad m
=> WriterOptions -> Text -> (Text,[Block]) => WriterOptions -> Text -> (Text,Ann.Cell)
-> OD m (Doc Text) -> OD m (Doc Text)
tableItemToOpenDocument o s (n,i) = tableItemToOpenDocument o s (n,c) =
let a = [ ("table:style-name" , s ) let (Ann.Cell _colspecs _colnum (Cell _ _ rs cs i) ) = c
, ("office:value-type", "string" ) csa = colspanAttrib cs
] rsa = rowspanAttrib rs
a = [ ("table:style-name" , s )
, ("office:value-type", "string" ) ] ++ csa ++ rsa
in inTags True "table:table-cell" a <$> in inTags True "table:table-cell" a <$>
withParagraphStyle o n (map plainToPara i) withParagraphStyle o n (map plainToPara i)

View file

@ -117,3 +117,160 @@
(TableFoot ("",[],[]) (TableFoot ("",[],[])
[])] [])]
``` ```
```
% pandoc -f native -t opendocument --quiet
[Table ("",[],[]) (Caption Nothing
[])
[(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)
,(AlignDefault,ColWidth 6.25e-2)]
(TableHead ("",[],[])
[])
[(TableBody ("",[],[]) (RowHeadColumns 0)
[]
[Row ("",[],[])
[Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8)
[Para [Strong [Str "Octet",Space,Str "no.",Space,Str "1"]]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8)
[Para [Strong [Str "Octet",Space,Str "no.",Space,Str "2"]]]]
,Row ("",[],[])
[Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "16"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "15"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "14"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "13"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "12"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "11"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "10"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "9"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "8"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "7"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "6"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "5"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "4"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "3"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "2"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
[Para [Str "1"]]]
,Row ("",[],[])
[Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8)
[Para [Str "Code",Space,Str "A"]]
,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8)
[Para [Str "Code",Space,Str "B"]]]])]
(TableFoot ("",[],[])
[])]
^D
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A" />
<table:table-column table:style-name="Table1.B" />
<table:table-column table:style-name="Table1.C" />
<table:table-column table:style-name="Table1.D" />
<table:table-column table:style-name="Table1.E" />
<table:table-column table:style-name="Table1.F" />
<table:table-column table:style-name="Table1.G" />
<table:table-column table:style-name="Table1.H" />
<table:table-column table:style-name="Table1.I" />
<table:table-column table:style-name="Table1.J" />
<table:table-column table:style-name="Table1.K" />
<table:table-column table:style-name="Table1.L" />
<table:table-column table:style-name="Table1.M" />
<table:table-column table:style-name="Table1.N" />
<table:table-column table:style-name="Table1.O" />
<table:table-column table:style-name="Table1.P" />
<table:table-row>
<table:table-cell table:style-name="TableRowCell" office:value-type="string" table:number-columns-spanned="8">
<text:p text:style-name="Table_20_Contents"><text:span text:style-name="T1">Octet
no. 1</text:span></text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string" table:number-columns-spanned="8">
<text:p text:style-name="Table_20_Contents"><text:span text:style-name="T1">Octet
no. 2</text:span></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">16</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">15</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">14</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">13</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">12</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">11</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">10</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">9</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">8</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">7</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">6</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">5</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">4</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">3</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">2</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string">
<text:p text:style-name="Table_20_Contents">1</text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="TableRowCell" office:value-type="string" table:number-columns-spanned="8">
<text:p text:style-name="Table_20_Contents">Code A</text:p>
</table:table-cell>
<table:table-cell table:style-name="TableRowCell" office:value-type="string" table:number-columns-spanned="8">
<text:p text:style-name="Table_20_Contents">Code B</text:p>
</table:table-cell>
</table:table-row>
</table:table>
```