git.iko.soy/typeable/pandoc
on master help sign in
Started moving StyleMap out of writer code
commit409111f647d3efa403ff1efff12eebc3173017b5 authorNikolay Yakimov <root@livid.pp.ru> dateSun 1 Mar 2015, 22:57 (12 years ago) parent13daf3e
5 files changed+165−67
pandoc.cabal+3−1 src/Text/Pandoc/Readers/Docx/Parse.hs+6−20 src/Text/Pandoc/Readers/Docx/StyleMap.hs+105 src/Text/Pandoc/Readers/Docx/Util.hs+26 src/Text/Pandoc/Writers/Docx.hs+25−46
pandoc.cabal+3−1
@@ -338,7 +338,9 @@ Library
338338 Other-Modules: Text.Pandoc.Readers.Docx.Lists,
339339 Text.Pandoc.Readers.Docx.Reducible,
340340 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
342344 Text.Pandoc.Writers.Shared,
343345 Text.Pandoc.Asciify,
344346 Text.Pandoc.MIME,
src/Text/Pandoc/Readers/Docx/Parse.hs+6−20
@@ -65,6 +65,7 @@ import Text.Pandoc.Compat.Except
6565import Text.TeXMath.Readers.OMML (readOMML)
6666import Text.Pandoc.Readers.Docx.Fonts (getUnicode, Font(..))
6767import Text.TeXMath (Exp)
68+import Text.Pandoc.Readers.Docx.Util
6869import Data.Char (readLitChar, ord, chr, isDigit)
6970
7071data ReaderEnv = ReaderEnv { envNotes :: Notes
@@ -108,8 +109,6 @@ mapD f xs =
108109 in
109110 concatMapM handler xs
110111
111−type NameSpaces = [(String, String)]
112−
113112data Docx = Docx Document
114113 deriving Show
115114
@@ -249,10 +248,6 @@ type ChangeId = String
249248type Author = String
250249type ChangeDate = String
251250
252−attrToNSPair :: Attr -> Maybe (String, String)
253−attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val)
254−attrToNSPair _ = Nothing
255−
256251archiveToDocx :: Archive -> Either DocxError Docx
257252archiveToDocx archive = do
258253 let notes = archiveToNotes archive
@@ -269,7 +264,7 @@ archiveToDocument :: Archive -> D Document
269264archiveToDocument zf = do
270265 entry <- maybeToD $ findEntryByPath "word/document.xml" zf
271266 docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
272− let namespaces = mapMaybe attrToNSPair (elAttribs docElem)
267+ let namespaces = elemToNameSpaces docElem
273268 bodyElem <- maybeToD $ findChild (elemName namespaces "w" "body") docElem
274269 body <- elemToBody namespaces bodyElem
275270 return $ Document namespaces body
@@ -288,7 +283,7 @@ archiveToStyles zf =
288283 case stylesElem of
289284 Nothing -> (M.empty, M.empty)
290285 Just styElem ->
291− let namespaces = mapMaybe attrToNSPair (elAttribs styElem)
286+ let namespaces = elemToNameSpaces styElem
292287 in
293288 ( M.fromList $ buildBasedOnList namespaces styElem
294289 (Nothing :: Maybe CharStyle),
@@ -356,10 +351,10 @@ archiveToNotes zf =
356351 enElem = findEntryByPath "word/endnotes.xml" zf
357352 >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry)
358353 fn_namespaces = case fnElem of
359− Just e -> mapMaybe attrToNSPair (elAttribs e)
354+ Just e -> elemToNameSpaces e
360355 Nothing -> []
361356 en_namespaces = case enElem of
362− Just e -> mapMaybe attrToNSPair (elAttribs e)
357+ Just e -> elemToNameSpaces e
363358 Nothing -> []
364359 ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces
365360 fn = fnElem >>= (elemToNotes ns "footnote")
@@ -459,7 +454,7 @@ archiveToNumbering' zf = do
459454 Nothing -> Just $ Numbering [] [] []
460455 Just entry -> do
461456 numberingElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry
462− let namespaces = mapMaybe attrToNSPair (elAttribs numberingElem)
457+ let namespaces = elemToNameSpaces numberingElem
463458 numElems = findChildren
464459 (QName "num" (lookup "w" namespaces) (Just "w"))
465460 numberingElem
@@ -488,15 +483,6 @@ elemToNotes _ _ _ = Nothing
488483---------------------------------------------
489484---------------------------------------------
490485
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−
500486elemToTblGrid :: NameSpaces -> Element -> D TblGrid
501487elemToTblGrid ns element | isElem ns "w" "tblGrid" element =
502488 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 ( StyleMap
2+ , ParaStyleMap
3+ , CharStyleMap
4+ , StyleMaps(..)
5+ , defaultStyleMaps
6+ , getStyleMaps
7+ , getStyleId
8+ , hasStyleName
9+ ) where
10+
11+import Text.XML.Light
12+import Text.Pandoc.Readers.Docx.Util
13+import Control.Monad.State
14+import Data.Char (toLower)
15+import Data.Maybe (fromMaybe)
16+import qualified Data.Map as M
17+
18+newtype ParaStyleMap = ParaStyleMap ( M.Map String String )
19+newtype CharStyleMap = CharStyleMap ( M.Map String String )
20+
21+class StyleMap a where
22+ alterMap :: (M.Map String String -> M.Map String String) -> a -> a
23+ getMap :: a -> M.Map String String
24+
25+instance StyleMap ParaStyleMap where
26+ alterMap f (ParaStyleMap m) = ParaStyleMap $ f m
27+ getMap (ParaStyleMap m) = m
28+
29+instance StyleMap CharStyleMap where
30+ alterMap f (CharStyleMap m) = CharStyleMap $ f m
31+ getMap (CharStyleMap m) = m
32+
33+insert :: (StyleMap a) => String -> String -> a -> a
34+insert k v = alterMap $ M.insert k v
35+
36+getStyleId :: (StyleMap a) => String -> a -> String
37+getStyleId s = M.findWithDefault (filter (/=' ') s) (map toLower s) . getMap
38+
39+hasStyleName :: (StyleMap a) => String -> a -> Bool
40+hasStyleName styleName = M.member (map toLower styleName) . getMap
41+
42+data StyleMaps = StyleMaps { sNameSpaces :: NameSpaces
43+ , sParaStyleMap :: ParaStyleMap
44+ , sCharStyleMap :: CharStyleMap
45+ }
46+
47+data StyleType = ParaStyle | CharStyle
48+
49+defaultStyleMaps :: StyleMaps
50+defaultStyleMaps = StyleMaps { sNameSpaces = []
51+ , sParaStyleMap = ParaStyleMap M.empty
52+ , sCharStyleMap = CharStyleMap M.empty
53+ }
54+
55+type StateM a = StateT StyleMaps Maybe a
56+
57+getStyleMaps :: Element -> StyleMaps
58+getStyleMaps docElem = fromMaybe state' $ execStateT genStyleMap state'
59+ where
60+ state' = defaultStyleMaps {sNameSpaces = elemToNameSpaces docElem}
61+ insertPara key val = modify $ \s ->
62+ s { sParaStyleMap = insert key val $ sParaStyleMap s }
63+ insertChar key val = modify $ \s ->
64+ s { sCharStyleMap = insert key val $ sCharStyleMap s }
65+ genStyleItem e = do
66+ styleType <- getStyleType e
67+ nameVal <- getNameVal e
68+ styleId <- getAttrStyleId e
69+ let nameValLC = map toLower nameVal
70+ case styleType of
71+ ParaStyle -> insertPara nameValLC styleId
72+ CharStyle -> insertChar nameValLC styleId
73+ genStyleMap = do
74+ style <- elemName' "style"
75+ let styles = findChildren style docElem
76+ forM_ styles genStyleItem
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+25−46
@@ -54,6 +54,8 @@ import Text.Pandoc.Walk
5454import Text.Highlighting.Kate.Types ()
5555import Text.XML.Light as XML
5656import Text.TeXMath
57+import Text.Pandoc.Readers.Docx.StyleMap
58+import Text.Pandoc.Readers.Docx.Util (elemName)
5759import Control.Monad.State
5860import Text.Highlighting.Kate
5961import Data.Unique (hashUnique, newUnique)
@@ -64,7 +66,6 @@ import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
6466 extensionFromMimeType)
6567import Control.Applicative ((<$>), (<|>), (<*>))
6668import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
67−import Data.Char (toLower)
6869
6970data ListMarker = NoMarker
7071 | BulletMarker
@@ -90,9 +91,6 @@ listMarkerToId (NumberMarker sty delim n) =
9091 OneParen -> '2'
9192 TwoParens -> '3'
9293
93−newtype ParaStyleMap = ParaStyleMap (M.Map String String) deriving Show
94−newtype CharStyleMap = CharStyleMap (M.Map String String) deriving Show
95−
9694data WriterState = WriterState{
9795 stTextProperties :: [Element]
9896 , stParaProperties :: [Element]
@@ -109,8 +107,7 @@ data WriterState = WriterState{
109107 , stChangesAuthor :: String
110108 , stChangesDate :: String
111109 , stPrintWidth :: Integer
112− , stParaStyles :: ParaStyleMap
113− , stCharStyles :: CharStyleMap
110+ , stStyleMaps :: StyleMaps
114111 , stFirstPara :: Bool
115112 }
116113
@@ -131,8 +128,7 @@ defaultWriterState = WriterState{
131128 , stChangesAuthor = "unknown"
132129 , stChangesDate = "1969-12-31T19:00:00Z"
133130 , stPrintWidth = 1
134− , stParaStyles = ParaStyleMap M.empty
135− , stCharStyles = CharStyleMap M.empty
131+ , stStyleMaps = defaultStyleMaps
136132 , stFirstPara = False
137133 }
138134
@@ -220,28 +216,14 @@ writeDocx opts doc@(Pandoc meta _) = do
220216 styledoc <- parseXml refArchive distArchive stylepath
221217
222218 -- parse styledoc for heading styles
223− let styleNamespaces = map ((,) <$> qName . attrKey <*> attrVal) .
224− filter ((==Just "xmlns") . qPrefix . attrKey) .
225− elAttribs $ styledoc
226− mywURI = lookup "w" styleNamespaces
227− myName name = QName name mywURI (Just "w")
228− getAttrStyleId = findAttr (myName "styleId")
229− getAttrType = findAttr (myName "type")
230− isParaStyle = (Just "paragraph" ==) . getAttrType
231− isCharStyle = (Just "character" ==) . getAttrType
232− getNameVal = findChild (myName "name") >=> findAttr (myName "val") >=> return . map toLower
233− genStyleItem f e | f e = liftM2 (,) <$> getNameVal <*> getAttrStyleId $ e
234− | otherwise = Nothing
235− genStyleMap f = M.fromList $ mapMaybe (genStyleItem f) $ findChildren (myName "style") styledoc
236− paraStyles = ParaStyleMap $ genStyleMap isParaStyle
237− charStyles = CharStyleMap $ genStyleMap isCharStyle
219+ let styleMaps = getStyleMaps styledoc
238220
239221 ((contents, footnotes), st) <- runStateT (writeOpenXML opts{writerWrapText = False} doc')
240222 defaultWriterState{ stChangesAuthor = fromMaybe "unknown" username
241223 , stChangesDate = formatTime defaultTimeLocale "%FT%XZ" utctime
242224 , stPrintWidth = (maybe 420 (\x -> quot x 20) pgContentWidth)
243− , stParaStyles = paraStyles
244− , stCharStyles = charStyles}
225+ , stStyleMaps = styleMaps
226+ }
245227 let epochtime = floor $ utcTimeToPOSIXSeconds utctime
246228 let imgs = M.elems $ stImages st
247229
@@ -394,7 +376,7 @@ writeDocx opts doc@(Pandoc meta _) = do
394376 linkrels
395377
396378 -- styles
397− let newstyles = styleToOpenXml charStyles paraStyles $ writerHighlightStyle opts
379+ let newstyles = styleToOpenXml styleMaps $ writerHighlightStyle opts
398380 let styledoc' = styledoc{ elContent = modifyContent (elContent styledoc) }
399381 where
400382 modifyContent
@@ -402,9 +384,10 @@ writeDocx opts doc@(Pandoc meta _) = do
402384 | otherwise = filter notTokStyle
403385 notTokStyle (Elem el) = notStyle el || notTokId el
404386 notTokStyle _ = True
405− notStyle = (/= myName "style") . elName
406− notTokId = maybe True (`notElem` tokStys) . getAttrStyleId
387+ notStyle = (/= elemName' "style") . elName
388+ notTokId = maybe True (`notElem` tokStys) . findAttr (elemName' "styleId")
407389 tokStys = "SourceCode" : map show (enumFromTo KeywordTok NormalTok)
390+ elemName' = elemName (sNameSpaces styleMaps) "w"
408391 let styleEntry = toEntry stylepath epochtime $ renderXml styledoc'
409392
410393 -- construct word/numbering.xml
@@ -481,12 +464,11 @@ writeDocx opts doc@(Pandoc meta _) = do
481464 miscRelEntries ++ otherMediaEntries
482465 return $ fromArchive archive
483466
484−styleToOpenXml :: CharStyleMap -> ParaStyleMap -> Style -> [Element]
485−styleToOpenXml (CharStyleMap csm) (ParaStyleMap psm) style =
467+styleToOpenXml :: StyleMaps -> Style -> [Element]
468+styleToOpenXml sm style =
486469 maybeToList parStyle ++ mapMaybe toStyle alltoktypes
487470 where alltoktypes = enumFromTo KeywordTok NormalTok
488− styleExists m styleName = M.member (map toLower styleName) m
489− toStyle toktype | styleExists csm $ show toktype = Nothing
471+ toStyle toktype | hasStyleName (show toktype) (sCharStyleMap sm) = Nothing
490472 | otherwise = Just $
491473 mknode "w:style" [("w:type","character"),
492474 ("w:customStyle","1"),("w:styleId",show toktype)]
@@ -509,7 +491,7 @@ styleToOpenXml (CharStyleMap csm) (ParaStyleMap psm) style =
509491 tokBg toktype = maybe "auto" (drop 1 . fromColor)
510492 $ (tokenBackground =<< lookup toktype tokStyles)
511493 `mplus` backgroundColor style
512− parStyle | styleExists psm "Source Code" = Nothing
494+ parStyle | hasStyleName "Source Code" (sParaStyleMap sm) = Nothing
513495 | otherwise = Just $
514496 mknode "w:style" [("w:type","paragraph"),
515497 ("w:customStyle","1"),("w:styleId","SourceCode")]
@@ -638,30 +620,27 @@ writeOpenXML opts (Pandoc meta blocks) = do
638620blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element]
639621blocksToOpenXML opts bls = concat `fmap` mapM (blockToOpenXML opts) bls
640622
641−getStyleId :: String -> M.Map String String -> String
642−getStyleId s = M.findWithDefault (filter (/=' ') s) (map toLower s)
643−
644−pStyle :: String -> ParaStyleMap -> Element
645−pStyle sty (ParaStyleMap m) = mknode "w:pStyle" [("w:val",sty')] ()
623+pStyle :: String -> StyleMaps -> Element
624+pStyle sty m = mknode "w:pStyle" [("w:val",sty')] ()
646625 where
647− sty' = getStyleId sty m
626+ sty' = getStyleId sty $ sParaStyleMap m
648627
649628pCustomStyle :: String -> Element
650629pCustomStyle sty = mknode "w:pStyle" [("w:val",sty)] ()
651630
652631pStyleM :: String -> WS XML.Element
653−pStyleM = flip fmap (gets stParaStyles) . pStyle
632+pStyleM = (`fmap` gets stStyleMaps) . pStyle
654633
655−rStyle :: String -> CharStyleMap -> Element
656−rStyle sty (CharStyleMap m) = mknode "w:rStyle" [("w:val",sty')] ()
634+rStyle :: String -> StyleMaps -> Element
635+rStyle sty m = mknode "w:rStyle" [("w:val",sty')] ()
657636 where
658− sty' = getStyleId sty m
637+ sty' = getStyleId sty $ sCharStyleMap m
659638
660639rCustomStyle :: String -> Element
661640rCustomStyle sty = mknode "w:rStyle" [("w:val",sty)] ()
662641
663642rStyleM :: String -> WS XML.Element
664−rStyleM = flip fmap (gets stCharStyles) . rStyle
643+rStyleM = (`fmap` gets stStyleMaps) . rStyle
665644
666645getUniqueId :: MonadIO m => m String
667646-- the + 20 is to ensure that there are no clashes with the rIds
@@ -710,10 +689,10 @@ blockToOpenXML opts (Para lst) = do
710689 paraProps <- getParaProps $ case lst of
711690 [Math DisplayMath _] -> True
712691 _ -> False
713− pSM <- gets stParaStyles
692+ sm <- gets stStyleMaps
714693 let paraProps' = case paraProps of
715694 [] | isFirstPara -> [mknode "w:pPr" [] [pCustomStyle "FirstParagraph"]]
716− [] -> [mknode "w:pPr" [] [pStyle "Body Text" pSM]]
695+ [] -> [mknode "w:pPr" [] [pStyle "Body Text" sm]]
717696 ps -> ps
718697 modify $ \s -> s { stFirstPara = False }
719698 contents <- inlinesToOpenXML opts lst