git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #1547 from jkr/styleparse

Docx reader: parsing styles

commit716ad5fd8aa3be2cfe46ab3d06fc1c9aee1f27f0 authorJohn MacFarlane <jgm@berkeley.edu> dateMon 18 Aug 2014, 14:14 (12 years ago) parent6dce8c64b38e9f
2 files changed+110−36
src/Text/Pandoc/Readers/Docx.hs+34−23 src/Text/Pandoc/Readers/Docx/Parse.hs+76−13
src/Text/Pandoc/Readers/Docx.hs+34−23
@@ -196,12 +196,6 @@ fixAuthors mv = mv
196196codeStyles :: [String]
197197codeStyles = ["VerbatimChar"]
198198
199−strongStyles :: [String]
200−strongStyles = ["Strong", "Bold"]
201−
202−emphStyles :: [String]
203−emphStyles = ["Emphasis", "Italic"]
204−
205199blockQuoteDivs :: [String]
206200blockQuoteDivs = ["Quote", "BlockQuote", "BlockQuotation"]
207201
@@ -228,27 +222,44 @@ parPartToString (InternalHyperLink _ runs) = concatMap runToString runs
228222parPartToString (ExternalHyperLink _ runs) = concatMap runToString runs
229223parPartToString _ = ""
230224
225+blacklistedCharStyles :: [String]
226+blacklistedCharStyles = ["Hyperlink"]
227+
228+resolveDependentRunStyle :: RunStyle -> RunStyle
229+resolveDependentRunStyle rPr
230+ | Just (s, _) <- rStyle rPr, s `elem` blacklistedCharStyles =
231+ rPr
232+ | Just (_, cs) <- rStyle rPr =
233+ let rPr' = resolveDependentRunStyle cs
234+ in
235+ RunStyle { isBold = case isBold rPr of
236+ Just bool -> Just bool
237+ Nothing -> isBold rPr'
238+ , isItalic = case isItalic rPr of
239+ Just bool -> Just bool
240+ Nothing -> isItalic rPr'
241+ , isSmallCaps = case isSmallCaps rPr of
242+ Just bool -> Just bool
243+ Nothing -> isSmallCaps rPr'
244+ , isStrike = case isStrike rPr of
245+ Just bool -> Just bool
246+ Nothing -> isStrike rPr'
247+ , rVertAlign = case rVertAlign rPr of
248+ Just valign -> Just valign
249+ Nothing -> rVertAlign rPr'
250+ , rUnderline = case rUnderline rPr of
251+ Just ulstyle -> Just ulstyle
252+ Nothing -> rUnderline rPr'
253+ , rStyle = rStyle rPr }
254+ | otherwise = rPr
255+
231256runStyleToTransform :: RunStyle -> (Inlines -> Inlines)
232257runStyleToTransform rPr
233− | Just s <- rStyle rPr
258+ | Just (s, _) <- rStyle rPr
234259 , s `elem` spansToKeep =
235260 let rPr' = rPr{rStyle = Nothing}
236261 in
237262 (spanWith ("", [s], [])) . (runStyleToTransform rPr')
238− | Just s <- rStyle rPr
239− , s `elem` emphStyles =
240− let rPr' = rPr{rStyle = Nothing, isItalic = Nothing}
241− in
242− case isItalic rPr of
243− Just False -> runStyleToTransform rPr'
244− _ -> emph . (runStyleToTransform rPr')
245− | Just s <- rStyle rPr
246− , s `elem` strongStyles =
247− let rPr' = rPr{rStyle = Nothing, isBold = Nothing}
248− in
249− case isBold rPr of
250− Just False -> runStyleToTransform rPr'
251− _ -> strong . (runStyleToTransform rPr')
252263 | Just True <- isItalic rPr =
253264 emph . (runStyleToTransform rPr {isItalic = Nothing})
254265 | Just True <- isBold rPr =
@@ -267,12 +278,12 @@ runStyleToTransform rPr
267278
268279runToInlines :: Run -> DocxContext Inlines
269280runToInlines (Run rs runElems)
270− | Just s <- rStyle rs
281+ | Just (s, _) <- rStyle rs
271282 , s `elem` codeStyles =
272283 return $ code $ concatMap runElemToString runElems
273284 | otherwise = do
274285 let ils = concatReduce (map runElemToInlines runElems)
275− return $ (runStyleToTransform rs) ils
286+ return $ (runStyleToTransform $ resolveDependentRunStyle rs) ils
276287runToInlines (Footnote bps) = do
277288 blksList <- concatReduce <$> (mapM bodyPartToBlocks bps)
278289 return $ note blksList
src/Text/Pandoc/Readers/Docx/Parse.hs+76−13
@@ -50,7 +50,6 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..)
5050 , Cell(..)
5151 , archiveToDocx
5252 ) where
53−
5453import Codec.Archive.Zip
5554import Text.XML.Light
5655import Data.Maybe
@@ -73,6 +72,7 @@ data ReaderEnv = ReaderEnv { envNotes :: Notes
7372 , envRelationships :: [Relationship]
7473 , envMedia :: Media
7574 , envFont :: Maybe Font
75+ , envCharStyles :: CharStyleMap
7676 }
7777 deriving Show
7878
@@ -120,6 +120,10 @@ data Body = Body [BodyPart]
120120
121121type Media = [(FilePath, B.ByteString)]
122122
123+type CharStyle = (String, RunStyle)
124+
125+type CharStyleMap = M.Map String RunStyle
126+
123127data Numbering = Numbering NameSpaces [Numb] [AbstractNumb]
124128 deriving Show
125129
@@ -206,7 +210,7 @@ data RunStyle = RunStyle { isBold :: Maybe Bool
206210 , isStrike :: Maybe Bool
207211 , rVertAlign :: Maybe VertAlign
208212 , rUnderline :: Maybe String
209− , rStyle :: Maybe String }
213+ , rStyle :: Maybe CharStyle}
210214 deriving Show
211215
212216defaultRunStyle :: RunStyle
@@ -216,8 +220,7 @@ defaultRunStyle = RunStyle { isBold = Nothing
216220 , isStrike = Nothing
217221 , rVertAlign = Nothing
218222 , rUnderline = Nothing
219− , rStyle = Nothing
220− }
223+ , rStyle = Nothing}
221224
222225
223226type Target = String
@@ -239,7 +242,8 @@ archiveToDocx archive = do
239242 numbering = archiveToNumbering archive
240243 rels = archiveToRelationships archive
241244 media = archiveToMedia archive
242− rEnv = ReaderEnv notes numbering rels media Nothing
245+ styles = archiveToStyles archive
246+ rEnv = ReaderEnv notes numbering rels media Nothing styles
243247 doc <- runD (archiveToDocument archive) rEnv
244248 return $ Docx doc
245249
@@ -259,6 +263,53 @@ elemToBody ns element | isElem ns "w" "body" element =
259263 (\bps -> return $ Body bps)
260264elemToBody _ _ = throwError WrongElem
261265
266+archiveToStyles :: Archive -> CharStyleMap
267+archiveToStyles zf =
268+ let stylesElem = findEntryByPath "word/styles.xml" zf >>=
269+ (parseXMLDoc . UTF8.toStringLazy . fromEntry)
270+ in
271+ case stylesElem of
272+ Nothing -> M.empty
273+ Just styElem ->
274+ let namespaces = mapMaybe attrToNSPair (elAttribs styElem)
275+ in
276+ M.fromList $ buildBasedOnList namespaces styElem Nothing
277+
278+isBasedOnStyle :: NameSpaces -> Element -> Maybe CharStyle -> Bool
279+isBasedOnStyle ns element parentStyle
280+ | isElem ns "w" "style" element
281+ , Just "character" <- findAttr (elemName ns "w" "type") element
282+ , Just basedOnVal <- findChild (elemName ns "w" "basedOn") element >>=
283+ findAttr (elemName ns "w" "val")
284+ , Just (parentId, _) <- parentStyle = (basedOnVal == parentId)
285+ | isElem ns "w" "style" element
286+ , Just "character" <- findAttr (elemName ns "w" "type") element
287+ , Nothing <- findChild (elemName ns "w" "basedOn") element
288+ , Nothing <- parentStyle = True
289+ | otherwise = False
290+
291+elemToCharStyle :: NameSpaces -> Element -> Maybe CharStyle -> Maybe CharStyle
292+elemToCharStyle ns element parentStyle
293+ | isElem ns "w" "style" element
294+ , Just "character" <- findAttr (elemName ns "w" "type") element
295+ , Just styleId <- findAttr (elemName ns "w" "styleId") element =
296+ Just (styleId, elemToRunStyle ns element parentStyle)
297+ | otherwise = Nothing
298+
299+getStyleChildren :: NameSpaces -> Element -> Maybe CharStyle -> [CharStyle]
300+getStyleChildren ns element parentStyle
301+ | isElem ns "w" "styles" element =
302+ mapMaybe (\e -> elemToCharStyle ns e parentStyle) $
303+ filterChildren (\e' -> isBasedOnStyle ns e' parentStyle) element
304+ | otherwise = []
305+
306+buildBasedOnList :: NameSpaces -> Element -> Maybe CharStyle -> [CharStyle]
307+buildBasedOnList ns element rootStyle =
308+ case (getStyleChildren ns element rootStyle) of
309+ [] -> []
310+ stys -> stys ++
311+ (concatMap (\s -> buildBasedOnList ns element (Just s)) stys)
312+
262313archiveToNotes :: Archive -> Notes
263314archiveToNotes zf =
264315 let fnElem = findEntryByPath "word/footnotes.xml" zf
@@ -629,7 +680,8 @@ elemToRun ns element
629680elemToRun ns element
630681 | isElem ns "w" "r" element = do
631682 runElems <- elemToRunElems ns element
632− return $ Run (elemToRunStyle ns element) runElems
683+ runStyle <- elemToRunStyleD ns element
684+ return $ Run runStyle runElems
633685elemToRun _ _ = throwError WrongElem
634686
635687elemToParagraphStyle :: NameSpaces -> Element -> ParagraphStyle
@@ -669,9 +721,22 @@ checkOnOff ns rPr tag
669721 | Just _ <- findChild tag rPr = Just True
670722checkOnOff _ _ _ = Nothing
671723
672−
673−elemToRunStyle :: NameSpaces -> Element -> RunStyle
674−elemToRunStyle ns element
724+elemToRunStyleD :: NameSpaces -> Element -> D RunStyle
725+elemToRunStyleD ns element
726+ | Just rPr <- findChild (elemName ns "w" "rPr") element = do
727+ charStyles <- asks envCharStyles
728+ let parentSty = case
729+ findChild (elemName ns "w" "rStyle") rPr >>=
730+ findAttr (elemName ns "w" "val")
731+ of
732+ Just styName | Just style <- M.lookup styName charStyles ->
733+ Just (styName, style)
734+ _ -> Nothing
735+ return $ elemToRunStyle ns element parentSty
736+elemToRunStyleD _ _ = return defaultRunStyle
737+
738+elemToRunStyle :: NameSpaces -> Element -> Maybe CharStyle -> RunStyle
739+elemToRunStyle ns element parentStyle
675740 | Just rPr <- findChild (elemName ns "w" "rPr") element =
676741 RunStyle
677742 {
@@ -689,11 +754,9 @@ elemToRunStyle ns element
689754 , rUnderline =
690755 findChild (elemName ns "w" "u") rPr >>=
691756 findAttr (elemName ns "w" "val")
692− , rStyle =
693− findChild (elemName ns "w" "rStyle") rPr >>=
694− findAttr (elemName ns "w" "val")
757+ , rStyle = parentStyle
695758 }
696−elemToRunStyle _ _ = defaultRunStyle
759+elemToRunStyle _ _ _ = defaultRunStyle
697760
698761elemToRunElem :: NameSpaces -> Element -> D RunElem
699762elemToRunElem ns element