git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #1968 from lierdakil/issue1607
619b2e8John MacFarlane
data/reference.docx

Binary file not shown.

pandoc.cabal+3−1
@@ -338,7 +338,9 @@ Library
338 Other-Modules: Text.Pandoc.Readers.Docx.Lists,
338 Other-Modules: Text.Pandoc.Readers.Docx.Lists,
339 Text.Pandoc.Readers.Docx.Reducible,
339 Text.Pandoc.Readers.Docx.Reducible,
340 Text.Pandoc.Readers.Docx.Parse,
340 Text.Pandoc.Readers.Docx.Parse,
341− Text.Pandoc.Readers.Docx.Fonts
341+ Text.Pandoc.Readers.Docx.Fonts,
342+ Text.Pandoc.Readers.Docx.Util,
343+ Text.Pandoc.Readers.Docx.StyleMap
342 Text.Pandoc.Writers.Shared,
344 Text.Pandoc.Writers.Shared,
343 Text.Pandoc.Asciify,
345 Text.Pandoc.Asciify,
344 Text.Pandoc.MIME,
346 Text.Pandoc.MIME,
src/Text/Pandoc/Readers/Docx/Parse.hs+6−20
@@ -65,6 +65,7 @@ import Text.Pandoc.Compat.Except
65import Text.TeXMath.Readers.OMML (readOMML)
65import Text.TeXMath.Readers.OMML (readOMML)
66import Text.Pandoc.Readers.Docx.Fonts (getUnicode, Font(..))
66import Text.Pandoc.Readers.Docx.Fonts (getUnicode, Font(..))
67import Text.TeXMath (Exp)
67import Text.TeXMath (Exp)
68+import Text.Pandoc.Readers.Docx.Util
68import Data.Char (readLitChar, ord, chr, isDigit)
69import Data.Char (readLitChar, ord, chr, isDigit)
69
70
70data ReaderEnv = ReaderEnv { envNotes :: Notes
71data ReaderEnv = ReaderEnv { envNotes :: Notes
@@ -108,8 +109,6 @@ mapD f xs =
108 in
109 in
109 concatMapM handler xs
110 concatMapM handler xs
110
111
111−type NameSpaces = [(String, String)]
112−
113data Docx = Docx Document
112data Docx = Docx Document
114 deriving Show
113 deriving Show
115
114
@@ -249,10 +248,6 @@ type ChangeId = String
249type Author = String
248type Author = String
250type ChangeDate = String
249type ChangeDate = String
251
250
252−attrToNSPair :: Attr -> Maybe (String, String)
253−attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
254−attrToNSPair _ = Nothing
255−
256archiveToDocx :: Archive -> Either DocxError Docx
251archiveToDocx :: Archive -> Either DocxError Docx
257archiveToDocx archive = do
252archiveToDocx archive = do
258 let notes = archiveToNotes archive
253 let notes = archiveToNotes archive
@@ -269,7 +264,7 @@ archiveToDocument :: Archive -> D Document
269archiveToDocument zf = do
264archiveToDocument zf = do
270 entry <- maybeToD $ findEntryByPath "word/document.xml" zf
265 entry <- maybeToD $ findEntryByPath "word/document.xml" zf
271 docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
266 docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
272− let namespaces = mapMaybe attrToNSPair (elAttribs docElem)
267+ let namespaces = elemToNameSpaces docElem
273 bodyElem <- maybeToD $ findChild (elemName namespaces "w" "body") docElem
268 bodyElem <- maybeToD $ findChild (elemName namespaces "w" "body") docElem
274 body <- elemToBody namespaces bodyElem
269 body <- elemToBody namespaces bodyElem
275 return $ Document namespaces body
270 return $ Document namespaces body
@@ -288,7 +283,7 @@ archiveToStyles zf =
288 case stylesElem of
283 case stylesElem of
289 Nothing -> (M.empty, M.empty)
284 Nothing -> (M.empty, M.empty)
290 Just styElem ->
285 Just styElem ->
291− let namespaces = mapMaybe attrToNSPair (elAttribs styElem)
286+ let namespaces = elemToNameSpaces styElem
292 in
287 in
293 ( M.fromList $ buildBasedOnList namespaces styElem
288 ( M.fromList $ buildBasedOnList namespaces styElem
294 (Nothing :: Maybe CharStyle),
289 (Nothing :: Maybe CharStyle),
@@ -356,10 +351,10 @@ archiveToNotes zf =
356 enElem = findEntryByPath "word/endnotes.xml" zf
351 enElem = findEntryByPath "word/endnotes.xml" zf
357 >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry)
352 >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry)
358 fn_namespaces = case fnElem of
353 fn_namespaces = case fnElem of
359− Just e -> mapMaybe attrToNSPair (elAttribs e)
354+ Just e -> elemToNameSpaces e
360 Nothing -> []
355 Nothing -> []
361 en_namespaces = case enElem of
356 en_namespaces = case enElem of
362− Just e -> mapMaybe attrToNSPair (elAttribs e)
357+ Just e -> elemToNameSpaces e
363 Nothing -> []
358 Nothing -> []
364 ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces
359 ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces
365 fn = fnElem >>= (elemToNotes ns "footnote")
360 fn = fnElem >>= (elemToNotes ns "footnote")
@@ -459,7 +454,7 @@ archiveToNumbering' zf = do
459 Nothing -> Just $ Numbering [] [] []
454 Nothing -> Just $ Numbering [] [] []
460 Just entry -> do
455 Just entry -> do
461 numberingElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
456 numberingElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
462− let namespaces = mapMaybe attrToNSPair (elAttribs numberingElem)
457+ let namespaces = elemToNameSpaces numberingElem
463 numElems = findChildren
458 numElems = findChildren
464 (QName "num" (lookup "w" namespaces) (Just "w"))
459 (QName "num" (lookup "w" namespaces) (Just "w"))
465 numberingElem
460 numberingElem
@@ -488,15 +483,6 @@ elemToNotes _ _ _ = Nothing
488---------------------------------------------
483---------------------------------------------
489---------------------------------------------
484---------------------------------------------
490
485
491−elemName :: NameSpaces -> String -> String -> QName
492−elemName ns prefix name = (QName name (lookup prefix ns) (Just prefix))
493−
494−isElem :: NameSpaces -> String -> String -> Element -> Bool
495−isElem ns prefix name element =
496− qName (elName element) == name &&
497− qURI (elName element) == (lookup prefix ns)
498−
499−
500elemToTblGrid :: NameSpaces -> Element -> D TblGrid
486elemToTblGrid :: NameSpaces -> Element -> D TblGrid
501elemToTblGrid ns element | isElem ns "w" "tblGrid" element =
487elemToTblGrid ns element | isElem ns "w" "tblGrid" element =
502 let cols = findChildren (elemName ns "w" "gridCol") element
488 let cols = findChildren (elemName ns "w" "gridCol") element
src/Text/Pandoc/Readers/Docx/StyleMap.hsnew file+105
@@ -0,0 +1,105 @@
1+module Text.Pandoc.Readers.Docx.StyleMap ( StyleMaps(..)
2+ , defaultStyleMaps
3+ , getStyleMaps
4+ , getStyleId
5+ , hasStyleName
6+ ) where
7+
8+import Text.XML.Light
9+import Text.Pandoc.Readers.Docx.Util
10+import Control.Monad.State
11+import Data.Char (toLower)
12+import Data.Maybe (fromMaybe)
13+import qualified Data.Map as M
14+
15+newtype ParaStyleMap = ParaStyleMap ( M.Map String String )
16+newtype CharStyleMap = CharStyleMap ( M.Map String String )
17+
18+class StyleMap a where
19+ alterMap :: (M.Map String String -> M.Map String String) -> a -> a
20+ getMap :: a -> M.Map String String
21+
22+instance StyleMap ParaStyleMap where
23+ alterMap f (ParaStyleMap m) = ParaStyleMap $ f m
24+ getMap (ParaStyleMap m) = m
25+
26+instance StyleMap CharStyleMap where
27+ alterMap f (CharStyleMap m) = CharStyleMap $ f m
28+ getMap (CharStyleMap m) = m
29+
30+insert :: (StyleMap a) => String -> String -> a -> a
31+insert k v = alterMap $ M.insert k v
32+
33+getStyleId :: (StyleMap a) => String -> a -> String
34+getStyleId s = M.findWithDefault (filter (/=' ') s) (map toLower s) . getMap
35+
36+hasStyleName :: (StyleMap a) => String -> a -> Bool
37+hasStyleName styleName = M.member (map toLower styleName) . getMap
38+
39+data StyleMaps = StyleMaps { sNameSpaces :: NameSpaces
40+ , sParaStyleMap :: ParaStyleMap
41+ , sCharStyleMap :: CharStyleMap
42+ }
43+
44+data StyleType = ParaStyle | CharStyle
45+
46+defaultStyleMaps :: StyleMaps
47+defaultStyleMaps = StyleMaps { sNameSpaces = []
48+ , sParaStyleMap = ParaStyleMap M.empty
49+ , sCharStyleMap = CharStyleMap M.empty
50+ }
51+
52+type StateM a = StateT StyleMaps Maybe a
53+
54+getStyleMaps :: Element -> StyleMaps
55+getStyleMaps docElem = fromMaybe state' $ execStateT genStyleMap state'
56+ where
57+ state' = defaultStyleMaps {sNameSpaces = elemToNameSpaces docElem}
58+ genStyleItem e = do
59+ styleType <- getStyleType e
60+ styleId <- getAttrStyleId e
61+ nameValLowercase <- map toLower `fmap` getNameVal e
62+ case styleType of
63+ ParaStyle -> modParaStyleMap $ insert nameValLowercase styleId
64+ CharStyle -> modCharStyleMap $ insert nameValLowercase styleId
65+ genStyleMap = do
66+ style <- elemName' "style"
67+ let styles = findChildren style docElem
68+ forM_ styles genStyleItem
69+
70+modParaStyleMap :: (ParaStyleMap -> ParaStyleMap) -> StateM ()
71+modParaStyleMap f = modify $ \s ->
72+ s {sParaStyleMap = f $ sParaStyleMap s}
73+
74+modCharStyleMap :: (CharStyleMap -> CharStyleMap) -> StateM ()
75+modCharStyleMap f = modify $ \s ->
76+ s {sCharStyleMap = f $ sCharStyleMap s}
77+
78+getStyleType :: Element -> StateM StyleType
79+getStyleType e = do
80+ styleTypeStr <- getAttrType e
81+ case styleTypeStr of
82+ "paragraph" -> return ParaStyle
83+ "character" -> return CharStyle
84+ _ -> lift Nothing
85+
86+getAttrType :: Element -> StateM String
87+getAttrType el = do
88+ name <- elemName' "type"
89+ lift $ findAttr name el
90+
91+getAttrStyleId :: Element -> StateM String
92+getAttrStyleId el = do
93+ name <- elemName' "styleId"
94+ lift $ findAttr name el
95+
96+getNameVal :: Element -> StateM String
97+getNameVal el = do
98+ name <- elemName' "name"
99+ val <- elemName' "val"
100+ lift $ findChild name el >>= findAttr val
101+
102+elemName' :: String -> StateM QName
103+elemName' name = do
104+ namespaces <- gets sNameSpaces
105+ return $ elemName namespaces "w" name
src/Text/Pandoc/Readers/Docx/Util.hsnew file+26
@@ -0,0 +1,26 @@
1+module Text.Pandoc.Readers.Docx.Util (
2+ NameSpaces
3+ , elemName
4+ , isElem
5+ , elemToNameSpaces
6+ ) where
7+
8+import Text.XML.Light
9+import Data.Maybe (mapMaybe)
10+
11+type NameSpaces = [(String, String)]
12+
13+elemToNameSpaces :: Element -> NameSpaces
14+elemToNameSpaces = mapMaybe attrToNSPair . elAttribs
15+
16+attrToNSPair :: Attr -> Maybe (String, String)
17+attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
18+attrToNSPair _ = Nothing
19+
20+elemName :: NameSpaces -> String -> String -> QName
21+elemName ns prefix name = QName name (lookup prefix ns) (Just prefix)
22+
23+isElem :: NameSpaces -> String -> String -> Element -> Bool
24+isElem ns prefix name element =
25+ qName (elName element) == name &&
26+ qURI (elName element) == lookup prefix ns
src/Text/Pandoc/Writers/Docx.hs+99−79
@@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29Conversion of 'Pandoc' documents to docx.
29Conversion of 'Pandoc' documents to docx.
30-}
30-}
31module Text.Pandoc.Writers.Docx ( writeDocx ) where
31module Text.Pandoc.Writers.Docx ( writeDocx ) where
32−import Data.List ( intercalate, isPrefixOf, isSuffixOf, stripPrefix )
32+import Data.List ( intercalate, isPrefixOf, isSuffixOf )
33import qualified Data.ByteString as B
33import qualified Data.ByteString as B
34import qualified Data.ByteString.Lazy as BL
34import qualified Data.ByteString.Lazy as BL
35import qualified Data.ByteString.Lazy.Char8 as BL8
35import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -54,6 +54,8 @@ import Text.Pandoc.Walk
54import Text.Highlighting.Kate.Types ()
54import Text.Highlighting.Kate.Types ()
55import Text.XML.Light as XML
55import Text.XML.Light as XML
56import Text.TeXMath
56import Text.TeXMath
57+import Text.Pandoc.Readers.Docx.StyleMap
58+import Text.Pandoc.Readers.Docx.Util (elemName)
57import Control.Monad.State
59import Control.Monad.State
58import Text.Highlighting.Kate
60import Text.Highlighting.Kate
59import Data.Unique (hashUnique, newUnique)
61import Data.Unique (hashUnique, newUnique)
@@ -63,8 +65,7 @@ import qualified Control.Exception as E
63import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
65import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
64 extensionFromMimeType)
66 extensionFromMimeType)
65import Control.Applicative ((<$>), (<|>), (<*>))
67import Control.Applicative ((<$>), (<|>), (<*>))
66−import Data.Maybe (fromMaybe, mapMaybe)
68+import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
67−import Data.Char (isDigit)
68
69
69data ListMarker = NoMarker
70data ListMarker = NoMarker
70 | BulletMarker
71 | BulletMarker
@@ -106,7 +107,7 @@ data WriterState = WriterState{
106 , stChangesAuthor :: String
107 , stChangesAuthor :: String
107 , stChangesDate :: String
108 , stChangesDate :: String
108 , stPrintWidth :: Integer
109 , stPrintWidth :: Integer
109− , stHeadingStyles :: [(Int,String)]
110+ , stStyleMaps :: StyleMaps
110 , stFirstPara :: Bool
111 , stFirstPara :: Bool
111 }
112 }
112
113
@@ -127,7 +128,7 @@ defaultWriterState = WriterState{
127 , stChangesAuthor = "unknown"
128 , stChangesAuthor = "unknown"
128 , stChangesDate = "1969-12-31T19:00:00Z"
129 , stChangesDate = "1969-12-31T19:00:00Z"
129 , stPrintWidth = 1
130 , stPrintWidth = 1
130− , stHeadingStyles = []
131+ , stStyleMaps = defaultStyleMaps
131 , stFirstPara = False
132 , stFirstPara = False
132 }
133 }
133
134
@@ -215,32 +216,14 @@ writeDocx opts doc@(Pandoc meta _) = do
215 styledoc <- parseXml refArchive distArchive stylepath
216 styledoc <- parseXml refArchive distArchive stylepath
216
217
217 -- parse styledoc for heading styles
218 -- parse styledoc for heading styles
218− let styleNamespaces = map ((,) <$> qName . attrKey <*> attrVal) .
219+ let styleMaps = getStyleMaps styledoc
219− filter ((==Just "xmlns") . qPrefix . attrKey) .
220− elAttribs $ styledoc
221− let headingStyles =
222− let
223− mywURI = lookup "w" styleNamespaces
224− myName name = QName name mywURI (Just "w")
225− getAttrStyleId = findAttr (myName "styleId")
226− getNameVal = findChild (myName "name") >=> findAttr (myName "val")
227− getNum s | not $ null s, all isDigit s = Just (read s :: Int)
228− | otherwise = Nothing
229− getEngHeader = getAttrStyleId >=> stripPrefix "Heading" >=> getNum
230− getIntHeader = getNameVal >=> stripPrefix "heading " >=> getNum
231− toTuple getF = liftM2 (,) <$> getF <*> getAttrStyleId
232− toMap getF = mapMaybe (toTuple getF) $
233− findChildren (myName "style") styledoc
234− select a b | not $ null a = a
235− | otherwise = b
236− in
237− select (toMap getEngHeader) (toMap getIntHeader)
238
220
239 ((contents, footnotes), st) <- runStateT (writeOpenXML opts{writerWrapText = False} doc')
221 ((contents, footnotes), st) <- runStateT (writeOpenXML opts{writerWrapText = False} doc')
240 defaultWriterState{ stChangesAuthor = fromMaybe "unknown" username
222 defaultWriterState{ stChangesAuthor = fromMaybe "unknown" username
241 , stChangesDate = formatTime defaultTimeLocale "%FT%XZ" utctime
223 , stChangesDate = formatTime defaultTimeLocale "%FT%XZ" utctime
242 , stPrintWidth = (maybe 420 (\x -> quot x 20) pgContentWidth)
224 , stPrintWidth = (maybe 420 (\x -> quot x 20) pgContentWidth)
243− , stHeadingStyles = headingStyles}
225+ , stStyleMaps = styleMaps
226+ }
244 let epochtime = floor $ utcTimeToPOSIXSeconds utctime
227 let epochtime = floor $ utcTimeToPOSIXSeconds utctime
245 let imgs = M.elems $ stImages st
228 let imgs = M.elems $ stImages st
246
229
@@ -393,9 +376,18 @@ writeDocx opts doc@(Pandoc meta _) = do
393 linkrels
376 linkrels
394
377
395 -- styles
378 -- styles
396− let newstyles = styleToOpenXml $ writerHighlightStyle opts
379+ let newstyles = styleToOpenXml styleMaps $ writerHighlightStyle opts
397− let styledoc' = styledoc{ elContent = elContent styledoc ++
380+ let styledoc' = styledoc{ elContent = modifyContent (elContent styledoc) }
381+ where
382+ modifyContent
398− [Elem x | x <- newstyles, writerHighlight opts] }
383+ | writerHighlight opts = (++ map Elem newstyles)
384+ | otherwise = filter notTokStyle
385+ notTokStyle (Elem el) = notStyle el || notTokId el
386+ notTokStyle _ = True
387+ notStyle = (/= elemName' "style") . elName
388+ notTokId = maybe True (`notElem` tokStys) . findAttr (elemName' "styleId")
389+ tokStys = "SourceCode" : map show (enumFromTo KeywordTok NormalTok)
390+ elemName' = elemName (sNameSpaces styleMaps) "w"
399 let styleEntry = toEntry stylepath epochtime $ renderXml styledoc'
391 let styleEntry = toEntry stylepath epochtime $ renderXml styledoc'
400
392
401 -- construct word/numbering.xml
393 -- construct word/numbering.xml
@@ -472,10 +464,13 @@ writeDocx opts doc@(Pandoc meta _) = do
472 miscRelEntries ++ otherMediaEntries
464 miscRelEntries ++ otherMediaEntries
473 return $ fromArchive archive
465 return $ fromArchive archive
474
466
475−styleToOpenXml :: Style -> [Element]
467+styleToOpenXml :: StyleMaps -> Style -> [Element]
476−styleToOpenXml style = parStyle : map toStyle alltoktypes
468+styleToOpenXml sm style =
469+ maybeToList parStyle ++ mapMaybe toStyle alltoktypes
477 where alltoktypes = enumFromTo KeywordTok NormalTok
470 where alltoktypes = enumFromTo KeywordTok NormalTok
471+ toStyle toktype | hasStyleName (show toktype) (sCharStyleMap sm) = Nothing
472+ | otherwise = Just $
478− toStyle toktype = mknode "w:style" [("w:type","character"),
473+ mknode "w:style" [("w:type","character"),
479 ("w:customStyle","1"),("w:styleId",show toktype)]
474 ("w:customStyle","1"),("w:styleId",show toktype)]
480 [ mknode "w:name" [("w:val",show toktype)] ()
475 [ mknode "w:name" [("w:val",show toktype)] ()
481 , mknode "w:basedOn" [("w:val","VerbatimChar")] ()
476 , mknode "w:basedOn" [("w:val","VerbatimChar")] ()
@@ -496,7 +491,9 @@ styleToOpenXml style = parStyle : map toStyle alltoktypes
496 tokBg toktype = maybe "auto" (drop 1 . fromColor)
491 tokBg toktype = maybe "auto" (drop 1 . fromColor)
497 $ (tokenBackground =<< lookup toktype tokStyles)
492 $ (tokenBackground =<< lookup toktype tokStyles)
498 `mplus` backgroundColor style
493 `mplus` backgroundColor style
494+ parStyle | hasStyleName "Source Code" (sParaStyleMap sm) = Nothing
495+ | otherwise = Just $
499− parStyle = mknode "w:style" [("w:type","paragraph"),
496+ mknode "w:style" [("w:type","paragraph"),
500 ("w:customStyle","1"),("w:styleId","SourceCode")]
497 ("w:customStyle","1"),("w:styleId","SourceCode")]
501 [ mknode "w:name" [("w:val","Source Code")] ()
498 [ mknode "w:name" [("w:val","Source Code")] ()
502 , mknode "w:basedOn" [("w:val","Normal")] ()
499 , mknode "w:basedOn" [("w:val","Normal")] ()
@@ -602,14 +599,14 @@ writeOpenXML opts (Pandoc meta blocks) = do
602 Just (MetaBlocks [Para xs]) -> xs
599 Just (MetaBlocks [Para xs]) -> xs
603 Just (MetaInlines xs) -> xs
600 Just (MetaInlines xs) -> xs
604 _ -> []
601 _ -> []
605− title <- withParaProp (pStyle "Title") $ blocksToOpenXML opts [Para tit | not (null tit)]
602+ title <- withParaPropM (pStyleM "Title") $ blocksToOpenXML opts [Para tit | not (null tit)]
606− subtitle <- withParaProp (pStyle "Subtitle") $ blocksToOpenXML opts [Para subtitle' | not (null subtitle')]
603+ subtitle <- withParaPropM (pStyleM "Subtitle") $ blocksToOpenXML opts [Para subtitle' | not (null subtitle')]
607− authors <- withParaProp (pStyle "Author") $ blocksToOpenXML opts $
604+ authors <- withParaProp (pCustomStyle "Author") $ blocksToOpenXML opts $
608 map Para auths
605 map Para auths
609− date <- withParaProp (pStyle "Date") $ blocksToOpenXML opts [Para dat | not (null dat)]
606+ date <- withParaPropM (pStyleM "Date") $ blocksToOpenXML opts [Para dat | not (null dat)]
610 abstract <- if null abstract'
607 abstract <- if null abstract'
611 then return []
608 then return []
612− else withParaProp (pStyle "Abstract") $ blocksToOpenXML opts abstract'
609+ else withParaProp (pCustomStyle "Abstract") $ blocksToOpenXML opts abstract'
613 let convertSpace (Str x : Space : Str y : xs) = Str (x ++ " " ++ y) : xs
610 let convertSpace (Str x : Space : Str y : xs) = Str (x ++ " " ++ y) : xs
614 convertSpace (Str x : Str y : xs) = Str (x ++ y) : xs
611 convertSpace (Str x : Str y : xs) = Str (x ++ y) : xs
615 convertSpace xs = xs
612 convertSpace xs = xs
@@ -623,11 +620,23 @@ writeOpenXML opts (Pandoc meta blocks) = do
623blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element]
620blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element]
624blocksToOpenXML opts bls = concat `fmap` mapM (blockToOpenXML opts) bls
621blocksToOpenXML opts bls = concat `fmap` mapM (blockToOpenXML opts) bls
625
622
626−pStyle :: String -> Element
623+pCustomStyle :: String -> Element
627−pStyle sty = mknode "w:pStyle" [("w:val",sty)] ()
624+pCustomStyle sty = mknode "w:pStyle" [("w:val",sty)] ()
628
625
626+pStyleM :: String -> WS XML.Element
627+pStyleM styleName = do
628+ styleMaps <- gets stStyleMaps
629+ let sty' = getStyleId styleName $ sParaStyleMap styleMaps
630+ return $ mknode "w:pStyle" [("w:val",sty')] ()
631+
632+rCustomStyle :: String -> Element
633+rCustomStyle sty = mknode "w:rStyle" [("w:val",sty)] ()
634+
629−rStyle :: String -> Element
635+rStyleM :: String -> WS XML.Element
636+rStyleM styleName = do
637+ styleMaps <- gets stStyleMaps
638+ let sty' = getStyleId styleName $ sCharStyleMap styleMaps
630−rStyle sty = mknode "w:rStyle" [("w:val",sty)] ()
639+ return $ mknode "w:rStyle" [("w:val",sty')] ()
631
640
632getUniqueId :: MonadIO m => m String
641getUniqueId :: MonadIO m => m String
633-- the + 20 is to ensure that there are no clashes with the rIds
642-- the + 20 is to ensure that there are no clashes with the rIds
@@ -641,13 +650,12 @@ blockToOpenXML opts (Div (_,["references"],_) bs) = do
641 let (hs, bs') = span isHeaderBlock bs
650 let (hs, bs') = span isHeaderBlock bs
642 header <- blocksToOpenXML opts hs
651 header <- blocksToOpenXML opts hs
643 -- We put the Bibliography style on paragraphs after the header
652 -- We put the Bibliography style on paragraphs after the header
644− rest <- withParaProp (pStyle "Bibliography") $ blocksToOpenXML opts bs'
653+ rest <- withParaPropM (pStyleM "Bibliography") $ blocksToOpenXML opts bs'
645 return (header ++ rest)
654 return (header ++ rest)
646blockToOpenXML opts (Div _ bs) = blocksToOpenXML opts bs
655blockToOpenXML opts (Div _ bs) = blocksToOpenXML opts bs
647blockToOpenXML opts (Header lev (ident,_,_) lst) = do
656blockToOpenXML opts (Header lev (ident,_,_) lst) = do
648 setFirstPara
657 setFirstPara
649− headingStyles <- gets stHeadingStyles
658+ paraProps <- withParaPropM (pStyleM ("Heading "++show lev)) $
650− paraProps <- maybe id (withParaProp . pStyle) (lookup lev headingStyles) $
651 getParaProps False
659 getParaProps False
652 contents <- inlinesToOpenXML opts lst
660 contents <- inlinesToOpenXML opts lst
653 usedIdents <- gets stSectionIds
661 usedIdents <- gets stSectionIds
@@ -660,26 +668,27 @@ blockToOpenXML opts (Header lev (ident,_,_) lst) = do
660 ,("w:name",bookmarkName)] ()
668 ,("w:name",bookmarkName)] ()
661 let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
669 let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
662 return [mknode "w:p" [] (paraProps ++ [bookmarkStart, bookmarkEnd] ++ contents)]
670 return [mknode "w:p" [] (paraProps ++ [bookmarkStart, bookmarkEnd] ++ contents)]
663−blockToOpenXML opts (Plain lst) = withParaProp (pStyle "Compact")
671+blockToOpenXML opts (Plain lst) = withParaProp (pCustomStyle "Compact")
664 $ blockToOpenXML opts (Para lst)
672 $ blockToOpenXML opts (Para lst)
665-- title beginning with fig: indicates that the image is a figure
673-- title beginning with fig: indicates that the image is a figure
666blockToOpenXML opts (Para [Image alt (src,'f':'i':'g':':':tit)]) = do
674blockToOpenXML opts (Para [Image alt (src,'f':'i':'g':':':tit)]) = do
667 setFirstPara
675 setFirstPara
668 paraProps <- getParaProps False
676 paraProps <- getParaProps False
669 contents <- inlinesToOpenXML opts [Image alt (src,tit)]
677 contents <- inlinesToOpenXML opts [Image alt (src,tit)]
670− captionNode <- withParaProp (pStyle "ImageCaption")
678+ captionNode <- withParaProp (pCustomStyle "ImageCaption")
671 $ blockToOpenXML opts (Para alt)
679 $ blockToOpenXML opts (Para alt)
672 return $ mknode "w:p" [] (paraProps ++ contents) : captionNode
680 return $ mknode "w:p" [] (paraProps ++ contents) : captionNode
673-- fixDisplayMath sometimes produces a Para [] as artifact
681-- fixDisplayMath sometimes produces a Para [] as artifact
674blockToOpenXML _ (Para []) = return []
682blockToOpenXML _ (Para []) = return []
675blockToOpenXML opts (Para lst) = do
683blockToOpenXML opts (Para lst) = do
676− isFirstPara <- gets stFirstPara
684+ isFirstPara <- gets stFirstPara
677 paraProps <- getParaProps $ case lst of
685 paraProps <- getParaProps $ case lst of
678 [Math DisplayMath _] -> True
686 [Math DisplayMath _] -> True
679 _ -> False
687 _ -> False
688+ bodyTextStyle <- pStyleM "Body Text"
680 let paraProps' = case paraProps of
689 let paraProps' = case paraProps of
681− [] | isFirstPara -> [mknode "w:pPr" [] [(pStyle "FirstParagraph")]]
690+ [] | isFirstPara -> [mknode "w:pPr" [] [pCustomStyle "FirstParagraph"]]
682− [] -> [mknode "w:pPr" [] [(pStyle "BodyText")]]
691+ [] -> [mknode "w:pPr" [] [bodyTextStyle]]
683 ps -> ps
692 ps -> ps
684 modify $ \s -> s { stFirstPara = False }
693 modify $ \s -> s { stFirstPara = False }
685 contents <- inlinesToOpenXML opts lst
694 contents <- inlinesToOpenXML opts lst
@@ -688,11 +697,11 @@ blockToOpenXML _ (RawBlock format str)
688 | format == Format "openxml" = return [ x | Elem x <- parseXML str ]
697 | format == Format "openxml" = return [ x | Elem x <- parseXML str ]
689 | otherwise = return []
698 | otherwise = return []
690blockToOpenXML opts (BlockQuote blocks) = do
699blockToOpenXML opts (BlockQuote blocks) = do
691− p <- withParaProp (pStyle "BlockQuote") $ blocksToOpenXML opts blocks
700+ p <- withParaPropM (pStyleM "Block Text") $ blocksToOpenXML opts blocks
692 setFirstPara
701 setFirstPara
693 return p
702 return p
694blockToOpenXML opts (CodeBlock attrs str) = do
703blockToOpenXML opts (CodeBlock attrs str) = do
695− p <- withParaProp (pStyle "SourceCode") $ (blockToOpenXML opts $ Para [Code attrs str])
704+ p <- withParaProp (pCustomStyle "SourceCode") (blockToOpenXML opts $ Para [Code attrs str])
696 setFirstPara
705 setFirstPara
697 return p
706 return p
698blockToOpenXML _ HorizontalRule = do
707blockToOpenXML _ HorizontalRule = do
@@ -707,7 +716,7 @@ blockToOpenXML opts (Table caption aligns widths headers rows) = do
707 let captionStr = stringify caption
716 let captionStr = stringify caption
708 caption' <- if null caption
717 caption' <- if null caption
709 then return []
718 then return []
710− else withParaProp (pStyle "TableCaption")
719+ else withParaProp (pCustomStyle "TableCaption")
711 $ blockToOpenXML opts (Para caption)
720 $ blockToOpenXML opts (Para caption)
712 let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] ()
721 let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] ()
713 let cellToOpenXML (al, cell) = withParaProp (alignmentFor al)
722 let cellToOpenXML (al, cell) = withParaProp (alignmentFor al)
@@ -718,32 +727,36 @@ blockToOpenXML opts (Table caption aligns widths headers rows) = do
718 [ mknode "w:tcBorders" []
727 [ mknode "w:tcBorders" []
719 $ mknode "w:bottom" [("w:val","single")] ()
728 $ mknode "w:bottom" [("w:val","single")] ()
720 , mknode "w:vAlign" [("w:val","bottom")] () ]
729 , mknode "w:vAlign" [("w:val","bottom")] () ]
721− let emptyCell = [mknode "w:p" [] [mknode "w:pPr" []
730+ let emptyCell = [mknode "w:p" [] [pCustomStyle "Compact"]]
722− [mknode "w:pStyle" [("w:val","Compact")] ()]]]
723 let mkcell border contents = mknode "w:tc" []
731 let mkcell border contents = mknode "w:tc" []
724 $ [ borderProps | border ] ++
732 $ [ borderProps | border ] ++
725 if null contents
733 if null contents
726 then emptyCell
734 then emptyCell
727 else contents
735 else contents
728− let mkrow border cells = mknode "w:tr" [] $ map (mkcell border) cells
736+ let mkrow border cells = mknode "w:tr" [] $
737+ [mknode "w:trPr" [] [
738+ mknode "w:cnfStyle" [("w:firstRow","1")] ()] | border]
739+ ++ map (mkcell border) cells
729 let textwidth = 7920 -- 5.5 in in twips, 1/20 pt
740 let textwidth = 7920 -- 5.5 in in twips, 1/20 pt
730 let fullrow = 5000 -- 100% specified in pct
741 let fullrow = 5000 -- 100% specified in pct
731 let rowwidth = fullrow * sum widths
742 let rowwidth = fullrow * sum widths
732 let mkgridcol w = mknode "w:gridCol"
743 let mkgridcol w = mknode "w:gridCol"
733 [("w:w", show (floor (textwidth * w) :: Integer))] ()
744 [("w:w", show (floor (textwidth * w) :: Integer))] ()
745+ let hasHeader = not (all null headers)
734 return $
746 return $
735 caption' ++
747 caption' ++
736 [mknode "w:tbl" []
748 [mknode "w:tbl" []
737 ( mknode "w:tblPr" []
749 ( mknode "w:tblPr" []
738 ( mknode "w:tblStyle" [("w:val","TableNormal")] () :
750 ( mknode "w:tblStyle" [("w:val","TableNormal")] () :
739 mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () :
751 mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () :
752+ mknode "w:tblLook" [("w:firstRow","1") | hasHeader ] () :
740 [ mknode "w:tblCaption" [("w:val", captionStr)] ()
753 [ mknode "w:tblCaption" [("w:val", captionStr)] ()
741 | not (null caption) ] )
754 | not (null caption) ] )
742 : mknode "w:tblGrid" []
755 : mknode "w:tblGrid" []
743 (if all (==0) widths
756 (if all (==0) widths
744 then []
757 then []
745 else map mkgridcol widths)
758 else map mkgridcol widths)
746− : [ mkrow True headers' | not (all null headers) ] ++
759+ : [ mkrow True headers' | hasHeader ] ++
747 map (mkrow False) rows'
760 map (mkrow False) rows'
748 )]
761 )]
749blockToOpenXML opts (BulletList lst) = do
762blockToOpenXML opts (BulletList lst) = do
@@ -767,9 +780,9 @@ blockToOpenXML opts (DefinitionList items) = do
767
780
768definitionListItemToOpenXML :: WriterOptions -> ([Inline],[[Block]]) -> WS [Element]
781definitionListItemToOpenXML :: WriterOptions -> ([Inline],[[Block]]) -> WS [Element]
769definitionListItemToOpenXML opts (term,defs) = do
782definitionListItemToOpenXML opts (term,defs) = do
770− term' <- withParaProp (pStyle "DefinitionTerm")
783+ term' <- withParaProp (pCustomStyle "DefinitionTerm")
771 $ blockToOpenXML opts (Para term)
784 $ blockToOpenXML opts (Para term)
772− defs' <- withParaProp (pStyle "Definition")
785+ defs' <- withParaProp (pCustomStyle "Definition")
773 $ concat `fmap` mapM (blocksToOpenXML opts) defs
786 $ concat `fmap` mapM (blocksToOpenXML opts) defs
774 return $ term' ++ defs'
787 return $ term' ++ defs'
775
788
@@ -833,6 +846,9 @@ withTextProp d p = do
833 popTextProp
846 popTextProp
834 return res
847 return res
835
848
849+withTextPropM :: WS Element -> WS a -> WS a
850+withTextPropM = (. flip withTextProp) . (>>=)
851+
836getParaProps :: Bool -> WS [Element]
852getParaProps :: Bool -> WS [Element]
837getParaProps displayMathPara = do
853getParaProps displayMathPara = do
838 props <- gets stParaProperties
854 props <- gets stParaProperties
@@ -861,6 +877,9 @@ withParaProp d p = do
861 popParaProp
877 popParaProp
862 return res
878 return res
863
879
880+withParaPropM :: WS Element -> WS a -> WS a
881+withParaPropM = (. flip withParaProp) . (>>=)
882+
864formattedString :: String -> WS [Element]
883formattedString :: String -> WS [Element]
865formattedString str = do
884formattedString str = do
866 props <- getTextProps
885 props <- getTextProps
@@ -943,25 +962,26 @@ inlineToOpenXML opts (Math mathType str) = do
943 Right r -> return [r]
962 Right r -> return [r]
944 Left _ -> inlinesToOpenXML opts (texMathToInlines mathType str)
963 Left _ -> inlinesToOpenXML opts (texMathToInlines mathType str)
945inlineToOpenXML opts (Cite _ lst) = inlinesToOpenXML opts lst
964inlineToOpenXML opts (Cite _ lst) = inlinesToOpenXML opts lst
946−inlineToOpenXML opts (Code attrs str) =
965+inlineToOpenXML opts (Code attrs str) = do
947− withTextProp (rStyle "VerbatimChar")
948− $ if writerHighlight opts
949− then case highlight formatOpenXML attrs str of
950− Nothing -> unhighlighted
951− Just h -> return h
952− else unhighlighted
953− where unhighlighted = intercalate [br] `fmap`
966+ let unhighlighted = intercalate [br] `fmap`
954− (mapM formattedString $ lines str)
967+ (mapM formattedString $ lines str)
955− formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok)
968+ formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok)
956− toHlTok (toktype,tok) = mknode "w:r" []
969+ toHlTok (toktype,tok) = mknode "w:r" []
957− [ mknode "w:rPr" []
970+ [ mknode "w:rPr" []
958− [ rStyle $ show toktype ]
971+ [ rCustomStyle (show toktype) ]
959− , mknode "w:t" [("xml:space","preserve")] tok ]
972+ , mknode "w:t" [("xml:space","preserve")] tok ]
973+ withTextProp (rCustomStyle "VerbatimChar")
974+ $ if writerHighlight opts
975+ then case highlight formatOpenXML attrs str of
976+ Nothing -> unhighlighted
977+ Just h -> return h
978+ else unhighlighted
960inlineToOpenXML opts (Note bs) = do
979inlineToOpenXML opts (Note bs) = do
961 notes <- gets stFootnotes
980 notes <- gets stFootnotes
962 notenum <- getUniqueId
981 notenum <- getUniqueId
982+ footnoteStyle <- rStyleM "Footnote Reference"
963 let notemarker = mknode "w:r" []
983 let notemarker = mknode "w:r" []
964− [ mknode "w:rPr" [] (rStyle "FootnoteRef")
984+ [ mknode "w:rPr" [] footnoteStyle
965 , mknode "w:footnoteRef" [] () ]
985 , mknode "w:footnoteRef" [] () ]
966 let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker
986 let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker
967 let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : ils) : xs
987 let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : ils) : xs
@@ -971,22 +991,22 @@ inlineToOpenXML opts (Note bs) = do
971 oldParaProperties <- gets stParaProperties
991 oldParaProperties <- gets stParaProperties
972 oldTextProperties <- gets stTextProperties
992 oldTextProperties <- gets stTextProperties
973 modify $ \st -> st{ stListLevel = -1, stParaProperties = [], stTextProperties = [] }
993 modify $ \st -> st{ stListLevel = -1, stParaProperties = [], stTextProperties = [] }
974− contents <- withParaProp (pStyle "FootnoteText") $ blocksToOpenXML opts
994+ contents <- withParaPropM (pStyleM "Footnote Text") $ blocksToOpenXML opts
975 $ insertNoteRef bs
995 $ insertNoteRef bs
976 modify $ \st -> st{ stListLevel = oldListLevel, stParaProperties = oldParaProperties,
996 modify $ \st -> st{ stListLevel = oldListLevel, stParaProperties = oldParaProperties,
977 stTextProperties = oldTextProperties }
997 stTextProperties = oldTextProperties }
978 let newnote = mknode "w:footnote" [("w:id", notenum)] $ contents
998 let newnote = mknode "w:footnote" [("w:id", notenum)] $ contents
979 modify $ \s -> s{ stFootnotes = newnote : notes }
999 modify $ \s -> s{ stFootnotes = newnote : notes }
980 return [ mknode "w:r" []
1000 return [ mknode "w:r" []
981− [ mknode "w:rPr" [] (rStyle "FootnoteRef")
1001+ [ mknode "w:rPr" [] footnoteStyle
982 , mknode "w:footnoteReference" [("w:id", notenum)] () ] ]
1002 , mknode "w:footnoteReference" [("w:id", notenum)] () ] ]
983-- internal link:
1003-- internal link:
984inlineToOpenXML opts (Link txt ('#':xs,_)) = do
1004inlineToOpenXML opts (Link txt ('#':xs,_)) = do
985− contents <- withTextProp (rStyle "Link") $ inlinesToOpenXML opts txt
1005+ contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
986 return [ mknode "w:hyperlink" [("w:anchor",xs)] contents ]
1006 return [ mknode "w:hyperlink" [("w:anchor",xs)] contents ]
987-- external link:
1007-- external link:
988inlineToOpenXML opts (Link txt (src,_)) = do
1008inlineToOpenXML opts (Link txt (src,_)) = do
989− contents <- withTextProp (rStyle "Link") $ inlinesToOpenXML opts txt
1009+ contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
990 extlinks <- gets stExternalLinks
1010 extlinks <- gets stExternalLinks
991 id' <- case M.lookup src extlinks of
1011 id' <- case M.lookup src extlinks of
992 Just i -> return i
1012 Just i -> return i
@@ -1088,7 +1108,7 @@ defaultFootnotes = [ mknode "w:footnote"
1088 [ mknode "w:p" [] $
1108 [ mknode "w:p" [] $
1089 [ mknode "w:r" [] $
1109 [ mknode "w:r" [] $
1090 [ mknode "w:continuationSeparator" [] ()]]]]
1110 [ mknode "w:continuationSeparator" [] ()]]]]
1091−
1111+
1092parseXml :: Archive -> Archive -> String -> IO Element
1112parseXml :: Archive -> Archive -> String -> IO Element
1093parseXml refArchive distArchive relpath =
1113parseXml refArchive distArchive relpath =
1094 case ((findEntryByPath relpath refArchive `mplus`
1114 case ((findEntryByPath relpath refArchive `mplus`
tests/Tests/Writers/Docx.hsnew file+128
@@ -0,0 +1,128 @@
1+module Tests.Writers.Docx (tests) where
2+
3+import Text.Pandoc.Options
4+import Text.Pandoc.Readers.Native
5+import Text.Pandoc.Definition
6+import Tests.Helpers
7+import Test.Framework
8+import Text.Pandoc.Readers.Docx
9+import Text.Pandoc.Writers.Docx
10+
11+type Options = (WriterOptions, ReaderOptions)
12+
13+compareOutput :: Options
14+ -> FilePath
15+ -> IO (Pandoc, Pandoc)
16+compareOutput opts nativeFile = do
17+ nf <- Prelude.readFile nativeFile
18+ df <- writeDocx (fst opts) (readNative nf)
19+ let (p, _) = readDocx (snd opts) df
20+ return (p, readNative nf)
21+
22+testCompareWithOptsIO :: Options -> String -> FilePath -> IO Test
23+testCompareWithOptsIO opts name nativeFile = do
24+ (dp, np) <- compareOutput opts nativeFile
25+ return $ test id name (dp, np)
26+
27+testCompareWithOpts :: Options -> String -> FilePath -> Test
28+testCompareWithOpts opts name nativeFile =
29+ buildTest $ testCompareWithOptsIO opts name nativeFile
30+
31+testCompare :: String -> FilePath -> Test
32+testCompare = testCompareWithOpts def
33+
34+tests :: [Test]
35+tests = [ testGroup "inlines"
36+ [ testCompare
37+ "font formatting"
38+ "docx/inline_formatting_writer.native"
39+ , testCompare
40+ "font formatting with character styles"
41+ "docx/char_styles.native"
42+ , testCompare
43+ "hyperlinks"
44+ "docx/links_writer.native"
45+ , testCompare
46+ "inline image"
47+ "docx/image_no_embed_writer.native"
48+ , testCompare
49+ "inline image in links"
50+ "docx/inline_images_writer.native"
51+ , testCompare
52+ "handling unicode input"
53+ "docx/unicode.native"
54+ , testCompare
55+ "literal tabs"
56+ "docx/tabs.native"
57+ , testCompare
58+ "normalizing inlines"
59+ "docx/normalize.native"
60+ , testCompare
61+ "normalizing inlines deep inside blocks"
62+ "docx/deep_normalize.native"
63+ , testCompare
64+ "move trailing spaces outside of formatting"
65+ "docx/trailing_spaces_in_formatting.native"
66+ , testCompare
67+ "inline code (with VerbatimChar style)"
68+ "docx/inline_code.native"
69+ , testCompare
70+ "inline code in subscript and superscript"
71+ "docx/verbatim_subsuper.native"
72+ ]
73+ , testGroup "blocks"
74+ [ testCompare
75+ "headers"
76+ "docx/headers.native"
77+ , testCompare
78+ "headers already having auto identifiers"
79+ "docx/already_auto_ident.native"
80+ , testCompare
81+ "numbered headers automatically made into list"
82+ "docx/numbered_header.native"
83+ , testCompare
84+ "i18n blocks (headers and blockquotes)"
85+ "docx/i18n_blocks.native"
86+ -- Continuation does not survive round-trip
87+ , testCompare
88+ "lists"
89+ "docx/lists_writer.native"
90+ , testCompare
91+ "definition lists"
92+ "docx/definition_list.native"
93+ , testCompare
94+ "custom defined lists in styles"
95+ "docx/german_styled_lists.native"
96+ , testCompare
97+ "footnotes and endnotes"
98+ "docx/notes.native"
99+ , testCompare
100+ "blockquotes (parsing indent as blockquote)"
101+ "docx/block_quotes_parse_indent.native"
102+ , testCompare
103+ "hanging indents"
104+ "docx/hanging_indent.native"
105+ -- tables headers do not survive round-trip, should look into that
106+ , testCompare
107+ "tables"
108+ "docx/tables.native"
109+ , testCompare
110+ "tables with lists in cells"
111+ "docx/table_with_list_cell.native"
112+ , testCompare
113+ "code block"
114+ "docx/codeblock.native"
115+ , testCompare
116+ "dropcap paragraphs"
117+ "docx/drop_cap.native"
118+ ]
119+ , testGroup "metadata"
120+ [ testCompareWithOpts (def,def{readerStandalone=True})
121+ "metadata fields"
122+ "docx/metadata.native"
123+ , testCompareWithOpts (def,def{readerStandalone=True})
124+ "stop recording metadata with normal text"
125+ "docx/metadata_after_normal.native"
126+ ]
127+
128+ ]
tests/docx/image_no_embed_writer.nativenew file+2
@@ -0,0 +1,2 @@
1+[Para [Str "An",Space,Str "image:"]
2+,Para [Image [] ("media/rId25.jpg","")]]
tests/docx/inline_formatting_writer.nativenew file+5
@@ -0,0 +1,5 @@
1+[Para [Str "Regular",Space,Str "text",Space,Emph [Str "italics"],Space,Strong [Str "bold",Space,Emph [Str "bold",Space,Str "italics"]],Str "."]
2+,Para [Str "This",Space,Str "is",Space,SmallCaps [Str "Small",Space,Str "Caps"],Str ",",Space,Str "and",Space,Str "this",Space,Str "is",Space,Strikeout [Str "strikethrough"],Str "."]
3+,Para [Str "Some",Space,Str "people",Space,Str "use",Space,Emph [Str "single",Space,Str "underlines",Space,Str "for",Space,Str "emphasis"],Str "."]
4+,Para [Str "Above",Space,Str "the",Space,Str "line",Space,Str "is",Space,Superscript [Str "superscript"],Space,Str "and",Space,Str "below",Space,Str "the",Space,Str "line",Space,Str "is",Space,Subscript [Str "subscript"],Str "."]
5+,Para [Str "A",Space,Str "line",LineBreak,Str "break."]]
tests/docx/inline_images_writer.nativenew file+2
@@ -0,0 +1,2 @@
1+[Para [Str "This",Space,Str "picture",Space,Image [] ("media/rId26.jpg",""),Space,Str "is",Space,Str "an",Space,Str "identicon."]
2+,Para [Str "Here",Space,Str "is",Space,Link [Str "one",Space,Image [] ("media/rId27.jpg",""),Space,Str "that"] ("http://www.google.com",""),Space,Str "links."]]
tests/docx/links_writer.nativenew file+6
@@ -0,0 +1,6 @@
1+[Header 2 ("an-internal-link-and-an-external-link",[],[]) [Str "An",Space,Str "internal",Space,Str "link",Space,Str "and",Space,Str "an",Space,Str "external",Space,Str "link"]
2+,Para [Str "An",Space,Link [Str "external",Space,Str "link"] ("http://google.com",""),Space,Str "to",Space,Str "a",Space,Str "popular",Space,Str "website."]
3+,Para [Str "An",Space,Link [Str "external",Space,Str "link"] ("http://johnmacfarlane.net/pandoc/README.html#synopsis",""),Space,Str "to",Space,Str "a",Space,Str "website",Space,Str "with",Space,Str "an",Space,Str "anchor."]
4+,Para [Str "An",Space,Link [Str "internal",Space,Str "link"] ("#a-section-for-testing-link-targets",""),Space,Str "to",Space,Str "a",Space,Str "section",Space,Str "header."]
5+,Para [Str "An",Space,Link [Str "internal",Space,Str "link"] ("#my_bookmark",""),Space,Str "to",Space,Str "a",Space,Str "bookmark."]
6+,Header 2 ("a-section-for-testing-link-targets",[],[]) [Str "A",Space,Str "section",Space,Str "for",Space,Str "testing",Space,Str "link",Space,Str "targets"]]
tests/docx/lists_writer.nativenew file+17
@@ -0,0 +1,17 @@
1+[Header 2 ("some-nested-lists",[],[]) [Str "Some",Space,Str "nested",Space,Str "lists"]
2+,OrderedList (1,Decimal,Period)
3+ [[Para [Str "one"]]
4+ ,[Para [Str "two"]
5+ ,OrderedList (1,LowerAlpha,DefaultDelim)
6+ [[Para [Str "a"]]
7+ ,[Para [Str "b"]]]]]
8+,BulletList
9+ [[Para [Str "one"]]
10+ ,[Para [Str "two"]
11+ ,BulletList
12+ [[Para [Str "three"]
13+ ,BulletList
14+ [[Para [Str "four"]]]]]]
15+ ,[Para [Str "Same",Space,Str "list"]]]
16+,BulletList
17+ [[Para [Str "Different",Space,Str "list",Space,Str "adjacent",Space,Str "to",Space,Str "the",Space,Str "one",Space,Str "above."]]]]
tests/media/rId25.jpgnew file

No changes to the content.

tests/media/rId26.jpgnew file

No changes to the content.

tests/media/rId27.jpgnew file

No changes to the content.

tests/test-pandoc.hs+2
@@ -20,6 +20,7 @@ import qualified Tests.Writers.Native
20import qualified Tests.Writers.Markdown
20import qualified Tests.Writers.Markdown
21import qualified Tests.Writers.Plain
21import qualified Tests.Writers.Plain
22import qualified Tests.Writers.AsciiDoc
22import qualified Tests.Writers.AsciiDoc
23+import qualified Tests.Writers.Docx
23import qualified Tests.Shared
24import qualified Tests.Shared
24import qualified Tests.Walk
25import qualified Tests.Walk
25import Text.Pandoc.Shared (inDirectory)
26import Text.Pandoc.Shared (inDirectory)
@@ -38,6 +39,7 @@ tests = [ testGroup "Old" Tests.Old.tests
38 , testGroup "Markdown" Tests.Writers.Markdown.tests
39 , testGroup "Markdown" Tests.Writers.Markdown.tests
39 , testGroup "Plain" Tests.Writers.Plain.tests
40 , testGroup "Plain" Tests.Writers.Plain.tests
40 , testGroup "AsciiDoc" Tests.Writers.AsciiDoc.tests
41 , testGroup "AsciiDoc" Tests.Writers.AsciiDoc.tests
42+ , testGroup "Docx" Tests.Writers.Docx.tests
41 ]
43 ]
42 , testGroup "Readers"
44 , testGroup "Readers"
43 [ testGroup "LaTeX" Tests.Readers.LaTeX.tests
45 [ testGroup "LaTeX" Tests.Readers.LaTeX.tests