git.iko.soy/typeable/pandoc
on master help sign in
Improve writing metadata for docx, pptx and odt (#5252)
9894d05Agustín Martín Barbero
MANUAL.txt+40−6
@@ -1355,7 +1355,7 @@ directory (see `--data-dir`, above). *Exceptions:*
1355 (or the `default.context` template, if you use `-t context`,
1355 (or the `default.context` template, if you use `-t context`,
1356 or the `default.ms` template, if you use `-t ms`, or the
1356 or the `default.ms` template, if you use `-t ms`, or the
1357 `default.html` template, if you use `-t html`).
1357 `default.html` template, if you use `-t html`).
1358−- `docx` has no template (however, you can use
1358+- `docx` and `pptx` have no template (however, you can use
1359 `--reference-doc` to customize the output).
1359 `--reference-doc` to customize the output).
1360
1360
1361Templates contain *variables*, which allow for the inclusion of
1361Templates contain *variables*, which allow for the inclusion of
@@ -1363,7 +1363,7 @@ arbitrary information at any point in the file. They may be set at the
1363command line using the `-V/--variable` option. If a variable is not set,
1363command line using the `-V/--variable` option. If a variable is not set,
1364pandoc will look for the key in the document's metadata – which can be set
1364pandoc will look for the key in the document's metadata – which can be set
1365using either [YAML metadata blocks][Extension: `yaml_metadata_block`]
1365using either [YAML metadata blocks][Extension: `yaml_metadata_block`]
1366−or with the `--metadata` option.
1366+or with the `-M/--metadata` option.
1367
1367
1368Metadata variables
1368Metadata variables
1369------------------
1369------------------
@@ -1381,14 +1381,48 @@ Metadata variables
1381 ...
1381 ...
1382
1382
1383`subtitle`
1383`subtitle`
1384−: document subtitle, included in HTML, EPUB, LaTeX, ConTeXt, and Word docx
1384+: document subtitle, included in HTML, EPUB, LaTeX, ConTeXt, and docx
1385+ documents
1385
1386
1386`abstract`
1387`abstract`
1387−: document summary, included in LaTeX, ConTeXt, AsciiDoc, and Word docx
1388+: document summary, included in LaTeX, ConTeXt, AsciiDoc, and docx
1389+ documents
1388
1390
1389`keywords`
1391`keywords`
1390−: list of keywords to be included in HTML, PDF, and AsciiDoc metadata;
1392+: list of keywords to be included in HTML, PDF, ODT, pptx, docx
1391− repeat as for `author`, above
1393+ and AsciiDoc metadata; repeat as for `author`, above
1394+
1395+`subject`
1396+: document subject, included in ODT, docx and pptx metadata
1397+
1398+`description`
1399+: document description, included in ODT, docx and pptx metadata. Some
1400+ applications show this as `Comments` metadata.
1401+
1402+`category`
1403+: document category, included in docx and pptx metadata
1404+
1405+Additionally,
1406+any root-level string metadata, not included in ODT, docx
1407+or pptx metadata is added as a *custom property*.
1408+The following YAML metadata block for instance:
1409+
1410+ ---
1411+ title: 'This is the title'
1412+ subtitle: "This is the subtitle"
1413+ author:
1414+ - Author One
1415+ - Author Two
1416+ description: |
1417+ This is a long
1418+ description.
1419+
1420+ It consists of two paragraphs
1421+ ...
1422+
1423+will include `title`, `author` and `description` as standard document
1424+properties and `subtitle` as a custom property when converting to docx,
1425+ODT or pptx.
1392
1426
1393Language variables
1427Language variables
1394------------------
1428------------------
src/Text/Pandoc/Writers/Docx.hs+17−2
@@ -496,7 +496,17 @@ writeDocx opts doc@(Pandoc meta _) = do
496 Just (MetaList xs) -> map stringify xs
496 Just (MetaList xs) -> map stringify xs
497 _ -> []
497 _ -> []
498
498
499+ -- docProps/core.xml
499 let docPropsPath = "docProps/core.xml"
500 let docPropsPath = "docProps/core.xml"
501+ let extraCoreProps = ["subject","lang","category","description"]
502+ let extraCorePropsMap = M.fromList $ zip extraCoreProps
503+ ["dc:subject","dc:language","cp:category","dc:description"]
504+ let lookupMetaString' :: String -> Meta -> String
505+ lookupMetaString' key' meta' =
506+ case key' of
507+ "description" -> intercalate "_x000d_\n" (map stringify $ lookupMetaBlocks "description" meta')
508+ _ -> lookupMetaString key' meta'
509+
500 let docProps = mknode "cp:coreProperties"
510 let docProps = mknode "cp:coreProperties"
501 [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
511 [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
502 ,("xmlns:dc","http://purl.org/dc/elements/1.1/")
512 ,("xmlns:dc","http://purl.org/dc/elements/1.1/")
@@ -505,14 +515,19 @@ writeDocx opts doc@(Pandoc meta _) = do
505 ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")]
515 ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")]
506 $ mknode "dc:title" [] (stringify $ docTitle meta)
516 $ mknode "dc:title" [] (stringify $ docTitle meta)
507 : mknode "dc:creator" [] (intercalate "; " (map stringify $ docAuthors meta))
517 : mknode "dc:creator" [] (intercalate "; " (map stringify $ docAuthors meta))
518+ : [ mknode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta)
519+ | k <- M.keys (unMeta meta), k `elem` extraCoreProps]
508− : mknode "cp:keywords" [] (intercalate ", " keywords)
520+ ++ mknode "cp:keywords" [] (intercalate ", " keywords)
509 : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
521 : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
510 , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
522 , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
511 ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
523 ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
512 let docPropsEntry = toEntry docPropsPath epochtime $ renderXml docProps
524 let docPropsEntry = toEntry docPropsPath epochtime $ renderXml docProps
513
525
526+ -- docProps/custom.xml
514 let customProperties :: [(String, String)]
527 let customProperties :: [(String, String)]
515− customProperties = [] -- FIXME
528+ customProperties = [(k, lookupMetaString k meta) | k <- M.keys (unMeta meta)
529+ , k `notElem` (["title", "author", "keywords"]
530+ ++ extraCoreProps)]
516 let mkCustomProp (k, v) pid = mknode "property"
531 let mkCustomProp (k, v) pid = mknode "property"
517 [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
532 [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
518 ,("pid", show pid)
533 ,("pid", show pid)
src/Text/Pandoc/Writers/ODT.hs+27−9
@@ -40,6 +40,7 @@ import Data.List (isPrefixOf, intercalate)
40import Data.Maybe (fromMaybe)
40import Data.Maybe (fromMaybe)
41import qualified Data.Map as Map
41import qualified Data.Map as Map
42import qualified Data.Text.Lazy as TL
42import qualified Data.Text.Lazy as TL
43+import Data.Time
43import System.FilePath (takeDirectory, takeExtension, (<.>))
44import System.FilePath (takeDirectory, takeExtension, (<.>))
44import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
45import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
45import Text.Pandoc.Class (PandocMonad, report, toLang)
46import Text.Pandoc.Class (PandocMonad, report, toLang)
@@ -50,8 +51,9 @@ import Text.Pandoc.Logging
50import Text.Pandoc.MIME (extensionFromMimeType, getMimeType)
51import Text.Pandoc.MIME (extensionFromMimeType, getMimeType)
51import Text.Pandoc.Options (WrapOption (..), WriterOptions (..))
52import Text.Pandoc.Options (WrapOption (..), WriterOptions (..))
52import Text.Pandoc.Pretty
53import Text.Pandoc.Pretty
53−import Text.Pandoc.Shared (stringify, normalizeDate)
54+import Text.Pandoc.Shared (stringify, pandocVersion)
54−import Text.Pandoc.Writers.Shared (lookupMetaString, fixDisplayMath)
55+import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks,
56+ fixDisplayMath)
55import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy)
57import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy)
56import Text.Pandoc.Walk
58import Text.Pandoc.Walk
57import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
59import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
@@ -83,6 +85,7 @@ pandocToODT :: PandocMonad m
83pandocToODT opts doc@(Pandoc meta _) = do
85pandocToODT opts doc@(Pandoc meta _) = do
84 let title = docTitle meta
86 let title = docTitle meta
85 let authors = docAuthors meta
87 let authors = docAuthors meta
88+ utctime <- P.getCurrentTime
86 lang <- toLang (getLang opts meta)
89 lang <- toLang (getLang opts meta)
87 refArchive <-
90 refArchive <-
88 case writerReferenceDoc opts of
91 case writerReferenceDoc opts of
@@ -125,9 +128,14 @@ pandocToODT opts doc@(Pandoc meta _) = do
125 )
128 )
126 )
129 )
127 let archive' = addEntryToArchive manifestEntry archive
130 let archive' = addEntryToArchive manifestEntry archive
131+ -- create meta.xml
128 let userDefinedMetaFields = [k | k <- Map.keys (unMeta meta)
132 let userDefinedMetaFields = [k | k <- Map.keys (unMeta meta)
129− , k `notElem` ["title", "lang", "author", "date"]]
133+ , k `notElem` ["title", "lang", "author"
134+ , "description", "subject", "keywords"]]
130 let escapedText = text . escapeStringForXML
135 let escapedText = text . escapeStringForXML
136+ let keywords = case lookupMeta "keywords" meta of
137+ Just (MetaList xs) -> map stringify xs
138+ _ -> []
131 let userDefinedMeta =
139 let userDefinedMeta =
132 map (\k -> inTags False "meta:user-defined"
140 map (\k -> inTags False "meta:user-defined"
133 [ ("meta:name", escapeStringForXML k)
141 [ ("meta:name", escapeStringForXML k)
@@ -146,19 +154,29 @@ pandocToODT opts doc@(Pandoc meta _) = do
146 ,("xmlns:ooo","http://openoffice.org/2004/office")
154 ,("xmlns:ooo","http://openoffice.org/2004/office")
147 ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#")
155 ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#")
148 ,("office:version","1.2")] ( inTags True "office:meta" [] $
156 ,("office:version","1.2")] ( inTags True "office:meta" [] $
157+ ( metaTag "meta:generator" ("Pandoc/" ++ pandocVersion)
158+ $$
149− ( metaTag "dc:title" (stringify title)
159+ metaTag "dc:title" (stringify title)
160+ $$
161+ metaTag "dc:description"
162+ (intercalate "\n" (map stringify $
163+ lookupMetaBlocks "description" meta))
164+ $$
165+ metaTag "dc:subject" (lookupMetaString "subject" meta)
166+ $$
167+ metaTag "meta:keyword" (intercalate ", " keywords)
150 $$
168 $$
151 case lang of
169 case lang of
152 Just l -> metaTag "dc:language" (renderLang l)
170 Just l -> metaTag "dc:language" (renderLang l)
153 Nothing -> empty
171 Nothing -> empty
154 $$
172 $$
173+ (\d a -> metaTag "meta:initial-creator" a
155− metaTag "dc:creator"
174+ $$ metaTag "dc:creator" a
175+ $$ metaTag "meta:creation-date" d
176+ $$ metaTag "dc:date" d
177+ ) (formatTime defaultTimeLocale "%FT%XZ" utctime)
156 (intercalate "; " (map stringify authors))
178 (intercalate "; " (map stringify authors))
157 $$
179 $$
158− maybe mempty
159− (metaTag "dc:date")
160− (normalizeDate (lookupMetaString "date" meta))
161− $$
162 vcat userDefinedMeta
180 vcat userDefinedMeta
163 )
181 )
164 )
182 )
src/Text/Pandoc/Writers/Powerpoint/Output.hs+36−5
@@ -58,7 +58,7 @@ import Text.Pandoc.MIME
58import qualified Data.ByteString.Lazy as BL
58import qualified Data.ByteString.Lazy as BL
59import Text.Pandoc.Writers.OOXML
59import Text.Pandoc.Writers.OOXML
60import qualified Data.Map as M
60import qualified Data.Map as M
61−import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes)
61+import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isNothing)
62import Text.Pandoc.ImageSize
62import Text.Pandoc.ImageSize
63import Control.Applicative ((<|>))
63import Control.Applicative ((<|>))
64import System.FilePath.Glob
64import System.FilePath.Glob
@@ -252,6 +252,8 @@ presentationToArchiveP p@(Presentation docProps slides) = do
252 newArch' <- foldM copyFileToArchive emptyArchive filePaths
252 newArch' <- foldM copyFileToArchive emptyArchive filePaths
253 -- we make a docProps/core.xml entry out of the presentation docprops
253 -- we make a docProps/core.xml entry out of the presentation docprops
254 docPropsEntry <- docPropsToEntry docProps
254 docPropsEntry <- docPropsToEntry docProps
255+ -- we make a docProps/custom.xml entry out of the custom properties
256+ docCustomPropsEntry <- docCustomPropsToEntry docProps
255 -- we make this ourself in case there's something unexpected in the
257 -- we make this ourself in case there's something unexpected in the
256 -- one in the reference doc.
258 -- one in the reference doc.
257 relsEntry <- topLevelRelsEntry
259 relsEntry <- topLevelRelsEntry
@@ -274,7 +276,8 @@ presentationToArchiveP p@(Presentation docProps slides) = do
274 spkNotesEntries ++
276 spkNotesEntries ++
275 spkNotesRelEntries ++
277 spkNotesRelEntries ++
276 mediaEntries ++
278 mediaEntries ++
277− [contentTypesEntry, docPropsEntry, relsEntry, presEntry, presRelsEntry]
279+ [contentTypesEntry, docPropsEntry, docCustomPropsEntry, relsEntry,
280+ presEntry, presRelsEntry]
278
281
279makeSlideIdMap :: Presentation -> M.Map SlideId Int
282makeSlideIdMap :: Presentation -> M.Map SlideId Int
280makeSlideIdMap (Presentation _ slides) =
283makeSlideIdMap (Presentation _ slides) =
@@ -1425,6 +1428,10 @@ topLevelRels =
1425 , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/extended-properties"
1428 , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/extended-properties"
1426 , relTarget = "docProps/app.xml"
1429 , relTarget = "docProps/app.xml"
1427 }
1430 }
1431+ , Relationship { relId = 4
1432+ , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"
1433+ , relTarget = "docProps/custom.xml"
1434+ }
1428 ]
1435 ]
1429
1436
1430topLevelRelsEntry :: PandocMonad m => P m Entry
1437topLevelRelsEntry :: PandocMonad m => P m Entry
@@ -1657,7 +1664,7 @@ docPropsElement :: PandocMonad m => DocProps -> P m Element
1657docPropsElement docProps = do
1664docPropsElement docProps = do
1658 utctime <- asks envUTCTime
1665 utctime <- asks envUTCTime
1659 let keywords = case dcKeywords docProps of
1666 let keywords = case dcKeywords docProps of
1660− Just xs -> intercalate "," xs
1667+ Just xs -> intercalate ", " xs
1661 Nothing -> ""
1668 Nothing -> ""
1662 return $
1669 return $
1663 mknode "cp:coreProperties"
1670 mknode "cp:coreProperties"
@@ -1669,7 +1676,13 @@ docPropsElement docProps = do
1669 $ (mknode "dc:title" [] $ fromMaybe "" $ dcTitle docProps)
1676 $ (mknode "dc:title" [] $ fromMaybe "" $ dcTitle docProps)
1670 : (mknode "dc:creator" [] $ fromMaybe "" $ dcCreator docProps)
1677 : (mknode "dc:creator" [] $ fromMaybe "" $ dcCreator docProps)
1671 : (mknode "cp:keywords" [] keywords)
1678 : (mknode "cp:keywords" [] keywords)
1679+ : (if isNothing (dcSubject docProps) then [] else
1680+ [mknode "dc:subject" [] $ fromMaybe "" $ dcSubject docProps])
1681+ ++ (if isNothing (dcDescription docProps) then [] else
1682+ [mknode "dc:description" [] $ fromMaybe "" $ dcDescription docProps])
1683+ ++ (if isNothing (cpCategory docProps) then [] else
1684+ [mknode "cp:category" [] $ fromMaybe "" $ cpCategory docProps])
1672− : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
1685+ ++ (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
1673 , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
1686 , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
1674 ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
1687 ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
1675
1688
@@ -1677,6 +1690,21 @@ docPropsToEntry :: PandocMonad m => DocProps -> P m Entry
1677docPropsToEntry docProps = docPropsElement docProps >>=
1690docPropsToEntry docProps = docPropsElement docProps >>=
1678 elemToEntry "docProps/core.xml"
1691 elemToEntry "docProps/core.xml"
1679
1692
1693+-- adapted from the Docx writer
1694+docCustomPropsElement :: PandocMonad m => DocProps -> P m Element
1695+docCustomPropsElement docProps = do
1696+ let mkCustomProp (k, v) pid = mknode "property"
1697+ [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
1698+ ,("pid", show pid)
1699+ ,("name", k)] $ mknode "vt:lpwstr" [] v
1700+ return $ mknode "Properties"
1701+ [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")
1702+ ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes")
1703+ ] $ zipWith mkCustomProp (fromMaybe [] $ customProperties docProps) [(2 :: Int)..]
1704+
1705+docCustomPropsToEntry :: PandocMonad m => DocProps -> P m Entry
1706+docCustomPropsToEntry docProps = docCustomPropsElement docProps >>=
1707+ elemToEntry "docProps/custom.xml"
1680
1708
1681defaultContentTypeToElem :: DefaultContentType -> Element
1709defaultContentTypeToElem :: DefaultContentType -> Element
1682defaultContentTypeToElem dct =
1710defaultContentTypeToElem dct =
@@ -1765,6 +1793,7 @@ presentationToContentTypes p@(Presentation _ slides) = do
1765
1793
1766 inheritedOverrides = mapMaybe pathToOverride filePaths
1794 inheritedOverrides = mapMaybe pathToOverride filePaths
1767 docPropsOverride = mapMaybe pathToOverride ["docProps/core.xml"]
1795 docPropsOverride = mapMaybe pathToOverride ["docProps/core.xml"]
1796+ docCustomPropsOverride = mapMaybe pathToOverride ["docProps/custom.xml"]
1768 presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"]
1797 presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"]
1769 relativePaths <- mapM slideToFilePath slides
1798 relativePaths <- mapM slideToFilePath slides
1770 let slideOverrides = mapMaybe
1799 let slideOverrides = mapMaybe
@@ -1773,7 +1802,8 @@ presentationToContentTypes p@(Presentation _ slides) = do
1773 speakerNotesOverrides <- (mapMaybe pathToOverride) <$> getSpeakerNotesFilePaths
1802 speakerNotesOverrides <- (mapMaybe pathToOverride) <$> getSpeakerNotesFilePaths
1774 return $ ContentTypes
1803 return $ ContentTypes
1775 (defaults ++ mediaDefaults)
1804 (defaults ++ mediaDefaults)
1805+ (inheritedOverrides ++ docPropsOverride ++ docCustomPropsOverride ++
1776− (inheritedOverrides ++ docPropsOverride ++ presOverride ++ slideOverrides ++ speakerNotesOverrides)
1806+ presOverride ++ slideOverrides ++ speakerNotesOverrides)
1777
1807
1778presML :: String
1808presML :: String
1779presML = "application/vnd.openxmlformats-officedocument.presentationml"
1809presML = "application/vnd.openxmlformats-officedocument.presentationml"
@@ -1788,6 +1818,7 @@ getContentType fp
1788 | fp == "ppt/viewProps.xml" = Just $ presML ++ ".viewProps+xml"
1818 | fp == "ppt/viewProps.xml" = Just $ presML ++ ".viewProps+xml"
1789 | fp == "ppt/tableStyles.xml" = Just $ presML ++ ".tableStyles+xml"
1819 | fp == "ppt/tableStyles.xml" = Just $ presML ++ ".tableStyles+xml"
1790 | fp == "docProps/core.xml" = Just $ "application/vnd.openxmlformats-package.core-properties+xml"
1820 | fp == "docProps/core.xml" = Just $ "application/vnd.openxmlformats-package.core-properties+xml"
1821+ | fp == "docProps/custom.xml" = Just $ "application/vnd.openxmlformats-officedocument.custom-properties+xml"
1791 | fp == "docProps/app.xml" = Just $ noPresML ++ ".extended-properties+xml"
1822 | fp == "docProps/app.xml" = Just $ noPresML ++ ".extended-properties+xml"
1792 | "ppt" : "slideMasters" : f : [] <- splitDirectories fp
1823 | "ppt" : "slideMasters" : f : [] <- splitDirectories fp
1793 , (_, ".xml") <- splitExtension f =
1824 , (_, ".xml") <- splitExtension f =
src/Text/Pandoc/Writers/Powerpoint/Presentation.hs+19−2
@@ -72,7 +72,8 @@ import Text.Pandoc.Logging
72import Text.Pandoc.Walk
72import Text.Pandoc.Walk
73import Data.Time (UTCTime)
73import Data.Time (UTCTime)
74import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element"
74import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element"
75−import Text.Pandoc.Writers.Shared (lookupMetaInlines, toTableOfContents)
75+import Text.Pandoc.Writers.Shared (lookupMetaInlines, lookupMetaBlocks
76+ , lookupMetaString, toTableOfContents)
76import qualified Data.Map as M
77import qualified Data.Map as M
77import qualified Data.Set as S
78import qualified Data.Set as S
78import Data.Maybe (maybeToList, fromMaybe)
79import Data.Maybe (maybeToList, fromMaybe)
@@ -180,7 +181,10 @@ data DocProps = DocProps { dcTitle :: Maybe String
180 , dcSubject :: Maybe String
181 , dcSubject :: Maybe String
181 , dcCreator :: Maybe String
182 , dcCreator :: Maybe String
182 , dcKeywords :: Maybe [String]
183 , dcKeywords :: Maybe [String]
184+ , dcDescription :: Maybe String
185+ , cpCategory :: Maybe String
183 , dcCreated :: Maybe UTCTime
186 , dcCreated :: Maybe UTCTime
187+ , customProperties :: Maybe [(String, String)]
184 } deriving (Show, Eq)
188 } deriving (Show, Eq)
185
189
186
190
@@ -930,13 +934,26 @@ metaToDocProps meta =
930
934
931 authors = case map Shared.stringify $ docAuthors meta of
935 authors = case map Shared.stringify $ docAuthors meta of
932 [] -> Nothing
936 [] -> Nothing
933− ss -> Just $ intercalate ";" ss
937+ ss -> Just $ intercalate "; " ss
938+
939+ description = case map Shared.stringify $ lookupMetaBlocks "description" meta of
940+ [] -> Nothing
941+ ss -> Just $ intercalate "_x000d_\n" ss
942+
943+ customProperties' = case [(k, lookupMetaString k meta) | k <- M.keys (unMeta meta)
944+ , k `notElem` (["title", "author", "keywords", "description"
945+ , "subject","lang","category"])] of
946+ [] -> Nothing
947+ ss -> Just ss
934 in
948 in
935 DocProps{ dcTitle = Shared.stringify <$> lookupMeta "title" meta
949 DocProps{ dcTitle = Shared.stringify <$> lookupMeta "title" meta
936 , dcSubject = Shared.stringify <$> lookupMeta "subject" meta
950 , dcSubject = Shared.stringify <$> lookupMeta "subject" meta
937 , dcCreator = authors
951 , dcCreator = authors
938 , dcKeywords = keywords
952 , dcKeywords = keywords
953+ , dcDescription = description
954+ , cpCategory = Shared.stringify <$> lookupMeta "category" meta
939 , dcCreated = Nothing
955 , dcCreated = Nothing
956+ , customProperties = customProperties'
940 }
957 }
941
958
942documentToPresentation :: WriterOptions
959documentToPresentation :: WriterOptions
test/Tests/Writers/Docx.hs+10
@@ -156,4 +156,14 @@ tests = [ testGroup "inlines"
156 "docx/custom_style.native"
156 "docx/custom_style.native"
157 "docx/golden/custom_style_reference.docx"
157 "docx/golden/custom_style_reference.docx"
158 ]
158 ]
159+ , testGroup "metadata"
160+ [ docxTest "document properties (core, custom)"
161+ def
162+ "docx/document-properties.native"
163+ "docx/golden/document-properties.docx"
164+ , docxTest "document properties (short description)"
165+ def
166+ "docx/document-properties-short-desc.native"
167+ "docx/golden/document-properties-short-desc.docx"
168+ ]
159 ]
169 ]
test/Tests/Writers/Powerpoint.hs+8
@@ -99,4 +99,12 @@ tests = groupPptxTests [ pptxTests "Inline formatting"
99 def
99 def
100 "pptx/raw_ooxml.native"
100 "pptx/raw_ooxml.native"
101 "pptx/raw_ooxml.pptx"
101 "pptx/raw_ooxml.pptx"
102+ , pptxTests "metadata, custom properties"
103+ def
104+ "pptx/document-properties.native"
105+ "pptx/document-properties.pptx"
106+ , pptxTests "metadata, short description"
107+ def
108+ "pptx/document-properties-short-desc.native"
109+ "pptx/document-properties-short-desc.pptx"
102 ]
110 ]
test/docx/document-properties-short-desc.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("description",MetaInlines [Str "Short",Space,RawInline (Format "html") "<i>",Str "description",RawInline (Format "html") "</i>",Space,Str "&."]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]})
2+[Para [Str "Testing",Space,Str "document",Space,Str "properties"]]
test/docx/document-properties.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("Company",MetaInlines [Str "My",Space,Str "Company"]),("Second Custom Property",MetaInlines [Str "Second",Space,Str "custom",Space,Str "property",Space,Str "value"]),("abstract",MetaBlocks [Plain [Str "Quite",Space,Str "a",Space,Str "long",Space,Str "description",SoftBreak,Str "spanning",Space,Str "several",Space,Str "lines"]]),("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("category",MetaInlines [Str "My",Space,Str "Category"]),("custom1",MetaInlines [Str "First",Space,Str "custom",Space,Str "property",Space,Str "value"]),("custom3",MetaInlines [Str "Escaping",Space,Str "amp",Space,Str "&",Space,Str "."]),("custom4",MetaInlines [Str "Escaping",Space,Str "LT,GT",Space,Str "<",Space,Str "asdf",Space,Str ">",Space,Str "<"]),("custom5",MetaInlines [Str "Escaping",Space,Str "html",Space,RawInline (Format "html") "<i>",Str "asdf",RawInline (Format "html") "</i>"]),("custom6",MetaInlines [Str "Escaping",Space,Emph [Str "MD"],Space,Str "\225",Space,Str "a"]),("custom9",MetaInlines [Str "Extended",Space,Str "chars:",Space,Str "\8364",Space,Str "\225",Space,Str "\233",Space,Str "\237",Space,Str "\243",Space,Str "\250",Space,Str "$"]),("description",MetaBlocks [Para [Str "Long",Space,Str "description",Space,Str "spanning",SoftBreak,Str "several",Space,Str "lines."],Plain [Str "This",Space,Str "is",Space,Str "\225",Space,Str "second",Space,RawInline (Format "html") "<i>",Str "line",RawInline (Format "html") "</i>",Str "."]]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("lang",MetaInlines [Str "en-US"]),("nested-custom",MetaList [MetaMap (fromList [("custom 7",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "7"])]),MetaMap (fromList [("custom 8",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "8"])])]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("subtitle",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "subtitle"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]})
2+[Para [Str "Testing",Space,Str "document",Space,Str "properties"]]
test/docx/golden/document-properties-short-desc.docxnew file

Binary file not shown.

test/docx/golden/document-properties.docxnew file

Binary file not shown.

test/pptx/document-properties-short-desc.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("description",MetaInlines [Str "Short",Space,RawInline (Format "html") "<i>",Str "description",RawInline (Format "html") "</i>",Space,Str "&."]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]})
2+[Para [Str "Testing",Space,Str "document",Space,Str "properties"]]
test/pptx/document-properties-short-desc.pptxnew file

