git.iko.soy/typeable/pandoc
on master help sign in
Docx writer: make more deterministic to facilitate testing
9cf9f1fJesse Rosenthal
src/Text/Pandoc/Writers/Docx.hs+16−10
@@ -47,7 +47,7 @@ import qualified Data.Set as Set
47import qualified Data.Text as T
47import qualified Data.Text as T
48import Data.Time.Clock.POSIX
48import Data.Time.Clock.POSIX
49import Skylighting
49import Skylighting
50−import System.Random (randomR)
50+import System.Random (randomR, StdGen, mkStdGen)
51import Text.Pandoc.BCP47 (getLang, renderLang)
51import Text.Pandoc.BCP47 (getLang, renderLang)
52import Text.Pandoc.Class (PandocMonad, report, toLang)
52import Text.Pandoc.Class (PandocMonad, report, toLang)
53import qualified Text.Pandoc.Class as P
53import qualified Text.Pandoc.Class as P
@@ -132,6 +132,7 @@ data WriterState = WriterState{
132 , stTocTitle :: [Inline]
132 , stTocTitle :: [Inline]
133 , stDynamicParaProps :: Set.Set String
133 , stDynamicParaProps :: Set.Set String
134 , stDynamicTextProps :: Set.Set String
134 , stDynamicTextProps :: Set.Set String
135+ , stCurId :: Int
135 }
136 }
136
137
137defaultWriterState :: WriterState
138defaultWriterState :: WriterState
@@ -149,6 +150,7 @@ defaultWriterState = WriterState{
149 , stTocTitle = [Str "Table of Contents"]
150 , stTocTitle = [Str "Table of Contents"]
150 , stDynamicParaProps = Set.empty
151 , stDynamicParaProps = Set.empty
151 , stDynamicTextProps = Set.empty
152 , stDynamicTextProps = Set.empty
153+ , stCurId = 20
152 }
154 }
153
155
154type WS m = ReaderT WriterEnv (StateT WriterState m)
156type WS m = ReaderT WriterEnv (StateT WriterState m)
@@ -642,7 +644,7 @@ baseListId = 1000
642
644
643mkNumbering :: (PandocMonad m) => [ListMarker] -> m [Element]
645mkNumbering :: (PandocMonad m) => [ListMarker] -> m [Element]
644mkNumbering lists = do
646mkNumbering lists = do
645− elts <- mapM mkAbstractNum (ordNub lists)
647+ elts <- evalStateT (mapM mkAbstractNum (ordNub lists)) (mkStdGen 1848)
646 return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
648 return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
647
649
648maxListLevel :: Int
650maxListLevel :: Int
@@ -660,10 +662,11 @@ mkNum marker numid =
660 $ mknode "w:startOverride" [("w:val",show start)] ())
662 $ mknode "w:startOverride" [("w:val",show start)] ())
661 [0..maxListLevel]
663 [0..maxListLevel]
662
664
663−mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element
665+mkAbstractNum :: (PandocMonad m) => ListMarker -> StateT StdGen m Element
664mkAbstractNum marker = do
666mkAbstractNum marker = do
665− gen <- P.newStdGen
667+ gen <- get
666− let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen
668+ let (nsid, gen') = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen
669+ put gen'
667 return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
670 return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
668 $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
671 $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
669 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
672 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
@@ -822,10 +825,13 @@ rStyleM styleName = do
822 let sty' = getStyleId styleName $ sCharStyleMap styleMaps
825 let sty' = getStyleId styleName $ sCharStyleMap styleMaps
823 return $ mknode "w:rStyle" [("w:val",sty')] ()
826 return $ mknode "w:rStyle" [("w:val",sty')] ()
824
827
825−getUniqueId :: (PandocMonad m) => m String
828+getUniqueId :: (PandocMonad m) => WS m String
826-- the + 20 is to ensure that there are no clashes with the rIds
829-- the + 20 is to ensure that there are no clashes with the rIds
827-- already in word/document.xml.rel
830-- already in word/document.xml.rel
828−getUniqueId = (show . (+ 20)) <$> P.newUniqueHash
831+getUniqueId = do
832+ n <- gets stCurId
833+ modify $ \st -> st{stCurId = n + 1}
834+ return $ show n
829
835
830-- | Key for specifying user-defined docx styles.
836-- | Key for specifying user-defined docx styles.
831dynamicStyleKey :: String
837dynamicStyleKey :: String
@@ -1232,7 +1238,7 @@ inlineToOpenXML' opts (Code attrs str) = do
1232 unhighlighted
1238 unhighlighted
1233inlineToOpenXML' opts (Note bs) = do
1239inlineToOpenXML' opts (Note bs) = do
1234 notes <- gets stFootnotes
1240 notes <- gets stFootnotes
1235− notenum <- (lift . lift) getUniqueId
1241+ notenum <- getUniqueId
1236 footnoteStyle <- rStyleM "Footnote Reference"
1242 footnoteStyle <- rStyleM "Footnote Reference"
1237 let notemarker = mknode "w:r" []
1243 let notemarker = mknode "w:r" []
1238 [ mknode "w:rPr" [] footnoteStyle
1244 [ mknode "w:rPr" [] footnoteStyle
@@ -1263,7 +1269,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do
1263 id' <- case M.lookup src extlinks of
1269 id' <- case M.lookup src extlinks of
1264 Just i -> return i
1270 Just i -> return i
1265 Nothing -> do
1271 Nothing -> do
1266− i <- ("rId"++) `fmap` (lift . lift) getUniqueId
1272+ i <- ("rId"++) `fmap` getUniqueId
1267 modify $ \st -> st{ stExternalLinks =
1273 modify $ \st -> st{ stExternalLinks =
1268 M.insert src i extlinks }
1274 M.insert src i extlinks }
1269 return i
1275 return i
@@ -1277,7 +1283,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do
1277 Nothing ->
1283 Nothing ->
1278 catchError
1284 catchError
1279 (do (img, mt) <- P.fetchItem src
1285 (do (img, mt) <- P.fetchItem src
1280− ident <- ("rId"++) `fmap` (lift . lift) getUniqueId
1286+ ident <- ("rId"++) `fmap` getUniqueId
1281 let (xpt,ypt) = desiredSizeInPoints opts attr
1287 let (xpt,ypt) = desiredSizeInPoints opts attr
1282 (either (const def) id (imageSize opts img))
1288 (either (const def) id (imageSize opts img))
1283 -- 12700 emu = 1 pt
1289 -- 12700 emu = 1 pt