git.iko.soy/typeable/pandoc
on master help sign in
Rename Text.Pandoc.XMLParser -> Text.Pandoc.XML.Light...

..and add new definitions isomorphic to xml-light's, but with Text instead of String. This allows us to keep most of the code in existing readers that use xml-light, but avoid lots of unnecessary allocation. We also add versions of the functions from xml-light's Text.XML.Light.Output and Text.XML.Light.Proc that operate on our modified XML types, and functions that convert xml-light types to our types (since some of our dependencies, like texmath, use xml-light). Update golden tests for docx and pptx. OOXML test: Use `showContent` instead of `ppContent` in `displayDiff`. Docx: Do a manual traversal to unwrap sdt and smartTag. This is faster, and needed to pass the tests. Benchmarks: A = prior to 8ca191604dcd13af27c11d2da225da646ebce6fc (Feb 8) B = as of 8ca191604dcd13af27c11d2da225da646ebce6fc (Feb 8) C = this commit | Reader | A | B | C | | ------- | ----- | ------ | ----- | | docbook | 18 ms | 12 ms | 10 ms | | opml | 65 ms | 62 ms | 35 ms | | jats | 15 ms | 11 ms | 9 ms | | docx | 72 ms | 69 ms | 44 ms | | odt | 78 ms | 41 ms | 28 ms | | epub | 64 ms | 61 ms | 56 ms | | fb2 | 14 ms | 5 ms | 4 ms |

