git.iko.soy/typeable/pandoc
on master help sign in
Docx reader: Make metavalues out of styled paragraphs.
840108aJesse Rosenthal
src/Text/Pandoc/Readers/Docx.hs+73−11
@@ -87,7 +87,7 @@ import Text.Pandoc.Readers.Docx.Reducible
87import Text.Pandoc.Readers.Docx.TexChar
87import Text.Pandoc.Readers.Docx.TexChar
88import Text.Pandoc.Shared
88import Text.Pandoc.Shared
89import Data.Maybe (mapMaybe, fromMaybe)
89import Data.Maybe (mapMaybe, fromMaybe)
90−import Data.List (delete, isPrefixOf, (\\), intercalate)
90+import Data.List (delete, isPrefixOf, (\\), intercalate, intersect)
91import qualified Data.ByteString as BS
91import qualified Data.ByteString as BS
92import qualified Data.ByteString.Lazy as B
92import qualified Data.ByteString.Lazy as B
93import Data.ByteString.Base64 (encode)
93import Data.ByteString.Base64 (encode)
@@ -101,7 +101,8 @@ readDocx :: ReaderOptions
101 -> Pandoc
101 -> Pandoc
102readDocx opts bytes =
102readDocx opts bytes =
103 case archiveToDocx (toArchive bytes) of
103 case archiveToDocx (toArchive bytes) of
104− Right docx -> Pandoc nullMeta (docxToBlocks opts docx)
104+ Right docx -> Pandoc meta blks where
105+ (meta, blks) = (docxToMetaAndBlocks opts docx)
105 Left _ -> error $ "couldn't parse docx file"
106 Left _ -> error $ "couldn't parse docx file"
106
107
107data DState = DState { docxAnchorMap :: M.Map String String
108data DState = DState { docxAnchorMap :: M.Map String String
@@ -134,6 +135,65 @@ spansToKeep = []
134divsToKeep :: [String]
135divsToKeep :: [String]
135divsToKeep = ["list-item", "Definition", "DefinitionTerm"]
136divsToKeep = ["list-item", "Definition", "DefinitionTerm"]
136
137
138+metaStyles :: M.Map String String
139+metaStyles = M.fromList [ ("Title", "title")
140+ , ("Subtitle", "subtitle")
141+ , ("Author", "author")
142+ , ("Date", "date")
143+ , ("Abstract", "abstract")]
144+
145+sepBodyParts :: [BodyPart] -> ([BodyPart], [BodyPart])
146+sepBodyParts = span (\bp -> (isMetaPar bp || isEmptyPar bp))
147+
148+isMetaPar :: BodyPart -> Bool
149+isMetaPar (Paragraph pPr _) =
150+ not $ null $ intersect (pStyle pPr) (M.keys metaStyles)
151+isMetaPar _ = False
152+
153+isEmptyPar :: BodyPart -> Bool
154+isEmptyPar (Paragraph _ parParts) =
155+ all isEmptyParPart parParts
156+ where
157+ isEmptyParPart (PlainRun (Run _ runElems)) = all isEmptyElem runElems
158+ isEmptyParPart _ = False
159+ isEmptyElem (TextRun s) = trim s == ""
160+ isEmptyElem _ = True
161+isEmptyPar _ = False
162+
163+bodyPartsToMeta' :: [BodyPart] -> DocxContext (M.Map String MetaValue)
164+bodyPartsToMeta' [] = return M.empty
165+bodyPartsToMeta' (bp : bps)
166+ | (Paragraph pPr parParts) <- bp
167+ , (c : _)<- intersect (pStyle pPr) (M.keys metaStyles)
168+ , (Just metaField) <- M.lookup c metaStyles = do
169+ inlines <- parPartsToInlines parParts
170+ remaining <- bodyPartsToMeta' bps
171+ let
172+ f (MetaInlines ils) (MetaInlines ils') = MetaBlocks [Para ils, Para ils']
173+ f (MetaInlines ils) (MetaBlocks blks) = MetaBlocks ((Para ils) : blks)
174+ f m (MetaList mv) = MetaList (m : mv)
175+ f m n = MetaList [m, n]
176+ return $ M.insertWith f metaField (MetaInlines inlines) remaining
177+bodyPartsToMeta' (_ : bps) = bodyPartsToMeta' bps
178+
179+bodyPartsToMeta :: [BodyPart] -> DocxContext Meta
180+bodyPartsToMeta bps = do
181+ mp <- bodyPartsToMeta' bps
182+ let mp' =
183+ case M.lookup "author" mp of
184+ Just mv -> M.insert "author" (fixAuthors mv) mp
185+ Nothing -> mp
186+ return $ Meta mp'
187+
188+fixAuthors :: MetaValue -> MetaValue
189+fixAuthors (MetaBlocks blks) =
190+ MetaList $ map g $ filter f blks
191+ where f (Para _) = True
192+ f _ = False
193+ g (Para ils) = MetaInlines ils
194+ g _ = MetaInlines []
195+fixAuthors mv = mv
196+
137runStyleToContainers :: RunStyle -> [Container Inline]
197runStyleToContainers :: RunStyle -> [Container Inline]
138runStyleToContainers rPr =
198runStyleToContainers rPr =
139 let spanClassToContainers :: String -> [Container Inline]
199 let spanClassToContainers :: String -> [Container Inline]
@@ -615,24 +675,26 @@ rewriteLink l@(Link ils ('#':target, title)) = do
615 Nothing -> l
675 Nothing -> l
616rewriteLink il = return il
676rewriteLink il = return il
617
677
618−
619−bodyToBlocks :: Body -> DocxContext [Block]
678+bodyToMetaAndBlocks :: Body -> DocxContext (Meta, [Block])
620−bodyToBlocks (Body bps) = do
679+bodyToMetaAndBlocks (Body bps) = do
680+ let (metabps, blkbps) = sepBodyParts bps
681+ meta <- bodyPartsToMeta metabps
621− blks <- concatMapM bodyPartToBlocks bps >>=
682+ blks <- concatMapM bodyPartToBlocks blkbps >>=
622 walkM rewriteLink
683 walkM rewriteLink
623 return $
684 return $
685+ (meta,
624− blocksToDefinitions $
686+ blocksToDefinitions $
625− blocksToBullets $ blks
687+ blocksToBullets $ blks)
626
688
627−docxToBlocks :: ReaderOptions -> Docx -> [Block]
689+docxToMetaAndBlocks :: ReaderOptions -> Docx -> (Meta, [Block])
628−docxToBlocks opts d@(Docx (Document _ body)) =
690+docxToMetaAndBlocks opts d@(Docx (Document _ body)) =
629 let dState = DState { docxAnchorMap = M.empty
691 let dState = DState { docxAnchorMap = M.empty
630 , docxInHeaderBlock = False
692 , docxInHeaderBlock = False
631 , docxInTexSubscript = False}
693 , docxInTexSubscript = False}
632 dEnv = DEnv { docxOptions = opts
694 dEnv = DEnv { docxOptions = opts
633 , docxDocument = d}
695 , docxDocument = d}
634 in
696 in
635− evalDocxContext (bodyToBlocks body) dEnv dState
697+ evalDocxContext (bodyToMetaAndBlocks body) dEnv dState
636
698
637ilToCode :: Inline -> String
699ilToCode :: Inline -> String
638ilToCode (Str s) = s
700ilToCode (Str s) = s
tests/Tests/Readers/Docx.hs+15
@@ -164,5 +164,20 @@ tests = [ testGroup "inlines"
164 "docx.track_changes_deletion.docx"
164 "docx.track_changes_deletion.docx"
165 "docx.track_changes_deletion_all.native"
165 "docx.track_changes_deletion_all.native"
166 ]
166 ]
167+ , testGroup "metadata"
168+ [ testCompareWithOpts def{readerStandalone=True}
169+ "metadata fields"
170+ "docx.metadata.docx"
171+ "docx.metadata.native"
172+ , testCompareWithOpts def{readerStandalone=True}
173+ "linebreak between authors"
174+ "docx.metadata_author_linebreak.docx"
175+ "docx.metadata_author_linebreak.native"
176+ , testCompareWithOpts def{readerStandalone=True}
177+ "stop recording metadata with normal text"
178+ "docx.metadata_after_normal.docx"
179+ "docx.metadata_after_normal.native"
180+ ]
181+
167 ]
182 ]
168
183
tests/docx.metadata.docxnew file