Binary file not shown.

test/pptx/document-properties-short-desc_templated.pptxnew file

Binary file not shown.

test/pptx/document-properties.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("Company",MetaInlines [Str "My",Space,Str "Company"]),("Second Custom Property",MetaInlines [Str "Second",Space,Str "custom",Space,Str "property",Space,Str "value"]),("abstract",MetaBlocks [Plain [Str "Quite",Space,Str "a",Space,Str "long",Space,Str "description",SoftBreak,Str "spanning",Space,Str "several",Space,Str "lines"]]),("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("category",MetaInlines [Str "My",Space,Str "Category"]),("custom1",MetaInlines [Str "First",Space,Str "custom",Space,Str "property",Space,Str "value"]),("custom3",MetaInlines [Str "Escaping",Space,Str "amp",Space,Str "&",Space,Str "."]),("custom4",MetaInlines [Str "Escaping",Space,Str "LT,GT",Space,Str "<",Space,Str "asdf",Space,Str ">",Space,Str "<"]),("custom5",MetaInlines [Str "Escaping",Space,Str "html",Space,RawInline (Format "html") "<i>",Str "asdf",RawInline (Format "html") "</i>"]),("custom6",MetaInlines [Str "Escaping",Space,Emph [Str "MD"],Space,Str "\225",Space,Str "a"]),("custom9",MetaInlines [Str "Extended",Space,Str "chars:",Space,Str "\8364",Space,Str "\225",Space,Str "\233",Space,Str "\237",Space,Str "\243",Space,Str "\250",Space,Str "$"]),("description",MetaBlocks [Para [Str "Long",Space,Str "description",Space,Str "spanning",SoftBreak,Str "several",Space,Str "lines."],Plain [Str "This",Space,Str "is",Space,Str "\225",Space,Str "second",Space,RawInline (Format "html") "<i>",Str "line",RawInline (Format "html") "</i>",Str "."]]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("lang",MetaInlines [Str "en-US"]),("nested-custom",MetaList [MetaMap (fromList [("custom 7",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "7"])]),MetaMap (fromList [("custom 8",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "8"])])]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("subtitle",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "subtitle"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]})
2+[Para [Str "Testing",Space,Str "document",Space,Str "properties"]]
test/pptx/document-properties.pptxnew file

Binary file not shown.

test/pptx/document-properties_templated.pptxnew file

Binary file not shown.

test/pptx/endnotes.pptx

Binary file not shown.

test/pptx/endnotes_templated.pptx

Binary file not shown.

test/pptx/endnotes_toc.pptx

Binary file not shown.

test/pptx/endnotes_toc_templated.pptx

Binary file not shown.

test/pptx/images.pptx

Binary file not shown.

test/pptx/images_templated.pptx

Binary file not shown.

test/pptx/inline_formatting.pptx

Binary file not shown.

test/pptx/inline_formatting_templated.pptx

Binary file not shown.

test/pptx/lists.pptx

Binary file not shown.

test/pptx/lists_templated.pptx

Binary file not shown.

test/pptx/raw_ooxml.pptx

Binary file not shown.

test/pptx/raw_ooxml_templated.pptx

Binary file not shown.

test/pptx/remove_empty_slides.pptx

Binary file not shown.

test/pptx/remove_empty_slides_templated.pptx

Binary file not shown.

test/pptx/slide_breaks.pptx

Binary file not shown.

test/pptx/slide_breaks_slide_level_1.pptx

Binary file not shown.

test/pptx/slide_breaks_slide_level_1_templated.pptx

Binary file not shown.

test/pptx/slide_breaks_templated.pptx

Binary file not shown.

test/pptx/slide_breaks_toc.pptx

Binary file not shown.

test/pptx/slide_breaks_toc_templated.pptx

Binary file not shown.

test/pptx/speaker_notes.pptx

Binary file not shown.

test/pptx/speaker_notes_afterseps.pptx

Binary file not shown.

test/pptx/speaker_notes_afterseps_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_templated.pptx

Binary file not shown.

test/pptx/tables.pptx

Binary file not shown.

test/pptx/tables_templated.pptx

Binary file not shown.

test/pptx/two_column.pptx

Binary file not shown.

test/pptx/two_column_templated.pptx

Binary file not shown.