commit967e7f5fb990b29de48b37be1db40fb149a8cf55 authorJohn MacFarlane <jgm@berkeley.edu> dateSun 14 Feb 2021, 22:29 (6 years ago) parentb5b5761
102 files changed+1388−930
.hlint.yaml+1 pandoc.cabal+1−1 src/Text/Pandoc/ImageSize.hs+4−5 src/Text/Pandoc/Readers/DocBook.hs+38−38 src/Text/Pandoc/Readers/Docx/Parse.hs+75−88 src/Text/Pandoc/Readers/Docx/Parse/Styles.hs+17−14 src/Text/Pandoc/Readers/Docx/Util.hs+11−16 src/Text/Pandoc/Readers/EPUB.hs+34−31 src/Text/Pandoc/Readers/FB2.hs+47−46 src/Text/Pandoc/Readers/JATS.hs+25−33 src/Text/Pandoc/Readers/OPML.hs+12−17 src/Text/Pandoc/Readers/Odt.hs+2−3 src/Text/Pandoc/Readers/Odt/ContentReader.hs+7−6 src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs+2−1 src/Text/Pandoc/Readers/Odt/Generic/Utils.hs+9−24 src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs+11−12 src/Text/Pandoc/Readers/Odt/Namespaces.hs+6−5 src/Text/Pandoc/Readers/Odt/StyleReader.hs+13−10 src/Text/Pandoc/Writers/Docx.hs+130−133 src/Text/Pandoc/Writers/EPUB.hs+173−183 src/Text/Pandoc/Writers/FB2.hs+53−74 src/Text/Pandoc/Writers/ODT.hs+9−7 src/Text/Pandoc/Writers/OOXML.hs+17−18 src/Text/Pandoc/Writers/Powerpoint/Output.hs+103−98 src/Text/Pandoc/XML/Light.hs+586 src/Text/Pandoc/XMLParser.hs−66 test/Tests/Writers/OOXML.hs+2−1 test/docx/golden/block_quotes.docx test/docx/golden/codeblock.docx test/docx/golden/comments.docx test/docx/golden/custom_style_no_reference.docx test/docx/golden/custom_style_preserve.docx test/docx/golden/custom_style_reference.docx test/docx/golden/definition_list.docx test/docx/golden/document-properties-short-desc.docx test/docx/golden/document-properties.docx test/docx/golden/headers.docx test/docx/golden/image.docx test/docx/golden/inline_code.docx test/docx/golden/inline_formatting.docx test/docx/golden/inline_images.docx test/docx/golden/link_in_notes.docx test/docx/golden/links.docx test/docx/golden/lists.docx test/docx/golden/lists_continuing.docx test/docx/golden/lists_multiple_initial.docx test/docx/golden/lists_restarting.docx test/docx/golden/nested_anchors_in_header.docx test/docx/golden/notes.docx test/docx/golden/raw-blocks.docx test/docx/golden/raw-bookmarks.docx test/docx/golden/table_one_row.docx test/docx/golden/table_with_list_cell.docx test/docx/golden/tables.docx test/docx/golden/track_changes_deletion.docx test/docx/golden/track_changes_insertion.docx test/docx/golden/track_changes_move.docx test/docx/golden/track_changes_scrubbed_metadata.docx test/docx/golden/unicode.docx test/docx/golden/verbatim_subsuper.docx test/pptx/code-custom.pptx test/pptx/code-custom_templated.pptx test/pptx/code.pptx test/pptx/code_templated.pptx test/pptx/document-properties-short-desc.pptx test/pptx/document-properties-short-desc_templated.pptx test/pptx/document-properties.pptx test/pptx/document-properties_templated.pptx test/pptx/endnotes.pptx test/pptx/endnotes_templated.pptx test/pptx/endnotes_toc.pptx test/pptx/endnotes_toc_templated.pptx test/pptx/images.pptx test/pptx/images_templated.pptx test/pptx/inline_formatting.pptx test/pptx/inline_formatting_templated.pptx test/pptx/lists.pptx test/pptx/lists_templated.pptx test/pptx/raw_ooxml.pptx test/pptx/raw_ooxml_templated.pptx test/pptx/remove_empty_slides.pptx test/pptx/remove_empty_slides_templated.pptx test/pptx/slide_breaks.pptx test/pptx/slide_breaks_slide_level_1.pptx test/pptx/slide_breaks_slide_level_1_templated.pptx test/pptx/slide_breaks_templated.pptx test/pptx/slide_breaks_toc.pptx test/pptx/slide_breaks_toc_templated.pptx test/pptx/speaker_notes.pptx test/pptx/speaker_notes_after_metadata.pptx test/pptx/speaker_notes_after_metadata_templated.pptx test/pptx/speaker_notes_afterheader.pptx test/pptx/speaker_notes_afterheader_templated.pptx test/pptx/speaker_notes_afterseps.pptx test/pptx/speaker_notes_afterseps_templated.pptx test/pptx/speaker_notes_templated.pptx test/pptx/start_numbering_at.pptx test/pptx/start_numbering_at_templated.pptx test/pptx/tables.pptx test/pptx/tables_templated.pptx test/pptx/two_column.pptx test/pptx/two_column_templated.pptx
.hlint.yaml+1
@@ -14,6 +14,7 @@
1414- ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained
1515- ignore: {name: "Use &&&"}
1616- ignore: {name: "Use String"}
17+- ignore: {name: "Use camelCase"}
1718- ignore: {name: "Use fmap"} # specific for GHC 7.8 compat
1819- ignore: {name: "Use isDigit"}
1920
pandoc.cabal+1−1
@@ -687,7 +687,7 @@ library
687687 Text.Pandoc.Lua.PandocLua,
688688 Text.Pandoc.Lua.Util,
689689 Text.Pandoc.Lua.Walk,
690− Text.Pandoc.XMLParser,
690+ Text.Pandoc.XML.Light,
691691 Text.Pandoc.CSS,
692692 Text.Pandoc.CSV,
693693 Text.Pandoc.RoffChar,
src/Text/Pandoc/ImageSize.hs+4−5
@@ -44,8 +44,7 @@ import Numeric (showFFloat)
4444import Text.Pandoc.Definition
4545import Text.Pandoc.Options
4646import qualified Text.Pandoc.UTF8 as UTF8
47−import qualified Text.XML.Light as Xml
48−import Text.Pandoc.XMLParser (parseXMLElement)
47+import Text.Pandoc.XML.Light hiding (Attr)
4948import qualified Data.Text as T
5049import qualified Data.Text.Lazy as TL
5150import qualified Data.Text.Encoding as TE
@@ -332,12 +331,12 @@ svgSize opts img = do
332331 doc <- either (const mzero) return $ parseXMLElement
333332 $ TL.fromStrict $ UTF8.toText img
334333 let viewboxSize = do
335− vb <- Xml.findAttrBy (== Xml.QName "viewBox" Nothing Nothing) doc
336− [_,_,w,h] <- mapM safeRead (T.words (T.pack vb))
334+ vb <- findAttrBy (== QName "viewBox" Nothing Nothing) doc
335+ [_,_,w,h] <- mapM safeRead (T.words vb)
337336 return (w,h)
338337 let dpi = fromIntegral $ writerDpi opts
339338 let dirToInt dir = do
340− dim <- Xml.findAttrBy (== Xml.QName dir Nothing Nothing) doc >>= lengthToDim . T.pack
339+ dim <- findAttrBy (== QName dir Nothing Nothing) doc >>= lengthToDim
341340 return $ inPixel opts dim
342341 w <- dirToInt "width" <|> (fst <$> viewboxSize)
343342 h <- dirToInt "height" <|> (snd <$> viewboxSize)
src/Text/Pandoc/Readers/DocBook.hs+38−38
@@ -31,8 +31,7 @@ import Text.Pandoc.Options
3131import Text.Pandoc.Logging (LogMessage(..))
3232import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces)
3333import Text.TeXMath (readMathML, writeTeX)
34−import Text.XML.Light
35−import Text.Pandoc.XMLParser (parseXMLContents)
34+import Text.Pandoc.XML.Light
3635
3736{-
3837
@@ -578,26 +577,27 @@ normalizeTree = everywhere (mkT go)
578577 where go :: [Content] -> [Content]
579578 go (Text (CData CDataRaw _ _):xs) = xs
580579 go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) =
581− Text (CData CDataText (s1 ++ s2) z):xs
580+ Text (CData CDataText (s1 <> s2) z):xs
582581 go (Text (CData CDataText s1 z):CRef r:xs) =
583− Text (CData CDataText (s1 ++ convertEntity r) z):xs
582+ Text (CData CDataText (s1 <> convertEntity r) z):xs
584583 go (CRef r:Text (CData CDataText s1 z):xs) =
585− Text (CData CDataText (convertEntity r ++ s1) z):xs
584+ Text (CData CDataText (convertEntity r <> s1) z):xs
586585 go (CRef r1:CRef r2:xs) =
587− Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs
586+ Text (CData CDataText (convertEntity r1 <>
587+ convertEntity r2) Nothing):xs
588588 go xs = xs
589589
590−convertEntity :: String -> String
591−convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e)
590+convertEntity :: Text -> Text
591+convertEntity e = maybe (T.map toUpper e) T.pack (lookupEntity $ T.unpack e)
592592
593593-- convenience function to get an attribute value, defaulting to ""
594−attrValue :: String -> Element -> Text
594+attrValue :: Text -> Element -> Text
595595attrValue attr elt =
596− maybe "" T.pack (lookupAttrBy (\x -> qName x == attr) (elAttribs elt))
596+ fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt))
597597
598598-- convenience function
599599named :: Text -> Element -> Bool
600−named s e = qName (elName e) == T.unpack s
600+named s e = qName (elName e) == s
601601
602602--
603603
@@ -634,7 +634,7 @@ isBlockElement :: Content -> Bool
634634isBlockElement (Elem e) = qName (elName e) `elem` blockTags
635635isBlockElement _ = False
636636
637−blockTags :: [String]
637+blockTags :: [Text]
638638blockTags =
639639 [ "abstract"
640640 , "ackno"
@@ -721,7 +721,7 @@ blockTags =
721721 , "variablelist"
722722 ] ++ admonitionTags
723723
724−admonitionTags :: [String]
724+admonitionTags :: [Text]
725725admonitionTags = ["important","caution","note","tip","warning"]
726726
727727-- Trim leading and trailing newline characters
@@ -779,10 +779,10 @@ getBlocks e = mconcat <$>
779779
780780parseBlock :: PandocMonad m => Content -> DB m Blocks
781781parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE
782−parseBlock (Text (CData _ s _)) = if all isSpace s
782+parseBlock (Text (CData _ s _)) = if T.all isSpace s
783783 then return mempty
784− else return $ plain $ trimInlines $ text $ T.pack s
785−parseBlock (CRef x) = return $ plain $ str $ T.toUpper $ T.pack x
784+ else return $ plain $ trimInlines $ text s
785+parseBlock (CRef x) = return $ plain $ str $ T.toUpper x
786786parseBlock (Elem e) =
787787 case qName (elName e) of
788788 "toc" -> skip -- skip TOC, since in pandoc it's autogenerated
@@ -837,7 +837,7 @@ parseBlock (Elem e) =
837837 "refsect2" -> sect 2
838838 "refsect3" -> sect 3
839839 "refsection" -> gets dbSectionLevel >>= sect . (+1)
840− l | l `elem` admonitionTags -> parseAdmonition $ T.pack l
840+ l | l `elem` admonitionTags -> parseAdmonition l
841841 "area" -> skip
842842 "areaset" -> skip
843843 "areaspec" -> skip
@@ -899,7 +899,7 @@ parseBlock (Elem e) =
899899 "subtitle" -> return mempty -- handled in parent element
900900 _ -> skip >> getBlocks e
901901 where skip = do
902− let qn = T.pack $ qName $ elName e
902+ let qn = qName $ elName e
903903 let name = if "pi-" `T.isPrefixOf` qn
904904 then "<?" <> qn <> "?>"
905905 else qn
@@ -911,7 +911,7 @@ parseBlock (Elem e) =
911911 "" -> []
912912 x -> [x]
913913 return $ codeBlockWith (attrValue "id" e, classes', [])
914− $ trimNl $ T.pack $ strContentRecursive e
914+ $ trimNl $ strContentRecursive e
915915 parseBlockquote = do
916916 attrib <- case filterChild (named "attribution") e of
917917 Nothing -> return mempty
@@ -965,7 +965,7 @@ parseBlock (Elem e) =
965965 w <- findAttr (unqual "colwidth") c
966966 n <- safeRead $ "0" <> T.filter (\x ->
967967 (x >= '0' && x <= '9')
968− || x == '.') (T.pack w)
968+ || x == '.') w
969969 if n > 0 then Just n else Nothing
970970 let numrows = case bodyrows of
971971 [] -> 0
@@ -1048,12 +1048,12 @@ parseMixed container conts = do
10481048 x <- parseMixed container rs
10491049 return $ p <> b <> x
10501050
1051−parseRow :: PandocMonad m => [String] -> Element -> DB m [Cell]
1051+parseRow :: PandocMonad m => [Text] -> Element -> DB m [Cell]
10521052parseRow cn = do
10531053 let isEntry x = named "entry" x || named "td" x || named "th" x
10541054 mapM (parseEntry cn) . filterChildren isEntry
10551055
1056−parseEntry :: PandocMonad m => [String] -> Element -> DB m Cell
1056+parseEntry :: PandocMonad m => [Text] -> Element -> DB m Cell
10571057parseEntry cn el = do
10581058 let colDistance sa ea = do
10591059 let iStrt = elemIndex sa cn
@@ -1075,7 +1075,7 @@ getInlines :: PandocMonad m => Element -> DB m Inlines
10751075getInlines e' = trimInlines . mconcat <$>
10761076 mapM parseInline (elContent e')
10771077
1078−strContentRecursive :: Element -> String
1078+strContentRecursive :: Element -> Text
10791079strContentRecursive = strContent .
10801080 (\e' -> e'{ elContent = map elementToStr $ elContent e' })
10811081
@@ -1084,9 +1084,9 @@ elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing
10841084elementToStr x = x
10851085
10861086parseInline :: PandocMonad m => Content -> DB m Inlines
1087−parseInline (Text (CData _ s _)) = return $ text $ T.pack s
1087+parseInline (Text (CData _ s _)) = return $ text s
10881088parseInline (CRef ref) =
1089− return $ text $ maybe (T.toUpper $ T.pack ref) T.pack $ lookupEntity ref
1089+ return $ text $ maybe (T.toUpper ref) T.pack $ lookupEntity (T.unpack ref)
10901090parseInline (Elem e) =
10911091 case qName (elName e) of
10921092 "anchor" -> do
@@ -1138,7 +1138,7 @@ parseInline (Elem e) =
11381138 "userinput" -> codeWithLang
11391139 "systemitem" -> codeWithLang
11401140 "varargs" -> return $ code "(...)"
1141− "keycap" -> return (str $ T.pack $ strContent e)
1141+ "keycap" -> return (str $ strContent e)
11421142 "keycombo" -> keycombo <$>
11431143 mapM parseInline (elContent e)
11441144 "menuchoice" -> menuchoice <$>
@@ -1150,17 +1150,17 @@ parseInline (Elem e) =
11501150 let title = case attrValue "endterm" e of
11511151 "" -> maybe "???" xrefTitleByElem
11521152 (findElementById linkend content)
1153− endterm -> maybe "???" (T.pack . strContent)
1153+ endterm -> maybe "???" strContent
11541154 (findElementById endterm content)
11551155 return $ link ("#" <> linkend) "" (text title)
1156− "email" -> return $ link ("mailto:" <> T.pack (strContent e)) ""
1157− $ str $ T.pack $ strContent e
1158− "uri" -> return $ link (T.pack $ strContent e) "" $ str $ T.pack $ strContent e
1156+ "email" -> return $ link ("mailto:" <> strContent e) ""
1157+ $ str $ strContent e
1158+ "uri" -> return $ link (strContent e) "" $ str $ strContent e
11591159 "ulink" -> innerInlines (link (attrValue "url" e) "")
11601160 "link" -> do
11611161 ils <- innerInlines id
11621162 let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of
1163− Just h -> T.pack h
1163+ Just h -> h
11641164 _ -> "#" <> attrValue "linkend" e
11651165 let ils' = if ils == mempty then str href else ils
11661166 let attr = (attrValue "id" e, T.words $ attrValue "role" e, [])
@@ -1180,7 +1180,7 @@ parseInline (Elem e) =
11801180 "pi-asciidoc-br" -> return linebreak
11811181 _ -> skip >> innerInlines id
11821182 where skip = do
1183− let qn = T.pack $ qName $ elName e
1183+ let qn = qName $ elName e
11841184 let name = if "pi-" `T.isPrefixOf` qn
11851185 then "<?" <> qn <> "?>"
11861186 else qn
@@ -1193,7 +1193,7 @@ parseInline (Elem e) =
11931193 let classes' = case attrValue "language" e of
11941194 "" -> []
11951195 l -> [l]
1196− return $ codeWith (attrValue "id" e,classes',[]) $ T.pack $ strContentRecursive e
1196+ return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e
11971197 simpleList = mconcat . intersperse (str "," <> space) <$> mapM getInlines
11981198 (filterChildren (named "member") e)
11991199 segmentedList = do
@@ -1234,10 +1234,10 @@ parseInline (Elem e) =
12341234 "sect5" -> descendantContent "title" el
12351235 "cmdsynopsis" -> descendantContent "command" el
12361236 "funcsynopsis" -> descendantContent "function" el
1237− _ -> T.pack $ qName (elName el) ++ "_title"
1237+ _ -> qName (elName el) <> "_title"
12381238 where
12391239 xrefLabel = attrValue "xreflabel" el
1240− descendantContent name = maybe "???" (T.pack . strContent)
1240+ descendantContent name = maybe "???" strContent
12411241 . filterElementName (\n -> qName n == name)
12421242
12431243-- | Extract a math equation from an element
@@ -1258,7 +1258,7 @@ equation e constructor =
12581258 mathMLEquations :: [Text]
12591259 mathMLEquations = map writeTeX $ rights $ readMath
12601260 (\x -> qName (elName x) == "math" && qPrefix (elName x) == Just "mml")
1261− (readMathML . T.pack . showElement)
1261+ (readMathML . showElement)
12621262
12631263 latexEquations :: [Text]
12641264 latexEquations = readMath (\x -> qName (elName x) == "mathphrase")
@@ -1272,8 +1272,8 @@ equation e constructor =
12721272-- | Get the actual text stored in a CData block. 'showContent'
12731273-- returns the text still surrounded by the [[CDATA]] tags.
12741274showVerbatimCData :: Content -> Text
1275−showVerbatimCData (Text (CData _ d _)) = T.pack d
1276−showVerbatimCData c = T.pack $ showContent c
1275+showVerbatimCData (Text (CData _ d _)) = d
1276+showVerbatimCData c = showContent c
12771277
12781278
12791279-- | Set the prefix of a name to 'Nothing'
src/Text/Pandoc/Readers/Docx/Parse.hs+75−88
@@ -63,6 +63,7 @@ import Data.Char (chr, ord, readLitChar)
6363import Data.List
6464import qualified Data.Map as M
6565import qualified Data.Text as T
66+import Data.Text (Text)
6667import Data.Maybe
6768import System.FilePath
6869import Text.Pandoc.Readers.Docx.Util
@@ -72,9 +73,7 @@ import qualified Text.Pandoc.UTF8 as UTF8
7273import Text.TeXMath (Exp)
7374import Text.TeXMath.Readers.OMML (readOMML)
7475import Text.TeXMath.Unicode.Fonts (Font (..), getUnicode, textToFont)
75−import Text.XML.Light
76−import qualified Text.XML.Light.Cursor as XMLC
77−import Text.Pandoc.XMLParser (parseXMLElement)
76+import Text.Pandoc.XML.Light
7877
7978data ReaderEnv = ReaderEnv { envNotes :: Notes
8079 , envComments :: Comments
@@ -128,37 +127,23 @@ mapD f xs =
128127 in
129128 concatMapM handler xs
130129
131−unwrap :: NameSpaces -> Content -> [Content]
132−unwrap ns (Elem element)
130+unwrapElement :: NameSpaces -> Element -> [Element]
131+unwrapElement ns element
133132 | isElem ns "w" "sdt" element
134133 , Just sdtContent <- findChildByName ns "w" "sdtContent" element
135− = concatMap (unwrap ns . Elem) (elChildren sdtContent)
134+ = concatMap (unwrapElement ns) (elChildren sdtContent)
136135 | isElem ns "w" "smartTag" element
137− = concatMap (unwrap ns . Elem) (elChildren element)
138−unwrap _ content = [content]
136+ = concatMap (unwrapElement ns) (elChildren element)
137+ | otherwise
138+ = [element{ elContent = concatMap (unwrapContent ns) (elContent element) }]
139139
140−unwrapChild :: NameSpaces -> Content -> Content
141−unwrapChild ns (Elem element) =
142− Elem $ element { elContent = concatMap (unwrap ns) (elContent element) }
143−unwrapChild _ content = content
140+unwrapContent :: NameSpaces -> Content -> [Content]
141+unwrapContent ns (Elem element) = map Elem $ unwrapElement ns element
142+unwrapContent _ content = [content]
144143
145−walkDocument' :: NameSpaces -> XMLC.Cursor -> XMLC.Cursor
146−walkDocument' ns cur =
147− let modifiedCur = XMLC.modifyContent (unwrapChild ns) cur
148− in
149− case XMLC.nextDF modifiedCur of
150− Just cur' -> walkDocument' ns cur'
151− Nothing -> XMLC.root modifiedCur
152−
153−walkDocument :: NameSpaces -> Element -> Maybe Element
144+walkDocument :: NameSpaces -> Element -> Element
154145walkDocument ns element =
155− let cur = XMLC.fromContent (Elem element)
156− cur' = walkDocument' ns cur
157− in
158− case XMLC.toTree cur' of
159− Elem element' -> Just element'
160− _ -> Nothing
161−
146+ element{ elContent = concatMap (unwrapContent ns) (elContent element) }
162147
163148newtype Docx = Docx Document
164149 deriving Show
@@ -361,9 +346,9 @@ getDocumentXmlPath zf = do
361346 fp <- findAttr (QName "Target" Nothing Nothing) rel
362347 -- sometimes there will be a leading slash, which windows seems to
363348 -- have trouble with.
364− return $ case fp of
349+ return $ case T.unpack fp of
365350 '/' : fp' -> fp'
366− _ -> fp
351+ fp' -> fp'
367352
368353archiveToDocument :: Archive -> D Document
369354archiveToDocument zf = do
@@ -372,7 +357,7 @@ archiveToDocument zf = do
372357 docElem <- maybeToD $ parseXMLFromEntry entry
373358 let namespaces = elemToNameSpaces docElem
374359 bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem
375− let bodyElem' = fromMaybe bodyElem (walkDocument namespaces bodyElem)
360+ let bodyElem' = walkDocument namespaces bodyElem
376361 body <- elemToBody namespaces bodyElem'
377362 return $ Document namespaces body
378363
@@ -414,8 +399,8 @@ archiveToNotes zf =
414399 fn_namespaces = maybe [] elemToNameSpaces fnElem
415400 en_namespaces = maybe [] elemToNameSpaces enElem
416401 ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces
417− fn = fnElem >>= walkDocument ns >>= elemToNotes ns "footnote"
418− en = enElem >>= walkDocument ns >>= elemToNotes ns "endnote"
402+ fn = fnElem >>= elemToNotes ns "footnote" . walkDocument ns
403+ en = enElem >>= elemToNotes ns "endnote" . walkDocument ns
419404 in
420405 Notes ns fn en
421406
@@ -424,7 +409,8 @@ archiveToComments zf =
424409 let cmtsElem = findEntryByPath "word/comments.xml" zf
425410 >>= parseXMLFromEntry
426411 cmts_namespaces = maybe [] elemToNameSpaces cmtsElem
427− cmts = elemToComments cmts_namespaces <$> (cmtsElem >>= walkDocument cmts_namespaces)
412+ cmts = elemToComments cmts_namespaces . walkDocument cmts_namespaces <$>
413+ cmtsElem
428414 in
429415 case cmts of
430416 Just c -> Comments cmts_namespaces c
@@ -443,8 +429,8 @@ filePathToRelType path docXmlPath =
443429relElemToRelationship :: DocumentLocation -> Element -> Maybe Relationship
444430relElemToRelationship relType element | qName (elName element) == "Relationship" =
445431 do
446− relId <- findAttrText (QName "Id" Nothing Nothing) element
447− target <- findAttrText (QName "Target" Nothing Nothing) element
432+ relId <- findAttr (QName "Id" Nothing Nothing) element
433+ target <- findAttr (QName "Target" Nothing Nothing) element
448434 return $ Relationship relType relId target
449435relElemToRelationship _ _ = Nothing
450436
@@ -485,10 +471,10 @@ lookupLevel numId ilvl (Numbering _ numbs absNumbs) = do
485471loElemToLevelOverride :: NameSpaces -> Element -> Maybe LevelOverride
486472loElemToLevelOverride ns element
487473 | isElem ns "w" "lvlOverride" element = do
488− ilvl <- findAttrTextByName ns "w" "ilvl" element
474+ ilvl <- findAttrByName ns "w" "ilvl" element
489475 let startOverride = findChildByName ns "w" "startOverride" element
490476 >>= findAttrByName ns "w" "val"
491− >>= (\s -> listToMaybe (map fst (reads s :: [(Integer, String)])))
477+ >>= stringToInteger
492478 lvl = findChildByName ns "w" "lvl" element
493479 >>= levelElemToLevel ns
494480 return $ LevelOverride ilvl startOverride lvl
@@ -497,9 +483,9 @@ loElemToLevelOverride _ _ = Nothing
497483numElemToNum :: NameSpaces -> Element -> Maybe Numb
498484numElemToNum ns element
499485 | isElem ns "w" "num" element = do
500− numId <- findAttrTextByName ns "w" "numId" element
486+ numId <- findAttrByName ns "w" "numId" element
501487 absNumId <- findChildByName ns "w" "abstractNumId" element
502− >>= findAttrTextByName ns "w" "val"
488+ >>= findAttrByName ns "w" "val"
503489 let lvlOverrides = mapMaybe
504490 (loElemToLevelOverride ns)
505491 (findChildrenByName ns "w" "lvlOverride" element)
@@ -509,7 +495,7 @@ numElemToNum _ _ = Nothing
509495absNumElemToAbsNum :: NameSpaces -> Element -> Maybe AbstractNumb
510496absNumElemToAbsNum ns element
511497 | isElem ns "w" "abstractNum" element = do
512− absNumId <- findAttrTextByName ns "w" "abstractNumId" element
498+ absNumId <- findAttrByName ns "w" "abstractNumId" element
513499 let levelElems = findChildrenByName ns "w" "lvl" element
514500 levels = mapMaybe (levelElemToLevel ns) levelElems
515501 return $ AbstractNumb absNumId levels
@@ -518,14 +504,14 @@ absNumElemToAbsNum _ _ = Nothing
518504levelElemToLevel :: NameSpaces -> Element -> Maybe Level
519505levelElemToLevel ns element
520506 | isElem ns "w" "lvl" element = do
521− ilvl <- findAttrTextByName ns "w" "ilvl" element
507+ ilvl <- findAttrByName ns "w" "ilvl" element
522508 fmt <- findChildByName ns "w" "numFmt" element
523− >>= findAttrTextByName ns "w" "val"
509+ >>= findAttrByName ns "w" "val"
524510 txt <- findChildByName ns "w" "lvlText" element
525− >>= findAttrTextByName ns "w" "val"
511+ >>= findAttrByName ns "w" "val"
526512 let start = findChildByName ns "w" "start" element
527513 >>= findAttrByName ns "w" "val"
528− >>= (\s -> listToMaybe (map fst (reads s :: [(Integer, String)])))
514+ >>= stringToInteger
529515 return (Level ilvl fmt txt start)
530516levelElemToLevel _ _ = Nothing
531517
@@ -546,11 +532,11 @@ archiveToNumbering :: Archive -> Numbering
546532archiveToNumbering archive =
547533 fromMaybe (Numbering [] [] []) (archiveToNumbering' archive)
548534
549−elemToNotes :: NameSpaces -> String -> Element -> Maybe (M.Map T.Text Element)
535+elemToNotes :: NameSpaces -> Text -> Element -> Maybe (M.Map T.Text Element)
550536elemToNotes ns notetype element
551537 | isElem ns "w" (notetype <> "s") element =
552538 let pairs = mapMaybe
553− (\e -> findAttrTextByName ns "w" "id" e >>=
539+ (\e -> findAttrByName ns "w" "id" e >>=
554540 (\a -> Just (a, e)))
555541 (findChildrenByName ns "w" notetype element)
556542 in
@@ -562,7 +548,7 @@ elemToComments :: NameSpaces -> Element -> M.Map T.Text Element
562548elemToComments ns element
563549 | isElem ns "w" "comments" element =
564550 let pairs = mapMaybe
565− (\e -> findAttrTextByName ns "w" "id" e >>=
551+ (\e -> findAttrByName ns "w" "id" e >>=
566552 (\a -> Just (a, e)))
567553 (findChildrenByName ns "w" "comment" element)
568554 in
@@ -622,12 +608,12 @@ elemToParIndentation ns element | isElem ns "w" "ind" element =
622608 stringToInteger
623609 , hangingParIndent =
624610 findAttrByName ns "w" "hanging" element >>=
625− stringToInteger}
611+ stringToInteger }
626612elemToParIndentation _ _ = Nothing
627613
628−testBitMask :: String -> Int -> Bool
614+testBitMask :: Text -> Int -> Bool
629615testBitMask bitMaskS n =
630− case (reads ("0x" ++ bitMaskS) :: [(Int, String)]) of
616+ case (reads ("0x" ++ T.unpack bitMaskS) :: [(Int, String)]) of
631617 [] -> False
632618 ((n', _) : _) -> (n' .|. n) /= 0
633619
@@ -642,7 +628,7 @@ elemToBodyPart ns element
642628 | isElem ns "w" "p" element
643629 , (c:_) <- findChildrenByName ns "m" "oMathPara" element =
644630 do
645− expsLst <- eitherToD $ readOMML $ T.pack $ showElement c
631+ expsLst <- eitherToD $ readOMML $ showElement c
646632 return $ OMathPara expsLst
647633elemToBodyPart ns element
648634 | isElem ns "w" "p" element
@@ -666,7 +652,7 @@ elemToBodyPart ns element
666652 | isElem ns "w" "tbl" element = do
667653 let caption' = findChildByName ns "w" "tblPr" element
668654 >>= findChildByName ns "w" "tblCaption"
669− >>= findAttrTextByName ns "w" "val"
655+ >>= findAttrByName ns "w" "val"
670656 caption = fromMaybe "" caption'
671657 grid' = case findChildByName ns "w" "tblGrid" element of
672658 Just g -> elemToTblGrid ns g
@@ -705,8 +691,8 @@ getTitleAndAlt :: NameSpaces -> Element -> (T.Text, T.Text)
705691getTitleAndAlt ns element =
706692 let mbDocPr = findChildByName ns "wp" "inline" element >>=
707693 findChildByName ns "wp" "docPr"
708− title = fromMaybe "" (mbDocPr >>= findAttrTextByName ns "" "title")
709− alt = fromMaybe "" (mbDocPr >>= findAttrTextByName ns "" "descr")
694+ title = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "title")
695+ alt = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "descr")
710696 in (title, alt)
711697
712698elemToParPart :: NameSpaces -> Element -> D ParPart
@@ -718,7 +704,7 @@ elemToParPart ns element
718704 = let (title, alt) = getTitleAndAlt ns drawingElem
719705 a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main"
720706 drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem
721− >>= findAttrTextByName ns "r" "embed"
707+ >>= findAttrByName ns "r" "embed"
722708 in
723709 case drawing of
724710 Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem)
@@ -728,7 +714,7 @@ elemToParPart ns element
728714 | isElem ns "w" "r" element
729715 , Just _ <- findChildByName ns "w" "pict" element =
730716 let drawing = findElement (elemName ns "v" "imagedata") element
731− >>= findAttrTextByName ns "r" "id"
717+ >>= findAttrByName ns "r" "id"
732718 in
733719 case drawing of
734720 -- Todo: check out title and attr for deprecated format.
@@ -797,7 +783,7 @@ elemToParPart ns element
797783 fldCharState <- gets stateFldCharState
798784 case fldCharState of
799785 FldCharOpen -> do
800− info <- eitherToD $ parseFieldInfo $ T.pack $ strContent instrText
786+ info <- eitherToD $ parseFieldInfo $ strContent instrText
801787 modify $ \st -> st{stateFldCharState = FldCharFieldInfo info}
802788 return NullParPart
803789 _ -> return NullParPart
@@ -818,48 +804,48 @@ elemToParPart ns element
818804 return $ ChangedRuns change runs
819805elemToParPart ns element
820806 | isElem ns "w" "bookmarkStart" element
821− , Just bmId <- findAttrTextByName ns "w" "id" element
822− , Just bmName <- findAttrTextByName ns "w" "name" element =
807+ , Just bmId <- findAttrByName ns "w" "id" element
808+ , Just bmName <- findAttrByName ns "w" "name" element =
823809 return $ BookMark bmId bmName
824810elemToParPart ns element
825811 | isElem ns "w" "hyperlink" element
826− , Just relId <- findAttrTextByName ns "r" "id" element = do
812+ , Just relId <- findAttrByName ns "r" "id" element = do
827813 location <- asks envLocation
828814 runs <- mapD (elemToRun ns) (elChildren element)
829815 rels <- asks envRelationships
830816 case lookupRelationship location relId rels of
831817 Just target ->
832− case findAttrTextByName ns "w" "anchor" element of
818+ case findAttrByName ns "w" "anchor" element of
833819 Just anchor -> return $ ExternalHyperLink (target <> "#" <> anchor) runs
834820 Nothing -> return $ ExternalHyperLink target runs
835821 Nothing -> return $ ExternalHyperLink "" runs
836822elemToParPart ns element
837823 | isElem ns "w" "hyperlink" element
838− , Just anchor <- findAttrTextByName ns "w" "anchor" element = do
824+ , Just anchor <- findAttrByName ns "w" "anchor" element = do
839825 runs <- mapD (elemToRun ns) (elChildren element)
840826 return $ InternalHyperLink anchor runs
841827elemToParPart ns element
842828 | isElem ns "w" "commentRangeStart" element
843− , Just cmtId <- findAttrTextByName ns "w" "id" element = do
829+ , Just cmtId <- findAttrByName ns "w" "id" element = do
844830 (Comments _ commentMap) <- asks envComments
845831 case M.lookup cmtId commentMap of
846832 Just cmtElem -> elemToCommentStart ns cmtElem
847833 Nothing -> throwError WrongElem
848834elemToParPart ns element
849835 | isElem ns "w" "commentRangeEnd" element
850− , Just cmtId <- findAttrTextByName ns "w" "id" element =
836+ , Just cmtId <- findAttrByName ns "w" "id" element =
851837 return $ CommentEnd cmtId
852838elemToParPart ns element
853839 | isElem ns "m" "oMath" element =
854− fmap PlainOMath (eitherToD $ readOMML $ T.pack $ showElement element)
840+ fmap PlainOMath (eitherToD $ readOMML $ showElement element)
855841elemToParPart _ _ = throwError WrongElem
856842
857843elemToCommentStart :: NameSpaces -> Element -> D ParPart
858844elemToCommentStart ns element
859845 | isElem ns "w" "comment" element
860− , Just cmtId <- findAttrTextByName ns "w" "id" element
861− , Just cmtAuthor <- findAttrTextByName ns "w" "author" element
862− , cmtDate <- findAttrTextByName ns "w" "date" element = do
846+ , Just cmtId <- findAttrByName ns "w" "id" element
847+ , Just cmtAuthor <- findAttrByName ns "w" "author" element
848+ , cmtDate <- findAttrByName ns "w" "date" element = do
863849 bps <- mapD (elemToBodyPart ns) (elChildren element)
864850 return $ CommentStart cmtId cmtAuthor cmtDate bps
865851elemToCommentStart _ _ = throwError WrongElem
@@ -878,7 +864,7 @@ elemToExtent drawingElem =
878864 where
879865 wp_ns = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
880866 getDim at = findElement (QName "extent" (Just wp_ns) (Just "wp")) drawingElem
881− >>= findAttr (QName at Nothing Nothing) >>= safeRead . T.pack
867+ >>= findAttr (QName at Nothing Nothing) >>= safeRead
882868
883869
884870childElemToRun :: NameSpaces -> Element -> D Run
@@ -889,7 +875,7 @@ childElemToRun ns element
889875 = let (title, alt) = getTitleAndAlt ns element
890876 a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main"
891877 drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem
892− >>= findAttrText (QName "embed" (lookup "r" ns) (Just "r"))
878+ >>= findAttr (QName "embed" (lookup "r" ns) (Just "r"))
893879 in
894880 case drawing of
895881 Just s -> expandDrawingId s >>=
@@ -902,7 +888,7 @@ childElemToRun ns element
902888 = return InlineChart
903889childElemToRun ns element
904890 | isElem ns "w" "footnoteReference" element
905− , Just fnId <- findAttrTextByName ns "w" "id" element = do
891+ , Just fnId <- findAttrByName ns "w" "id" element = do
906892 notes <- asks envNotes
907893 case lookupFootnote fnId notes of
908894 Just e -> do bps <- local (\r -> r {envLocation=InFootnote}) $ mapD (elemToBodyPart ns) (elChildren e)
@@ -910,7 +896,7 @@ childElemToRun ns element
910896 Nothing -> return $ Footnote []
911897childElemToRun ns element
912898 | isElem ns "w" "endnoteReference" element
913− , Just enId <- findAttrTextByName ns "w" "id" element = do
899+ , Just enId <- findAttrByName ns "w" "id" element = do
914900 notes <- asks envNotes
915901 case lookupEndnote enId notes of
916902 Just e -> do bps <- local (\r -> r {envLocation=InEndnote}) $ mapD (elemToBodyPart ns) (elChildren e)
@@ -963,15 +949,15 @@ getParStyleField _ _ = Nothing
963949getTrackedChange :: NameSpaces -> Element -> Maybe TrackedChange
964950getTrackedChange ns element
965951 | isElem ns "w" "ins" element || isElem ns "w" "moveTo" element
966− , Just cId <- findAttrTextByName ns "w" "id" element
967− , Just cAuthor <- findAttrTextByName ns "w" "author" element
968− , mcDate <- findAttrTextByName ns "w" "date" element =
952+ , Just cId <- findAttrByName ns "w" "id" element
953+ , Just cAuthor <- findAttrByName ns "w" "author" element
954+ , mcDate <- findAttrByName ns "w" "date" element =
969955 Just $ TrackedChange Insertion (ChangeInfo cId cAuthor mcDate)
970956getTrackedChange ns element
971957 | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element
972− , Just cId <- findAttrTextByName ns "w" "id" element
973− , Just cAuthor <- findAttrTextByName ns "w" "author" element
974− , mcDate <- findAttrTextByName ns "w" "date" element =
958+ , Just cId <- findAttrByName ns "w" "id" element
959+ , Just cAuthor <- findAttrByName ns "w" "author" element
960+ , mcDate <- findAttrByName ns "w" "date" element =
975961 Just $ TrackedChange Deletion (ChangeInfo cId cAuthor mcDate)
976962getTrackedChange _ _ = Nothing
977963
@@ -980,7 +966,7 @@ elemToParagraphStyle ns element sty
980966 | Just pPr <- findChildByName ns "w" "pPr" element =
981967 let style =
982968 mapMaybe
983− (fmap ParaStyleId . findAttrTextByName ns "w" "val")
969+ (fmap ParaStyleId . findAttrByName ns "w" "val")
984970 (findChildrenByName ns "w" "pStyle" pPr)
985971 in ParagraphStyle
986972 {pStyle = mapMaybe (`M.lookup` sty) style
@@ -1012,7 +998,7 @@ elemToRunStyleD ns element
1012998 charStyles <- asks envCharStyles
1013999 let parentSty =
10141000 findChildByName ns "w" "rStyle" rPr >>=
1015− findAttrTextByName ns "w" "val" >>=
1001+ findAttrByName ns "w" "val" >>=
10161002 flip M.lookup charStyles . CharStyleId
10171003 return $ elemToRunStyle ns element parentSty
10181004elemToRunStyleD _ _ = return defaultRunStyle
@@ -1022,7 +1008,7 @@ elemToRunElem ns element
10221008 | isElem ns "w" "t" element
10231009 || isElem ns "w" "delText" element
10241010 || isElem ns "m" "t" element = do
1025− let str = T.pack $ strContent element
1011+ let str = strContent element
10261012 font <- asks envFont
10271013 case font of
10281014 Nothing -> return $ TextRun str
@@ -1044,14 +1030,14 @@ getSymChar :: NameSpaces -> Element -> RunElem
10441030getSymChar ns element
10451031 | Just s <- lowerFromPrivate <$> getCodepoint
10461032 , Just font <- getFont =
1047− case readLitChar ("\\x" ++ s) of
1033+ case readLitChar ("\\x" ++ T.unpack s) of
10481034 [(char, _)] -> TextRun . maybe "" T.singleton $ getUnicode font char
10491035 _ -> TextRun ""
10501036 where
10511037 getCodepoint = findAttrByName ns "w" "char" element
1052− getFont = textToFont . T.pack =<< findAttrByName ns "w" "font" element
1053− lowerFromPrivate ('F':xs) = '0':xs
1054− lowerFromPrivate xs = xs
1038+ getFont = textToFont =<< findAttrByName ns "w" "font" element
1039+ lowerFromPrivate t | "F" `T.isPrefixOf` t = "0" <> T.drop 1 t
1040+ | otherwise = t
10551041getSymChar _ _ = TextRun ""
10561042
10571043elemToRunElems :: NameSpaces -> Element -> D [RunElem]
@@ -1061,8 +1047,9 @@ elemToRunElems ns element
10611047 let qualName = elemName ns "w"
10621048 let font = do
10631049 fontElem <- findElement (qualName "rFonts") element
1064− textToFont . T.pack =<<
1065− foldr ((<|>) . (flip findAttr fontElem . qualName)) Nothing ["ascii", "hAnsi"]
1050+ textToFont =<<
1051+ foldr ((<|>) . (flip findAttr fontElem . qualName))
1052+ Nothing ["ascii", "hAnsi"]
10661053 local (setFont font) (mapD (elemToRunElem ns) (elChildren element))
10671054elemToRunElems _ _ = throwError WrongElem
10681055
src/Text/Pandoc/Readers/Docx/Parse/Styles.hs+17−14
@@ -48,12 +48,13 @@ import Data.Function (on)
4848import Data.String (IsString(..))
4949import qualified Data.Map as M
5050import qualified Data.Text as T
51+import qualified Data.Text.Read
52+import Data.Text (Text)
5153import Data.Maybe
5254import Data.Coerce
5355import Text.Pandoc.Readers.Docx.Util
5456import qualified Text.Pandoc.UTF8 as UTF8
55−import Text.XML.Light
56−import Text.Pandoc.XMLParser (parseXMLElement)
57+import Text.Pandoc.XML.Light
5758
5859newtype CharStyleId = CharStyleId T.Text
5960 deriving (Show, Eq, Ord, IsString, FromStyleId)
@@ -109,7 +110,7 @@ data RunStyle = RunStyle { isBold :: Maybe Bool
109110 , isRTL :: Maybe Bool
110111 , isForceCTL :: Maybe Bool
111112 , rVertAlign :: Maybe VertAlign
112− , rUnderline :: Maybe String
113+ , rUnderline :: Maybe Text
113114 , rParentStyle :: Maybe CharStyle
114115 }
115116 deriving Show
@@ -159,7 +160,7 @@ isBasedOnStyle ns element parentStyle
159160 , Just styleType <- findAttrByName ns "w" "type" element
160161 , styleType == cStyleType parentStyle
161162 , Just basedOnVal <- findChildByName ns "w" "basedOn" element >>=
162− findAttrTextByName ns "w" "val"
163+ findAttrByName ns "w" "val"
163164 , Just ps <- parentStyle = basedOnVal == fromStyleId (getStyleId ps)
164165 | isElem ns "w" "style" element
165166 , Just styleType <- findAttrByName ns "w" "type" element
@@ -169,7 +170,7 @@ isBasedOnStyle ns element parentStyle
169170 | otherwise = False
170171
171172class HasStyleId a => ElemToStyle a where
172− cStyleType :: Maybe a -> String
173+ cStyleType :: Maybe a -> Text
173174 elemToStyle :: NameSpaces -> Element -> Maybe a -> Maybe a
174175
175176class FromStyleId (StyleId a) => HasStyleId a where
@@ -226,8 +227,10 @@ buildBasedOnList ns element rootStyle =
226227 stys -> stys ++
227228 concatMap (buildBasedOnList ns element . Just) stys
228229
229−stringToInteger :: String -> Maybe Integer
230−stringToInteger s = listToMaybe $ map fst (reads s :: [(Integer, String)])
230+stringToInteger :: Text -> Maybe Integer
231+stringToInteger s = case Data.Text.Read.decimal s of
232+ Right (x,_) -> Just x
233+ Left _ -> Nothing
231234
232235checkOnOff :: NameSpaces -> Element -> QName -> Maybe Bool
233236checkOnOff ns rPr tag
@@ -247,7 +250,7 @@ checkOnOff _ _ _ = Nothing
247250elemToCharStyle :: NameSpaces
248251 -> Element -> Maybe CharStyle -> Maybe CharStyle
249252elemToCharStyle ns element parentStyle
250− = CharStyle <$> (CharStyleId <$> findAttrTextByName ns "w" "styleId" element)
253+ = CharStyle <$> (CharStyleId <$> findAttrByName ns "w" "styleId" element)
251254 <*> getElementStyleName ns element
252255 <*> Just (elemToRunStyle ns element parentStyle)
253256
@@ -281,7 +284,7 @@ elemToRunStyle _ _ _ = defaultRunStyle
281284getHeaderLevel :: NameSpaces -> Element -> Maybe (ParaStyleName, Int)
282285getHeaderLevel ns element
283286 | Just styleName <- getElementStyleName ns element
284− , Just n <- stringToInteger . T.unpack =<<
287+ , Just n <- stringToInteger =<<
285288 (T.stripPrefix "heading " . T.toLower $
286289 fromStyleName styleName)
287290 , n > 0 = Just (styleName, fromInteger n)
@@ -289,8 +292,8 @@ getHeaderLevel _ _ = Nothing
289292
290293getElementStyleName :: Coercible T.Text a => NameSpaces -> Element -> Maybe a
291294getElementStyleName ns el = coerce <$>
292− ((findChildByName ns "w" "name" el >>= findAttrTextByName ns "w" "val")
293− <|> findAttrTextByName ns "w" "styleId" el)
295+ ((findChildByName ns "w" "name" el >>= findAttrByName ns "w" "val")
296+ <|> findAttrByName ns "w" "styleId" el)
294297
295298getNumInfo :: NameSpaces -> Element -> Maybe (T.Text, T.Text)
296299getNumInfo ns element = do
@@ -298,15 +301,15 @@ getNumInfo ns element = do
298301 findChildByName ns "w" "numPr"
299302 lvl = fromMaybe "0" (numPr >>=
300303 findChildByName ns "w" "ilvl" >>=
301− findAttrTextByName ns "w" "val")
304+ findAttrByName ns "w" "val")
302305 numId <- numPr >>=
303306 findChildByName ns "w" "numId" >>=
304− findAttrTextByName ns "w" "val"
307+ findAttrByName ns "w" "val"
305308 return (numId, lvl)
306309
307310elemToParStyleData :: NameSpaces -> Element -> Maybe ParStyle -> Maybe ParStyle
308311elemToParStyleData ns element parentStyle
309− | Just styleId <- findAttrTextByName ns "w" "styleId" element
312+ | Just styleId <- findAttrByName ns "w" "styleId" element
310313 , Just styleName <- getElementStyleName ns element
311314 = Just $ ParStyle
312315 {
src/Text/Pandoc/Readers/Docx/Util.hs+11−16
@@ -1,3 +1,4 @@
1+{-# LANGUAGE OverloadedStrings #-}
12{- |
23 Module : Text.Pandoc.Readers.Docx.StyleMaps
34 Copyright : © 2014-2020 Jesse Rosenthal <jrosenthal@jhu.edu>,
@@ -18,51 +19,45 @@ module Text.Pandoc.Readers.Docx.Util (
1819 , elemToNameSpaces
1920 , findChildByName
2021 , findChildrenByName
21− , findAttrText
2222 , findAttrByName
23− , findAttrTextByName
2423 ) where
2524
2625import Data.Maybe (mapMaybe)
2726import qualified Data.Text as T
28−import Text.XML.Light
27+import Data.Text (Text)
28+import Text.Pandoc.XML.Light
2929
30−type NameSpaces = [(String, String)]
30+type NameSpaces = [(Text, Text)]
3131
3232elemToNameSpaces :: Element -> NameSpaces
3333elemToNameSpaces = mapMaybe attrToNSPair . elAttribs
3434
35−attrToNSPair :: Attr -> Maybe (String, String)
35+attrToNSPair :: Attr -> Maybe (Text, Text)
3636attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
3737attrToNSPair _ = Nothing
3838
39−elemName :: NameSpaces -> String -> String -> QName
39+elemName :: NameSpaces -> Text -> Text -> QName
4040elemName ns prefix name =
41− QName name (lookup prefix ns) (if null prefix then Nothing else Just prefix)
41+ QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix)
4242
43−isElem :: NameSpaces -> String -> String -> Element -> Bool
43+isElem :: NameSpaces -> Text -> Text -> Element -> Bool
4444isElem ns prefix name element =
4545 let ns' = ns ++ elemToNameSpaces element
4646 in qName (elName element) == name &&
4747 qURI (elName element) == lookup prefix ns'
4848
49−findChildByName :: NameSpaces -> String -> String -> Element -> Maybe Element
49+findChildByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element
5050findChildByName ns pref name el =
5151 let ns' = ns ++ elemToNameSpaces el
5252 in findChild (elemName ns' pref name) el
5353
54−findChildrenByName :: NameSpaces -> String -> String -> Element -> [Element]
54+findChildrenByName :: NameSpaces -> Text -> Text -> Element -> [Element]
5555findChildrenByName ns pref name el =
5656 let ns' = ns ++ elemToNameSpaces el
5757 in findChildren (elemName ns' pref name) el
5858
59−findAttrText :: QName -> Element -> Maybe T.Text
60−findAttrText x = fmap T.pack . findAttr x
61−
62−findAttrByName :: NameSpaces -> String -> String -> Element -> Maybe String
59+findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text
6360findAttrByName ns pref name el =
6461 let ns' = ns ++ elemToNameSpaces el
6562 in findAttr (elemName ns' pref name) el
6663
67−findAttrTextByName :: NameSpaces -> String -> String -> Element -> Maybe T.Text
68−findAttrTextByName a b c = fmap T.pack . findAttrByName a b c
src/Text/Pandoc/Readers/EPUB.hs+34−31
@@ -23,8 +23,8 @@ import Control.DeepSeq (NFData, deepseq)
2323import Control.Monad (guard, liftM, liftM2, mplus)
2424import Control.Monad.Except (throwError)
2525import qualified Data.ByteString.Lazy as BL (ByteString)
26−import Data.List (isInfixOf)
2726import qualified Data.Text as T
27+import Data.Text (Text)
2828import qualified Data.Map as M (Map, elems, fromList, lookup)
2929import Data.Maybe (mapMaybe)
3030import qualified Data.Text.Lazy as TL
@@ -40,13 +40,12 @@ import Text.Pandoc.Extensions (Extension (Ext_raw_html), enableExtension)
4040import Text.Pandoc.MIME (MimeType)
4141import Text.Pandoc.Options (ReaderOptions (..))
4242import Text.Pandoc.Readers.HTML (readHtml)
43−import Text.Pandoc.Shared (addMetaField, collapseFilePath, escapeURI)
43+import Text.Pandoc.Shared (addMetaField, collapseFilePath, escapeURI, tshow)
4444import qualified Text.Pandoc.UTF8 as UTF8 (toTextLazy)
4545import Text.Pandoc.Walk (query, walk)
46−import Text.XML.Light
47−import Text.Pandoc.XMLParser (parseXMLElement)
46+import Text.Pandoc.XML.Light
4847
49−type Items = M.Map String (FilePath, MimeType)
48+type Items = M.Map Text (FilePath, MimeType)
5049
5150readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc
5251readEPUB opts bytes = case toArchiveOrFail bytes of
@@ -126,26 +125,27 @@ imageToPandoc s = B.doc . B.para $ B.image (T.pack s) "" mempty
126125imageMimes :: [MimeType]
127126imageMimes = ["image/gif", "image/jpeg", "image/png"]
128127
129−type CoverId = String
128+type CoverId = Text
130129
131130type CoverImage = FilePath
132131
133−parseManifest :: (PandocMonad m) => Element -> Maybe CoverId -> m (Maybe CoverImage, Items)
132+parseManifest :: (PandocMonad m)
133+ => Element -> Maybe CoverId -> m (Maybe CoverImage, Items)
134134parseManifest content coverId = do
135135 manifest <- findElementE (dfName "manifest") content
136136 let items = findChildren (dfName "item") manifest
137137 r <- mapM parseItem items
138138 let cover = findAttr (emptyName "href") =<< filterChild findCover manifest
139− return (cover `mplus` coverId, M.fromList r)
139+ return (T.unpack <$> (cover `mplus` coverId), M.fromList r)
140140 where
141− findCover e = maybe False (isInfixOf "cover-image")
141+ findCover e = maybe False (T.isInfixOf "cover-image")
142142 (findAttr (emptyName "properties") e)
143143 || Just True == liftM2 (==) coverId (findAttr (emptyName "id") e)
144144 parseItem e = do
145145 uid <- findAttrE (emptyName "id") e
146146 href <- findAttrE (emptyName "href") e
147147 mime <- findAttrE (emptyName "media-type") e
148− return (uid, (href, T.pack mime))
148+ return (uid, (T.unpack href, mime))
149149
150150parseSpine :: PandocMonad m => Items -> Element -> m [(FilePath, MimeType)]
151151parseSpine is e = do
@@ -173,11 +173,11 @@ parseMeta content = do
173173-- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-metadata-elem
174174parseMetaItem :: Element -> Meta -> Meta
175175parseMetaItem e@(stripNamespace . elName -> field) meta =
176− addMetaField (renameMeta field) (B.str $ T.pack $ strContent e) meta
176+ addMetaField (renameMeta field) (B.str $ strContent e) meta
177177
178−renameMeta :: String -> T.Text
178+renameMeta :: Text -> Text
179179renameMeta "creator" = "author"
180−renameMeta s = T.pack s
180+renameMeta s = s
181181
182182getManifest :: PandocMonad m => Archive -> m (String, Element)
183183getManifest archive = do
@@ -187,7 +187,7 @@ getManifest archive = do
187187 ns <- mkE "xmlns not in namespaces" (lookup "xmlns" namespaces)
188188 as <- fmap (map attrToPair . elAttribs)
189189 (findElementE (QName "rootfile" (Just ns) Nothing) docElem)
190− manifestFile <- mkE "Root not found" (lookup "full-path" as)
190+ manifestFile <- T.unpack <$> mkE "Root not found" (lookup "full-path" as)
191191 let rootdir = dropFileName manifestFile
192192 --mime <- lookup "media-type" as
193193 manifest <- findEntryByPathE manifestFile archive
@@ -201,7 +201,8 @@ fixInternalReferences pathToFile =
201201 . walk (fixBlockIRs filename)
202202 . walk (fixInlineIRs filename)
203203 where
204− (root, T.unpack . escapeURI . T.pack -> filename) = splitFileName pathToFile
204+ (root, T.unpack . escapeURI . T.pack -> filename) =
205+ splitFileName pathToFile
205206
206207fixInlineIRs :: String -> Inline -> Inline
207208fixInlineIRs s (Span as v) =
@@ -214,7 +215,7 @@ fixInlineIRs s (Link as is t) =
214215 Link (fixAttrs s as) is t
215216fixInlineIRs _ v = v
216217
217−prependHash :: [T.Text] -> Inline -> Inline
218+prependHash :: [Text] -> Inline -> Inline
218219prependHash ps l@(Link attr is (url, tit))
219220 | or [s `T.isPrefixOf` url | s <- ps] =
220221 Link attr is ("#" <> url, tit)
@@ -231,16 +232,17 @@ fixBlockIRs s (CodeBlock as code) =
231232fixBlockIRs _ b = b
232233
233234fixAttrs :: FilePath -> B.Attr -> B.Attr
234−fixAttrs s (ident, cs, kvs) = (addHash s ident, filter (not . T.null) cs, removeEPUBAttrs kvs)
235+fixAttrs s (ident, cs, kvs) =
236+ (addHash s ident, filter (not . T.null) cs, removeEPUBAttrs kvs)
235237
236−addHash :: String -> T.Text -> T.Text
238+addHash :: FilePath -> Text -> Text
237239addHash _ "" = ""
238240addHash s ident = T.pack (takeFileName s) <> "#" <> ident
239241
240−removeEPUBAttrs :: [(T.Text, T.Text)] -> [(T.Text, T.Text)]
242+removeEPUBAttrs :: [(Text, Text)] -> [(Text, Text)]
241243removeEPUBAttrs kvs = filter (not . isEPUBAttr) kvs
242244
243−isEPUBAttr :: (T.Text, a) -> Bool
245+isEPUBAttr :: (Text, a) -> Bool
244246isEPUBAttr (k, _) = "epub:" `T.isPrefixOf` k
245247
246248-- Library
@@ -257,33 +259,33 @@ uncurry3 f (a, b, c) = f a b c
257259
258260-- Utility
259261
260−stripNamespace :: QName -> String
262+stripNamespace :: QName -> Text
261263stripNamespace (QName v _ _) = v
262264
263−attrToNSPair :: Attr -> Maybe (String, String)
265+attrToNSPair :: Attr -> Maybe (Text, Text)
264266attrToNSPair (Attr (QName "xmlns" _ _) val) = Just ("xmlns", val)
265267attrToNSPair _ = Nothing
266268
267−attrToPair :: Attr -> (String, String)
269+attrToPair :: Attr -> (Text, Text)
268270attrToPair (Attr (QName name _ _) val) = (name, val)
269271
270−defaultNameSpace :: Maybe String
272+defaultNameSpace :: Maybe Text
271273defaultNameSpace = Just "http://www.idpf.org/2007/opf"
272274
273−dfName :: String -> QName
275+dfName :: Text -> QName
274276dfName s = QName s defaultNameSpace Nothing
275277
276−emptyName :: String -> QName
278+emptyName :: Text -> QName
277279emptyName s = QName s Nothing Nothing
278280
279281-- Convert Maybe interface to Either
280282
281−findAttrE :: PandocMonad m => QName -> Element -> m String
283+findAttrE :: PandocMonad m => QName -> Element -> m Text
282284findAttrE q e = mkE "findAttr" $ findAttr q e
283285
284286findEntryByPathE :: PandocMonad m => FilePath -> Archive -> m Entry
285287findEntryByPathE (normalise . unEscapeString -> path) a =
286− mkE ("No entry on path: " ++ path) $ findEntryByPath path a
288+ mkE ("No entry on path: " <> T.pack path) $ findEntryByPath path a
287289
288290parseXMLDocE :: PandocMonad m => Entry -> m Element
289291parseXMLDocE entry =
@@ -293,7 +295,8 @@ parseXMLDocE entry =
293295 fp = T.pack $ eRelativePath entry
294296
295297findElementE :: PandocMonad m => QName -> Element -> m Element
296−findElementE e x = mkE ("Unable to find element: " ++ show e) $ findElement e x
298+findElementE e x =
299+ mkE ("Unable to find element: " <> tshow e) $ findElement e x
297300
298−mkE :: PandocMonad m => String -> Maybe a -> m a
299−mkE s = maybe (throwError . PandocParseError $ T.pack s) return
301+mkE :: PandocMonad m => Text -> Maybe a -> m a
302+mkE s = maybe (throwError . PandocParseError $ s) return
src/Text/Pandoc/Readers/FB2.hs+47−46
@@ -25,7 +25,6 @@ TODO:
2525module Text.Pandoc.Readers.FB2 ( readFB2 ) where
2626import Control.Monad.Except (throwError)
2727import Control.Monad.State.Strict
28−import Data.ByteString.Lazy.Char8 ( pack )
2928import Data.ByteString.Base64.Lazy
3029import Data.Functor
3130import Data.List (intersperse)
@@ -42,8 +41,8 @@ import Text.Pandoc.Error
4241import Text.Pandoc.Logging
4342import Text.Pandoc.Options
4443import Text.Pandoc.Shared (crFilter)
45−import Text.XML.Light
46−import Text.Pandoc.XMLParser (parseXMLElement)
44+import Text.Pandoc.XML.Light
45+import qualified Text.Pandoc.UTF8 as UTF8
4746
4847type FB2 m = StateT FB2State m
4948
@@ -85,12 +84,12 @@ removeHash t = case T.uncons t of
8584 Just ('#', xs) -> xs
8685 _ -> t
8786
88−convertEntity :: String -> Text
89−convertEntity e = maybe (T.toUpper $ T.pack e) T.pack $ lookupEntity e
87+convertEntity :: Text -> Text
88+convertEntity e = maybe (T.toUpper e) T.pack $ lookupEntity (T.unpack e)
9089
9190parseInline :: PandocMonad m => Content -> FB2 m Inlines
9291parseInline (Elem e) =
93− case T.pack $ qName $ elName e of
92+ case qName $ elName e of
9493 "strong" -> strong <$> parseStyleType e
9594 "emphasis" -> emph <$> parseStyleType e
9695 "style" -> parseNamedStyle e
@@ -98,12 +97,12 @@ parseInline (Elem e) =
9897 "strikethrough" -> strikeout <$> parseStyleType e
9998 "sub" -> subscript <$> parseStyleType e
10099 "sup" -> superscript <$> parseStyleType e
101− "code" -> pure $ code $ T.pack $ strContent e
100+ "code" -> pure $ code $ strContent e
102101 "image" -> parseInlineImageElement e
103102 name -> do
104103 report $ IgnoredElement name
105104 pure mempty
106−parseInline (Text x) = pure $ text $ T.pack $ cdData x
105+parseInline (Text x) = pure $ text $ cdData x
107106parseInline (CRef r) = pure $ str $ convertEntity r
108107
109108parseSubtitle :: PandocMonad m => Element -> FB2 m Blocks
@@ -113,7 +112,7 @@ parseSubtitle e = headerWith ("", ["unnumbered"], []) <$> gets fb2SectionLevel <
113112
114113parseRootElement :: PandocMonad m => Element -> FB2 m Blocks
115114parseRootElement e =
116− case T.pack $ qName $ elName e of
115+ case qName $ elName e of
117116 "FictionBook" -> do
118117 -- Parse notes before parsing the rest of the content.
119118 case filterChild isNotesBody e of
@@ -146,7 +145,7 @@ parseNote e =
146145 Just sectionId -> do
147146 content <- mconcat <$> mapM parseSectionChild (dropTitle $ elChildren e)
148147 oldNotes <- gets fb2Notes
149− modify $ \s -> s { fb2Notes = M.insert ("#" <> T.pack sectionId) content oldNotes }
148+ modify $ \s -> s { fb2Notes = M.insert ("#" <> sectionId) content oldNotes }
150149 pure ()
151150 where
152151 isTitle x = qName (elName x) == "title"
@@ -158,7 +157,7 @@ parseNote e =
158157-- | Parse a child of @\<FictionBook>@ element.
159158parseFictionBookChild :: PandocMonad m => Element -> FB2 m Blocks
160159parseFictionBookChild e =
161− case T.pack $ qName $ elName e of
160+ case qName $ elName e of
162161 "stylesheet" -> pure mempty -- stylesheet is ignored
163162 "description" -> mempty <$ mapM_ parseDescriptionChild (elChildren e)
164163 "body" -> if isNotesBody e
@@ -170,7 +169,7 @@ parseFictionBookChild e =
170169-- | Parse a child of @\<description>@ element.
171170parseDescriptionChild :: PandocMonad m => Element -> FB2 m ()
172171parseDescriptionChild e =
173− case T.pack $ qName $ elName e of
172+ case qName $ elName e of
174173 "title-info" -> mapM_ parseTitleInfoChild (elChildren e)
175174 "src-title-info" -> pure () -- ignore
176175 "document-info" -> pure ()
@@ -184,7 +183,7 @@ parseDescriptionChild e =
184183-- | Parse a child of @\<body>@ element.
185184parseBodyChild :: PandocMonad m => Element -> FB2 m Blocks
186185parseBodyChild e =
187− case T.pack $ qName $ elName e of
186+ case qName $ elName e of
188187 "image" -> parseImageElement e
189188 "title" -> header <$> gets fb2SectionLevel <*> parseTitleType (elContent e)
190189 "epigraph" -> parseEpigraph e
@@ -198,7 +197,10 @@ parseBinaryElement e =
198197 (Nothing, _) -> report $ IgnoredElement "binary without id attribute"
199198 (Just _, Nothing) ->
200199 report $ IgnoredElement "binary without content-type attribute"
201− (Just filename, contentType) -> insertMedia filename (T.pack <$> contentType) (decodeLenient (pack (strContent e)))
200+ (Just filename, contentType) ->
201+ insertMedia (T.unpack filename) contentType
202+ (decodeLenient
203+ (UTF8.fromTextLazy . TL.fromStrict . strContent $ e))
202204
203205-- * Type parsers
204206
@@ -208,13 +210,13 @@ parseAuthor e = T.unwords . catMaybes <$> mapM parseAuthorChild (elChildren e)
208210
209211parseAuthorChild :: PandocMonad m => Element -> FB2 m (Maybe Text)
210212parseAuthorChild e =
211− case T.pack $ qName $ elName e of
212− "first-name" -> pure $ Just $ T.pack $ strContent e
213− "middle-name" -> pure $ Just $ T.pack $ strContent e
214− "last-name" -> pure $ Just $ T.pack $ strContent e
215− "nickname" -> pure $ Just $ T.pack $ strContent e
216− "home-page" -> pure $ Just $ T.pack $ strContent e
217− "email" -> pure $ Just $ T.pack $ strContent e
213+ case qName $ elName e of
214+ "first-name" -> pure $ Just $ strContent e
215+ "middle-name" -> pure $ Just $ strContent e
216+ "last-name" -> pure $ Just $ strContent e
217+ "nickname" -> pure $ Just $ strContent e
218+ "home-page" -> pure $ Just $ strContent e
219+ "email" -> pure $ Just $ strContent e
218220 name -> do
219221 report $ IgnoredElement $ name <> " in author"
220222 pure Nothing
@@ -238,13 +240,13 @@ parseTitleContent _ = pure Nothing
238240parseImageElement :: PandocMonad m => Element -> FB2 m Blocks
239241parseImageElement e =
240242 case href of
241− Just src -> pure $ para $ imageWith (imgId, [], []) (removeHash $ T.pack src) title alt
243+ Just src -> pure $ para $ imageWith (imgId, [], []) (removeHash src) title alt
242244 Nothing -> do
243245 report $ IgnoredElement " image without href"
244246 pure mempty
245− where alt = maybe mempty (str . T.pack) $ findAttr (unqual "alt") e
246− title = maybe "" T.pack $ findAttr (unqual "title") e
247− imgId = maybe "" T.pack $ findAttr (unqual "id") e
247+ where alt = maybe mempty str $ findAttr (unqual "alt") e
248+ title = fromMaybe "" $ findAttr (unqual "title") e
249+ imgId = fromMaybe "" $ findAttr (unqual "id") e
248250 href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e
249251
250252-- | Parse @pType@
@@ -258,7 +260,7 @@ parseCite e = blockQuote . mconcat <$> mapM parseCiteChild (elChildren e)
258260-- | Parse @citeType@ child
259261parseCiteChild :: PandocMonad m => Element -> FB2 m Blocks
260262parseCiteChild e =
261− case T.pack $ qName $ elName e of
263+ case qName $ elName e of
262264 "p" -> para <$> parsePType e
263265 "poem" -> parsePoem e
264266 "empty-line" -> pure horizontalRule
@@ -273,13 +275,13 @@ parsePoem e = mconcat <$> mapM parsePoemChild (elChildren e)
273275
274276parsePoemChild :: PandocMonad m => Element -> FB2 m Blocks
275277parsePoemChild e =
276− case T.pack $ qName $ elName e of
278+ case qName $ elName e of
277279 "title" -> parseTitle e
278280 "subtitle" -> parseSubtitle e
279281 "epigraph" -> parseEpigraph e
280282 "stanza" -> parseStanza e
281283 "text-author" -> para <$> parsePType e
282− "date" -> pure $ para $ text $ T.pack $ strContent e
284+ "date" -> pure $ para $ text $ strContent e
283285 name -> report (UnexpectedXmlElement name "poem") $> mempty
284286
285287parseStanza :: PandocMonad m => Element -> FB2 m Blocks
@@ -292,7 +294,7 @@ joinLineBlocks [] = []
292294
293295parseStanzaChild :: PandocMonad m => Element -> FB2 m Blocks
294296parseStanzaChild e =
295− case T.pack $ qName $ elName e of
297+ case qName $ elName e of
296298 "title" -> parseTitle e
297299 "subtitle" -> parseSubtitle e
298300 "v" -> lineBlock . (:[]) <$> parsePType e
@@ -302,11 +304,11 @@ parseStanzaChild e =
302304parseEpigraph :: PandocMonad m => Element -> FB2 m Blocks
303305parseEpigraph e =
304306 divWith (divId, ["epigraph"], []) . mconcat <$> mapM parseEpigraphChild (elChildren e)
305− where divId = maybe "" T.pack $ findAttr (unqual "id") e
307+ where divId = fromMaybe "" $ findAttr (unqual "id") e
306308
307309parseEpigraphChild :: PandocMonad m => Element -> FB2 m Blocks
308310parseEpigraphChild e =
309− case T.pack $ qName $ elName e of
311+ case qName $ elName e of
310312 "p" -> para <$> parsePType e
311313 "poem" -> parsePoem e
312314 "cite" -> parseCite e
@@ -320,7 +322,7 @@ parseAnnotation e = mconcat <$> mapM parseAnnotationChild (elChildren e)
320322
321323parseAnnotationChild :: PandocMonad m => Element -> FB2 m Blocks
322324parseAnnotationChild e =
323− case T.pack $ qName $ elName e of
325+ case qName $ elName e of
324326 "p" -> para <$> parsePType e
325327 "poem" -> parsePoem e
326328 "cite" -> parseCite e
@@ -334,14 +336,14 @@ parseSection :: PandocMonad m => Element -> FB2 m Blocks
334336parseSection e = do
335337 n <- gets fb2SectionLevel
336338 modify $ \st -> st{ fb2SectionLevel = n + 1 }
337− let sectionId = maybe "" T.pack $ findAttr (unqual "id") e
339+ let sectionId = fromMaybe "" $ findAttr (unqual "id") e
338340 bs <- divWith (sectionId, ["section"], []) . mconcat <$> mapM parseSectionChild (elChildren e)
339341 modify $ \st -> st{ fb2SectionLevel = n }
340342 pure bs
341343
342344parseSectionChild :: PandocMonad m => Element -> FB2 m Blocks
343345parseSectionChild e =
344− case T.pack $ qName $ elName e of
346+ case qName $ elName e of
345347 "title" -> parseBodyChild e
346348 "epigraph" -> parseEpigraph e
347349 "image" -> parseImageElement e
@@ -363,16 +365,16 @@ parseStyleType e = mconcat <$> mapM parseInline (elContent e)
363365parseNamedStyle :: PandocMonad m => Element -> FB2 m Inlines
364366parseNamedStyle e = do
365367 content <- mconcat <$> mapM parseNamedStyleChild (elContent e)
366− let lang = maybeToList $ ("lang",) . T.pack <$> findAttr (QName "lang" Nothing (Just "xml")) e
368+ let lang = maybeToList $ ("lang",) <$> findAttr (QName "lang" Nothing (Just "xml")) e
367369 case findAttr (unqual "name") e of
368− Just name -> pure $ spanWith ("", [T.pack name], lang) content
370+ Just name -> pure $ spanWith ("", [name], lang) content
369371 Nothing -> do
370372 report $ IgnoredElement "link without required name"
371373 pure mempty
372374
373375parseNamedStyleChild :: PandocMonad m => Content -> FB2 m Inlines
374376parseNamedStyleChild (Elem e) =
375− case T.pack $ qName (elName e) of
377+ case qName (elName e) of
376378 "strong" -> strong <$> parseStyleType e
377379 "emphasis" -> emph <$> parseStyleType e
378380 "style" -> parseNamedStyle e
@@ -380,7 +382,7 @@ parseNamedStyleChild (Elem e) =
380382 "strikethrough" -> strikeout <$> parseStyleType e
381383 "sub" -> subscript <$> parseStyleType e
382384 "sup" -> superscript <$> parseStyleType e
383− "code" -> pure $ code $ T.pack $ strContent e
385+ "code" -> pure $ code $ strContent e
384386 "image" -> parseInlineImageElement e
385387 name -> do
386388 report $ IgnoredElement $ name <> " in style"
@@ -392,7 +394,7 @@ parseLinkType :: PandocMonad m => Element -> FB2 m Inlines
392394parseLinkType e = do
393395 content <- mconcat <$> mapM parseStyleLinkType (elContent e)
394396 notes <- gets fb2Notes
395− case T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of
397+ case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of
396398 Just href -> case findAttr (QName "type" Nothing Nothing) e of
397399 Just "note" -> case M.lookup href notes of
398400 Nothing -> pure $ link href "" content
@@ -419,15 +421,14 @@ parseTable _ = pure mempty -- TODO: tables are not supported yet
419421-- | Parse @title-infoType@
420422parseTitleInfoChild :: PandocMonad m => Element -> FB2 m ()
421423parseTitleInfoChild e =
422− case T.pack $ qName (elName e) of
424+ case qName (elName e) of
423425 "genre" -> pure ()
424426 "author" -> parseAuthor e >>= \author -> modify (\st -> st {fb2Authors = author:fb2Authors st})
425− "book-title" -> modify (setMeta "title" (text $ T.pack $ strContent e))
427+ "book-title" -> modify (setMeta "title" (text $ strContent e))
426428 "annotation" -> parseAnnotation e >>= modify . setMeta "abstract"
427429 "keywords" -> modify (setMeta "keywords" (map (MetaString . trim) $ T.splitOn ","
428− $ T.pack
429430 $ strContent e))
430− "date" -> modify (setMeta "date" (text $ T.pack $ strContent e))
431+ "date" -> modify (setMeta "date" (text $ strContent e))
431432 "coverpage" -> parseCoverPage e
432433 "lang" -> pure ()
433434 "src-lang" -> pure ()
@@ -441,7 +442,7 @@ parseCoverPage e =
441442 Just img -> case href of
442443 Just src -> modify (setMeta "cover-image" (MetaString $ removeHash src))
443444 Nothing -> pure ()
444− where href = T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) img
445+ where href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) img
445446 Nothing -> pure ()
446447
447448-- | Parse @inlineImageType@ element
@@ -454,5 +455,5 @@ parseInlineImageElement e =
454455 Nothing -> do
455456 report $ IgnoredElement "inline image without href"
456457 pure mempty
457− where alt = maybe mempty (str . T.pack) $ findAttr (unqual "alt") e
458− href = T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e
458+ where alt = maybe mempty str $ findAttr (unqual "alt") e
459+ href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e
src/Text/Pandoc/Readers/JATS.hs+25−33
@@ -16,7 +16,7 @@ module Text.Pandoc.Readers.JATS ( readJATS ) where
1616import Control.Monad.State.Strict
1717import Control.Monad.Except (throwError)
1818import Text.Pandoc.Error (PandocError(..))
19−import Data.Char (isDigit, isSpace, toUpper)
19+import Data.Char (isDigit, isSpace)
2020import Data.Default
2121import Data.Generics
2222import Data.List (foldl', intersperse)
@@ -31,8 +31,7 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad)
3131import Text.Pandoc.Options
3232import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces)
3333import Text.TeXMath (readMathML, writeTeX)
34−import Text.XML.Light
35−import Text.Pandoc.XMLParser (parseXMLContents)
34+import Text.Pandoc.XML.Light
3635import qualified Data.Set as S (fromList, member)
3736import Data.Set ((\\))
3837
@@ -67,29 +66,29 @@ normalizeTree = everywhere (mkT go)
6766 where go :: [Content] -> [Content]
6867 go (Text (CData CDataRaw _ _):xs) = xs
6968 go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) =
70− Text (CData CDataText (s1 ++ s2) z):xs
69+ Text (CData CDataText (s1 <> s2) z):xs
7170 go (Text (CData CDataText s1 z):CRef r:xs) =
72− Text (CData CDataText (s1 ++ convertEntity r) z):xs
71+ Text (CData CDataText (s1 <> convertEntity r) z):xs
7372 go (CRef r:Text (CData CDataText s1 z):xs) =
74− Text (CData CDataText (convertEntity r ++ s1) z):xs
73+ Text (CData CDataText (convertEntity r <> s1) z):xs
7574 go (CRef r1:CRef r2:xs) =
76− Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs
75+ Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs
7776 go xs = xs
7877
79−convertEntity :: String -> String
80−convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e)
78+convertEntity :: Text -> Text
79+convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity $ T.unpack e)
8180
8281-- convenience function to get an attribute value, defaulting to ""
83−attrValue :: String -> Element -> Text
82+attrValue :: Text -> Element -> Text
8483attrValue attr =
8584 fromMaybe "" . maybeAttrValue attr
8685
87−maybeAttrValue :: String -> Element -> Maybe Text
86+maybeAttrValue :: Text -> Element -> Maybe Text
8887maybeAttrValue attr elt =
89− T.pack <$> lookupAttrBy (\x -> qName x == attr) (elAttribs elt)
88+ lookupAttrBy (\x -> qName x == attr) (elAttribs elt)
9089
9190-- convenience function
92−named :: String -> Element -> Bool
91+named :: Text -> Element -> Bool
9392named s e = qName (elName e) == s
9493
9594--
@@ -155,10 +154,10 @@ getBlocks e = mconcat <$>
155154
156155parseBlock :: PandocMonad m => Content -> JATS m Blocks
157156parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE
158−parseBlock (Text (CData _ s _)) = if all isSpace s
157+parseBlock (Text (CData _ s _)) = if T.all isSpace s
159158 then return mempty
160− else return $ plain $ trimInlines $ text $ T.pack s
161−parseBlock (CRef x) = return $ plain $ str $ T.toUpper $ T.pack x
159+ else return $ plain $ trimInlines $ text s
160+parseBlock (CRef x) = return $ plain $ str $ T.toUpper x
162161parseBlock (Elem e) =
163162 case qName (elName e) of
164163 "p" -> parseMixed para (elContent e)
@@ -207,7 +206,7 @@ parseBlock (Elem e) =
207206 "" -> []
208207 x -> [x]
209208 return $ codeBlockWith (attrValue "id" e, classes', [])
210− $ trimNl $ textContentRecursive e
209+ $ trimNl $ strContentRecursive e
211210 parseBlockquote = do
212211 attrib <- case filterChild (named "attribution") e of
213212 Nothing -> return mempty
@@ -271,7 +270,7 @@ parseBlock (Elem e) =
271270 Just "center" -> AlignCenter
272271 _ -> AlignDefault
273272 let toWidth c = do
274− w <- findAttrText (unqual "colwidth") c
273+ w <- findAttr (unqual "colwidth") c
275274 n <- safeRead $ "0" <> T.filter (\x -> isDigit x || x == '.') w
276275 if n > 0 then Just n else Nothing
277276 let numrows = foldl' max 0 $ map length bodyrows
@@ -442,16 +441,10 @@ parseRef e = do
442441 Nothing -> return $ Map.insert "id" (toMetaValue refId) mempty
443442 -- TODO handle mixed-citation
444443
445−findAttrText :: QName -> Element -> Maybe Text
446−findAttrText x = fmap T.pack . findAttr x
447−
448444textContent :: Element -> Text
449−textContent = T.pack . strContent
450−
451−textContentRecursive :: Element -> Text
452−textContentRecursive = T.pack . strContentRecursive
445+textContent = strContent
453446
454−strContentRecursive :: Element -> String
447+strContentRecursive :: Element -> Text
455448strContentRecursive = strContent .
456449 (\e' -> e'{ elContent = map elementToStr $ elContent e' })
457450
@@ -460,9 +453,8 @@ elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing
460453elementToStr x = x
461454
462455parseInline :: PandocMonad m => Content -> JATS m Inlines
463−parseInline (Text (CData _ s _)) = return $ text $ T.pack s
464−parseInline (CRef ref) =
465− return . text . maybe (T.toUpper $ T.pack ref) T.pack $ lookupEntity ref
456+parseInline (Text (CData _ s _)) = return $ text s
457+parseInline (CRef ref) = return . text . convertEntity $ ref
466458parseInline (Elem e) =
467459 case qName (elName e) of
468460 "italic" -> innerInlines emph
@@ -507,9 +499,9 @@ parseInline (Elem e) =
507499 else linkWith attr ("#" <> rid) "" ils
508500 "ext-link" -> do
509501 ils <- innerInlines id
510− let title = fromMaybe "" $ findAttrText (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e
502+ let title = fromMaybe "" $ findAttr (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e
511503 let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of
512− Just h -> T.pack h
504+ Just h -> h
513505 _ -> "#" <> attrValue "rid" e
514506 let ils' = if ils == mempty then str href else ils
515507 let attr = (attrValue "id" e, [], [])
@@ -529,7 +521,7 @@ parseInline (Elem e) =
529521 where innerInlines f = extractSpaces f . mconcat <$>
530522 mapM parseInline (elContent e)
531523 mathML x =
532− case readMathML . T.pack . showElement $ everywhere (mkT removePrefix) x of
524+ case readMathML . showElement $ everywhere (mkT removePrefix) x of
533525 Left _ -> mempty
534526 Right m -> writeTeX m
535527 formula constructor = do
@@ -547,4 +539,4 @@ parseInline (Elem e) =
547539 let classes' = case attrValue "language" e of
548540 "" -> []
549541 l -> [l]
550− return $ codeWith (attrValue "id" e,classes',[]) $ textContentRecursive e
542+ return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e
src/Text/Pandoc/Readers/OPML.hs+12−17
@@ -13,7 +13,6 @@ Conversion of OPML to 'Pandoc' document.
1313
1414module Text.Pandoc.Readers.OPML ( readOPML ) where
1515import Control.Monad.State.Strict
16−import Data.Char (toUpper)
1716import Data.Default
1817import Data.Generics
1918import Data.Maybe (fromMaybe)
@@ -28,8 +27,7 @@ import Text.Pandoc.Error (PandocError(..))
2827import Text.Pandoc.Readers.HTML (readHtml)
2928import Text.Pandoc.Readers.Markdown (readMarkdown)
3029import Text.Pandoc.Shared (crFilter, blocksToInlines')
31−import Text.XML.Light
32−import Text.Pandoc.XMLParser (parseXMLContents)
30+import Text.Pandoc.XML.Light
3331import Control.Monad.Except (throwError)
3432
3533type OPML m = StateT OPMLState m
@@ -69,25 +67,22 @@ normalizeTree = everywhere (mkT go)
6967 where go :: [Content] -> [Content]
7068 go (Text (CData CDataRaw _ _):xs) = xs
7169 go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) =
72− Text (CData CDataText (s1 ++ s2) z):xs
70+ Text (CData CDataText (s1 <> s2) z):xs
7371 go (Text (CData CDataText s1 z):CRef r:xs) =
74− Text (CData CDataText (s1 ++ convertEntity r) z):xs
72+ Text (CData CDataText (s1 <> convertEntity r) z):xs
7573 go (CRef r:Text (CData CDataText s1 z):xs) =
76− Text (CData CDataText (convertEntity r ++ s1) z):xs
74+ Text (CData CDataText (convertEntity r <> s1) z):xs
7775 go (CRef r1:CRef r2:xs) =
78− Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs
76+ Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs
7977 go xs = xs
8078
81−convertEntity :: String -> String
82−convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e)
79+convertEntity :: Text -> Text
80+convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity (T.unpack e))
8381
8482-- convenience function to get an attribute value, defaulting to ""
85−attrValue :: String -> Element -> Text
83+attrValue :: Text -> Element -> Text
8684attrValue attr elt =
87− maybe "" T.pack (lookupAttrBy (\x -> qName x == attr) (elAttribs elt))
88−
89−textContent :: Element -> Text
90−textContent = T.pack . strContent
85+ fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt))
9186
9287-- exceptT :: PandocMonad m => Either PandocError a -> OPML m a
9388-- exceptT = either throwError return
@@ -111,11 +106,11 @@ parseBlock :: PandocMonad m => Content -> OPML m Blocks
111106parseBlock (Elem e) =
112107 case qName (elName e) of
113108 "ownerName" -> mempty <$ modify (\st ->
114− st{opmlDocAuthors = [text $ textContent e]})
109+ st{opmlDocAuthors = [text $ strContent e]})
115110 "dateModified" -> mempty <$ modify (\st ->
116− st{opmlDocDate = text $ textContent e})
111+ st{opmlDocDate = text $ strContent e})
117112 "title" -> mempty <$ modify (\st ->
118− st{opmlDocTitle = text $ textContent e})
113+ st{opmlDocTitle = text $ strContent e})
119114 "outline" -> gets opmlSectionLevel >>= sect . (+1)
120115 "?xml" -> return mempty
121116 _ -> getBlocks e
src/Text/Pandoc/Readers/Odt.hs+2−3
@@ -14,8 +14,7 @@ Entry point to the odt reader.
1414module Text.Pandoc.Readers.Odt ( readOdt ) where
1515
1616import Codec.Archive.Zip
17−import qualified Text.XML.Light as XML
18−import Text.Pandoc.XMLParser (parseXMLElement)
17+import Text.Pandoc.XML.Light
1918
2019import qualified Data.ByteString.Lazy as B
2120
@@ -91,7 +90,7 @@ archiveToOdt archive = do
9190
9291
9392--
94−entryToXmlElem :: Entry -> Either PandocError XML.Element
93+entryToXmlElem :: Entry -> Either PandocError Element
9594entryToXmlElem entry =
9695 case parseXMLElement . UTF8.toTextLazy . fromEntry $ entry of
9796 Right x -> Right x
src/Text/Pandoc/Readers/Odt/ContentReader.hs+7−6
@@ -29,14 +29,14 @@ import Control.Monad ((<=<))
2929
3030import qualified Data.ByteString.Lazy as B
3131import Data.Foldable (fold)
32−import Data.List (find, stripPrefix)
32+import Data.List (find)
3333import qualified Data.Map as M
3434import qualified Data.Text as T
3535import Data.Maybe
3636import Data.Semigroup (First(..), Option(..))
3737
3838import Text.TeXMath (readMathML, writeTeX)
39−import qualified Text.XML.Light as XML
39+import qualified Text.Pandoc.XML.Light as XML
4040
4141import Text.Pandoc.Builder hiding (underline)
4242import Text.Pandoc.MediaBag (MediaBag, insertMedia)
@@ -557,7 +557,7 @@ read_plain_text = fst ^&&& read_plain_text' >>% recover
557557 >>?% mappend
558558 --
559559 extractText :: XML.Content -> Fallible T.Text
560− extractText (XML.Text cData) = succeedWith (T.pack $ XML.cdData cData)
560+ extractText (XML.Text cData) = succeedWith (XML.cdData cData)
561561 extractText _ = failEmpty
562562
563563read_text_seq :: InlineMatcher
@@ -777,14 +777,14 @@ read_frame_img =
777777 "" -> returnV mempty -< ()
778778 src' -> do
779779 let exts = extensionsFromList [Ext_auto_identifiers]
780− resource <- lookupResource -< src'
780+ resource <- lookupResource -< T.unpack src'
781781 _ <- updateMediaWithResource -< resource
782782 w <- findAttrText' NsSVG "width" -< ()
783783 h <- findAttrText' NsSVG "height" -< ()
784784 titleNodes <- matchChildContent' [ read_frame_title ] -< ()
785785 alt <- matchChildContent [] read_plain_text -< ()
786786 arr (firstMatch . uncurry4 imageWith) -<
787− (image_attributes w h, T.pack src', inlineListToIdentifier exts (toList titleNodes), alt)
787+ (image_attributes w h, src', inlineListToIdentifier exts (toList titleNodes), alt)
788788
789789read_frame_title :: InlineMatcher
790790read_frame_title = matchingElement NsSVG "title" (matchChildContent [] read_plain_text)
@@ -804,7 +804,8 @@ read_frame_mathml =
804804 case fold src of
805805 "" -> returnV mempty -< ()
806806 src' -> do
807− let path = fromMaybe src' (stripPrefix "./" src') ++ "/content.xml"
807+ let path = T.unpack $
808+ fromMaybe src' (T.stripPrefix "./" src') <> "/content.xml"
808809 (_, mathml) <- lookupResource -< path
809810 case readMathML (UTF8.toText $ B.toStrict mathml) of
810811 Left _ -> returnV mempty -< ()
src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs+2−1
@@ -14,9 +14,10 @@ typesafe Haskell namespace identifiers and unsafe "real world" namespaces.
1414module Text.Pandoc.Readers.Odt.Generic.Namespaces where
1515
1616import qualified Data.Map as M
17+import Data.Text (Text)
1718
1819--
19−type NameSpaceIRI = String
20+type NameSpaceIRI = Text
2021
2122--
2223type NameSpaceIRIs nsID = M.Map nsID NameSpaceIRI
src/Text/Pandoc/Readers/Odt/Generic/Utils.hs+9−24
@@ -20,7 +20,6 @@ module Text.Pandoc.Readers.Odt.Generic.Utils
2020, reverseComposition
2121, tryToRead
2222, Lookupable(..)
23−, readLookupables
2423, readLookupable
2524, readPercent
2625, findBy
@@ -30,11 +29,11 @@ module Text.Pandoc.Readers.Odt.Generic.Utils
3029
3130import Control.Category (Category, (<<<), (>>>))
3231import qualified Control.Category as Cat (id)
33−import Control.Monad (msum)
34−
32+import Data.Char (isSpace)
3533import qualified Data.Foldable as F (Foldable, foldr)
3634import Data.Maybe
37−
35+import Data.Text (Text)
36+import qualified Data.Text as T
3837
3938-- | Equivalent to
4039-- > foldr (.) id
@@ -76,8 +75,8 @@ swing = flip.(.flip id)
7675-- (nobody wants that) while the latter returns "to much" for simple purposes.
7776-- This function instead applies 'reads' and returns the first match (if any)
7877-- in a 'Maybe'.
79−tryToRead :: (Read r) => String -> Maybe r
80−tryToRead = reads >>> listToMaybe >>> fmap fst
78+tryToRead :: (Read r) => Text -> Maybe r
79+tryToRead = (reads . T.unpack) >>> listToMaybe >>> fmap fst
8180
8281-- | A version of 'reads' that requires a '%' sign after the number
8382readPercent :: ReadS Int
@@ -88,26 +87,12 @@ readPercent s = [ (i,s') | (i , r ) <- reads s
8887-- | Data that can be looked up.
8988-- This is mostly a utility to read data with kind *.
9089class Lookupable a where
91− lookupTable :: [(String, a)]
92−
93−-- | The idea is to use this function as if there was a declaration like
94−--
95−-- > instance (Lookupable a) => (Read a) where
96−-- > readsPrec _ = readLookupables
97−-- .
98−-- But including this code in this form would need UndecideableInstances.
99−-- That is a bad idea. Luckily 'readLookupable' (without the s at the end)
100−-- can be used directly in almost any case.
101−readLookupables :: (Lookupable a) => String -> [(a,String)]
102−readLookupables s = [ (a,rest) | (word,rest) <- lex s,
103− a <- maybeToList (lookup word lookupTable)
104− ]
90+ lookupTable :: [(Text, a)]
10591
10692-- | Very similar to a simple 'lookup' in the 'lookupTable', but with a lexer.
107−readLookupable :: (Lookupable a) => String -> Maybe a
108−readLookupable s = msum
109− $ map ((`lookup` lookupTable).fst)
110− $ lex s
93+readLookupable :: (Lookupable a) => Text -> Maybe a
94+readLookupable s =
95+ lookup (T.takeWhile (not . isSpace) $ T.dropWhile isSpace s) lookupTable
11196
11297uncurry3 :: (a->b->c -> z) -> (a,b,c ) -> z
11398uncurry4 :: (a->b->c->d -> z) -> (a,b,c,d ) -> z
src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs+11−12
@@ -1,3 +1,4 @@
1+{-# LANGUAGE OverloadedStrings #-}
12{-# LANGUAGE TupleSections #-}
23{-# LANGUAGE GADTs #-}
34{-# LANGUAGE LambdaCase #-}
@@ -60,11 +61,11 @@ import Control.Arrow
6061import Data.Bool ( bool )
6162import Data.Either ( rights )
6263import qualified Data.Map as M
63−import qualified Data.Text as T
64+import Data.Text (Text)
6465import Data.Default
6566import Data.Maybe
6667
67−import qualified Text.XML.Light as XML
68+import qualified Text.Pandoc.XML.Light as XML
6869
6970import Text.Pandoc.Readers.Odt.Arrows.State
7071import Text.Pandoc.Readers.Odt.Arrows.Utils
@@ -78,13 +79,13 @@ import Text.Pandoc.Readers.Odt.Generic.Fallible
7879--------------------------------------------------------------------------------
7980
8081--
81−type ElementName = String
82−type AttributeName = String
83−type AttributeValue = String
84−type TextAttributeValue = T.Text
82+type ElementName = Text
83+type AttributeName = Text
84+type AttributeValue = Text
85+type TextAttributeValue = Text
8586
8687--
87−type NameSpacePrefix = String
88+type NameSpacePrefix = Text
8889
8990--
9091type NameSpacePrefixes nsID = M.Map nsID NameSpacePrefix
@@ -461,7 +462,7 @@ lookupDefaultingAttr :: (NameSpaceID nsID, Lookupable a, Default a)
461462lookupDefaultingAttr nsID attrName
462463 = lookupAttrWithDefault nsID attrName def
463464
464−-- | Return value as a (Maybe String)
465+-- | Return value as a (Maybe Text)
465466findAttr' :: (NameSpaceID nsID)
466467 => nsID -> AttributeName
467468 -> XMLConverter nsID extraState x (Maybe AttributeValue)
@@ -477,7 +478,6 @@ findAttrText' nsID attrName
477478 = qualifyName nsID attrName
478479 &&& getCurrentElement
479480 >>% XML.findAttr
480− >>^ fmap T.pack
481481
482482-- | Return value as string or fail
483483findAttr :: (NameSpaceID nsID)
@@ -492,7 +492,6 @@ findAttrText :: (NameSpaceID nsID)
492492 -> FallibleXMLConverter nsID extraState x TextAttributeValue
493493findAttrText nsID attrName
494494 = findAttr' nsID attrName
495− >>^ fmap T.pack
496495 >>> maybeToChoice
497496
498497-- | Return value as string or return provided default value
@@ -511,7 +510,7 @@ findAttrTextWithDefault :: (NameSpaceID nsID)
511510 -> XMLConverter nsID extraState x TextAttributeValue
512511findAttrTextWithDefault nsID attrName deflt
513512 = findAttr' nsID attrName
514− >>^ maybe deflt T.pack
513+ >>^ fromMaybe deflt
515514
516515-- | Read and return value or fail
517516readAttr :: (NameSpaceID nsID, Read attrValue)
@@ -748,7 +747,7 @@ matchContent lookups fallback
748747-- Internals
749748--------------------------------------------------------------------------------
750749
751−stringToBool' :: String -> Maybe Bool
750+stringToBool' :: Text -> Maybe Bool
752751stringToBool' val | val `elem` trueValues = Just True
753752 | val `elem` falseValues = Just False
754753 | otherwise = Nothing
src/Text/Pandoc/Readers/Odt/Namespaces.hs+6−5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE OverloadedStrings #-}
12{- |
23 Module : Text.Pandoc.Reader.Odt.Namespaces
34 Copyright : Copyright (C) 2015 Martin Linnemann
@@ -13,10 +14,10 @@ Namespaces used in odt files.
1314module Text.Pandoc.Readers.Odt.Namespaces ( Namespace (..)
1415 ) where
1516
16−import Data.List (isPrefixOf)
1717import qualified Data.Map as M (empty, insert)
1818import Data.Maybe (fromMaybe, listToMaybe)
19−
19+import Data.Text (Text)
20+import qualified Data.Text as T
2021import Text.Pandoc.Readers.Odt.Generic.Namespaces
2122
2223
@@ -30,7 +31,7 @@ instance NameSpaceID Namespace where
3031
3132
3233findID :: NameSpaceIRI -> Maybe Namespace
33−findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `isPrefixOf` iri]
34+findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `T.isPrefixOf` iri]
3435
3536nsIDmap :: NameSpaceIRIs Namespace
3637nsIDmap = foldr (uncurry $ flip M.insert) M.empty nsIDs
@@ -54,12 +55,12 @@ data Namespace = -- Open Document core
5455 -- Core XML (basically only for the 'id'-attribute)
5556 | NsXML
5657 -- Fallback
57− | NsOther String
58+ | NsOther Text
5859 deriving ( Eq, Ord, Show )
5960
6061-- | Not the actual iri's, but large prefixes of them - this way there are
6162-- less versioning problems and the like.
62−nsIDs :: [(String,Namespace)]
63+nsIDs :: [(Text, Namespace)]
6364nsIDs = [
6465 ("urn:oasis:names:tc:opendocument:xmlns:animation" , NsAnim ),
6566 ("urn:oasis:names:tc:opendocument:xmlns:chart" , NsChart ),
src/Text/Pandoc/Readers/Odt/StyleReader.hs+13−10
@@ -2,6 +2,7 @@
22{-# LANGUAGE Arrows #-}
33{-# LANGUAGE RecordWildCards #-}
44{-# LANGUAGE TupleSections #-}
5+{-# LANGUAGE OverloadedStrings #-}
56{- |
67 Module : Text.Pandoc.Readers.Odt.StyleReader
78 Copyright : Copyright (C) 2015 Martin Linnemann
@@ -46,11 +47,13 @@ import qualified Data.Foldable as F
4647import Data.List (unfoldr)
4748import qualified Data.Map as M
4849import Data.Maybe
50+import Data.Text (Text)
51+import qualified Data.Text as T
4952import qualified Data.Set as S
5053
51−import qualified Text.XML.Light as XML
54+import qualified Text.Pandoc.XML.Light as XML
5255
53−import Text.Pandoc.Shared (safeRead)
56+import Text.Pandoc.Shared (safeRead, tshow)
5457
5558import Text.Pandoc.Readers.Odt.Arrows.Utils
5659
@@ -90,7 +93,7 @@ instance Default FontPitch where
9093--
9194-- Thus, we want
9295
93−type FontFaceName = String
96+type FontFaceName = Text
9497
9598type FontPitches = M.Map FontFaceName FontPitch
9699
@@ -151,7 +154,7 @@ findPitch = ( lookupAttr NsStyle "font-pitch"
151154-- Definitions of main data
152155--------------------------------------------------------------------------------
153156
154−type StyleName = String
157+type StyleName = Text
155158
156159-- | There are two types of styles: named styles with a style family and an
157160-- optional style parent, and default styles for each style family,
@@ -355,8 +358,8 @@ getListLevelStyle level ListStyle{..} =
355358 -- \^ simpler, but in general less efficient
356359
357360data ListLevelStyle = ListLevelStyle { listLevelType :: ListLevelType
358− , listItemPrefix :: Maybe String
359− , listItemSuffix :: Maybe String
361+ , listItemPrefix :: Maybe Text
362+ , listItemSuffix :: Maybe Text
360363 , listItemFormat :: ListItemNumberFormat
361364 , listItemStart :: Int
362365 }
@@ -366,9 +369,9 @@ instance Show ListLevelStyle where
366369 show ListLevelStyle{..} = "<LLS|"
367370 ++ show listLevelType
368371 ++ "|"
369− ++ maybeToString listItemPrefix
372+ ++ maybeToString (T.unpack <$> listItemPrefix)
370373 ++ show listItemFormat
371− ++ maybeToString listItemSuffix
374+ ++ maybeToString (T.unpack <$> listItemSuffix)
372375 ++ ">"
373376 where maybeToString = fromMaybe ""
374377
@@ -471,7 +474,7 @@ readTextProperties =
471474 )
472475 where isFontEmphasised = [("normal",False),("italic",True),("oblique",True)]
473476 isFontBold = ("normal",False):("bold",True)
474− :map ((,True).show) ([100,200..900]::[Int])
477+ :map ((,True) . tshow) ([100,200..900]::[Int])
475478
476479readUnderlineMode :: StyleReaderSafe _x (Maybe UnderlineMode)
477480readUnderlineMode = readLineMode "text-underline-mode"
@@ -481,7 +484,7 @@ readStrikeThroughMode :: StyleReaderSafe _x (Maybe UnderlineMode)
481484readStrikeThroughMode = readLineMode "text-line-through-mode"
482485 "text-line-through-style"
483486
484−readLineMode :: String -> String -> StyleReaderSafe _x (Maybe UnderlineMode)
487+readLineMode :: Text -> Text -> StyleReaderSafe _x (Maybe UnderlineMode)
485488readLineMode modeAttr styleAttr = proc x -> do
486489 isUL <- searchAttr NsStyle styleAttr False isLinePresent -< x
487490 mode <- lookupAttr' NsStyle modeAttr -< x
src/Text/Pandoc/Writers/Docx.hs+130−133
@@ -31,6 +31,7 @@ import qualified Data.Map as M
3131import Data.Maybe (fromMaybe, isNothing, mapMaybe, maybeToList)
3232import qualified Data.Set as Set
3333import qualified Data.Text as T
34+import Data.Text (Text)
3435import qualified Data.Text.Lazy as TL
3536import Data.Time.Clock.POSIX
3637import Data.Digest.Pure.SHA (sha1, showDigest)
@@ -57,19 +58,19 @@ import Text.Pandoc.Writers.Math
5758import Text.Pandoc.Writers.Shared
5859import Text.Printf (printf)
5960import Text.TeXMath
60−import Text.XML.Light as XML
61−import Text.XML.Light.Cursor as XMLC
6261import Text.Pandoc.Writers.OOXML
62+import Text.Pandoc.XML.Light as XML
63+import Data.Generics (mkT, everywhere)
6364
6465data ListMarker = NoMarker
6566 | BulletMarker
6667 | NumberMarker ListNumberStyle ListNumberDelim Int
6768 deriving (Show, Read, Eq, Ord)
6869
69−listMarkerToId :: ListMarker -> String
70+listMarkerToId :: ListMarker -> Text
7071listMarkerToId NoMarker = "990"
7172listMarkerToId BulletMarker = "991"
72−listMarkerToId (NumberMarker sty delim n) =
73+listMarkerToId (NumberMarker sty delim n) = T.pack $
7374 '9' : '9' : styNum : delimNum : show n
7475 where styNum = case sty of
7576 DefaultStyle -> '2'
@@ -106,8 +107,8 @@ data WriterEnv = WriterEnv{ envTextProperties :: EnvProps
106107 , envListLevel :: Int
107108 , envListNumId :: Int
108109 , envInDel :: Bool
109− , envChangesAuthor :: T.Text
110− , envChangesDate :: T.Text
110+ , envChangesAuthor :: Text
111+ , envChangesDate :: Text
111112 , envPrintWidth :: Integer
112113 }
113114
@@ -125,9 +126,9 @@ defaultWriterEnv = WriterEnv{ envTextProperties = mempty
125126
126127data WriterState = WriterState{
127128 stFootnotes :: [Element]
128− , stComments :: [([(T.Text, T.Text)], [Inline])]
129− , stSectionIds :: Set.Set T.Text
130− , stExternalLinks :: M.Map String String
129+ , stComments :: [([(Text, Text)], [Inline])]
130+ , stSectionIds :: Set.Set Text
131+ , stExternalLinks :: M.Map Text Text
131132 , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString)
132133 , stLists :: [ListMarker]
133134 , stInsId :: Int
@@ -164,18 +165,18 @@ defaultWriterState = WriterState{
164165
165166type WS m = ReaderT WriterEnv (StateT WriterState m)
166167
167−renumIdMap :: Int -> [Element] -> M.Map String String
168+renumIdMap :: Int -> [Element] -> M.Map Text Text
168169renumIdMap _ [] = M.empty
169170renumIdMap n (e:es)
170171 | Just oldId <- findAttr (QName "Id" Nothing Nothing) e =
171− M.insert oldId ("rId" ++ show n) (renumIdMap (n+1) es)
172+ M.insert oldId ("rId" <> tshow n) (renumIdMap (n+1) es)
172173 | otherwise = renumIdMap n es
173174
174−replaceAttr :: (QName -> Bool) -> String -> [XML.Attr] -> [XML.Attr]
175+replaceAttr :: (QName -> Bool) -> Text -> [XML.Attr] -> [XML.Attr]
175176replaceAttr f val = map $
176177 \a -> if f (attrKey a) then XML.Attr (attrKey a) val else a
177178
178−renumId :: (QName -> Bool) -> M.Map String String -> Element -> Element
179+renumId :: (QName -> Bool) -> M.Map Text Text -> Element -> Element
179180renumId f renumMap e
180181 | Just oldId <- findAttrBy f e
181182 , Just newId <- M.lookup oldId renumMap =
@@ -184,18 +185,12 @@ renumId f renumMap e
184185 e { elAttribs = attrs' }
185186 | otherwise = e
186187
187−renumIds :: (QName -> Bool) -> M.Map String String -> [Element] -> [Element]
188+renumIds :: (QName -> Bool) -> M.Map Text Text -> [Element] -> [Element]
188189renumIds f renumMap = map (renumId f renumMap)
189190
190−findAttrTextBy :: (QName -> Bool) -> Element -> Maybe T.Text
191−findAttrTextBy x = fmap T.pack . findAttrBy x
192−
193−lookupAttrTextBy :: (QName -> Bool) -> [XML.Attr] -> Maybe T.Text
194−lookupAttrTextBy x = fmap T.pack . lookupAttrBy x
195−
196191-- | Certain characters are invalid in XML even if escaped.
197192-- See #1992
198−stripInvalidChars :: T.Text -> T.Text
193+stripInvalidChars :: Text -> Text
199194stripInvalidChars = T.filter isValidChar
200195
201196-- | See XML reference
@@ -234,11 +229,11 @@ writeDocx opts doc = do
234229
235230 -- Gets the template size
236231 let mbpgsz = mbsectpr >>= filterElementName (wname (=="pgSz"))
237− let mbAttrSzWidth = mbpgsz >>= lookupAttrTextBy ((=="w") . qName) . elAttribs
232+ let mbAttrSzWidth = mbpgsz >>= lookupAttrBy ((=="w") . qName) . elAttribs
238233
239234 let mbpgmar = mbsectpr >>= filterElementName (wname (=="pgMar"))
240− let mbAttrMarLeft = mbpgmar >>= lookupAttrTextBy ((=="left") . qName) . elAttribs
241− let mbAttrMarRight = mbpgmar >>= lookupAttrTextBy ((=="right") . qName) . elAttribs
235+ let mbAttrMarLeft = mbpgmar >>= lookupAttrBy ((=="left") . qName) . elAttribs
236+ let mbAttrMarRight = mbpgmar >>= lookupAttrBy ((=="right") . qName) . elAttribs
242237
243238 -- Get the available area (converting the size and the margins to int and
244239 -- doing the difference
@@ -250,24 +245,21 @@ writeDocx opts doc = do
250245
251246 -- styles
252247 mblang <- toLang $ getLang opts meta
248+ -- TODO FIXME avoid this generic traversal!
249+ -- lang is in w:docDefaults / w:rPr / w:lang
253250 let addLang :: Element -> Element
254− addLang e = case (\l -> XMLC.toTree . go (T.unpack $ renderLang l) $
255− XMLC.fromElement e) <$> mblang of
256− Just (Elem e') -> e'
257− _ -> e -- return original
258− where go :: String -> Cursor -> Cursor
259− go l cursor = case XMLC.findRec (isLangElt . current) cursor of
260− Nothing -> cursor
261− Just t -> XMLC.modifyContent (setval l) t
262− setval :: String -> Content -> Content
263− setval l (Elem e') = Elem $ e'{ elAttribs = map (setvalattr l) $
264− elAttribs e' }
265− setval _ x = x
266− setvalattr :: String -> XML.Attr -> XML.Attr
267− setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l
268− setvalattr _ x = x
269− isLangElt (Elem e') = qName (elName e') == "lang"
270− isLangElt _ = False
251+ addLang = case mblang of
252+ Nothing -> id
253+ Just l -> everywhere (mkT (go (renderLang l)))
254+ where
255+ go :: Text -> Element -> Element
256+ go l e'
257+ | qName (elName e') == "lang"
258+ = e'{ elAttribs = map (setvalattr l) $ elAttribs e' }
259+ | otherwise = e'
260+
261+ setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l
262+ setvalattr _ x = x
271263
272264 let stylepath = "word/styles.xml"
273265 styledoc <- addLang <$> parseXml refArchive distArchive stylepath
@@ -337,12 +329,13 @@ writeDocx opts doc = do
337329
338330 -- [Content_Types].xml
339331 let mkOverrideNode (part', contentType') = mknode "Override"
340− [("PartName",part'),("ContentType",contentType')] ()
332+ [("PartName", T.pack part')
333+ ,("ContentType", contentType')] ()
341334 let mkImageOverride (_, imgpath, mbMimeType, _) =
342− mkOverrideNode ("/word/" ++ imgpath,
343− maybe "application/octet-stream" T.unpack mbMimeType)
335+ mkOverrideNode ("/word/" <> imgpath,
336+ fromMaybe "application/octet-stream" mbMimeType)
344337 let mkMediaOverride imgpath =
345− mkOverrideNode ('/':imgpath, T.unpack $ getMimeTypeDef imgpath)
338+ mkOverrideNode ("/" <> imgpath, getMimeTypeDef imgpath)
346339 let overrides = map mkOverrideNode (
347340 [("/word/webSettings.xml",
348341 "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml")
@@ -369,13 +362,14 @@ writeDocx opts doc = do
369362 ,("/word/footnotes.xml",
370363 "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml")
371364 ] ++
372− map (\x -> (maybe "" ("/word/" ++) $ extractTarget x,
365+ map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
373366 "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++
374− map (\x -> (maybe "" ("/word/" ++) $ extractTarget x,
367+ map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
375368 "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml")) footers) ++
376369 map mkImageOverride imgs ++
377− [ mkMediaOverride (eRelativePath e) | e <- zEntries refArchive
378− , "word/media/" `isPrefixOf` eRelativePath e ]
370+ [ mkMediaOverride (eRelativePath e)
371+ | e <- zEntries refArchive
372+ , "word/media/" `isPrefixOf` eRelativePath e ]
379373
380374 let defaultnodes = [mknode "Default"
381375 [("Extension","xml"),("ContentType","application/xml")] (),
@@ -421,7 +415,7 @@ writeDocx opts doc = do
421415 let renumHeaders = renumIds (\q -> qName q == "Id") idMap headers
422416 let renumFooters = renumIds (\q -> qName q == "Id") idMap footers
423417 let baserels = baserels' ++ renumHeaders ++ renumFooters
424− let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",path)] ()
418+ let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",T.pack ident),("Target",T.pack path)] ()
425419 let imgrels = map toImgRel imgs
426420 let toLinkRel (src,ident) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"),("Id",ident),("Target",src),("TargetMode","External") ] ()
427421 let linkrels = map toLinkRel $ M.toList $ stExternalLinks st
@@ -489,10 +483,10 @@ writeDocx opts doc = do
489483 numbering <- parseXml refArchive distArchive numpath
490484 let newNumElts = mkNumbering (stLists st)
491485 let pandocAdded e =
492− case findAttrTextBy ((== "abstractNumId") . qName) e >>= safeRead of
486+ case findAttrBy ((== "abstractNumId") . qName) e >>= safeRead of
493487 Just numid -> numid >= (990 :: Int)
494488 Nothing ->
495− case findAttrTextBy ((== "numId") . qName) e >>= safeRead of
489+ case findAttrBy ((== "numId") . qName) e >>= safeRead of
496490 Just numid -> numid >= (1000 :: Int)
497491 Nothing -> False
498492 let oldElts = filter (not . pandocAdded) $ onlyElems (elContent numbering)
@@ -514,7 +508,7 @@ writeDocx opts doc = do
514508 let extraCoreProps = ["subject","lang","category","description"]
515509 let extraCorePropsMap = M.fromList $ zip extraCoreProps
516510 ["dc:subject","dc:language","cp:category","dc:description"]
517− let lookupMetaString' :: T.Text -> Meta -> T.Text
511+ let lookupMetaString' :: Text -> Meta -> Text
518512 lookupMetaString' key' meta' =
519513 case key' of
520514 "description" -> T.intercalate "_x000d_\n" (map stringify $ lookupMetaBlocks "description" meta')
@@ -530,21 +524,21 @@ writeDocx opts doc = do
530524 : mktnode "dc:creator" [] (T.intercalate "; " (map stringify $ docAuthors meta))
531525 : [ mktnode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta)
532526 | k <- M.keys (unMeta meta), k `elem` extraCoreProps]
533− ++ mknode "cp:keywords" [] (T.unpack $ T.intercalate ", " keywords)
527+ ++ mknode "cp:keywords" [] (T.intercalate ", " keywords)
534528 : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
535529 , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
536− ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
530+ ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime)
537531 let docPropsEntry = toEntry docPropsPath epochtime $ renderXml docProps
538532
539533 -- docProps/custom.xml
540− let customProperties :: [(String, String)]
541− customProperties = [ (T.unpack k, T.unpack $ lookupMetaString k meta)
534+ let customProperties :: [(Text, Text)]
535+ customProperties = [ (k, lookupMetaString k meta)
542536 | k <- M.keys (unMeta meta)
543537 , k `notElem` (["title", "author", "keywords"]
544538 ++ extraCoreProps)]
545539 let mkCustomProp (k, v) pid = mknode "property"
546540 [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
547− ,("pid", show pid)
541+ ,("pid", tshow pid)
548542 ,("name", k)] $ mknode "vt:lpwstr" [] v
549543 let customPropsPath = "docProps/custom.xml"
550544 let customProps = mknode "Properties"
@@ -594,7 +588,8 @@ writeDocx opts doc = do
594588 fontTableEntry <- entryFromArchive refArchive "word/fontTable.xml"
595589 webSettingsEntry <- entryFromArchive refArchive "word/webSettings.xml"
596590 headerFooterEntries <- mapM (entryFromArchive refArchive . ("word/" ++)) $
597− mapMaybe extractTarget (headers ++ footers)
591+ mapMaybe (fmap T.unpack . extractTarget)
592+ (headers ++ footers)
598593 let miscRelEntries = [ e | e <- zEntries refArchive
599594 , "word/_rels/" `isPrefixOf` eRelativePath e
600595 , ".xml.rels" `isSuffixOf` eRelativePath e
@@ -620,8 +615,8 @@ newParaPropToOpenXml (fromStyleName -> s) =
620615 let styleId = T.filter (not . isSpace) s
621616 in mknode "w:style" [ ("w:type", "paragraph")
622617 , ("w:customStyle", "1")
623− , ("w:styleId", T.unpack styleId)]
624− [ mknode "w:name" [("w:val", T.unpack s)] ()
618+ , ("w:styleId", styleId)]
619+ [ mknode "w:name" [("w:val", s)] ()
625620 , mknode "w:basedOn" [("w:val","BodyText")] ()
626621 , mknode "w:qFormat" [] ()
627622 ]
@@ -631,8 +626,8 @@ newTextPropToOpenXml (fromStyleName -> s) =
631626 let styleId = T.filter (not . isSpace) s
632627 in mknode "w:style" [ ("w:type", "character")
633628 , ("w:customStyle", "1")
634− , ("w:styleId", T.unpack styleId)]
635− [ mknode "w:name" [("w:val", T.unpack s)] ()
629+ , ("w:styleId", styleId)]
630+ [ mknode "w:name" [("w:val", s)] ()
636631 , mknode "w:basedOn" [("w:val","BodyTextChar")] ()
637632 ]
638633
@@ -643,13 +638,14 @@ styleToOpenXml sm style =
643638 toStyle toktype | hasStyleName (fromString $ show toktype) (smCharStyle sm) = Nothing
644639 | otherwise = Just $
645640 mknode "w:style" [("w:type","character"),
646− ("w:customStyle","1"),("w:styleId",show toktype)]
647− [ mknode "w:name" [("w:val",show toktype)] ()
641+ ("w:customStyle","1"),("w:styleId", tshow toktype)]
642+ [ mknode "w:name" [("w:val", tshow toktype)] ()
648643 , mknode "w:basedOn" [("w:val","VerbatimChar")] ()
649644 , mknode "w:rPr" [] $
650− [ mknode "w:color" [("w:val",tokCol toktype)] ()
645+ [ mknode "w:color" [("w:val", tokCol toktype)] ()
651646 | tokCol toktype /= "auto" ] ++
652− [ mknode "w:shd" [("w:val","clear"),("w:fill",tokBg toktype)] ()
647+ [ mknode "w:shd" [("w:val","clear")
648+ ,("w:fill",tokBg toktype)] ()
653649 | tokBg toktype /= "auto" ] ++
654650 [ mknode "w:b" [] () | tokFeature tokenBold toktype ] ++
655651 [ mknode "w:i" [] () | tokFeature tokenItalic toktype ] ++
@@ -657,10 +653,10 @@ styleToOpenXml sm style =
657653 ]
658654 tokStyles = tokenStyles style
659655 tokFeature f toktype = maybe False f $ M.lookup toktype tokStyles
660− tokCol toktype = maybe "auto" (drop 1 . fromColor)
656+ tokCol toktype = maybe "auto" (T.pack . drop 1 . fromColor)
661657 $ (tokenColor =<< M.lookup toktype tokStyles)
662658 `mplus` defaultColor style
663− tokBg toktype = maybe "auto" (drop 1 . fromColor)
659+ tokBg toktype = maybe "auto" (T.pack . drop 1 . fromColor)
664660 $ (tokenBackground =<< M.lookup toktype tokStyles)
665661 `mplus` backgroundColor style
666662 parStyle | hasStyleName "Source Code" (smParaStyle sm) = Nothing
@@ -673,10 +669,11 @@ styleToOpenXml sm style =
673669 , mknode "w:pPr" []
674670 $ mknode "w:wordWrap" [("w:val","off")] ()
675671 :
676− maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill",drop 1 $ fromColor col)] ()]) (backgroundColor style)
672+ maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill", T.pack $ drop 1 $ fromColor col)] ()]) (backgroundColor style)
677673 ]
678674
679−copyChildren :: (PandocMonad m) => Archive -> Archive -> String -> Integer -> [String] -> m Entry
675+copyChildren :: (PandocMonad m)
676+ => Archive -> Archive -> String -> Integer -> [Text] -> m Entry
680677copyChildren refArchive distArchive path timestamp elNames = do
681678 ref <- parseXml refArchive distArchive path
682679 dist <- parseXml distArchive distArchive path
@@ -685,7 +682,7 @@ copyChildren refArchive distArchive path timestamp elNames = do
685682 }
686683 where
687684 strName QName{qName=name, qPrefix=prefix}
688− | Just p <- prefix = p++":"++name
685+ | Just p <- prefix = p <> ":" <> name
689686 | otherwise = name
690687 shouldCopy = (`elem` elNames) . strName
691688 cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}}
@@ -706,35 +703,35 @@ maxListLevel = 8
706703
707704mkNum :: ListMarker -> Int -> Element
708705mkNum marker numid =
709− mknode "w:num" [("w:numId",show numid)]
706+ mknode "w:num" [("w:numId",tshow numid)]
710707 $ mknode "w:abstractNumId" [("w:val",listMarkerToId marker)] ()
711708 : case marker of
712709 NoMarker -> []
713710 BulletMarker -> []
714711 NumberMarker _ _ start ->
715− map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))]
716− $ mknode "w:startOverride" [("w:val",show start)] ())
712+ map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",tshow (lvl :: Int))]
713+ $ mknode "w:startOverride" [("w:val",tshow start)] ())
717714 [0..maxListLevel]
718715
719716mkAbstractNum :: ListMarker -> Integer -> Element
720717mkAbstractNum marker nsid =
721718 mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
722− $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
719+ $ mknode "w:nsid" [("w:val", T.pack $ printf "%8x" nsid)] ()
723720 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
724721 : map (mkLvl marker)
725722 [0..maxListLevel]
726723
727724mkLvl :: ListMarker -> Int -> Element
728725mkLvl marker lvl =
729− mknode "w:lvl" [("w:ilvl",show lvl)] $
726+ mknode "w:lvl" [("w:ilvl",tshow lvl)] $
730727 [ mknode "w:start" [("w:val",start)] ()
731728 | marker /= NoMarker && marker /= BulletMarker ] ++
732729 [ mknode "w:numFmt" [("w:val",fmt)] ()
733− , mknode "w:lvlText" [("w:val",lvltxt)] ()
730+ , mknode "w:lvlText" [("w:val", lvltxt)] ()
734731 , mknode "w:lvlJc" [("w:val","left")] ()
735732 , mknode "w:pPr" []
736− [ mknode "w:ind" [ ("w:left",show $ lvl * step + step)
737− , ("w:hanging",show (hang :: Int))
733+ [ mknode "w:ind" [ ("w:left",tshow $ lvl * step + step)
734+ , ("w:hanging",tshow (hang :: Int))
738735 ] ()
739736 ]
740737 ]
@@ -743,8 +740,8 @@ mkLvl marker lvl =
743740 NoMarker -> ("bullet"," ","1")
744741 BulletMarker -> ("bullet",bulletFor lvl,"1")
745742 NumberMarker st de n -> (styleFor st lvl
746− ,patternFor de ("%" ++ show (lvl + 1))
747− ,show n)
743+ ,patternFor de ("%" <> tshow (lvl + 1))
744+ ,tshow n)
748745 step = 720
749746 hang = 480
750747 bulletFor 0 = "\x2022" -- filled circle
@@ -767,9 +764,9 @@ mkLvl marker lvl =
767764 styleFor DefaultStyle 5 = "lowerRoman"
768765 styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 6)
769766 styleFor _ _ = "decimal"
770− patternFor OneParen s = s ++ ")"
771− patternFor TwoParens s = "(" ++ s ++ ")"
772− patternFor _ s = s ++ "."
767+ patternFor OneParen s = s <> ")"
768+ patternFor TwoParens s = "(" <> s <> ")"
769+ patternFor _ s = s <> "."
773770
774771getNumId :: (PandocMonad m) => WS m Int
775772getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists
@@ -777,8 +774,8 @@ getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists
777774
778775makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element]
779776makeTOC opts = do
780− let depth = "1-"++show (writerTOCDepth opts)
781− let tocCmd = "TOC \\o \""++depth++"\" \\h \\z \\u"
777+ let depth = "1-" <> tshow (writerTOCDepth opts)
778+ let tocCmd = "TOC \\o \"" <> depth <> "\" \\h \\z \\u"
782779 tocTitle <- gets stTocTitle
783780 title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para tocTitle])
784781 return
@@ -831,7 +828,7 @@ writeOpenXML opts (Pandoc meta blocks) = do
831828 let toComment (kvs, ils) = do
832829 annotation <- inlinesToOpenXML opts ils
833830 return $
834− mknode "w:comment" [('w':':':T.unpack k,T.unpack v) | (k,v) <- kvs]
831+ mknode "w:comment" [("w:" <> k, v) | (k,v) <- kvs]
835832 [ mknode "w:p" [] $
836833 map Elem
837834 [ mknode "w:pPr" []
@@ -867,24 +864,24 @@ pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element
867864pStyleM styleName = do
868865 pStyleMap <- gets (smParaStyle . stStyleMaps)
869866 let sty' = getStyleIdFromName styleName pStyleMap
870− return $ mknode "w:pStyle" [("w:val", T.unpack $ fromStyleId sty')] ()
867+ return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] ()
871868
872869rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element
873870rStyleM styleName = do
874871 cStyleMap <- gets (smCharStyle . stStyleMaps)
875872 let sty' = getStyleIdFromName styleName cStyleMap
876− return $ mknode "w:rStyle" [("w:val", T.unpack $ fromStyleId sty')] ()
873+ return $ mknode "w:rStyle" [("w:val", fromStyleId sty')] ()
877874
878−getUniqueId :: (PandocMonad m) => WS m String
875+getUniqueId :: (PandocMonad m) => WS m Text
879876-- the + 20 is to ensure that there are no clashes with the rIds
880877-- already in word/document.xml.rel
881878getUniqueId = do
882879 n <- gets stCurId
883880 modify $ \st -> st{stCurId = n + 1}
884− return $ show n
881+ return $ tshow n
885882
886883-- | Key for specifying user-defined docx styles.
887−dynamicStyleKey :: T.Text
884+dynamicStyleKey :: Text
888885dynamicStyleKey = "custom-style"
889886
890887-- | Convert a Pandoc block element to OpenXML.
@@ -979,7 +976,7 @@ blockToOpenXML' opts (Para lst)
979976blockToOpenXML' opts (LineBlock lns) = blockToOpenXML opts $ linesToPara lns
980977blockToOpenXML' _ b@(RawBlock format str)
981978 | format == Format "openxml" = return [
982− Text (CData CDataRaw (T.unpack str) Nothing)
979+ Text (CData CDataRaw str Nothing)
983980 ]
984981 | otherwise = do
985982 report $ BlockNotRendered b
@@ -1036,7 +1033,7 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do
10361033 let fullrow = 5000 -- 100% specified in pct
10371034 let rowwidth = fullrow * sum widths
10381035 let mkgridcol w = mknode "w:gridCol"
1039− [("w:w", show (floor (textwidth * w) :: Integer))] ()
1036+ [("w:w", tshow (floor (textwidth * w) :: Integer))] ()
10401037 let hasHeader = not $ all null headers
10411038 modify $ \s -> s { stInTable = False }
10421039 -- for compatibility with Word <= 2007, we include a val with a bitmask
@@ -1054,16 +1051,16 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do
10541051 mknode "w:tbl" []
10551052 ( mknode "w:tblPr" []
10561053 ( mknode "w:tblStyle" [("w:val","Table")] () :
1057− mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () :
1054+ mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () :
10581055 mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0")
10591056 ,("w:lastRow","0")
10601057 ,("w:firstColumn","0")
10611058 ,("w:lastColumn","0")
10621059 ,("w:noHBand","0")
10631060 ,("w:noVBand","0")
1064− ,("w:val", printf "%04x" tblLookVal)
1061+ ,("w:val", T.pack $ printf "%04x" tblLookVal)
10651062 ] () :
1066− [ mknode "w:tblCaption" [("w:val", T.unpack captionStr)] ()
1063+ [ mknode "w:tblCaption" [("w:val", captionStr)] ()
10671064 | not (null caption) ] )
10681065 : mknode "w:tblGrid" []
10691066 (if all (==0) widths
@@ -1126,7 +1123,7 @@ listItemToOpenXML opts numid (first:rest) = do
11261123 modify $ \st -> st{ stInList = oldInList }
11271124 return $ first'' ++ rest''
11281125
1129−alignmentToString :: Alignment -> [Char]
1126+alignmentToString :: Alignment -> Text
11301127alignmentToString alignment = case alignment of
11311128 AlignLeft -> "left"
11321129 AlignRight -> "right"
@@ -1169,8 +1166,8 @@ getParaProps displayMathPara = do
11691166 listLevel <- asks envListLevel
11701167 numid <- asks envListNumId
11711168 let listPr = [mknode "w:numPr" []
1172− [ mknode "w:ilvl" [("w:val",show listLevel)] ()
1173− , mknode "w:numId" [("w:val",show numid)] () ] | listLevel >= 0 && not displayMathPara]
1169+ [ mknode "w:ilvl" [("w:val",tshow listLevel)] ()
1170+ , mknode "w:numId" [("w:val",tshow numid)] () ] | listLevel >= 0 && not displayMathPara]
11741171 return $ case listPr ++ squashProps props of
11751172 [] -> []
11761173 ps -> [mknode "w:pPr" [] ps]
@@ -1185,7 +1182,7 @@ withParaPropM md p = do
11851182 d <- md
11861183 withParaProp d p
11871184
1188−formattedString :: PandocMonad m => T.Text -> WS m [Element]
1185+formattedString :: PandocMonad m => Text -> WS m [Element]
11891186formattedString str =
11901187 -- properly handle soft hyphens
11911188 case splitTextBy (=='\173') str of
@@ -1194,7 +1191,7 @@ formattedString str =
11941191 sh <- formattedRun [mknode "w:softHyphen" [] ()]
11951192 intercalate sh <$> mapM formattedString' ws
11961193
1197−formattedString' :: PandocMonad m => T.Text -> WS m [Element]
1194+formattedString' :: PandocMonad m => Text -> WS m [Element]
11981195formattedString' str = do
11991196 inDel <- asks envInDel
12001197 formattedRun [ mktnode (if inDel then "w:delText" else "w:t")
@@ -1226,7 +1223,7 @@ inlineToOpenXML' opts (Span ("",["csl-right-inline"],[]) ils) =
12261223 mknode "w:r" []
12271224 (mknode "w:t"
12281225 [("xml:space","preserve")]
1229− ("\t" :: String))] ++)
1226+ ("\t" :: Text))] ++)
12301227 <$> inlinesToOpenXML opts ils
12311228inlineToOpenXML' opts (Span ("",["csl-indent"],[]) ils) =
12321229 inlinesToOpenXML opts ils
@@ -1236,17 +1233,17 @@ inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do
12361233 let ident' = fromMaybe ident (lookup "id" kvs)
12371234 kvs' = filter (("id" /=) . fst) kvs
12381235 modify $ \st -> st{ stComments = (("id",ident'):kvs', ils) : stComments st }
1239− return [ Elem $ mknode "w:commentRangeStart" [("w:id", T.unpack ident')] () ]
1236+ return [ Elem $ mknode "w:commentRangeStart" [("w:id", ident')] () ]
12401237inlineToOpenXML' _ (Span (ident,["comment-end"],kvs) _) =
12411238 -- prefer the "id" in kvs, since that is the one produced by the docx
12421239 -- reader.
12431240 let ident' = fromMaybe ident (lookup "id" kvs)
12441241 in return . map Elem $
1245− [ mknode "w:commentRangeEnd" [("w:id", T.unpack ident')] ()
1242+ [ mknode "w:commentRangeEnd" [("w:id", ident')] ()
12461243 , mknode "w:r" []
12471244 [ mknode "w:rPr" []
12481245 [ mknode "w:rStyle" [("w:val", "CommentReference")] () ]
1249− , mknode "w:commentReference" [("w:id", T.unpack ident')] () ]
1246+ , mknode "w:commentReference" [("w:id", ident')] () ]
12501247 ]
12511248inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do
12521249 stylemod <- case lookup dynamicStyleKey kvs of
@@ -1270,8 +1267,8 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do
12701267 defaultAuthor <- asks envChangesAuthor
12711268 let author = fromMaybe defaultAuthor (lookup "author" kvs)
12721269 let mdate = lookup "date" kvs
1273− return $ ("w:author", T.unpack author) :
1274− maybe [] (\date -> [("w:date", T.unpack date)]) mdate
1270+ return $ ("w:author", author) :
1271+ maybe [] (\date -> [("w:date", date)]) mdate
12751272 insmod <- if "insertion" `elem` classes
12761273 then do
12771274 changeAuthorDate <- getChangeAuthorDate
@@ -1281,7 +1278,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do
12811278 x <- f
12821279 return [Elem $
12831280 mknode "w:ins"
1284− (("w:id", show insId) : changeAuthorDate) x]
1281+ (("w:id", tshow insId) : changeAuthorDate) x]
12851282 else return id
12861283 delmod <- if "deletion" `elem` classes
12871284 then do
@@ -1291,7 +1288,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do
12911288 return $ \f -> local (\env->env{envInDel=True}) $ do
12921289 x <- f
12931290 return [Elem $ mknode "w:del"
1294− (("w:id", show delId) : changeAuthorDate) x]
1291+ (("w:id", tshow delId) : changeAuthorDate) x]
12951292 else return id
12961293 contents <- insmod $ delmod $ dirmod $ stylemod $ pmod
12971294 $ inlinesToOpenXML opts ils
@@ -1322,7 +1319,7 @@ inlineToOpenXML' opts (Strikeout lst) =
13221319inlineToOpenXML' _ LineBreak = return [Elem br]
13231320inlineToOpenXML' _ il@(RawInline f str)
13241321 | f == Format "openxml" = return
1325− [Text (CData CDataRaw (T.unpack str) Nothing)]
1322+ [Text (CData CDataRaw str Nothing)]
13261323 | otherwise = do
13271324 report $ InlineNotRendered il
13281325 return []
@@ -1335,7 +1332,7 @@ inlineToOpenXML' opts (Math mathType str) = do
13351332 when (mathType == DisplayMath) setFirstPara
13361333 res <- (lift . lift) (convertMath writeOMML mathType str)
13371334 case res of
1338− Right r -> return [Elem r]
1335+ Right r -> return [Elem $ fromXLElement r]
13391336 Left il -> inlineToOpenXML' opts il
13401337inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst
13411338inlineToOpenXML' opts (Code attrs str) = do
@@ -1348,7 +1345,7 @@ inlineToOpenXML' opts (Code attrs str) = do
13481345 mknode "w:r" []
13491346 [ mknode "w:rPr" [] $
13501347 maybeToList (lookup toktype tokTypesMap)
1351− , mknode "w:t" [("xml:space","preserve")] (T.unpack tok) ]
1348+ , mknode "w:t" [("xml:space","preserve")] tok ]
13521349 withTextPropM (rStyleM "Verbatim Char")
13531350 $ if isNothing (writerHighlightStyle opts)
13541351 then unhighlighted
@@ -1365,7 +1362,7 @@ inlineToOpenXML' opts (Note bs) = do
13651362 let notemarker = mknode "w:r" []
13661363 [ mknode "w:rPr" [] footnoteStyle
13671364 , mknode "w:footnoteRef" [] () ]
1368− let notemarkerXml = RawInline (Format "openxml") $ T.pack $ ppElement notemarker
1365+ let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker
13691366 let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : Space : ils) : xs
13701367 insertNoteRef (Para ils : xs) = Para (notemarkerXml : Space : ils) : xs
13711368 insertNoteRef xs = Para [notemarkerXml] : xs
@@ -1384,17 +1381,17 @@ inlineToOpenXML' opts (Note bs) = do
13841381inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),_)) = do
13851382 contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
13861383 return
1387− [ Elem $ mknode "w:hyperlink" [("w:anchor", T.unpack $ toBookmarkName xs)] contents ]
1384+ [ Elem $ mknode "w:hyperlink" [("w:anchor", toBookmarkName xs)] contents ]
13881385-- external link:
13891386inlineToOpenXML' opts (Link _ txt (src,_)) = do
13901387 contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
13911388 extlinks <- gets stExternalLinks
1392− id' <- case M.lookup (T.unpack src) extlinks of
1389+ id' <- case M.lookup src extlinks of
13931390 Just i -> return i
13941391 Nothing -> do
1395− i <- ("rId"++) `fmap` getUniqueId
1392+ i <- ("rId" <>) <$> getUniqueId
13961393 modify $ \st -> st{ stExternalLinks =
1397− M.insert (T.unpack src) i extlinks }
1394+ M.insert src i extlinks }
13981395 return i
13991396 return [ Elem $ mknode "w:hyperlink" [("r:id",id')] contents ]
14001397inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
@@ -1414,17 +1411,17 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
14141411 ,("noChangeAspect","1")] ()
14151412 nvPicPr = mknode "pic:nvPicPr" []
14161413 [ mknode "pic:cNvPr"
1417− [("descr",T.unpack src),("id","0"),("name","Picture")] ()
1414+ [("descr",src),("id","0"),("name","Picture")] ()
14181415 , cNvPicPr ]
14191416 blipFill = mknode "pic:blipFill" []
1420− [ mknode "a:blip" [("r:embed",ident)] ()
1417+ [ mknode "a:blip" [("r:embed",T.pack ident)] ()
14211418 , mknode "a:stretch" [] $
14221419 mknode "a:fillRect" [] ()
14231420 ]
14241421 xfrm = mknode "a:xfrm" []
14251422 [ mknode "a:off" [("x","0"),("y","0")] ()
1426− , mknode "a:ext" [("cx",show xemu)
1427− ,("cy",show yemu)] () ]
1423+ , mknode "a:ext" [("cx",tshow xemu)
1424+ ,("cy",tshow yemu)] () ]
14281425 prstGeom = mknode "a:prstGeom" [("prst","rect")] $
14291426 mknode "a:avLst" [] ()
14301427 ln = mknode "a:ln" [("w","9525")]
@@ -1445,12 +1442,12 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
14451442 imgElt = mknode "w:r" [] $
14461443 mknode "w:drawing" [] $
14471444 mknode "wp:inline" []
1448− [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] ()
1445+ [ mknode "wp:extent" [("cx",tshow xemu),("cy",tshow yemu)] ()
14491446 , mknode "wp:effectExtent"
14501447 [("b","0"),("l","0"),("r","0"),("t","0")] ()
14511448 , mknode "wp:docPr"
1452− [ ("descr", T.unpack $ stringify alt)
1453− , ("title", T.unpack title)
1449+ [ ("descr", stringify alt)
1450+ , ("title", title)
14541451 , ("id","1")
14551452 , ("name","Picture")
14561453 ] ()
@@ -1463,7 +1460,7 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
14631460 Just imgData -> return [Elem $ generateImgElt imgData]
14641461 Nothing -> ( do --try
14651462 (img, mt) <- P.fetchItem src
1466− ident <- ("rId"++) `fmap` getUniqueId
1463+ ident <- ("rId" <>) <$> getUniqueId
14671464
14681465 let
14691466 imgext = case mt >>= extensionFromMimeType of
@@ -1477,10 +1474,10 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
14771474 Just Svg -> ".svg"
14781475 Just Emf -> ".emf"
14791476 Nothing -> ""
1480− imgpath = "media/" <> ident <> T.unpack imgext
1481− mbMimeType = mt <|> getMimeType imgpath
1477+ imgpath = "media/" <> ident <> imgext
1478+ mbMimeType = mt <|> getMimeType (T.unpack imgpath)
14821479
1483− imgData = (ident, imgpath, mbMimeType, img)
1480+ imgData = (T.unpack ident, T.unpack imgpath, mbMimeType, img)
14841481
14851482 if T.null imgext
14861483 then -- without an extension there is no rule for content type
@@ -1538,20 +1535,20 @@ withDirection x = do
15381535 , envTextProperties = EnvProps textStyle textProps'
15391536 }
15401537
1541−wrapBookmark :: (PandocMonad m) => T.Text -> [Content] -> WS m [Content]
1538+wrapBookmark :: (PandocMonad m) => Text -> [Content] -> WS m [Content]
15421539wrapBookmark "" contents = return contents
15431540wrapBookmark ident contents = do
15441541 id' <- getUniqueId
15451542 let bookmarkStart = mknode "w:bookmarkStart"
15461543 [("w:id", id')
1547− ,("w:name", T.unpack $ toBookmarkName ident)] ()
1544+ ,("w:name", toBookmarkName ident)] ()
15481545 bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
15491546 return $ Elem bookmarkStart : contents ++ [Elem bookmarkEnd]
15501547
15511548-- Word imposes a 40 character limit on bookmark names and requires
15521549-- that they begin with a letter. So we just use a hash of the
15531550-- identifier when otherwise we'd have an illegal bookmark name.
1554−toBookmarkName :: T.Text -> T.Text
1551+toBookmarkName :: Text -> Text
15551552toBookmarkName s
15561553 | Just (c, _) <- T.uncons s
15571554 , isLetter c
src/Text/Pandoc/Writers/EPUB.hs+173−183
@@ -24,12 +24,13 @@ import Control.Monad.State.Strict (StateT, evalState, evalStateT, get,
2424 gets, lift, modify)
2525import qualified Data.ByteString.Lazy as B
2626import qualified Data.ByteString.Lazy.Char8 as B8
27−import Data.Char (isAlphaNum, isAscii, isDigit, toLower)
27+import Data.Char (isAlphaNum, isAscii, isDigit)
2828import Data.List (isInfixOf, isPrefixOf)
2929import qualified Data.Map as M
3030import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust)
3131import qualified Data.Set as Set
32−import qualified Data.Text as TS
32+import qualified Data.Text as T
33+import Data.Text (Text)
3334import qualified Data.Text.Lazy as TL
3435import Network.HTTP (urlEncode)
3536import System.FilePath (takeExtension, takeFileName, makeRelative)
@@ -48,16 +49,13 @@ import Text.Pandoc.Options (EPUBVersion (..), HTMLMathMethod (..),
4849 ObfuscationMethod (NoObfuscation), WrapOption (..),
4950 WriterOptions (..))
5051import Text.Pandoc.Shared (makeSections, normalizeDate, renderTags',
51− safeRead, stringify, trim, uniqueIdent, tshow)
52+ stringify, uniqueIdent, tshow)
5253import qualified Text.Pandoc.UTF8 as UTF8
5354import Text.Pandoc.UUID (getRandomUUID)
5455import Text.Pandoc.Walk (query, walk, walkM)
5556import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB)
5657import Text.Printf (printf)
57−import Text.XML.Light (Attr (..), Element (..), Node (..), QName (..),
58− add_attrs, lookupAttr, node, onlyElems,
59− ppElement, showElement, strContent, unode, unqual)
60−import Text.Pandoc.XMLParser (parseXMLContents)
58+import Text.Pandoc.XML.Light
6159import Text.Pandoc.XML (escapeStringForXML)
6260import Text.DocTemplates (FromContext(lookupContext), Context(..),
6361 ToContext(toVal), Val(..))
@@ -69,7 +67,7 @@ newtype Chapter = Chapter [Block]
6967data EPUBState = EPUBState {
7068 stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))]
7169 , stMediaNextId :: Int
72− , stEpubSubdir :: String
70+ , stEpubSubdir :: FilePath
7371 }
7472
7573type E m = StateT EPUBState m
@@ -78,62 +76,63 @@ data EPUBMetadata = EPUBMetadata{
7876 epubIdentifier :: [Identifier]
7977 , epubTitle :: [Title]
8078 , epubDate :: [Date]
81− , epubLanguage :: String
79+ , epubLanguage :: Text
8280 , epubCreator :: [Creator]
8381 , epubContributor :: [Creator]
84− , epubSubject :: [String]
85− , epubDescription :: Maybe String
86− , epubType :: Maybe String
87− , epubFormat :: Maybe String
88− , epubPublisher :: Maybe String
89− , epubSource :: Maybe String
90− , epubRelation :: Maybe String
91− , epubCoverage :: Maybe String
92− , epubRights :: Maybe String
93− , epubBelongsToCollection :: Maybe String
94− , epubGroupPosition :: Maybe String
95− , epubCoverImage :: Maybe String
82+ , epubSubject :: [Text]
83+ , epubDescription :: Maybe Text
84+ , epubType :: Maybe Text
85+ , epubFormat :: Maybe Text
86+ , epubPublisher :: Maybe Text
87+ , epubSource :: Maybe Text
88+ , epubRelation :: Maybe Text
89+ , epubCoverage :: Maybe Text
90+ , epubRights :: Maybe Text
91+ , epubBelongsToCollection :: Maybe Text
92+ , epubGroupPosition :: Maybe Text
93+ , epubCoverImage :: Maybe FilePath
9694 , epubStylesheets :: [FilePath]
9795 , epubPageDirection :: Maybe ProgressionDirection
98− , epubIbooksFields :: [(String, String)]
99− , epubCalibreFields :: [(String, String)]
96+ , epubIbooksFields :: [(Text, Text)]
97+ , epubCalibreFields :: [(Text, Text)]
10098 } deriving Show
10199
102100data Date = Date{
103− dateText :: String
104− , dateEvent :: Maybe String
101+ dateText :: Text
102+ , dateEvent :: Maybe Text
105103 } deriving Show
106104
107105data Creator = Creator{
108− creatorText :: String
109− , creatorRole :: Maybe String
110− , creatorFileAs :: Maybe String
106+ creatorText :: Text
107+ , creatorRole :: Maybe Text
108+ , creatorFileAs :: Maybe Text
111109 } deriving Show
112110
113111data Identifier = Identifier{
114− identifierText :: String
115− , identifierScheme :: Maybe String
112+ identifierText :: Text
113+ , identifierScheme :: Maybe Text
116114 } deriving Show
117115
118116data Title = Title{
119− titleText :: String
120− , titleFileAs :: Maybe String
121− , titleType :: Maybe String
117+ titleText :: Text
118+ , titleFileAs :: Maybe Text
119+ , titleType :: Maybe Text
122120 } deriving Show
123121
124122data ProgressionDirection = LTR | RTL deriving Show
125123
126−dcName :: String -> QName
124+dcName :: Text -> QName
127125dcName n = QName n Nothing (Just "dc")
128126
129−dcNode :: Node t => String -> t -> Element
127+dcNode :: Node t => Text -> t -> Element
130128dcNode = node . dcName
131129
132−opfName :: String -> QName
130+opfName :: Text -> QName
133131opfName n = QName n Nothing (Just "opf")
134132
135−toId :: FilePath -> String
136−toId = map (\x -> if isAlphaNum x || x == '-' || x == '_'
133+toId :: FilePath -> Text
134+toId = T.pack .
135+ map (\x -> if isAlphaNum x || x == '-' || x == '_'
137136 then x
138137 else '_') . takeFileName
139138
@@ -141,8 +140,8 @@ removeNote :: Inline -> Inline
141140removeNote (Note _) = Str ""
142141removeNote x = x
143142
144−toVal' :: String -> Val TS.Text
145−toVal' = toVal . TS.pack
143+toVal' :: Text -> Val T.Text
144+toVal' = toVal
146145
147146mkEntry :: PandocMonad m => FilePath -> B.ByteString -> E m Entry
148147mkEntry path content = do
@@ -172,21 +171,21 @@ getEPUBMetadata opts meta = do
172171 if null (epubIdentifier m)
173172 then do
174173 randomId <- getRandomUUID
175− return $ m{ epubIdentifier = [Identifier (show randomId) Nothing] }
174+ return $ m{ epubIdentifier = [Identifier (tshow randomId) Nothing] }
176175 else return m
177176 let addLanguage m =
178− if null (epubLanguage m)
177+ if T.null (epubLanguage m)
179178 then case lookupContext "lang" (writerVariables opts) of
180− Just x -> return m{ epubLanguage = TS.unpack x }
179+ Just x -> return m{ epubLanguage = x }
181180 Nothing -> do
182181 mLang <- lift $ P.lookupEnv "LANG"
183182 let localeLang =
184183 case mLang of
185184 Just lang ->
186− TS.map (\c -> if c == '_' then '-' else c) $
187− TS.takeWhile (/='.') lang
185+ T.map (\c -> if c == '_' then '-' else c) $
186+ T.takeWhile (/='.') lang
188187 Nothing -> "en-US"
189− return m{ epubLanguage = TS.unpack localeLang }
188+ return m{ epubLanguage = localeLang }
190189 else return m
191190 let fixDate m =
192191 if null (epubDate m)
@@ -201,7 +200,7 @@ getEPUBMetadata opts meta = do
201200 then return m
202201 else do
203202 let authors' = map stringify $ docAuthors meta
204− let toAuthor name = Creator{ creatorText = TS.unpack name
203+ let toAuthor name = Creator{ creatorText = name
205204 , creatorRole = Just "aut"
206205 , creatorFileAs = Nothing }
207206 return $ m{ epubCreator = map toAuthor authors' ++ epubCreator m }
@@ -249,31 +248,31 @@ addMetadataFromXML e@(Element (QName name _ (Just "dc")) attrs _ _) md
249248 where getAttr n = lookupAttr (opfName n) attrs
250249addMetadataFromXML e@(Element (QName "meta" _ _) attrs _ _) md =
251250 case getAttr "property" of
252− Just s | "ibooks:" `isPrefixOf` s ->
253− md{ epubIbooksFields = (drop 7 s, strContent e) :
251+ Just s | "ibooks:" `T.isPrefixOf` s ->
252+ md{ epubIbooksFields = (T.drop 7 s, strContent e) :
254253 epubIbooksFields md }
255254 _ -> case getAttr "name" of
256− Just s | "calibre:" `isPrefixOf` s ->
255+ Just s | "calibre:" `T.isPrefixOf` s ->
257256 md{ epubCalibreFields =
258− (drop 8 s, fromMaybe "" $ getAttr "content") :
257+ (T.drop 8 s, fromMaybe "" $ getAttr "content") :
259258 epubCalibreFields md }
260259 _ -> md
261260 where getAttr n = lookupAttr (unqual n) attrs
262261addMetadataFromXML _ md = md
263262
264−metaValueToString :: MetaValue -> String
265−metaValueToString (MetaString s) = TS.unpack s
266−metaValueToString (MetaInlines ils) = TS.unpack $ stringify ils
267−metaValueToString (MetaBlocks bs) = TS.unpack $ stringify bs
263+metaValueToString :: MetaValue -> Text
264+metaValueToString (MetaString s) = s
265+metaValueToString (MetaInlines ils) = stringify ils
266+metaValueToString (MetaBlocks bs) = stringify bs
268267metaValueToString (MetaBool True) = "true"
269268metaValueToString (MetaBool False) = "false"
270269metaValueToString _ = ""
271270
272271metaValueToPaths :: MetaValue -> [FilePath]
273−metaValueToPaths (MetaList xs) = map metaValueToString xs
274−metaValueToPaths x = [metaValueToString x]
272+metaValueToPaths (MetaList xs) = map (T.unpack . metaValueToString) xs
273+metaValueToPaths x = [T.unpack $ metaValueToString x]
275274
276−getList :: TS.Text -> Meta -> (MetaValue -> a) -> [a]
275+getList :: T.Text -> Meta -> (MetaValue -> a) -> [a]
277276getList s meta handleMetaValue =
278277 case lookupMeta s meta of
279278 Just (MetaList xs) -> map handleMetaValue xs
@@ -297,7 +296,7 @@ getTitle meta = getList "title" meta handleMetaValue
297296 , titleType = metaValueToString <$> M.lookup "type" m }
298297 handleMetaValue mv = Title (metaValueToString mv) Nothing Nothing
299298
300−getCreator :: TS.Text -> Meta -> [Creator]
299+getCreator :: T.Text -> Meta -> [Creator]
301300getCreator s meta = getList s meta handleMetaValue
302301 where handleMetaValue (MetaMap m) =
303302 Creator{ creatorText = maybe "" metaValueToString $ M.lookup "text" m
@@ -305,7 +304,7 @@ getCreator s meta = getList s meta handleMetaValue
305304 , creatorRole = metaValueToString <$> M.lookup "role" m }
306305 handleMetaValue mv = Creator (metaValueToString mv) Nothing Nothing
307306
308−getDate :: TS.Text -> Meta -> [Date]
307+getDate :: T.Text -> Meta -> [Date]
309308getDate s meta = getList s meta handleMetaValue
310309 where handleMetaValue (MetaMap m) =
311310 Date{ dateText = fromMaybe "" $
@@ -314,7 +313,7 @@ getDate s meta = getList s meta handleMetaValue
314313 handleMetaValue mv = Date { dateText = fromMaybe "" $ normalizeDate' $ metaValueToString mv
315314 , dateEvent = Nothing }
316315
317−simpleList :: TS.Text -> Meta -> [String]
316+simpleList :: T.Text -> Meta -> [Text]
318317simpleList s meta =
319318 case lookupMeta s meta of
320319 Just (MetaList xs) -> map metaValueToString xs
@@ -339,7 +338,7 @@ metadataFromMeta opts meta = EPUBMetadata{
339338 , epubCoverage = coverage
340339 , epubRights = rights
341340 , epubBelongsToCollection = belongsToCollection
342− , epubGroupPosition = groupPosition
341+ , epubGroupPosition = groupPosition
343342 , epubCoverImage = coverImage
344343 , epubStylesheets = stylesheets
345344 , epubPageDirection = pageDirection
@@ -363,31 +362,30 @@ metadataFromMeta opts meta = EPUBMetadata{
363362 coverage = metaValueToString <$> lookupMeta "coverage" meta
364363 rights = metaValueToString <$> lookupMeta "rights" meta
365364 belongsToCollection = metaValueToString <$> lookupMeta "belongs-to-collection" meta
366− groupPosition = metaValueToString <$> lookupMeta "group-position" meta
367− coverImage =
368− (TS.unpack <$> lookupContext "epub-cover-image"
369− (writerVariables opts))
365+ groupPosition = metaValueToString <$> lookupMeta "group-position" meta
366+ coverImage = T.unpack <$>
367+ lookupContext "epub-cover-image" (writerVariables opts)
370368 `mplus` (metaValueToString <$> lookupMeta "cover-image" meta)
371369 mCss = lookupMeta "css" meta <|> lookupMeta "stylesheet" meta
372370 stylesheets = maybe [] metaValueToPaths mCss ++
373371 case lookupContext "css" (writerVariables opts) of
374− Just xs -> map TS.unpack xs
372+ Just xs -> map T.unpack xs
375373 Nothing ->
376374 case lookupContext "css" (writerVariables opts) of
377− Just x -> [TS.unpack x]
375+ Just x -> [T.unpack x]
378376 Nothing -> []
379− pageDirection = case map toLower . metaValueToString <$>
377+ pageDirection = case T.toLower . metaValueToString <$>
380378 lookupMeta "page-progression-direction" meta of
381379 Just "ltr" -> Just LTR
382380 Just "rtl" -> Just RTL
383381 _ -> Nothing
384382 ibooksFields = case lookupMeta "ibooks" meta of
385383 Just (MetaMap mp)
386− -> M.toList $ M.mapKeys TS.unpack $ M.map metaValueToString mp
384+ -> M.toList $ M.map metaValueToString mp
387385 _ -> []
388386 calibreFields = case lookupMeta "calibre" meta of
389387 Just (MetaMap mp)
390− -> M.toList $ M.mapKeys TS.unpack $ M.map metaValueToString mp
388+ -> M.toList $ M.map metaValueToString mp
391389 _ -> []
392390
393391-- | Produce an EPUB2 file from a Pandoc document.
@@ -413,9 +411,11 @@ writeEPUB :: PandocMonad m
413411writeEPUB epubVersion opts doc = do
414412 let epubSubdir = writerEpubSubdirectory opts
415413 -- sanity check on epubSubdir
416− unless (TS.all (\c -> isAscii c && isAlphaNum c) epubSubdir) $
414+ unless (T.all (\c -> isAscii c && isAlphaNum c) epubSubdir) $
417415 throwError $ PandocEpubSubdirectoryError epubSubdir
418− let initState = EPUBState { stMediaPaths = [], stMediaNextId = 0, stEpubSubdir = TS.unpack epubSubdir }
416+ let initState = EPUBState { stMediaPaths = []
417+ , stMediaNextId = 0
418+ , stEpubSubdir = T.unpack epubSubdir }
419419 evalStateT (pandocToEPUB epubVersion opts doc) initState
420420
421421pandocToEPUB :: PandocMonad m
@@ -439,7 +439,7 @@ pandocToEPUB version opts doc = do
439439 [] -> case epubTitle metadata of
440440 [] -> "UNTITLED"
441441 (x:_) -> titleText x
442− x -> TS.unpack $ stringify x
442+ x -> stringify x
443443
444444 -- stylesheet
445445 stylesheets <- case epubStylesheets metadata of
@@ -461,7 +461,8 @@ pandocToEPUB version opts doc = do
461461 (ListVal $ map
462462 (\e -> toVal' $
463463 (if useprefix then "../" else "") <>
464− makeRelative epubSubdir (eRelativePath e))
464+ T.pack
465+ (makeRelative epubSubdir (eRelativePath e)))
465466 stylesheetEntries)
466467 mempty
467468
@@ -490,18 +491,19 @@ pandocToEPUB version opts doc = do
490491 case imageSize opts' (B.toStrict imgContent) of
491492 Right sz -> return $ sizeInPixels sz
492493 Left err' -> (0, 0) <$ report
493− (CouldNotDetermineImageSize (TS.pack img) err')
494+ (CouldNotDetermineImageSize (T.pack img) err')
494495 cpContent <- lift $ writeHtml
495496 opts'{ writerVariables =
496497 Context (M.fromList [
497498 ("coverpage", toVal' "true"),
498499 ("pagetitle", toVal $
499− escapeStringForXML $ TS.pack plainTitle),
500− ("cover-image", toVal' coverImageName),
500+ escapeStringForXML plainTitle),
501+ ("cover-image",
502+ toVal' $ T.pack coverImageName),
501503 ("cover-image-width", toVal' $
502− show coverImageWidth),
504+ tshow coverImageWidth),
503505 ("cover-image-height", toVal' $
504− show coverImageHeight)]) <>
506+ tshow coverImageHeight)]) <>
505507 cssvars True <> vars }
506508 (Pandoc meta [])
507509 coverEntry <- mkEntry "text/cover.xhtml" cpContent
@@ -517,7 +519,7 @@ pandocToEPUB version opts doc = do
517519 ("titlepage", toVal' "true"),
518520 ("body-type", toVal' "frontmatter"),
519521 ("pagetitle", toVal $
520− escapeStringForXML $ TS.pack plainTitle)])
522+ escapeStringForXML plainTitle)])
521523 <> cssvars True <> vars }
522524 (Pandoc meta [])
523525 tpEntry <- mkEntry "text/title_page.xhtml" tpContent
@@ -526,7 +528,7 @@ pandocToEPUB version opts doc = do
526528 let matchingGlob f = do
527529 xs <- lift $ P.glob f
528530 when (null xs) $
529− report $ CouldNotFetchResource (TS.pack f) "glob did not match any font files"
531+ report $ CouldNotFetchResource (T.pack f) "glob did not match any font files"
530532 return xs
531533 let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<<
532534 lift (P.readFileLazy f)
@@ -573,13 +575,13 @@ pandocToEPUB version opts doc = do
573575
574576 let chapters' = secsToChapters secs
575577
576− let extractLinkURL' :: Int -> Inline -> [(TS.Text, TS.Text)]
578+ let extractLinkURL' :: Int -> Inline -> [(T.Text, T.Text)]
577579 extractLinkURL' num (Span (ident, _, _) _)
578− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
580+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
579581 extractLinkURL' num (Link (ident, _, _) _ _)
580− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
582+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
581583 extractLinkURL' num (Image (ident, _, _) _ _)
582− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
584+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
583585 extractLinkURL' num (RawInline fmt raw)
584586 | isHtmlFormat fmt
585587 = foldr (\tag ->
@@ -587,18 +589,18 @@ pandocToEPUB version opts doc = do
587589 TagOpen{} ->
588590 case fromAttrib "id" tag of
589591 "" -> id
590− x -> ((x, TS.pack (showChapter num) <> "#" <> x):)
592+ x -> ((x, showChapter num <> "#" <> x):)
591593 _ -> id)
592594 [] (parseTags raw)
593595 extractLinkURL' _ _ = []
594596
595− let extractLinkURL :: Int -> Block -> [(TS.Text, TS.Text)]
597+ let extractLinkURL :: Int -> Block -> [(T.Text, T.Text)]
596598 extractLinkURL num (Div (ident, _, _) _)
597− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
599+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
598600 extractLinkURL num (Header _ (ident, _, _) _)
599− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
601+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
600602 extractLinkURL num (Table (ident,_,_) _ _ _ _ _)
601− | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)]
603+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
602604 extractLinkURL num (RawBlock fmt raw)
603605 | isHtmlFormat fmt
604606 = foldr (\tag ->
@@ -606,7 +608,7 @@ pandocToEPUB version opts doc = do
606608 TagOpen{} ->
607609 case fromAttrib "id" tag of
608610 "" -> id
609− x -> ((x, TS.pack (showChapter num) <> "#" <> x):)
611+ x -> ((x, showChapter num <> "#" <> x):)
610612 _ -> id)
611613 [] (parseTags raw)
612614 extractLinkURL num b = query (extractLinkURL' num) b
@@ -617,7 +619,7 @@ pandocToEPUB version opts doc = do
617619
618620 let fixInternalReferences :: Inline -> Inline
619621 fixInternalReferences (Link attr lab (src, tit))
620− | Just ('#', xs) <- TS.uncons src = case lookup xs reftable of
622+ | Just ('#', xs) <- T.uncons src = case lookup xs reftable of
621623 Just ys -> Link attr lab (ys, tit)
622624 Nothing -> Link attr lab (src, tit)
623625 fixInternalReferences x = x
@@ -630,7 +632,7 @@ pandocToEPUB version opts doc = do
630632 chapters'
631633
632634 let chapToEntry num (Chapter bs) =
633− mkEntry ("text/" ++ showChapter num) =<<
635+ mkEntry ("text/" ++ T.unpack (showChapter num)) =<<
634636 writeHtml opts'{ writerVariables =
635637 Context (M.fromList
636638 [("body-type", toVal' bodyType),
@@ -677,12 +679,12 @@ pandocToEPUB version opts doc = do
677679 let chapterNode ent = unode "item" !
678680 ([("id", toId $ makeRelative epubSubdir
679681 $ eRelativePath ent),
680− ("href", makeRelative epubSubdir
682+ ("href", T.pack $ makeRelative epubSubdir
681683 $ eRelativePath ent),
682684 ("media-type", "application/xhtml+xml")]
683685 ++ case props ent of
684686 [] -> []
685− xs -> [("properties", unwords xs)])
687+ xs -> [("properties", T.unwords xs)])
686688 $ ()
687689
688690 let chapterRefNode ent = unode "itemref" !
@@ -691,17 +693,17 @@ pandocToEPUB version opts doc = do
691693 let pictureNode ent = unode "item" !
692694 [("id", toId $ makeRelative epubSubdir
693695 $ eRelativePath ent),
694− ("href", makeRelative epubSubdir
696+ ("href", T.pack $ makeRelative epubSubdir
695697 $ eRelativePath ent),
696698 ("media-type",
697− maybe "application/octet-stream" TS.unpack
699+ fromMaybe "application/octet-stream"
698700 $ mediaTypeOf $ eRelativePath ent)] $ ()
699701 let fontNode ent = unode "item" !
700702 [("id", toId $ makeRelative epubSubdir
701703 $ eRelativePath ent),
702− ("href", makeRelative epubSubdir
704+ ("href", T.pack $ makeRelative epubSubdir
703705 $ eRelativePath ent),
704− ("media-type", maybe "" TS.unpack $
706+ ("media-type", fromMaybe "" $
705707 getMimeType $ eRelativePath ent)] $ ()
706708
707709 let tocTitle = maybe plainTitle
@@ -710,7 +712,7 @@ pandocToEPUB version opts doc = do
710712 (x:_) -> return $ identifierText x -- use first identifier as UUID
711713 [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen
712714 currentTime <- lift P.getTimestamp
713− let contentsData = UTF8.fromStringLazy $ ppTopElement $
715+ let contentsData = UTF8.fromTextLazy $ TL.fromStrict $ ppTopElement $
714716 unode "package" !
715717 ([("version", case version of
716718 EPUB2 -> "2.0"
@@ -728,7 +730,8 @@ pandocToEPUB version opts doc = do
728730 ,("media-type","application/xhtml+xml")] ++
729731 [("properties","nav") | epub3 ]) $ ()
730732 ] ++
731− [ unode "item" ! [("id","stylesheet" ++ show n), ("href",fp)
733+ [ unode "item" ! [("id","stylesheet" <> tshow n)
734+ , ("href", T.pack fp)
732735 ,("media-type","text/css")] $ () |
733736 (n :: Int, fp) <- zip [1..] (map
734737 (makeRelative epubSubdir . eRelativePath)
@@ -773,7 +776,7 @@ pandocToEPUB version opts doc = do
773776 let tocLevel = writerTOCDepth opts
774777
775778 let navPointNode :: PandocMonad m
776− => (Int -> [Inline] -> TS.Text -> [Element] -> Element)
779+ => (Int -> [Inline] -> T.Text -> [Element] -> Element)
777780 -> Block -> StateT Int m [Element]
778781 navPointNode formatter (Div (ident,_,_)
779782 (Header lvl (_,_,kvs) ils : children)) =
@@ -783,7 +786,7 @@ pandocToEPUB version opts doc = do
783786 n <- get
784787 modify (+1)
785788 let num = fromMaybe "" $ lookup "number" kvs
786− let tit = if writerNumberSections opts && not (TS.null num)
789+ let tit = if writerNumberSections opts && not (T.null num)
787790 then Span ("", ["section-header-number"], [])
788791 [Str num] : Space : ils
789792 else ils
@@ -797,21 +800,21 @@ pandocToEPUB version opts doc = do
797800 concat <$> mapM (navPointNode formatter) bs
798801 navPointNode _ _ = return []
799802
800− let navMapFormatter :: Int -> [Inline] -> TS.Text -> [Element] -> Element
803+ let navMapFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element
801804 navMapFormatter n tit src subs = unode "navPoint" !
802− [("id", "navPoint-" ++ show n)] $
803− [ unode "navLabel" $ unode "text" $ TS.unpack $ stringify tit
804− , unode "content" ! [("src", "text/" <> TS.unpack src)] $ ()
805+ [("id", "navPoint-" <> tshow n)] $
806+ [ unode "navLabel" $ unode "text" $ stringify tit
807+ , unode "content" ! [("src", "text/" <> src)] $ ()
805808 ] ++ subs
806809
807810 let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $
808− [ unode "navLabel" $ unode "text" (TS.unpack $ stringify $ docTitle' meta)
811+ [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta)
809812 , unode "content" ! [("src", "text/title_page.xhtml")]
810813 $ () ]
811814
812815 navMap <- lift $ evalStateT
813816 (concat <$> mapM (navPointNode navMapFormatter) secs) 1
814− let tocData = UTF8.fromStringLazy $ ppTopElement $
817+ let tocData = B.fromStrict $ UTF8.fromText $ ppTopElement $
815818 unode "ncx" ! [("version","2005-1")
816819 ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $
817820 [ unode "head" $
@@ -833,11 +836,11 @@ pandocToEPUB version opts doc = do
833836 ]
834837 tocEntry <- mkEntry "toc.ncx" tocData
835838
836− let navXhtmlFormatter :: Int -> [Inline] -> TS.Text -> [Element] -> Element
839+ let navXhtmlFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element
837840 navXhtmlFormatter n tit src subs = unode "li" !
838− [("id", "toc-li-" ++ show n)] $
841+ [("id", "toc-li-" <> tshow n)] $
839842 (unode "a" !
840− [("href", "text/" <> TS.unpack src)]
843+ [("href", "text/" <> src)]
841844 $ titElements)
842845 : case subs of
843846 [] -> []
@@ -850,7 +853,7 @@ pandocToEPUB version opts doc = do
850853 , writerVariables =
851854 Context (M.fromList
852855 [("pagetitle", toVal $
853− escapeStringForXML $ TS.pack plainTitle)])
856+ escapeStringForXML plainTitle)])
854857 <> writerVariables opts}
855858 (Pandoc nullMeta
856859 [Plain $ walk clean tit])) of
@@ -865,7 +868,7 @@ pandocToEPUB version opts doc = do
865868 tocBlocks <- lift $ evalStateT
866869 (concat <$> mapM (navPointNode navXhtmlFormatter) secs) 1
867870 let navBlocks = [RawBlock (Format "html")
868− $ TS.pack $ showElement $ -- prettyprinting introduces bad spaces
871+ $ showElement $ -- prettyprinting introduces bad spaces
869872 unode navtag ! ([("epub:type","toc") | epub3] ++
870873 [("id","toc")]) $
871874 [ unode "h1" ! [("id","toc-title")] $ tocTitle
@@ -875,21 +878,21 @@ pandocToEPUB version opts doc = do
875878 [ unode "a" ! [("href",
876879 "text/title_page.xhtml")
877880 ,("epub:type", "titlepage")] $
878− ("Title Page" :: String) ] :
881+ ("Title Page" :: Text) ] :
879882 [ unode "li"
880883 [ unode "a" ! [("href", "text/cover.xhtml")
881884 ,("epub:type", "cover")] $
882− ("Cover" :: String)] |
885+ ("Cover" :: Text)] |
883886 isJust (epubCoverImage metadata)
884887 ] ++
885888 [ unode "li"
886889 [ unode "a" ! [("href", "#toc")
887890 ,("epub:type", "toc")] $
888− ("Table of Contents" :: String)
891+ ("Table of Contents" :: Text)
889892 ] | writerTableOfContents opts
890893 ]
891894 else []
892− let landmarks = [RawBlock (Format "html") $ TS.pack $ ppElement $
895+ let landmarks = [RawBlock (Format "html") $ ppElement $
893896 unode "nav" ! [("epub:type","landmarks")
894897 ,("id","landmarks")
895898 ,("hidden","hidden")] $
@@ -910,22 +913,22 @@ pandocToEPUB version opts doc = do
910913 UTF8.fromStringLazy "application/epub+zip"
911914
912915 -- container.xml
913− let containerData = UTF8.fromStringLazy $ ppTopElement $
916+ let containerData = B.fromStrict $ UTF8.fromText $ ppTopElement $
914917 unode "container" ! [("version","1.0")
915918 ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $
916919 unode "rootfiles" $
917920 unode "rootfile" ! [("full-path",
918921 (if null epubSubdir
919922 then ""
920− else epubSubdir ++ "/") ++ "content.opf")
923+ else T.pack epubSubdir <> "/") <> "content.opf")
921924 ,("media-type","application/oebps-package+xml")] $ ()
922925 containerEntry <- mkEntry "META-INF/container.xml" containerData
923926
924927 -- com.apple.ibooks.display-options.xml
925− let apple = UTF8.fromStringLazy $ ppTopElement $
928+ let apple = B.fromStrict $ UTF8.fromText $ ppTopElement $
926929 unode "display_options" $
927930 unode "platform" ! [("name","*")] $
928− unode "option" ! [("name","specified-fonts")] $ ("true" :: String)
931+ unode "option" ! [("name","specified-fonts")] $ ("true" :: Text)
929932 appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple
930933
931934 -- construct archive
@@ -947,7 +950,8 @@ metadataElement version md currentTime =
947950 ++ publisherNodes ++ sourceNodes ++ relationNodes
948951 ++ coverageNodes ++ rightsNodes ++ coverImageNodes
949952 ++ modifiedNodes ++ belongsToCollectionNodes
950− withIds base f = concat . zipWith f (map (\x -> base ++ ('-' : show x))
953+ withIds base f = concat . zipWith f (map (\x -> base <>
954+ T.cons '-' (tshow x))
951955 ([1..] :: [Int]))
952956 identifierNodes = withIds "epub-id" toIdentifierNode $
953957 epubIdentifier md
@@ -961,9 +965,9 @@ metadataElement version md currentTime =
961965 (x:_) -> [dcNode "date" ! [("id","epub-date")]
962966 $ dateText x]
963967 ibooksNodes = map ibooksNode (epubIbooksFields md)
964− ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" ++ k)] $ v
968+ ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" <> k)] $ v
965969 calibreNodes = map calibreNode (epubCalibreFields md)
966− calibreNode (k, v) = unode "meta" ! [("name", "calibre:" ++ k),
970+ calibreNode (k, v) = unode "meta" ! [("name", "calibre:" <> k),
967971 ("content", v)] $ ()
968972 languageNodes = [dcTag "language" $ epubLanguage md]
969973 creatorNodes = withIds "epub-creator" (toCreatorNode "creator") $
@@ -989,12 +993,12 @@ metadataElement version md currentTime =
989993 maybe []
990994 (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-id-1")] $ belongsToCollection )
991995 :
992− [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: String) ])
996+ [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: Text) ])
993997 (epubBelongsToCollection md)++
994998 maybe []
995999 (\groupPosition -> [unode "meta" ! [("refines", "#epub-id-1"), ("property", "group-position")] $ groupPosition ])
9961000 (epubGroupPosition md)
997− dcTag n s = unode ("dc:" ++ n) s
1001+ dcTag n s = unode ("dc:" <> n) s
9981002 dcTag' n s = [dcTag n s]
9991003 toIdentifierNode id' (Identifier txt scheme)
10001004 | version == EPUB2 = [dcNode "identifier" !
@@ -1002,7 +1006,7 @@ metadataElement version md currentTime =
10021006 txt]
10031007 | otherwise = (dcNode "identifier" ! [("id",id')] $ txt) :
10041008 maybe [] ((\x -> [unode "meta" !
1005− [ ("refines",'#':id')
1009+ [ ("refines","#" <> id')
10061010 , ("property","identifier-type")
10071011 , ("scheme","onix:codelist5")
10081012 ]
@@ -1018,10 +1022,10 @@ metadataElement version md currentTime =
10181022 (creatorRole creator >>= toRelator)) $ creatorText creator]
10191023 | otherwise = [dcNode s ! [("id",id')] $ creatorText creator] ++
10201024 maybe [] (\x -> [unode "meta" !
1021− [("refines",'#':id'),("property","file-as")] $ x])
1025+ [("refines","#" <> id'),("property","file-as")] $ x])
10221026 (creatorFileAs creator) ++
10231027 maybe [] (\x -> [unode "meta" !
1024− [("refines",'#':id'),("property","role"),
1028+ [("refines","#" <> id'),("property","role"),
10251029 ("scheme","marc:relators")] $ x])
10261030 (creatorRole creator >>= toRelator)
10271031 toTitleNode id' title
@@ -1033,16 +1037,16 @@ metadataElement version md currentTime =
10331037 | otherwise = [dcNode "title" ! [("id",id')] $ titleText title]
10341038 ++
10351039 maybe [] (\x -> [unode "meta" !
1036− [("refines",'#':id'),("property","file-as")] $ x])
1040+ [("refines","#" <> id'),("property","file-as")] $ x])
10371041 (titleFileAs title) ++
10381042 maybe [] (\x -> [unode "meta" !
1039− [("refines",'#':id'),("property","title-type")] $ x])
1043+ [("refines","#" <> id'),("property","title-type")] $ x])
10401044 (titleType title)
10411045 toDateNode id' date = [dcNode "date" !
10421046 (("id",id') :
10431047 maybe [] (\x -> [("opf:event",x)]) (dateEvent date)) $
10441048 dateText date]
1045− schemeToOnix :: String -> String
1049+ schemeToOnix :: Text -> Text
10461050 schemeToOnix "ISBN-10" = "02"
10471051 schemeToOnix "GTIN-13" = "03"
10481052 schemeToOnix "UPC" = "04"
@@ -1060,48 +1064,48 @@ metadataElement version md currentTime =
10601064 schemeToOnix "OLCC" = "28"
10611065 schemeToOnix _ = "01"
10621066
1063−showDateTimeISO8601 :: UTCTime -> String
1064−showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ"
1067+showDateTimeISO8601 :: UTCTime -> Text
1068+showDateTimeISO8601 = T.pack . formatTime defaultTimeLocale "%FT%TZ"
10651069
10661070transformTag :: PandocMonad m
1067− => Tag TS.Text
1068− -> E m (Tag TS.Text)
1071+ => Tag T.Text
1072+ -> E m (Tag T.Text)
10691073transformTag tag@(TagOpen name attr)
10701074 | name `elem` ["video", "source", "img", "audio"] &&
10711075 isNothing (lookup "data-external" attr) = do
10721076 let src = fromAttrib "src" tag
10731077 let poster = fromAttrib "poster" tag
1074− newsrc <- modifyMediaRef $ TS.unpack src
1075− newposter <- modifyMediaRef $ TS.unpack poster
1078+ newsrc <- modifyMediaRef $ T.unpack src
1079+ newposter <- modifyMediaRef $ T.unpack poster
10761080 let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++
1077− [("src", "../" <> newsrc) | not (TS.null newsrc)] ++
1078− [("poster", "../" <> newposter) | not (TS.null newposter)]
1081+ [("src", "../" <> newsrc) | not (T.null newsrc)] ++
1082+ [("poster", "../" <> newposter) | not (T.null newposter)]
10791083 return $ TagOpen name attr'
10801084transformTag tag = return tag
10811085
10821086modifyMediaRef :: PandocMonad m
10831087 => FilePath
1084− -> E m TS.Text
1088+ -> E m T.Text
10851089modifyMediaRef "" = return ""
10861090modifyMediaRef oldsrc = do
10871091 media <- gets stMediaPaths
10881092 case lookup oldsrc media of
1089− Just (n,_) -> return $ TS.pack n
1093+ Just (n,_) -> return $ T.pack n
10901094 Nothing -> catchError
1091− (do (img, mbMime) <- P.fetchItem $ TS.pack oldsrc
1092− let ext = maybe (takeExtension (takeWhile (/='?') oldsrc)) TS.unpack
1095+ (do (img, mbMime) <- P.fetchItem $ T.pack oldsrc
1096+ let ext = maybe (takeExtension (takeWhile (/='?') oldsrc)) T.unpack
10931097 (("." <>) <$> (mbMime >>= extensionFromMimeType))
10941098 newName <- getMediaNextNewName ext
10951099 let newPath = "media/" ++ newName
10961100 entry <- mkEntry newPath (B.fromChunks . (:[]) $ img)
10971101 modify $ \st -> st{ stMediaPaths =
10981102 (oldsrc, (newPath, Just entry)):media}
1099− return $ TS.pack newPath)
1103+ return $ T.pack newPath)
11001104 (\e -> do
1101− report $ CouldNotFetchResource (TS.pack oldsrc) (tshow e)
1102− return $ TS.pack oldsrc)
1105+ report $ CouldNotFetchResource (T.pack oldsrc) (tshow e)
1106+ return $ T.pack oldsrc)
11031107
1104−getMediaNextNewName :: PandocMonad m => String -> E m String
1108+getMediaNextNewName :: PandocMonad m => FilePath -> E m FilePath
11051109getMediaNextNewName ext = do
11061110 nextId <- gets stMediaNextId
11071111 modify $ \st -> st { stMediaNextId = nextId + 1 }
@@ -1128,11 +1132,11 @@ transformInline :: PandocMonad m
11281132 -> Inline
11291133 -> E m Inline
11301134transformInline _opts (Image attr lab (src,tit)) = do
1131− newsrc <- modifyMediaRef $ TS.unpack src
1135+ newsrc <- modifyMediaRef $ T.unpack src
11321136 return $ Image attr lab ("../" <> newsrc, tit)
11331137transformInline opts x@(Math t m)
11341138 | WebTeX url <- writerHTMLMathMethod opts = do
1135− newsrc <- modifyMediaRef (TS.unpack url <> urlEncode (TS.unpack m))
1139+ newsrc <- modifyMediaRef (T.unpack url <> urlEncode (T.unpack m))
11361140 let mathclass = if t == DisplayMath then "display" else "inline"
11371141 return $ Span ("",["math",mathclass],[])
11381142 [Image nullAttr [x] ("../" <> newsrc, "")]
@@ -1143,40 +1147,26 @@ transformInline _opts (RawInline fmt raw)
11431147 return $ RawInline fmt (renderTags' tags')
11441148transformInline _ x = return x
11451149
1146−(!) :: (t -> Element) -> [(String, String)] -> t -> Element
1150+(!) :: (t -> Element) -> [(Text, Text)] -> t -> Element
11471151(!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n)
11481152
1149−-- | Version of 'ppTopElement' that specifies UTF-8 encoding.
1150−ppTopElement :: Element -> String
1151−ppTopElement = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ++) . unEntity . ppElement
1152− -- unEntity removes numeric entities introduced by ppElement
1153− -- (kindlegen seems to choke on these).
1154− where unEntity [] = ""
1155− unEntity ('&':'#':xs) =
1156− let (ds,ys) = break (==';') xs
1157− rest = drop 1 ys
1158− in case safeRead (TS.pack $ "'\\" <> ds <> "'") of
1159− Just x -> x : unEntity rest
1160− Nothing -> '&':'#':unEntity xs
1161− unEntity (x:xs) = x : unEntity xs
1162−
11631153mediaTypeOf :: FilePath -> Maybe MimeType
11641154mediaTypeOf x =
11651155 let mediaPrefixes = ["image", "video", "audio"] in
11661156 case getMimeType x of
1167− Just y | any (`TS.isPrefixOf` y) mediaPrefixes -> Just y
1157+ Just y | any (`T.isPrefixOf` y) mediaPrefixes -> Just y
11681158 _ -> Nothing
11691159
11701160-- Returns filename for chapter number.
1171−showChapter :: Int -> String
1172−showChapter = printf "ch%03d.xhtml"
1161+showChapter :: Int -> Text
1162+showChapter = T.pack . printf "ch%03d.xhtml"
11731163
11741164-- Add identifiers to any headers without them.
11751165addIdentifiers :: WriterOptions -> [Block] -> [Block]
11761166addIdentifiers opts bs = evalState (mapM go bs) Set.empty
11771167 where go (Header n (ident,classes,kvs) ils) = do
11781168 ids <- get
1179− let ident' = if TS.null ident
1169+ let ident' = if T.null ident
11801170 then uniqueIdent (writerExtensions opts) ils ids
11811171 else ident
11821172 modify $ Set.insert ident'
@@ -1184,27 +1174,27 @@ addIdentifiers opts bs = evalState (mapM go bs) Set.empty
11841174 go x = return x
11851175
11861176-- Variant of normalizeDate that allows partial dates: YYYY, YYYY-MM
1187−normalizeDate' :: String -> Maybe String
1188−normalizeDate' = fmap TS.unpack . go . trim . TS.pack
1177+normalizeDate' :: Text -> Maybe Text
1178+normalizeDate' = go . T.strip
11891179 where
11901180 go xs
1191− | TS.length xs == 4 -- YYY
1192− , TS.all isDigit xs = Just xs
1193− | (y, s) <- TS.splitAt 4 xs -- YYY-MM
1194− , Just ('-', m) <- TS.uncons s
1195− , TS.length m == 2
1196− , TS.all isDigit y && TS.all isDigit m = Just xs
1181+ | T.length xs == 4 -- YYY
1182+ , T.all isDigit xs = Just xs
1183+ | (y, s) <- T.splitAt 4 xs -- YYY-MM
1184+ , Just ('-', m) <- T.uncons s
1185+ , T.length m == 2
1186+ , T.all isDigit y && T.all isDigit m = Just xs
11971187 | otherwise = normalizeDate xs
11981188
1199−toRelator :: String -> Maybe String
1189+toRelator :: Text -> Maybe Text
12001190toRelator x
12011191 | x `elem` relators = Just x
1202− | otherwise = lookup (map toLower x) relatorMap
1192+ | otherwise = lookup (T.toLower x) relatorMap
12031193
1204−relators :: [String]
1194+relators :: [Text]
12051195relators = map snd relatorMap
12061196
1207−relatorMap :: [(String, String)]
1197+relatorMap :: [(Text, Text)]
12081198relatorMap =
12091199 [("abridger", "abr")
12101200 ,("actor", "act")
src/Text/Pandoc/Writers/FB2.hs+53−74
@@ -25,15 +25,12 @@ import Data.ByteString.Base64 (encode)
2525import Data.Char (isAscii, isControl, isSpace)
2626import Data.Either (lefts, rights)
2727import Data.List (intercalate)
28−import Data.Text (Text, pack)
28+import Data.Text (Text)
2929import qualified Data.Text as T
3030import qualified Data.Text.Lazy as TL
3131import qualified Data.Text.Encoding as TE
3232import Network.HTTP (urlEncode)
33−import Text.XML.Light
34−import qualified Text.XML.Light as X
35−import qualified Text.XML.Light.Cursor as XC
36−import Text.Pandoc.XMLParser (parseXMLContents)
33+import Text.Pandoc.XML.Light as X
3734
3835import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
3936import qualified Text.Pandoc.Class.PandocMonad as P
@@ -44,6 +41,7 @@ import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def)
4441import Text.Pandoc.Shared (capitalize, isURI, orderedListMarkers,
4542 makeSections, tshow, stringify)
4643import Text.Pandoc.Writers.Shared (lookupMetaString, toLegacyTable)
44+import Data.Generics (everywhere, mkT)
4745
4846-- | Data to be written at the end of the document:
4947-- (foot)notes, URLs, references, images.
@@ -88,7 +86,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do
8886 (imgs,missing) <- get >>= (lift . fetchImages . imagesToFetch)
8987 let body' = replaceImagesWithAlt missing body
9088 let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs)
91− return $ pack $ xml_head ++ showContent fb2_xml ++ "\n"
89+ return $ xml_head <> showContent fb2_xml <> "\n"
9290 where
9391 xml_head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
9492 fb2_attrs =
@@ -100,8 +98,8 @@ pandocToFB2 opts (Pandoc meta blocks) = do
10098description :: PandocMonad m => Meta -> FBM m Content
10199description meta' = do
102100 let genre = case lookupMetaString "genre" meta' of
103− "" -> el "genre" ("unrecognised" :: String)
104− s -> el "genre" (T.unpack s)
101+ "" -> el "genre" ("unrecognised" :: Text)
102+ s -> el "genre" s
105103 bt <- booktitle meta'
106104 let as = authors meta'
107105 dd <- docdate meta'
@@ -112,7 +110,7 @@ description meta' = do
112110 Just (MetaInlines [Str s]) -> [el "lang" $ iso639 s]
113111 Just (MetaString s) -> [el "lang" $ iso639 s]
114112 _ -> []
115− where iso639 = T.unpack . T.takeWhile (/= '-') -- Convert BCP 47 to ISO 639
113+ where iso639 = T.takeWhile (/= '-') -- Convert BCP 47 to ISO 639
116114 let coverimage url = do
117115 let img = Image nullAttr mempty (url, "")
118116 im <- insertImage InlineImage img
@@ -124,7 +122,7 @@ description meta' = do
124122 return $ el "description"
125123 [ el "title-info" (genre :
126124 (as ++ bt ++ annotation ++ dd ++ coverpage ++ lang))
127− , el "document-info" [el "program-used" ("pandoc" :: String)]
125+ , el "document-info" [el "program-used" ("pandoc" :: Text)]
128126 ]
129127
130128booktitle :: PandocMonad m => Meta -> FBM m [Content]
@@ -137,15 +135,15 @@ authors meta' = cMap author (docAuthors meta')
137135
138136author :: [Inline] -> [Content]
139137author ss =
140− let ws = words . cMap plain $ ss
141− email = el "email" <$> take 1 (filter ('@' `elem`) ws)
142− ws' = filter ('@' `notElem`) ws
138+ let ws = T.words $ mconcat $ map plain ss
139+ email = el "email" <$> take 1 (filter (T.any (=='@')) ws)
140+ ws' = filter (not . T.any (== '@')) ws
143141 names = case ws' of
144142 [nickname] -> [ el "nickname" nickname ]
145143 [fname, lname] -> [ el "first-name" fname
146144 , el "last-name" lname ]
147145 (fname:rest) -> [ el "first-name" fname
148− , el "middle-name" (concat . init $ rest)
146+ , el "middle-name" (T.concat . init $ rest)
149147 , el "last-name" (last rest) ]
150148 [] -> []
151149 in list $ el "author" (names ++ email)
@@ -206,7 +204,7 @@ renderFootnotes = do
206204 el "body" ([uattr "name" "notes"], map renderFN (reverse fns))
207205 where
208206 renderFN (n, idstr, cs) =
209− let fn_texts = el "title" (el "p" (show n)) : cs
207+ let fn_texts = el "title" (el "p" (tshow n)) : cs
210208 in el "section" ([uattr "id" idstr], fn_texts)
211209
212210-- | Fetch images and encode them for the FictionBook XML.
@@ -282,7 +280,7 @@ isMimeType s =
282280 where
283281 types = ["text","image","audio","video","application","message","multipart"]
284282 valid c = isAscii c && not (isControl c) && not (isSpace c) &&
285− c `notElem` ("()<>@,;:\\\"/[]?=" :: String)
283+ c `notElem` ("()<>@,;:\\\"/[]?=" :: [Char])
286284
287285footnoteID :: Int -> Text
288286footnoteID i = "n" <> tshow i
@@ -306,7 +304,7 @@ blockToXml (Para [Image atr alt (src,tgt)])
306304 = insertImage NormalImage (Image atr alt (src,tit))
307305blockToXml (Para ss) = list . el "p" <$> cMapM toXml ss
308306blockToXml (CodeBlock _ s) = return . spaceBeforeAfter .
309− map (el "p" . el "code" . T.unpack) . T.lines $ s
307+ map (el "p" . el "code") . T.lines $ s
310308blockToXml (RawBlock f str) =
311309 if f == Format "fb2"
312310 then
@@ -346,11 +344,11 @@ blockToXml (Table _ blkCapt specs thead tbody tfoot) = do
346344 c <- el "emphasis" <$> cMapM toXml caption
347345 return [el "table" (hd <> bd), el "p" c]
348346 where
349− mkrow :: PandocMonad m => String -> [[Block]] -> [Alignment] -> FBM m Content
347+ mkrow :: PandocMonad m => Text -> [[Block]] -> [Alignment] -> FBM m Content
350348 mkrow tag cells aligns' =
351349 el "tr" <$> mapM (mkcell tag) (zip cells aligns')
352350 --
353− mkcell :: PandocMonad m => String -> ([Block], Alignment) -> FBM m Content
351+ mkcell :: PandocMonad m => Text -> ([Block], Alignment) -> FBM m Content
354352 mkcell tag (cell, align) = do
355353 cblocks <- cMapM blockToXml cell
356354 return $ el tag ([align_attr align], cblocks)
@@ -424,7 +422,7 @@ toXml (Quoted DoubleQuote ss) = do
424422 inner <- cMapM toXml ss
425423 return $ [txt "“"] ++ inner ++ [txt "”"]
426424toXml (Cite _ ss) = cMapM toXml ss -- FIXME: support citation styles
427−toXml (Code _ s) = return [el "code" $ T.unpack s]
425+toXml (Code _ s) = return [el "code" s]
428426toXml Space = return [txt " "]
429427toXml SoftBreak = return [txt "\n"]
430428toXml LineBreak = return [txt "\n"]
@@ -456,7 +454,7 @@ insertMath immode formula = do
456454 let imgurl = url <> T.pack (urlEncode $ T.unpack formula)
457455 let img = Image nullAttr alt (imgurl, "")
458456 insertImage immode img
459− _ -> return [el "code" $ T.unpack formula]
457+ _ -> return [el "code" formula]
460458
461459insertImage :: PandocMonad m => ImageMode -> Inline -> FBM m [Content]
462460insertImage immode (Image _ alt (url,ttl)) = do
@@ -471,31 +469,16 @@ insertImage immode (Image _ alt (url,ttl)) = do
471469 el "image" $
472470 [ attr ("l","href") ("#" <> fname)
473471 , attr ("l","type") (tshow immode)
474− , uattr "alt" (T.pack $ cMap plain alt) ]
472+ , uattr "alt" (mconcat $ map plain alt) ]
475473 ++ ttlattr
476474insertImage _ _ = error "unexpected inline instead of image"
477475
478476replaceImagesWithAlt :: [Text] -> Content -> Content
479−replaceImagesWithAlt missingHrefs body =
480− let cur = XC.fromContent body
481− cur' = replaceAll cur
482− in XC.toTree . XC.root $ cur'
477+replaceImagesWithAlt missingHrefs = everywhere (mkT go)
483478 where
484− --
485− replaceAll :: XC.Cursor -> XC.Cursor
486− replaceAll c =
487− let n = XC.current c
488− c' = if isImage n && isMissing n
489− then XC.modifyContent replaceNode c
490− else c
491− in case XC.nextDF c' of
492− (Just cnext) -> replaceAll cnext
493− Nothing -> c' -- end of document
494− --
495− isImage :: Content -> Bool
496− isImage (Elem e) = elName e == uname "image"
497− isImage _ = False
498− --
479+ go c = if isMissing c
480+ then replaceNode c
481+ else c
499482 isMissing (Elem img@Element{}) =
500483 let imgAttrs = elAttribs img
501484 badAttrs = map (attr ("l","href")) missingHrefs
@@ -505,18 +488,18 @@ replaceImagesWithAlt missingHrefs body =
505488 replaceNode :: Content -> Content
506489 replaceNode n@(Elem img@Element{}) =
507490 let attrs = elAttribs img
508− alt = getAttrVal attrs (uname "alt")
491+ alt = getAttrVal attrs (unqual "alt")
509492 imtype = getAttrVal attrs (qname "l" "type")
510493 in case (alt, imtype) of
511494 (Just alt', Just imtype') ->
512− if imtype' == show NormalImage
495+ if imtype' == tshow NormalImage
513496 then el "p" alt'
514− else txt $ T.pack alt'
515− (Just alt', Nothing) -> txt $ T.pack alt' -- no type attribute
497+ else txt alt'
498+ (Just alt', Nothing) -> txt alt' -- no type attribute
516499 _ -> n -- don't replace if alt text is not found
517500 replaceNode n = n
518501 --
519− getAttrVal :: [X.Attr] -> QName -> Maybe String
502+ getAttrVal :: [X.Attr] -> QName -> Maybe Text
520503 getAttrVal attrs name =
521504 case filter ((name ==) . attrKey) attrs of
522505 (a:_) -> Just (attrVal a)
@@ -524,7 +507,7 @@ replaceImagesWithAlt missingHrefs body =
524507
525508
526509-- | Wrap all inlines with an XML tag (given its unqualified name).
527−wrap :: PandocMonad m => String -> [Inline] -> FBM m Content
510+wrap :: PandocMonad m => Text -> [Inline] -> FBM m Content
528511wrap tagname inlines = el tagname `liftM` cMapM toXml inlines
529512
530513-- " Create a singleton list.
@@ -532,31 +515,31 @@ list :: a -> [a]
532515list = (:[])
533516
534517-- | Convert an 'Inline' to plaintext.
535−plain :: Inline -> String
536−plain (Str s) = T.unpack s
537−plain (Emph ss) = cMap plain ss
538−plain (Underline ss) = cMap plain ss
539−plain (Span _ ss) = cMap plain ss
540−plain (Strong ss) = cMap plain ss
541−plain (Strikeout ss) = cMap plain ss
542−plain (Superscript ss) = cMap plain ss
543−plain (Subscript ss) = cMap plain ss
544−plain (SmallCaps ss) = cMap plain ss
545−plain (Quoted _ ss) = cMap plain ss
546−plain (Cite _ ss) = cMap plain ss -- FIXME
547−plain (Code _ s) = T.unpack s
518+plain :: Inline -> Text
519+plain (Str s) = s
520+plain (Emph ss) = mconcat $ map plain ss
521+plain (Underline ss) = mconcat $ map plain ss
522+plain (Span _ ss) = mconcat $ map plain ss
523+plain (Strong ss) = mconcat $ map plain ss
524+plain (Strikeout ss) = mconcat $ map plain ss
525+plain (Superscript ss) = mconcat $ map plain ss
526+plain (Subscript ss) = mconcat $ map plain ss
527+plain (SmallCaps ss) = mconcat $ map plain ss
528+plain (Quoted _ ss) = mconcat $ map plain ss
529+plain (Cite _ ss) = mconcat $ map plain ss -- FIXME
530+plain (Code _ s) = s
548531plain Space = " "
549532plain SoftBreak = " "
550533plain LineBreak = "\n"
551−plain (Math _ s) = T.unpack s
534+plain (Math _ s) = s
552535plain (RawInline _ _) = ""
553−plain (Link _ text (url,_)) = concat (map plain text ++ [" <", T.unpack url, ">"])
554−plain (Image _ alt _) = cMap plain alt
536+plain (Link _ text (url,_)) = mconcat (map plain text ++ [" <", url, ">"])
537+plain (Image _ alt _) = mconcat $ map plain alt
555538plain (Note _) = "" -- FIXME
556539
557540-- | Create an XML element.
558541el :: (Node t)
559− => String -- ^ unqualified element name
542+ => Text -- ^ unqualified element name
560543 -> t -- ^ node contents
561544 -> Content -- ^ XML content
562545el name cs = Elem $ unode name cs
@@ -569,22 +552,18 @@ spaceBeforeAfter cs =
569552
570553-- | Create a plain-text XML content.
571554txt :: Text -> Content
572−txt s = Text $ CData CDataText (T.unpack s) Nothing
555+txt s = Text $ CData CDataText s Nothing
573556
574557-- | Create an XML attribute with an unqualified name.
575−uattr :: String -> Text -> Text.XML.Light.Attr
576−uattr name = Attr (uname name) . T.unpack
558+uattr :: Text -> Text -> X.Attr
559+uattr name = Attr (unqual name)
577560
578561-- | Create an XML attribute with a qualified name from given namespace.
579−attr :: (String, String) -> Text -> Text.XML.Light.Attr
580−attr (ns, name) = Attr (qname ns name) . T.unpack
581−
582−-- | Unqualified name
583−uname :: String -> QName
584−uname name = QName name Nothing Nothing
562+attr :: (Text, Text) -> Text -> X.Attr
563+attr (ns, name) = Attr (qname ns name)
585564
586565-- | Qualified name
587−qname :: String -> String -> QName
566+qname :: Text -> Text -> QName
588567qname ns name = QName name Nothing (Just ns)
589568
590569-- | Abbreviation for 'concatMap'.
src/Text/Pandoc/Writers/ODT.hs+9−7
@@ -40,9 +40,9 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy)
4040import Text.Pandoc.Walk
4141import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
4242import Text.Pandoc.XML
43−import Text.Pandoc.XMLParser (parseXMLElement)
43+import Text.Pandoc.XML.Light
4444import Text.TeXMath
45−import Text.XML.Light
45+import qualified Text.XML.Light as XL
4646
4747newtype ODTState = ODTState { stEntries :: [Entry]
4848 }
@@ -181,18 +181,20 @@ updateStyleWithLang (Just lang) arch = do
181181 PandocXMLError "styles.xml" msg
182182 Right d -> return $
183183 toEntry "styles.xml" epochtime
184− ( fromStringLazy
184+ ( fromTextLazy
185+ . TL.fromStrict
185186 . ppTopElement
186187 . addLang lang $ d )
187188 else return e) (zEntries arch)
188189 return arch{ zEntries = entries }
189190
191+-- TODO FIXME avoid this generic traversal!
190192addLang :: Lang -> Element -> Element
191193addLang lang = everywhere' (mkT updateLangAttr)
192194 where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _)
193− = Attr n (T.unpack $ langLanguage lang)
195+ = Attr n (langLanguage lang)
194196 updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _)
195− = Attr n (T.unpack $ langRegion lang)
197+ = Attr n (langRegion lang)
196198 updateLangAttr x = x
197199
198200-- | transform both Image and Math elements
@@ -238,8 +240,8 @@ transformPicMath _ (Math t math) = do
238240 case writeMathML dt <$> readTeX math of
239241 Left _ -> return $ Math t math
240242 Right r -> do
241− let conf = useShortEmptyTags (const False) defaultConfigPP
242− let mathml = ppcTopElement conf r
243+ let conf = XL.useShortEmptyTags (const False) XL.defaultConfigPP
244+ let mathml = XL.ppcTopElement conf r
243245 epochtime <- floor `fmap` lift P.getPOSIXTime
244246 let dirname = "Formula-" ++ show (length entries) ++ "/"
245247 let fname = dirname ++ "content.xml"
src/Text/Pandoc/Writers/OOXML.hs+17−18
@@ -29,33 +29,32 @@ import Control.Monad.Except (throwError)
2929import Text.Pandoc.Error
3030import qualified Data.ByteString as B
3131import qualified Data.ByteString.Lazy as BL
32−import qualified Data.ByteString.Lazy.Char8 as BL8
3332import Data.Maybe (mapMaybe)
3433import qualified Data.Text as T
34+import Data.Text (Text)
3535import Text.Pandoc.Class.PandocMonad (PandocMonad)
3636import qualified Text.Pandoc.UTF8 as UTF8
37−import Text.XML.Light as XML
38−import Text.Pandoc.XMLParser (parseXMLElement)
37+import Text.Pandoc.XML.Light
3938
40−mknode :: Node t => String -> [(String,String)] -> t -> Element
39+mknode :: Node t => Text -> [(Text,Text)] -> t -> Element
4140mknode s attrs =
4241 add_attrs (map (\(k,v) -> Attr (nodename k) v) attrs) . node (nodename s)
4342
44−mktnode :: String -> [(String,String)] -> T.Text -> Element
45−mktnode s attrs = mknode s attrs . T.unpack
43+mktnode :: Text -> [(Text,Text)] -> T.Text -> Element
44+mktnode s attrs = mknode s attrs
4645
47−nodename :: String -> QName
46+nodename :: Text -> QName
4847nodename s = QName{ qName = name, qURI = Nothing, qPrefix = prefix }
49− where (name, prefix) = case break (==':') s of
50− (xs,[]) -> (xs, Nothing)
51− (ys, _:zs) -> (zs, Just ys)
48+ where (name, prefix) = case T.break (==':') s of
49+ (xs,ys) -> case T.uncons ys of
50+ Nothing -> (xs, Nothing)
51+ Just (_,zs) -> (zs, Just xs)
5252
5353toLazy :: B.ByteString -> BL.ByteString
5454toLazy = BL.fromChunks . (:[])
5555
5656renderXml :: Element -> BL.ByteString
57−renderXml elt = BL8.pack "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" <>
58− UTF8.fromStringLazy (showElement elt)
57+renderXml elt = BL.fromStrict (UTF8.fromText (showTopElement elt))
5958
6059parseXml :: PandocMonad m => Archive -> Archive -> String -> m Element
6160parseXml refArchive distArchive relpath =
@@ -70,25 +69,25 @@ parseXml refArchive distArchive relpath =
7069
7170-- Copied from Util
7271
73−attrToNSPair :: XML.Attr -> Maybe (String, String)
74−attrToNSPair (XML.Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
72+attrToNSPair :: Attr -> Maybe (Text, Text)
73+attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
7574attrToNSPair _ = Nothing
7675
7776
7877elemToNameSpaces :: Element -> NameSpaces
7978elemToNameSpaces = mapMaybe attrToNSPair . elAttribs
8079
81−elemName :: NameSpaces -> String -> String -> QName
80+elemName :: NameSpaces -> Text -> Text -> QName
8281elemName ns prefix name =
83− QName name (lookup prefix ns) (if null prefix then Nothing else Just prefix)
82+ QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix)
8483
85−isElem :: NameSpaces -> String -> String -> Element -> Bool
84+isElem :: NameSpaces -> Text -> Text -> Element -> Bool
8685isElem ns prefix name element =
8786 let ns' = ns ++ elemToNameSpaces element
8887 in qName (elName element) == name &&
8988 qURI (elName element) == lookup prefix ns'
9089
91−type NameSpaces = [(String, String)]
90+type NameSpaces = [(Text, Text)]
9291
9392-- | Scales the image to fit the page
9493-- sizes are passed in emu
src/Text/Pandoc/Writers/Powerpoint/Output.hs+103−98
@@ -20,16 +20,16 @@ import Control.Monad.Except (throwError, catchError)
2020import Control.Monad.Reader
2121import Control.Monad.State
2222import Codec.Archive.Zip
23−import Data.Char (toUpper)
2423import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf, intersperse)
2524import Data.Default
25+import Data.Text (Text)
2626import qualified Data.Text as T
27+import qualified Data.Text.Read
2728import Data.Time (formatTime, defaultTimeLocale)
2829import Data.Time.Clock (UTCTime)
2930import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime)
3031import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension)
31−import Text.XML.Light
32−import Text.Pandoc.XMLParser (parseXMLElement)
32+import Text.Pandoc.XML.Light as XML
3333import Text.Pandoc.Definition
3434import qualified Text.Pandoc.UTF8 as UTF8
3535import Text.Pandoc.Class.PandocMonad (PandocMonad)
@@ -48,6 +48,7 @@ import Text.DocTemplates (FromContext(lookupContext))
4848import Text.TeXMath
4949import Text.Pandoc.Writers.Math (convertMath)
5050import Text.Pandoc.Writers.Powerpoint.Presentation
51+import Text.Pandoc.Shared (tshow)
5152import Skylighting (fromColor)
5253
5354-- |The 'EMU' type is used to specify sizes in English Metric Units.
@@ -84,10 +85,13 @@ getPresentationSize refArchive distArchive = do
8485 sldSize <- findChild (elemName ns "p" "sldSz") presElement
8586 cxS <- findAttr (QName "cx" Nothing Nothing) sldSize
8687 cyS <- findAttr (QName "cy" Nothing Nothing) sldSize
87− (cx, _) <- listToMaybe $ reads cxS :: Maybe (Integer, String)
88− (cy, _) <- listToMaybe $ reads cyS :: Maybe (Integer, String)
88+ cx <- readTextAsInteger cxS
89+ cy <- readTextAsInteger cyS
8990 return (cx `div` 12700, cy `div` 12700)
9091
92+readTextAsInteger :: Text -> Maybe Integer
93+readTextAsInteger = either (const Nothing) (Just . fst) . Data.Text.Read.decimal
94+
9195data WriterEnv = WriterEnv { envRefArchive :: Archive
9296 , envDistArchive :: Archive
9397 , envUTCTime :: UTCTime
@@ -161,9 +165,6 @@ runP env st p = evalStateT (runReaderT p env) st
161165
162166--------------------------------------------------------------------
163167
164−findAttrText :: QName -> Element -> Maybe T.Text
165−findAttrText n = fmap T.pack . findAttr n
166−
167168monospaceFont :: Monad m => P m T.Text
168169monospaceFont = do
169170 vars <- writerVariables <$> asks envOpts
@@ -171,10 +172,9 @@ monospaceFont = do
171172 Just s -> return s
172173 Nothing -> return "Courier"
173174
174−-- Kept as string for XML.Light
175−fontSizeAttributes :: Monad m => RunProps -> P m [(String, String)]
175+fontSizeAttributes :: Monad m => RunProps -> P m [(Text, Text)]
176176fontSizeAttributes RunProps { rPropForceSize = Just sz } =
177− return [("sz", show $ sz * 100)]
177+ return [("sz", tshow $ sz * 100)]
178178fontSizeAttributes _ = return []
179179
180180copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive
@@ -365,7 +365,7 @@ shapeHasId :: NameSpaces -> T.Text -> Element -> Bool
365365shapeHasId ns ident element
366366 | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element
367367 , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr
368− , Just nm <- findAttrText (QName "id" Nothing Nothing) cNvPr =
368+ , Just nm <- findAttr (QName "id" Nothing Nothing) cNvPr =
369369 nm == ident
370370 | otherwise = False
371371
@@ -396,10 +396,10 @@ getShapeDimensions ns element
396396 ext <- findChild (elemName ns "a" "ext") xfrm
397397 cxS <- findAttr (QName "cx" Nothing Nothing) ext
398398 cyS <- findAttr (QName "cy" Nothing Nothing) ext
399− (x, _) <- listToMaybe $ reads xS
400− (y, _) <- listToMaybe $ reads yS
401− (cx, _) <- listToMaybe $ reads cxS
402− (cy, _) <- listToMaybe $ reads cyS
399+ x <- readTextAsInteger xS
400+ y <- readTextAsInteger yS
401+ cx <- readTextAsInteger cxS
402+ cy <- readTextAsInteger cyS
403403 return ((x `div` 12700, y `div` 12700),
404404 (cx `div` 12700, cy `div` 12700))
405405 | otherwise = Nothing
@@ -430,7 +430,7 @@ getContentShapeSize ns layout master
430430 Nothing -> do let mbSz =
431431 findChild (elemName ns "p" "nvSpPr") sp >>=
432432 findChild (elemName ns "p" "cNvPr") >>=
433− findAttrText (QName "id" Nothing Nothing) >>=
433+ findAttr (QName "id" Nothing Nothing) >>=
434434 flip getMasterShapeDimensionsById master
435435 case mbSz of
436436 Just sz' -> return sz'
@@ -450,8 +450,8 @@ buildSpTree ns spTreeElem newShapes =
450450 fn _ = True
451451
452452replaceNamedChildren :: NameSpaces
453− -> String
454− -> String
453+ -> Text
454+ -> Text
455455 -> [Element]
456456 -> Element
457457 -> Element
@@ -654,10 +654,10 @@ createCaption contentShapeDimensions paraElements = do
654654 ]
655655 , mknode "p:spPr" []
656656 [ mknode "a:xfrm" []
657− [ mknode "a:off" [("x", show $ 12700 * x),
658− ("y", show $ 12700 * (y + cy - captionHeight))] ()
659− , mknode "a:ext" [("cx", show $ 12700 * cx),
660− ("cy", show $ 12700 * captionHeight)] ()
657+ [ mknode "a:off" [("x", tshow $ 12700 * x),
658+ ("y", tshow $ 12700 * (y + cy - captionHeight))] ()
659+ , mknode "a:ext" [("cx", tshow $ 12700 * cx),
660+ ("cy", tshow $ 12700 * captionHeight)] ()
661661 ]
662662 , mknode "a:prstGeom" [("prst", "rect")]
663663 [ mknode "a:avLst" [] ()
@@ -706,11 +706,13 @@ makePicElements layout picProps mInfo alt = do
706706 ,("noChangeAspect","1")] ()
707707 -- cNvPr will contain the link information so we do that separately,
708708 -- and register the link if necessary.
709− let cNvPrAttr = [("descr", mInfoFilePath mInfo), ("id","0"),("name","Picture 1")]
709+ let cNvPrAttr = [("descr", T.pack $ mInfoFilePath mInfo),
710+ ("id","0"),
711+ ("name","Picture 1")]
710712 cNvPr <- case picPropLink picProps of
711713 Just link -> do idNum <- registerLink link
712714 return $ mknode "p:cNvPr" cNvPrAttr $
713− mknode "a:hlinkClick" [("r:id", "rId" <> show idNum)] ()
715+ mknode "a:hlinkClick" [("r:id", "rId" <> tshow idNum)] ()
714716 Nothing -> return $ mknode "p:cNvPr" cNvPrAttr ()
715717 let nvPicPr = mknode "p:nvPicPr" []
716718 [ cNvPr
@@ -718,13 +720,13 @@ makePicElements layout picProps mInfo alt = do
718720 , mknode "p:nvPr" [] ()]
719721 let blipFill = mknode "p:blipFill" []
720722 [ mknode "a:blip" [("r:embed", "rId" <>
721− show (mInfoLocalId mInfo))] ()
723+ tshow (mInfoLocalId mInfo))] ()
722724 , mknode "a:stretch" [] $
723725 mknode "a:fillRect" [] () ]
724726 let xfrm = mknode "a:xfrm" []
725− [ mknode "a:off" [("x",show xoff'), ("y",show yoff')] ()
726− , mknode "a:ext" [("cx",show dimX')
727− ,("cy",show dimY')] () ]
727+ [ mknode "a:off" [("x", tshow xoff'), ("y", tshow yoff')] ()
728+ , mknode "a:ext" [("cx", tshow dimX')
729+ ,("cy", tshow dimY')] () ]
728730 let prstGeom = mknode "a:prstGeom" [("prst","rect")] $
729731 mknode "a:avLst" [] ()
730732 let ln = mknode "a:ln" [("w","9525")]
@@ -763,7 +765,7 @@ paraElemToElements (Run rpr s) = do
763765 Just DoubleStrike -> [("strike", "dblStrike")]
764766 Nothing -> []) <>
765767 (case rBaseline rpr of
766− Just n -> [("baseline", show n)]
768+ Just n -> [("baseline", tshow n)]
767769 Nothing -> []) <>
768770 (case rCap rpr of
769771 Just NoCapitals -> [("cap", "none")]
@@ -780,43 +782,44 @@ paraElemToElements (Run rpr s) = do
780782 return $ case link of
781783 InternalTarget _ ->
782784 let linkAttrs =
783− [ ("r:id", "rId" <> show idNum)
785+ [ ("r:id", "rId" <> tshow idNum)
784786 , ("action", "ppaction://hlinksldjump")
785787 ]
786788 in [mknode "a:hlinkClick" linkAttrs ()]
787789 -- external
788790 ExternalTarget _ ->
789791 let linkAttrs =
790− [ ("r:id", "rId" <> show idNum)
792+ [ ("r:id", "rId" <> tshow idNum)
791793 ]
792794 in [mknode "a:hlinkClick" linkAttrs ()]
793795 Nothing -> return []
794796 let colorContents = case rSolidFill rpr of
795797 Just color ->
796798 case fromColor color of
797− '#':hx -> [mknode "a:solidFill" []
798− [mknode "a:srgbClr" [("val", map toUpper hx)] ()]
799− ]
799+ '#':hx ->
800+ [mknode "a:solidFill" []
801+ [mknode "a:srgbClr"
802+ [("val", T.toUpper $ T.pack hx)] ()]]
800803 _ -> []
801804 Nothing -> []
802805 codeFont <- monospaceFont
803806 let codeContents =
804− [mknode "a:latin" [("typeface", T.unpack codeFont)] () | rPropCode rpr]
807+ [mknode "a:latin" [("typeface", codeFont)] () | rPropCode rpr]
805808 let propContents = linkProps <> colorContents <> codeContents
806809 return [Elem $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents
807− , mknode "a:t" [] $ T.unpack s
810+ , mknode "a:t" [] s
808811 ]]
809812paraElemToElements (MathElem mathType texStr) = do
810813 isInSpkrNotes <- asks envInSpeakerNotes
811814 if isInSpkrNotes
812815 then paraElemToElements $ Run def $ unTeXString texStr
813816 else do res <- convertMath writeOMML mathType (unTeXString texStr)
814− case res of
817+ case fromXLElement <$> res of
815818 Right r -> return [Elem $ mknode "a14:m" [] $ addMathInfo r]
816819 Left (Str s) -> paraElemToElements (Run def s)
817820 Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback"
818821paraElemToElements (RawOOXMLParaElem str) = return
819− [Text (CData CDataRaw (T.unpack str) Nothing)]
822+ [Text (CData CDataRaw str Nothing)]
820823
821824
822825-- This is a bit of a kludge -- really requires adding an option to
@@ -824,9 +827,10 @@ paraElemToElements (RawOOXMLParaElem str) = return
824827-- step at a time.
825828addMathInfo :: Element -> Element
826829addMathInfo element =
827− let mathspace = Attr { attrKey = QName "m" Nothing (Just "xmlns")
828− , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math"
829− }
830+ let mathspace =
831+ Attr { attrKey = QName "m" Nothing (Just "xmlns")
832+ , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math"
833+ }
830834 in add_attr mathspace element
831835
832836-- We look through the element to see if it contains an a14:m
@@ -849,13 +853,13 @@ surroundWithMathAlternate element =
849853paragraphToElement :: PandocMonad m => Paragraph -> P m Element
850854paragraphToElement par = do
851855 let
852− attrs = [("lvl", show $ pPropLevel $ paraProps par)] <>
856+ attrs = [("lvl", tshow $ pPropLevel $ paraProps par)] <>
853857 (case pPropMarginLeft (paraProps par) of
854− Just px -> [("marL", show $ pixelsToEmu px)]
858+ Just px -> [("marL", tshow $ pixelsToEmu px)]
855859 Nothing -> []
856860 ) <>
857861 (case pPropIndent (paraProps par) of
858− Just px -> [("indent", show $ pixelsToEmu px)]
862+ Just px -> [("indent", tshow $ pixelsToEmu px)]
859863 Nothing -> []
860864 ) <>
861865 (case pPropAlign (paraProps par) of
@@ -867,7 +871,7 @@ paragraphToElement par = do
867871 props = [] <>
868872 (case pPropSpaceBefore $ paraProps par of
869873 Just px -> [mknode "a:spcBef" [] [
870− mknode "a:spcPts" [("val", show $ 100 * px)] ()
874+ mknode "a:spcPts" [("val", tshow $ 100 * px)] ()
871875 ]
872876 ]
873877 Nothing -> []
@@ -910,7 +914,7 @@ shapeToElements layout (Pic picProps fp alt) = do
910914shapeToElements layout (GraphicFrame tbls cptn) = map Elem <$>
911915 graphicFrameToElements layout tbls cptn
912916shapeToElements _ (RawOOXMLShape str) = return
913− [Text (CData CDataRaw (T.unpack str) Nothing)]
917+ [Text (CData CDataRaw str Nothing)]
914918shapeToElements layout shp = do
915919 element <- shapeToElement layout shp
916920 return [Elem element]
@@ -942,8 +946,10 @@ graphicFrameToElements layout tbls caption = do
942946 [mknode "p:ph" [("idx", "1")] ()]
943947 ]
944948 , mknode "p:xfrm" []
945− [ mknode "a:off" [("x", show $ 12700 * x), ("y", show $ 12700 * y)] ()
946− , mknode "a:ext" [("cx", show $ 12700 * cx), ("cy", show $ 12700 * cy)] ()
949+ [ mknode "a:off" [("x", tshow $ 12700 * x),
950+ ("y", tshow $ 12700 * y)] ()
951+ , mknode "a:ext" [("cx", tshow $ 12700 * cx),
952+ ("cy", tshow $ 12700 * cy)] ()
947953 ]
948954 ] <> elements
949955
@@ -957,7 +963,7 @@ getDefaultTableStyle = do
957963 refArchive <- asks envRefArchive
958964 distArchive <- asks envDistArchive
959965 tblStyleLst <- parseXml refArchive distArchive "ppt/tableStyles.xml"
960− return $ findAttrText (QName "def" Nothing Nothing) tblStyleLst
966+ return $ findAttr (QName "def" Nothing Nothing) tblStyleLst
961967
962968graphicToElement :: PandocMonad m => Integer -> Graphic -> P m Element
963969graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do
@@ -995,7 +1001,7 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do
9951001 let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells
9961002
9971003 let mkgridcol w = mknode "a:gridCol"
998− [("w", show ((12700 * w) :: Integer))] ()
1004+ [("w", tshow ((12700 * w) :: Integer))] ()
9991005 let hasHeader = not (all null hdrCells)
10001006
10011007 mbDefTblStyle <- getDefaultTableStyle
@@ -1004,7 +1010,7 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do
10041010 , ("bandRow", if tblPrBandRow tblPr then "1" else "0")
10051011 ] (case mbDefTblStyle of
10061012 Nothing -> []
1007− Just sty -> [mknode "a:tableStyleId" [] $ T.unpack sty])
1013+ Just sty -> [mknode "a:tableStyleId" [] sty])
10081014
10091015 return $ mknode "a:graphic" []
10101016 [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")]
@@ -1037,7 +1043,7 @@ findPHType ns spElem phType
10371043 -- if it's a named PHType, we want to check that the attribute
10381044 -- value matches.
10391045 Just phElem | (PHType tp) <- phType ->
1040− case findAttrText (QName "type" Nothing Nothing) phElem of
1046+ case findAttr (QName "type" Nothing Nothing) phElem of
10411047 Just tp' -> tp == tp'
10421048 Nothing -> False
10431049 -- if it's an ObjType, we want to check that there is NO
@@ -1204,7 +1210,7 @@ getSlideNumberFieldId notesMaster
12041210 , Just txBody <- findChild (elemName ns "p" "txBody") sp
12051211 , Just p <- findChild (elemName ns "a" "p") txBody
12061212 , Just fld <- findChild (elemName ns "a" "fld") p
1207− , Just fldId <- findAttrText (QName "id" Nothing Nothing) fld =
1213+ , Just fldId <- findAttr (QName "id" Nothing Nothing) fld =
12081214 return fldId
12091215 | otherwise = throwError $
12101216 PandocSomeError
@@ -1283,11 +1289,11 @@ speakerNotesSlideNumber pgNum fieldId =
12831289 [ mknode "a:bodyPr" [] ()
12841290 , mknode "a:lstStyle" [] ()
12851291 , mknode "a:p" []
1286− [ mknode "a:fld" [ ("id", T.unpack fieldId)
1292+ [ mknode "a:fld" [ ("id", fieldId)
12871293 , ("type", "slidenum")
12881294 ]
12891295 [ mknode "a:rPr" [("lang", "en-US")] ()
1290− , mknode "a:t" [] (show pgNum)
1296+ , mknode "a:t" [] (tshow pgNum)
12911297 ]
12921298 , mknode "a:endParaRPr" [("lang", "en-US")] ()
12931299 ]
@@ -1339,7 +1345,7 @@ getSlideIdNum sldId = do
13391345 Just n -> return n
13401346 Nothing -> throwError $
13411347 PandocShouldNeverHappenError $
1342− "Slide Id " <> T.pack (show sldId) <> " not found."
1348+ "Slide Id " <> tshow sldId <> " not found."
13431349
13441350slideNum :: PandocMonad m => Slide -> P m Int
13451351slideNum slide = getSlideIdNum $ slideId slide
@@ -1356,7 +1362,7 @@ slideToRelId :: PandocMonad m => Slide -> P m T.Text
13561362slideToRelId slide = do
13571363 n <- slideNum slide
13581364 offset <- asks envSlideIdOffset
1359− return $ "rId" <> T.pack (show $ n + offset)
1365+ return $ "rId" <> tshow (n + offset)
13601366
13611367
13621368data Relationship = Relationship { relId :: Int
@@ -1368,13 +1374,11 @@ elementToRel :: Element -> Maybe Relationship
13681374elementToRel element
13691375 | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing =
13701376 do rId <- findAttr (QName "Id" Nothing Nothing) element
1371− numStr <- stripPrefix "rId" rId
1372− num <- case reads numStr :: [(Int, String)] of
1373− (n, _) : _ -> Just n
1374− [] -> Nothing
1375− type' <- findAttrText (QName "Type" Nothing Nothing) element
1377+ numStr <- T.stripPrefix "rId" rId
1378+ num <- fromIntegral <$> readTextAsInteger numStr
1379+ type' <- findAttr (QName "Type" Nothing Nothing) element
13761380 target <- findAttr (QName "Target" Nothing Nothing) element
1377− return $ Relationship num type' target
1381+ return $ Relationship num type' (T.unpack target)
13781382 | otherwise = Nothing
13791383
13801384slideToPresRel :: PandocMonad m => Slide -> P m Relationship
@@ -1463,10 +1467,9 @@ topLevelRelsEntry :: PandocMonad m => P m Entry
14631467topLevelRelsEntry = elemToEntry "_rels/.rels" $ relsToElement topLevelRels
14641468
14651469relToElement :: Relationship -> Element
1466−relToElement rel = mknode "Relationship" [ ("Id", "rId" <>
1467− show (relId rel))
1468− , ("Type", T.unpack $ relType rel)
1469− , ("Target", relTarget rel) ] ()
1470+relToElement rel = mknode "Relationship" [ ("Id", "rId" <> tshow (relId rel))
1471+ , ("Type", relType rel)
1472+ , ("Target", T.pack $ relTarget rel) ] ()
14701473
14711474relsToElement :: [Relationship] -> Element
14721475relsToElement rels = mknode "Relationships"
@@ -1501,7 +1504,8 @@ slideToSpeakerNotesEntry slide = do
15011504 Just element | Just notesIdNum <- mbNotesIdNum ->
15021505 Just <$>
15031506 elemToEntry
1504− ("ppt/notesSlides/notesSlide" <> show notesIdNum <> ".xml")
1507+ ("ppt/notesSlides/notesSlide" <> show notesIdNum <>
1508+ ".xml")
15051509 element
15061510 _ -> return Nothing
15071511
@@ -1514,7 +1518,7 @@ slideToSpeakerNotesRelElement slide@Slide{} = do
15141518 [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")]
15151519 [ mknode "Relationship" [ ("Id", "rId2")
15161520 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide")
1517− , ("Target", "../slides/slide" <> show idNum <> ".xml")
1521+ , ("Target", "../slides/slide" <> tshow idNum <> ".xml")
15181522 ] ()
15191523 , mknode "Relationship" [ ("Id", "rId1")
15201524 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster")
@@ -1547,15 +1551,15 @@ linkRelElement :: PandocMonad m => (Int, LinkTarget) -> P m Element
15471551linkRelElement (rIdNum, InternalTarget targetId) = do
15481552 targetIdNum <- getSlideIdNum targetId
15491553 return $
1550− mknode "Relationship" [ ("Id", "rId" <> show rIdNum)
1554+ mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum)
15511555 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide")
1552− , ("Target", "slide" <> show targetIdNum <> ".xml")
1556+ , ("Target", "slide" <> tshow targetIdNum <> ".xml")
15531557 ] ()
15541558linkRelElement (rIdNum, ExternalTarget (url, _)) =
15551559 return $
1556− mknode "Relationship" [ ("Id", "rId" <> show rIdNum)
1560+ mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum)
15571561 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
1558− , ("Target", T.unpack url)
1562+ , ("Target", url)
15591563 , ("TargetMode", "External")
15601564 ] ()
15611565
@@ -1567,10 +1571,10 @@ mediaRelElement mInfo =
15671571 let ext = fromMaybe "" (mInfoExt mInfo)
15681572 in
15691573 mknode "Relationship" [ ("Id", "rId" <>
1570− show (mInfoLocalId mInfo))
1574+ tshow (mInfoLocalId mInfo))
15711575 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
15721576 , ("Target", "../media/image" <>
1573− show (mInfoGlobalId mInfo) <> T.unpack ext)
1577+ tshow (mInfoGlobalId mInfo) <> ext)
15741578 ] ()
15751579
15761580speakerNotesSlideRelElement :: PandocMonad m => Slide -> P m (Maybe Element)
@@ -1580,7 +1584,7 @@ speakerNotesSlideRelElement slide = do
15801584 return $ case M.lookup idNum mp of
15811585 Nothing -> Nothing
15821586 Just n ->
1583− let target = "../notesSlides/notesSlide" <> show n <> ".xml"
1587+ let target = "../notesSlides/notesSlide" <> tshow n <> ".xml"
15841588 in Just $
15851589 mknode "Relationship" [ ("Id", "rId2")
15861590 , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide")
@@ -1619,9 +1623,9 @@ slideToSlideRelElement slide = do
16191623slideToSldIdElement :: PandocMonad m => Slide -> P m Element
16201624slideToSldIdElement slide = do
16211625 n <- slideNum slide
1622− let id' = show $ n + 255
1626+ let id' = tshow $ n + 255
16231627 rId <- slideToRelId slide
1624− return $ mknode "p:sldId" [("id", id'), ("r:id", T.unpack rId)] ()
1628+ return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] ()
16251629
16261630presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element
16271631presentationToSldIdLst (Presentation _ slides) = do
@@ -1646,7 +1650,7 @@ presentationToPresentationElement pres@(Presentation _ slds) = do
16461650 notesMasterElem = mknode "p:notesMasterIdLst" []
16471651 [ mknode
16481652 "p:NotesMasterId"
1649− [("r:id", "rId" <> show notesMasterRId)]
1653+ [("r:id", "rId" <> tshow notesMasterRId)]
16501654 ()
16511655 ]
16521656
@@ -1702,17 +1706,17 @@ docPropsElement docProps = do
17021706 ,("xmlns:dcmitype","http://purl.org/dc/dcmitype/")
17031707 ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")]
17041708 $
1705− mknode "dc:title" [] (maybe "" T.unpack $ dcTitle docProps)
1709+ mknode "dc:title" [] (fromMaybe "" $ dcTitle docProps)
17061710 :
1707− mknode "dc:creator" [] (maybe "" T.unpack $ dcCreator docProps)
1711+ mknode "dc:creator" [] (fromMaybe "" $ dcCreator docProps)
17081712 :
1709− mknode "cp:keywords" [] (T.unpack keywords)
1710− : ( [mknode "dc:subject" [] $ maybe "" T.unpack $ dcSubject docProps | isJust (dcSubject docProps)])
1711− <> ( [mknode "dc:description" [] $ maybe "" T.unpack $ dcDescription docProps | isJust (dcDescription docProps)])
1712− <> ( [mknode "cp:category" [] $ maybe "" T.unpack $ cpCategory docProps | isJust (cpCategory docProps)])
1713+ mknode "cp:keywords" [] keywords
1714+ : ( [mknode "dc:subject" [] $ fromMaybe "" $ dcSubject docProps | isJust (dcSubject docProps)])
1715+ <> ( [mknode "dc:description" [] $ fromMaybe "" $ dcDescription docProps | isJust (dcDescription docProps)])
1716+ <> ( [mknode "cp:category" [] $ fromMaybe "" $ cpCategory docProps | isJust (cpCategory docProps)])
17131717 <> (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
1714− , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
1715− ]) (formatTime defaultTimeLocale "%FT%XZ" utctime)
1718+ , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
1719+ ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime)
17161720
17171721docPropsToEntry :: PandocMonad m => DocProps -> P m Entry
17181722docPropsToEntry docProps = docPropsElement docProps >>=
@@ -1723,8 +1727,8 @@ docCustomPropsElement :: PandocMonad m => DocProps -> P m Element
17231727docCustomPropsElement docProps = do
17241728 let mkCustomProp (k, v) pid = mknode "property"
17251729 [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
1726− ,("pid", show pid)
1727− ,("name", T.unpack k)] $ mknode "vt:lpwstr" [] (T.unpack v)
1730+ ,("pid", tshow pid)
1731+ ,("name", k)] $ mknode "vt:lpwstr" [] v
17281732 return $ mknode "Properties"
17291733 [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")
17301734 ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes")
@@ -1743,7 +1747,7 @@ viewPropsElement = do
17431747 distArchive <- asks envDistArchive
17441748 viewPrElement <- parseXml refArchive distArchive "ppt/viewProps.xml"
17451749 -- remove "lastView" if it exists:
1746− let notLastView :: Text.XML.Light.Attr -> Bool
1750+ let notLastView :: XML.Attr -> Bool
17471751 notLastView attr =
17481752 qName (attrKey attr) /= "lastView"
17491753 return $
@@ -1755,15 +1759,15 @@ makeViewPropsEntry = viewPropsElement >>= elemToEntry "ppt/viewProps.xml"
17551759defaultContentTypeToElem :: DefaultContentType -> Element
17561760defaultContentTypeToElem dct =
17571761 mknode "Default"
1758− [("Extension", T.unpack $ defContentTypesExt dct),
1759− ("ContentType", T.unpack $ defContentTypesType dct)]
1762+ [("Extension", defContentTypesExt dct),
1763+ ("ContentType", defContentTypesType dct)]
17601764 ()
17611765
17621766overrideContentTypeToElem :: OverrideContentType -> Element
17631767overrideContentTypeToElem oct =
17641768 mknode "Override"
1765− [("PartName", overrideContentTypesPart oct),
1766− ("ContentType", T.unpack $ overrideContentTypesType oct)]
1769+ [("PartName", T.pack $ overrideContentTypesPart oct),
1770+ ("ContentType", overrideContentTypesType oct)]
17671771 ()
17681772
17691773contentTypesToElement :: ContentTypes -> Element
@@ -1821,7 +1825,8 @@ getSpeakerNotesFilePaths :: PandocMonad m => P m [FilePath]
18211825getSpeakerNotesFilePaths = do
18221826 mp <- asks envSpeakerNotesIdMap
18231827 let notesIdNums = M.elems mp
1824− return $ map (\n -> "ppt/notesSlides/notesSlide" <> show n <> ".xml") notesIdNums
1828+ return $ map (\n -> "ppt/notesSlides/notesSlide" <> show n <> ".xml")
1829+ notesIdNums
18251830
18261831presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes
18271832presentationToContentTypes p@(Presentation _ slides) = do
@@ -1885,11 +1890,11 @@ getContentType fp
18851890 | otherwise = Nothing
18861891
18871892-- Kept as String for XML.Light
1888−autoNumAttrs :: ListAttributes -> [(String, String)]
1893+autoNumAttrs :: ListAttributes -> [(Text, Text)]
18891894autoNumAttrs (startNum, numStyle, numDelim) =
18901895 numAttr <> typeAttr
18911896 where
1892− numAttr = [("startAt", show startNum) | startNum /= 1]
1897+ numAttr = [("startAt", tshow startNum) | startNum /= 1]
18931898 typeAttr = [("type", typeString <> delimString)]
18941899 typeString = case numStyle of
18951900 Decimal -> "arabic"
src/Text/Pandoc/XML/Light.hsnew file+586
@@ -0,0 +1,586 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{- |
5+ Module : Text.Pandoc.XML.Light
6+ Copyright : Copyright (C) 2021 John MacFarlane
7+ License : GNU GPL, version 2 or above
8+
9+ Maintainer : John MacFarlane <jgm@berkeley.edu>
10+ Stability : alpha
11+ Portability : portable
12+
13+xml-light, which we used in pandoc's the XML-based readers, has
14+some limitations: in particular, it produces nodes with String
15+instead of Text, and the parser falls over on processing instructions
16+(see #7091).
17+
18+This module exports much of the API of xml-light, but using Text instead
19+of String. In addition, the xml-light parsers are replaced by xml-conduit's
20+well-tested parser. (The xml-conduit types are mapped to types
21+isomorphic to xml-light's, to avoid the need for massive code modifications
22+elsewhere.) Bridge functions to map xml-light types to this module's
23+types are also provided (since libraries like texmath still use xml-light).
24+
25+Another advantage of the xml-conduit parser is that it gives us
26+detailed information on xml parse errors.
27+
28+In the future we may want to move to using xml-conduit or another
29+xml library in the code base, but this change gives us
30+better performance and accuracy without much change in the
31+code that used xml-light.
32+-}
33+module Text.Pandoc.XML.Light
34+ ( -- * Basic types, duplicating those from xml-light but with Text
35+ -- instead of String
36+ Line
37+ , Content(..)
38+ , Element(..)
39+ , Attr(..)
40+ , CData(..)
41+ , CDataKind(..)
42+ , QName(..)
43+ , Node(..)
44+ , unode
45+ , unqual
46+ , add_attr
47+ , add_attrs
48+ -- * Conversion functions from xml-light types
49+ , fromXLQName
50+ , fromXLCData
51+ , fromXLElement
52+ , fromXLAttr
53+ , fromXLContent
54+ -- * Replacement for xml-light's Text.XML.Proc
55+ , strContent
56+ , onlyElems
57+ , elChildren
58+ , onlyText
59+ , findChildren
60+ , filterChildren
61+ , filterChildrenName
62+ , findChild
63+ , filterChild
64+ , filterChildName
65+ , findElement
66+ , filterElement
67+ , filterElementName
68+ , findElements
69+ , filterElements
70+ , filterElementsName
71+ , findAttr
72+ , lookupAttr
73+ , lookupAttrBy
74+ , findAttrBy
75+ -- * Replacement for xml-light's Text.XML.Output
76+ , ppTopElement
77+ , ppElement
78+ , ppContent
79+ , ppcElement
80+ , ppcContent
81+ , showTopElement
82+ , showElement
83+ , showContent
84+ , useShortEmptyTags
85+ , defaultConfigPP
86+ , ConfigPP(..)
87+ -- * Replacement for xml-light's Text.XML.Input
88+ , parseXMLElement
89+ , parseXMLContents
90+ ) where
91+
92+import qualified Control.Exception as E
93+import qualified Text.XML as Conduit
94+import Text.XML.Unresolved (InvalidEventStream(..))
95+import Data.Text (Text)
96+import qualified Data.Text as T
97+import qualified Data.Text.Lazy as TL
98+import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText)
99+import qualified Data.Map as M
100+import Data.Data (Data)
101+import Data.Typeable (Typeable)
102+import Data.Maybe (mapMaybe, listToMaybe)
103+import Data.List(find)
104+import qualified Text.XML.Light as XL
105+
106+-- Drop in replacement for parseXMLDoc in xml-light.
107+parseXMLElement :: TL.Text -> Either T.Text Element
108+parseXMLElement t =
109+ elementToElement . Conduit.documentRoot <$>
110+ either (Left . T.pack . E.displayException) Right
111+ (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t)
112+
113+parseXMLContents :: TL.Text -> Either T.Text [Content]
114+parseXMLContents t =
115+ case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t of
116+ Left e ->
117+ case E.fromException e of
118+ Just (ContentAfterRoot _) ->
119+ elContent <$> parseXMLElement ("<wrapper>" <> t <> "</wrapper>")
120+ _ -> Left . T.pack . E.displayException $ e
121+ Right x -> Right [Elem . elementToElement . Conduit.documentRoot $ x]
122+
123+elementToElement :: Conduit.Element -> Element
124+elementToElement (Conduit.Element name attribMap nodes) =
125+ Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing
126+ where
127+ attrs = map (\(n,v) -> Attr (nameToQname n) v) $
128+ M.toList attribMap
129+ nameToQname (Conduit.Name localName mbns mbpref) =
130+ case mbpref of
131+ Nothing ->
132+ case T.stripPrefix "xmlns:" localName of
133+ Just rest -> QName rest mbns (Just "xmlns")
134+ Nothing -> QName localName mbns mbpref
135+ _ -> QName localName mbns mbpref
136+
137+nodeToContent :: Conduit.Node -> Maybe Content
138+nodeToContent (Conduit.NodeElement el) =
139+ Just (Elem (elementToElement el))
140+nodeToContent (Conduit.NodeContent t) =
141+ Just (Text (CData CDataText t Nothing))
142+nodeToContent _ = Nothing
143+
144+unqual :: Text -> QName
145+unqual x = QName x Nothing Nothing
146+
147+-- | Add an attribute to an element.
148+add_attr :: Attr -> Element -> Element
149+add_attr a e = add_attrs [a] e
150+
151+-- | Add some attributes to an element.
152+add_attrs :: [Attr] -> Element -> Element
153+add_attrs as e = e { elAttribs = as ++ elAttribs e }
154+
155+--
156+-- type definitions lightly modified from xml-light
157+--
158+
159+-- | A line is an Integer
160+type Line = Integer
161+
162+-- | XML content
163+data Content = Elem Element
164+ | Text CData
165+ | CRef Text
166+ deriving (Show, Typeable, Data)
167+
168+-- | XML elements
169+data Element = Element {
170+ elName :: QName,
171+ elAttribs :: [Attr],
172+ elContent :: [Content],
173+ elLine :: Maybe Line
174+ } deriving (Show, Typeable, Data)
175+
176+-- | XML attributes
177+data Attr = Attr {
178+ attrKey :: QName,
179+ attrVal :: Text
180+ } deriving (Eq, Ord, Show, Typeable, Data)
181+
182+-- | XML CData
183+data CData = CData {
184+ cdVerbatim :: CDataKind,
185+ cdData :: Text,
186+ cdLine :: Maybe Line
187+ } deriving (Show, Typeable, Data)
188+
189+data CDataKind
190+ = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc.
191+ | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in <![CDATA[..
192+ | CDataRaw -- ^ As-is character data; pretty printer passes it along without any escaping or CDATA wrap-up.
193+ deriving ( Eq, Show, Typeable, Data )
194+
195+-- | XML qualified names
196+data QName = QName {
197+ qName :: Text,
198+ qURI :: Maybe Text,
199+ qPrefix :: Maybe Text
200+ } deriving (Show, Typeable, Data)
201+
202+
203+instance Eq QName where
204+ q1 == q2 = compare q1 q2 == EQ
205+
206+instance Ord QName where
207+ compare q1 q2 =
208+ case compare (qName q1) (qName q2) of
209+ EQ -> case (qURI q1, qURI q2) of
210+ (Nothing,Nothing) -> compare (qPrefix q1) (qPrefix q2)
211+ (u1,u2) -> compare u1 u2
212+ x -> x
213+
214+class Node t where
215+ node :: QName -> t -> Element
216+
217+instance Node ([Attr],[Content]) where
218+ node n (attrs,cont) = Element { elName = n
219+ , elAttribs = attrs
220+ , elContent = cont
221+ , elLine = Nothing
222+ }
223+
224+instance Node [Attr] where node n as = node n (as,[]::[Content])
225+instance Node Attr where node n a = node n [a]
226+instance Node () where node n () = node n ([]::[Attr])
227+
228+instance Node [Content] where node n cs = node n ([]::[Attr],cs)
229+instance Node Content where node n c = node n [c]
230+instance Node ([Attr],Content) where node n (as,c) = node n (as,[c])
231+instance Node (Attr,Content) where node n (a,c) = node n ([a],[c])
232+
233+instance Node ([Attr],[Element]) where
234+ node n (as,cs) = node n (as,map Elem cs)
235+
236+instance Node ([Attr],Element) where node n (as,c) = node n (as,[c])
237+instance Node (Attr,Element) where node n (a,c) = node n ([a],c)
238+instance Node [Element] where node n es = node n ([]::[Attr],es)
239+instance Node Element where node n e = node n [e]
240+
241+instance Node ([Attr],[CData]) where
242+ node n (as,cs) = node n (as,map Text cs)
243+
244+instance Node ([Attr],CData) where node n (as,c) = node n (as,[c])
245+instance Node (Attr,CData) where node n (a,c) = node n ([a],c)
246+instance Node [CData] where node n es = node n ([]::[Attr],es)
247+instance Node CData where node n e = node n [e]
248+
249+instance Node ([Attr],Text) where
250+ node n (as,t) = node n (as, CData { cdVerbatim = CDataText
251+ , cdData = t
252+ , cdLine = Nothing })
253+
254+instance Node (Attr,Text ) where node n (a,t) = node n ([a],t)
255+instance Node Text where node n t = node n ([]::[Attr],t)
256+
257+-- | Create node with unqualified name
258+unode :: Node t => Text -> t -> Element
259+unode = node . unqual
260+
261+--
262+-- conversion from xml-light
263+--
264+
265+fromXLQName :: XL.QName -> QName
266+fromXLQName qn = QName { qName = T.pack $ XL.qName qn
267+ , qURI = T.pack <$> XL.qURI qn
268+ , qPrefix = T.pack <$> XL.qPrefix qn }
269+
270+fromXLCData :: XL.CData -> CData
271+fromXLCData cd = CData { cdVerbatim = case XL.cdVerbatim cd of
272+ XL.CDataText -> CDataText
273+ XL.CDataVerbatim -> CDataVerbatim
274+ XL.CDataRaw -> CDataRaw
275+ , cdData = T.pack $ XL.cdData cd
276+ , cdLine = XL.cdLine cd }
277+
278+fromXLElement :: XL.Element -> Element
279+fromXLElement el = Element { elName = fromXLQName $ XL.elName el
280+ , elAttribs = map fromXLAttr $ XL.elAttribs el
281+ , elContent = map fromXLContent $ XL.elContent el
282+ , elLine = XL.elLine el }
283+
284+fromXLAttr :: XL.Attr -> Attr
285+fromXLAttr (XL.Attr qn s) = Attr (fromXLQName qn) (T.pack s)
286+
287+fromXLContent :: XL.Content -> Content
288+fromXLContent (XL.Elem el) = Elem $ fromXLElement el
289+fromXLContent (XL.Text cd) = Text $ fromXLCData cd
290+fromXLContent (XL.CRef s) = CRef (T.pack s)
291+
292+--
293+-- copied from xml-light Text.XML.Proc
294+--
295+
296+-- | Get the text value of an XML element. This function
297+-- ignores non-text elements, and concatenates all text elements.
298+strContent :: Element -> Text
299+strContent = mconcat . map cdData . onlyText . elContent
300+
301+-- | Select only the elements from a list of XML content.
302+onlyElems :: [Content] -> [Element]
303+onlyElems xs = [ x | Elem x <- xs ]
304+
305+-- | Select only the elements from a parent.
306+elChildren :: Element -> [Element]
307+elChildren e = [ x | Elem x <- elContent e ]
308+
309+-- | Select only the text from a list of XML content.
310+onlyText :: [Content] -> [CData]
311+onlyText xs = [ x | Text x <- xs ]
312+
313+-- | Find all immediate children with the given name.
314+findChildren :: QName -> Element -> [Element]
315+findChildren q e = filterChildren ((q ==) . elName) e
316+
317+-- | Filter all immediate children wrt a given predicate.
318+filterChildren :: (Element -> Bool) -> Element -> [Element]
319+filterChildren p e = filter p (onlyElems (elContent e))
320+
321+
322+-- | Filter all immediate children wrt a given predicate over their names.
323+filterChildrenName :: (QName -> Bool) -> Element -> [Element]
324+filterChildrenName p e = filter (p.elName) (onlyElems (elContent e))
325+
326+
327+-- | Find an immediate child with the given name.
328+findChild :: QName -> Element -> Maybe Element
329+findChild q e = listToMaybe (findChildren q e)
330+
331+-- | Find an immediate child with the given name.
332+filterChild :: (Element -> Bool) -> Element -> Maybe Element
333+filterChild p e = listToMaybe (filterChildren p e)
334+
335+-- | Find an immediate child with name matching a predicate.
336+filterChildName :: (QName -> Bool) -> Element -> Maybe Element
337+filterChildName p e = listToMaybe (filterChildrenName p e)
338+
339+-- | Find the left-most occurrence of an element matching given name.
340+findElement :: QName -> Element -> Maybe Element
341+findElement q e = listToMaybe (findElements q e)
342+
343+-- | Filter the left-most occurrence of an element wrt. given predicate.
344+filterElement :: (Element -> Bool) -> Element -> Maybe Element
345+filterElement p e = listToMaybe (filterElements p e)
346+
347+-- | Filter the left-most occurrence of an element wrt. given predicate.
348+filterElementName :: (QName -> Bool) -> Element -> Maybe Element
349+filterElementName p e = listToMaybe (filterElementsName p e)
350+
351+-- | Find all non-nested occurances of an element.
352+-- (i.e., once we have found an element, we do not search
353+-- for more occurances among the element's children).
354+findElements :: QName -> Element -> [Element]
355+findElements qn e = filterElementsName (qn==) e
356+
357+-- | Find all non-nested occurrences of an element wrt. given predicate.
358+-- (i.e., once we have found an element, we do not search
359+-- for more occurances among the element's children).
360+filterElements :: (Element -> Bool) -> Element -> [Element]
361+filterElements p e
362+ | p e = [e]
363+ | otherwise = concatMap (filterElements p) $ onlyElems $ elContent e
364+
365+-- | Find all non-nested occurences of an element wrt a predicate over element names.
366+-- (i.e., once we have found an element, we do not search
367+-- for more occurances among the element's children).
368+filterElementsName :: (QName -> Bool) -> Element -> [Element]
369+filterElementsName p e = filterElements (p.elName) e
370+
371+-- | Lookup the value of an attribute.
372+findAttr :: QName -> Element -> Maybe Text
373+findAttr x e = lookupAttr x (elAttribs e)
374+
375+-- | Lookup attribute name from list.
376+lookupAttr :: QName -> [Attr] -> Maybe Text
377+lookupAttr x = lookupAttrBy (x ==)
378+
379+-- | Lookup the first attribute whose name satisfies the given predicate.
380+lookupAttrBy :: (QName -> Bool) -> [Attr] -> Maybe Text
381+lookupAttrBy p as = attrVal `fmap` find (p . attrKey) as
382+
383+-- | Lookup the value of the first attribute whose name
384+-- satisfies the given predicate.
385+findAttrBy :: (QName -> Bool) -> Element -> Maybe Text
386+findAttrBy p e = lookupAttrBy p (elAttribs e)
387+
388+
389+--
390+-- duplicates functinos from Text.XML.Output
391+--
392+
393+-- | The XML 1.0 header
394+xmlHeader :: Text
395+xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
396+
397+
398+--------------------------------------------------------------------------------
399+data ConfigPP = ConfigPP
400+ { shortEmptyTag :: QName -> Bool
401+ , prettify :: Bool
402+ }
403+
404+-- | Default pretty orinting configuration.
405+-- * Always use abbreviate empty tags.
406+defaultConfigPP :: ConfigPP
407+defaultConfigPP = ConfigPP { shortEmptyTag = const True
408+ , prettify = False
409+ }
410+
411+-- | The predicate specifies for which empty tags we should use XML's
412+-- abbreviated notation <TAG />. This is useful if we are working with
413+-- some XML-ish standards (such as certain versions of HTML) where some
414+-- empty tags should always be displayed in the <TAG></TAG> form.
415+useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP
416+useShortEmptyTags p c = c { shortEmptyTag = p }
417+
418+
419+-- | Specify if we should use extra white-space to make document more readable.
420+-- WARNING: This adds additional white-space to text elements,
421+-- and so it may change the meaning of the document.
422+useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP
423+useExtraWhiteSpace p c = c { prettify = p }
424+
425+-- | A configuration that tries to make things pretty
426+-- (possibly at the cost of changing the semantics a bit
427+-- through adding white space.)
428+prettyConfigPP :: ConfigPP
429+prettyConfigPP = useExtraWhiteSpace True defaultConfigPP
430+
431+
432+--------------------------------------------------------------------------------
433+
434+
435+-- | Pretty printing renders XML documents faithfully,
436+-- with the exception that whitespace may be added\/removed
437+-- in non-verbatim character data.
438+ppTopElement :: Element -> Text
439+ppTopElement = ppcTopElement prettyConfigPP
440+
441+-- | Pretty printing elements
442+ppElement :: Element -> Text
443+ppElement = ppcElement prettyConfigPP
444+
445+-- | Pretty printing content
446+ppContent :: Content -> Text
447+ppContent = ppcContent prettyConfigPP
448+
449+-- | Pretty printing renders XML documents faithfully,
450+-- with the exception that whitespace may be added\/removed
451+-- in non-verbatim character data.
452+ppcTopElement :: ConfigPP -> Element -> Text
453+ppcTopElement c e = T.unlines [xmlHeader,ppcElement c e]
454+
455+-- | Pretty printing elements
456+ppcElement :: ConfigPP -> Element -> Text
457+ppcElement c = TL.toStrict . toLazyText . ppElementS c mempty
458+
459+-- | Pretty printing content
460+ppcContent :: ConfigPP -> Content -> Text
461+ppcContent c = TL.toStrict . toLazyText . ppContentS c mempty
462+
463+ppcCData :: ConfigPP -> CData -> Text
464+ppcCData c = TL.toStrict . toLazyText . ppCDataS c mempty
465+
466+type Indent = Builder
467+
468+-- | Pretty printing content using ShowT
469+ppContentS :: ConfigPP -> Indent -> Content -> Builder
470+ppContentS c i x = case x of
471+ Elem e -> ppElementS c i e
472+ Text t -> ppCDataS c i t
473+ CRef r -> showCRefS r
474+
475+ppElementS :: ConfigPP -> Indent -> Element -> Builder
476+ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <>
477+ (case elContent e of
478+ [] | "?" `T.isPrefixOf` qName name -> fromText " ?>"
479+ | shortEmptyTag c name -> fromText " />"
480+ [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name
481+ cs -> singleton '>' <> nl <>
482+ mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <>
483+ i <> tagEnd name
484+ where (nl,sp) = if prettify c then ("\n"," ") else ("","")
485+ )
486+ where name = elName e
487+
488+ppCDataS :: ConfigPP -> Indent -> CData -> Builder
489+ppCDataS c i t = i <> if cdVerbatim t /= CDataText || not (prettify c)
490+ then showCDataS t
491+ else foldr cons mempty (T.unpack (showCData t))
492+ where cons :: Char -> Builder -> Builder
493+ cons '\n' ys = singleton '\n' <> i <> ys
494+ cons y ys = singleton y <> ys
495+
496+
497+
498+--------------------------------------------------------------------------------
499+
500+-- | Adds the <?xml?> header.
501+showTopElement :: Element -> Text
502+showTopElement c = xmlHeader <> showElement c
503+
504+showContent :: Content -> Text
505+showContent = ppcContent defaultConfigPP
506+
507+showElement :: Element -> Text
508+showElement = ppcElement defaultConfigPP
509+
510+showCData :: CData -> Text
511+showCData = ppcCData defaultConfigPP
512+
513+-- Note: crefs should not contain '&', ';', etc.
514+showCRefS :: Text -> Builder
515+showCRefS r = singleton '&' <> fromText r <> singleton ';'
516+
517+-- | Convert a text element to characters.
518+showCDataS :: CData -> Builder
519+showCDataS cd =
520+ case cdVerbatim cd of
521+ CDataText -> escStr (cdData cd)
522+ CDataVerbatim -> fromText "<![CDATA[" <> escCData (cdData cd) <>
523+ fromText "]]>"
524+ CDataRaw -> fromText (cdData cd)
525+
526+--------------------------------------------------------------------------------
527+escCData :: Text -> Builder
528+escCData t
529+ | "]]>" `T.isPrefixOf` t =
530+ fromText "]]]]><![CDATA[>" <> fromText (T.drop 3 t)
531+escCData t
532+ = case T.uncons t of
533+ Nothing -> mempty
534+ Just (c,t') -> singleton c <> escCData t'
535+
536+escChar :: Char -> Builder
537+escChar c = case c of
538+ '<' -> fromText "&lt;"
539+ '>' -> fromText "&gt;"
540+ '&' -> fromText "&amp;"
541+ '"' -> fromText "&quot;"
542+ -- we use &#39 instead of &apos; because IE apparently has difficulties
543+ -- rendering &apos; in xhtml.
544+ -- Reported by Rohan Drape <rohan.drape@gmail.com>.
545+ '\'' -> fromText "&#39;"
546+ _ -> singleton c
547+
548+ {- original xml-light version:
549+ -- NOTE: We escape '\r' explicitly because otherwise they get lost
550+ -- when parsed back in because of then end-of-line normalization rules.
551+ _ | isPrint c || c == '\n' -> singleton c
552+ | otherwise -> showText "&#" . showsT oc . singleton ';'
553+ where oc = ord c
554+ -}
555+
556+escStr :: Text -> Builder
557+escStr cs = if T.any needsEscape cs
558+ then mconcat (map escChar (T.unpack cs))
559+ else fromText cs
560+ where
561+ needsEscape '<' = True
562+ needsEscape '>' = True
563+ needsEscape '&' = True
564+ needsEscape '"' = True
565+ needsEscape '\'' = True
566+ needsEscape _ = False
567+
568+tagEnd :: QName -> Builder
569+tagEnd qn = fromText "</" <> showQName qn <> singleton '>'
570+
571+tagStart :: QName -> [Attr] -> Builder
572+tagStart qn as = singleton '<' <> showQName qn <> as_str
573+ where as_str = if null as
574+ then mempty
575+ else mconcat (map showAttr as)
576+
577+showAttr :: Attr -> Builder
578+showAttr (Attr qn v) = singleton ' ' <> showQName qn <>
579+ singleton '=' <>
580+ singleton '"' <> escStr v <> singleton '"'
581+
582+showQName :: QName -> Builder
583+showQName q =
584+ case qPrefix q of
585+ Nothing -> fromText (qName q)
586+ Just p -> fromText p <> singleton ':' <> fromText (qName q)
src/Text/Pandoc/XMLParser.hsdeleted−66
@@ -1,66 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{- |
3− Module : Text.Pandoc.XMLParser
4− Copyright : Copyright (C) 2021 John MacFarlane
5− License : GNU GPL, version 2 or above
6−
7− Maintainer : John MacFarlane <jgm@berkeley.edu>
8− Stability : alpha
9− Portability : portable
10−
11−Bridge to allow using xml-conduit's parser with xml-light's types.
12−-}
13−module Text.Pandoc.XMLParser
14− ( parseXMLElement
15− , parseXMLContents
16− , module Text.XML.Light.Types
17− ) where
18−
19−import qualified Control.Exception as E
20−import qualified Text.XML as Conduit
21−import Text.XML.Unresolved (InvalidEventStream(..))
22−import qualified Text.XML.Light as Light
23−import Text.XML.Light.Types
24−import qualified Data.Text as T
25−import qualified Data.Text.Lazy as TL
26−import qualified Data.Map as M
27−import Data.Maybe (mapMaybe)
28−
29−-- Drop in replacement for parseXMLDoc in xml-light.
30−parseXMLElement :: TL.Text -> Either T.Text Light.Element
31−parseXMLElement t =
32− elementToElement . Conduit.documentRoot <$>
33− either (Left . T.pack . E.displayException) Right
34− (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t)
35−
36−parseXMLContents :: TL.Text -> Either T.Text [Light.Content]
37−parseXMLContents t =
38− case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t of
39− Left e ->
40− case E.fromException e of
41− Just (ContentAfterRoot _) ->
42− elContent <$> parseXMLElement ("<wrapper>" <> t <> "</wrapper>")
43− _ -> Left . T.pack . E.displayException $ e
44− Right x -> Right [Light.Elem . elementToElement . Conduit.documentRoot $ x]
45−
46−elementToElement :: Conduit.Element -> Light.Element
47−elementToElement (Conduit.Element name attribMap nodes) =
48− Light.Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing
49− where
50− attrs = map (\(n,v) -> Light.Attr (nameToQname n) (T.unpack v)) $
51− M.toList attribMap
52− nameToQname (Conduit.Name localName mbns mbpref) =
53− case mbpref of
54− Nothing | "xmlns:" `T.isPrefixOf` localName ->
55− Light.QName (T.unpack $ T.drop 6 localName) (T.unpack <$> mbns)
56− (Just "xmlns")
57− _ -> Light.QName (T.unpack localName) (T.unpack <$> mbns)
58− (T.unpack <$> mbpref)
59−
60−nodeToContent :: Conduit.Node -> Maybe Light.Content
61−nodeToContent (Conduit.NodeElement el) =
62− Just (Light.Elem (elementToElement el))
63−nodeToContent (Conduit.NodeContent t) =
64− Just (Light.Text (Light.CData Light.CDataText (T.unpack t) Nothing))
65−nodeToContent _ = Nothing
66−
test/Tests/Writers/OOXML.hs+2−1
@@ -43,7 +43,8 @@ compareXMLBool _ _ = False
4343
4444displayDiff :: Content -> Content -> String
4545displayDiff elemA elemB =
46− showDiff (1,1) $ getDiff (lines $ ppContent elemA) (lines $ ppContent elemB)
46+ showDiff (1,1)
47+ (getDiff (lines $ showContent elemA) (lines $ showContent elemB))
4748
4849goldenArchive :: FilePath -> IO Archive
4950goldenArchive fp = toArchive . BL.fromStrict <$> BS.readFile fp
test/docx/golden/block_quotes.docx

Binary file not shown.

test/docx/golden/codeblock.docx

Binary file not shown.

test/docx/golden/comments.docx

Binary file not shown.

test/docx/golden/custom_style_no_reference.docx

Binary file not shown.

test/docx/golden/custom_style_preserve.docx

Binary file not shown.

test/docx/golden/custom_style_reference.docx

Binary file not shown.

test/docx/golden/definition_list.docx

Binary file not shown.

test/docx/golden/document-properties-short-desc.docx

Binary file not shown.

test/docx/golden/document-properties.docx

Binary file not shown.

test/docx/golden/headers.docx

Binary file not shown.

test/docx/golden/image.docx

Binary file not shown.

test/docx/golden/inline_code.docx

Binary file not shown.

test/docx/golden/inline_formatting.docx

Binary file not shown.

test/docx/golden/inline_images.docx

Binary file not shown.

test/docx/golden/link_in_notes.docx

Binary file not shown.

test/docx/golden/links.docx

Binary file not shown.

test/docx/golden/lists.docx

Binary file not shown.

test/docx/golden/lists_continuing.docx

Binary file not shown.

test/docx/golden/lists_multiple_initial.docx

Binary file not shown.

test/docx/golden/lists_restarting.docx

Binary file not shown.

test/docx/golden/nested_anchors_in_header.docx

Binary file not shown.

test/docx/golden/notes.docx

Binary file not shown.

test/docx/golden/raw-blocks.docx

Binary file not shown.

test/docx/golden/raw-bookmarks.docx

Binary file not shown.

test/docx/golden/table_one_row.docx

Binary file not shown.

test/docx/golden/table_with_list_cell.docx

Binary file not shown.

test/docx/golden/tables.docx

Binary file not shown.

test/docx/golden/track_changes_deletion.docx

Binary file not shown.

test/docx/golden/track_changes_insertion.docx

Binary file not shown.

test/docx/golden/track_changes_move.docx

Binary file not shown.

test/docx/golden/track_changes_scrubbed_metadata.docx

Binary file not shown.

test/docx/golden/unicode.docx

Binary file not shown.

test/docx/golden/verbatim_subsuper.docx

Binary file not shown.

test/pptx/code-custom.pptx

Binary file not shown.

test/pptx/code-custom_templated.pptx

Binary file not shown.

test/pptx/code.pptx

Binary file not shown.

test/pptx/code_templated.pptx

Binary file not shown.

test/pptx/document-properties-short-desc.pptx

Binary file not shown.

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

Binary file not shown.

test/pptx/document-properties.pptx

Binary file not shown.

test/pptx/document-properties_templated.pptx

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_after_metadata.pptx

Binary file not shown.

test/pptx/speaker_notes_after_metadata_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_afterheader.pptx

Binary file not shown.

test/pptx/speaker_notes_afterheader_templated.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/start_numbering_at.pptx

Binary file not shown.

test/pptx/start_numbering_at_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.