Binary file not shown.

tests/docx.metadata.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("abstract",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]),("author",MetaList [MetaInlines [Str "Mary",Space,Str "Ann",Space,Str "Evans"],MetaInlines [Str "Aurore",Space,Str "Dupin"]]),("date",MetaInlines [Str "July",Space,Str "28,",Space,Str "2014"]),("title",MetaInlines [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"])]})
2+[Para [Str "And",Space,Str "now",Space,Str "this",Space,Str "is",Space,Str "normal",Space,Str "text."]]
tests/docx.metadata_after_normal.docxnew file

Binary file not shown.

tests/docx.metadata_after_normal.nativenew file+7
@@ -0,0 +1,7 @@
1+Pandoc (Meta {unMeta = fromList [("abstract",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]),("author",MetaList [MetaInlines [Str "Mary",Space,Str "Ann",Space,Str "Evans"],MetaInlines [Str "Aurore",Space,Str "Dupin"]]),("date",MetaInlines [Str "July",Space,Str "28,",Space,Str "2014"]),("title",MetaInlines [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"])]})
2+[Para [Str "And",Space,Str "now",Space,Str "this",Space,Str "is",Space,Str "normal",Space,Str "text."]
3+,Para [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"]
4+,Para [Str "Mary",Space,Str "Ann",Space,Str "Evans"]
5+,Para [Str "Aurore",Space,Str "Dupin"]
6+,Para [Str "July",Space,Str "28,",Space,Str "2014"]
7+,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]]
tests/docx.metadata_author_linebreak.docxnew file

Binary file not shown.

tests/docx.metadata_author_linebreak.nativenew file+2
@@ -0,0 +1,2 @@
1+Pandoc (Meta {unMeta = fromList [("abstract",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]),("author",MetaList [MetaInlines [Str "Mary",Space,Str "Ann",Space,Str "Evans"],MetaInlines [Str "Aurore",Space,Str "Dupin"]]),("date",MetaInlines [Str "July",Space,Str "28,",Space,Str "2014"]),("title",MetaInlines [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"])]})
2+[Para [Str "And",Space,Str "now",Space,Str "this",Space,Str "is",Space,Str "normal",Space,Str "text."]]