git.iko.soy/typeable/pandoc
on master help sign in
Docx Reader: Combine adjacent anchors.

There isn't any reason to have numberous anchors in the same place, since we can't maintain docx's non-nesting overlapping. So we reduce to a single anchor, and have all links pointing to one of the overlapping anchors point to that one. This changes the behavior from commit e90c714c7 slightly (use the first anchor instead of the last) so we change the expected test result. Note that because this produces a state that has to be set after every invocation of `parPartToInlines`, we make the main function into a primed subfunction `parPartToInlines'`, and make `parPartToInlines` a wrapper around that.

commit836153de43933dca3205e4459f55979d467f927e authorJesse Rosenthal <jrosenthal@jhu.edu> dateSun 31 Dec 2017, 09:09 (9 years ago) parenta274e15
2 files changed+49−22
src/Text/Pandoc/Readers/Docx.hs+47−20 test/docx/unused_anchors.native+2−2
src/Text/Pandoc/Readers/Docx.hs+47−20
@@ -82,6 +82,7 @@ import qualified Data.ByteString.Lazy as B
8282import Data.Default (Default)
8383import Data.List (delete, intersect)
8484import qualified Data.Map as M
85+import Data.Maybe (isJust)
8586import Data.Sequence (ViewL (..), viewl)
8687import qualified Data.Sequence as Seq
8788import qualified Data.Set as Set
@@ -119,6 +120,7 @@ readDocx _ _ =
119120
120121data DState = DState { docxAnchorMap :: M.Map String String
121122 , docxAnchorSet :: Set.Set String
123+ , docxImmedPrevAnchor :: Maybe String
122124 , docxMediaBag :: MediaBag
123125 , docxDropCap :: Inlines
124126 , docxWarnings :: [String]
@@ -130,6 +132,7 @@ data DState = DState { docxAnchorMap :: M.Map String String
130132instance Default DState where
131133 def = DState { docxAnchorMap = M.empty
132134 , docxAnchorSet = mempty
135+ , docxImmedPrevAnchor = Nothing
133136 , docxMediaBag = mempty
134137 , docxDropCap = mempty
135138 , docxWarnings = []
@@ -341,9 +344,26 @@ blocksToInlinesWarn cmtId blks = do
341344 "Docx comment " ++ cmtId ++ " will not retain formatting"
342345 return $ blocksToInlines' blkList
343346
347+-- The majority of work in this function is done in the primted
348+-- subfunction `partPartToInlines'`. We make this wrapper so that we
349+-- don't have to modify `docxImmedPrevAnchor` state after every function.
344350parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines
345−parPartToInlines (PlainRun r) = runToInlines r
346−parPartToInlines (Insertion _ author date runs) = do
351+parPartToInlines parPart =
352+ case parPart of
353+ (BookMark _ anchor) | not $ anchor `elem` dummyAnchors -> do
354+ inHdrBool <- asks docxInHeaderBlock
355+ ils <- parPartToInlines' parPart
356+ immedPrevAnchor <- gets docxImmedPrevAnchor
357+ unless (isJust immedPrevAnchor || inHdrBool)
358+ (modify $ \s -> s{ docxImmedPrevAnchor = Just anchor})
359+ return ils
360+ _ -> do
361+ ils <- parPartToInlines' parPart
362+ modify $ \s -> s{ docxImmedPrevAnchor = Nothing}
363+ return ils
364+parPartToInlines' :: PandocMonad m => ParPart -> DocxContext m Inlines
365+parPartToInlines' (PlainRun r) = runToInlines r
366+parPartToInlines' (Insertion _ author date runs) = do
347367 opts <- asks docxOptions
348368 case readerTrackChanges opts of
349369 AcceptChanges -> smushInlines <$> mapM runToInlines runs
@@ -352,7 +372,7 @@ parPartToInlines (Insertion _ author date runs) = do
352372 ils <- smushInlines <$> mapM runToInlines runs
353373 let attr = ("", ["insertion"], [("author", author), ("date", date)])
354374 return $ spanWith attr ils
355−parPartToInlines (Deletion _ author date runs) = do
375+parPartToInlines' (Deletion _ author date runs) = do
356376 opts <- asks docxOptions
357377 case readerTrackChanges opts of
358378 AcceptChanges -> return mempty
@@ -361,7 +381,7 @@ parPartToInlines (Deletion _ author date runs) = do
361381 ils <- smushInlines <$> mapM runToInlines runs
362382 let attr = ("", ["deletion"], [("author", author), ("date", date)])
363383 return $ spanWith attr ils
364−parPartToInlines (CommentStart cmtId author date bodyParts) = do
384+parPartToInlines' (CommentStart cmtId author date bodyParts) = do
365385 opts <- asks docxOptions
366386 case readerTrackChanges opts of
367387 AllChanges -> do
@@ -370,16 +390,16 @@ parPartToInlines (CommentStart cmtId author date bodyParts) = do
370390 let attr = ("", ["comment-start"], [("id", cmtId), ("author", author), ("date", date)])
371391 return $ spanWith attr ils
372392 _ -> return mempty
373−parPartToInlines (CommentEnd cmtId) = do
393+parPartToInlines' (CommentEnd cmtId) = do
374394 opts <- asks docxOptions
375395 case readerTrackChanges opts of
376396 AllChanges -> do
377397 let attr = ("", ["comment-end"], [("id", cmtId)])
378398 return $ spanWith attr mempty
379399 _ -> return mempty
380−parPartToInlines (BookMark _ anchor) | anchor `elem` dummyAnchors =
400+parPartToInlines' (BookMark _ anchor) | anchor `elem` dummyAnchors =
381401 return mempty
382−parPartToInlines (BookMark _ anchor) =
402+parPartToInlines' (BookMark _ anchor) =
383403 -- We record these, so we can make sure not to overwrite
384404 -- user-defined anchor links with header auto ids.
385405 do
@@ -395,27 +415,34 @@ parPartToInlines (BookMark _ anchor) =
395415 -- of rewriting user-defined anchor links. However, since these
396416 -- are not defined in pandoc, it seems like a necessary evil to
397417 -- avoid an extra pass.
398− let newAnchor =
399− if not inHdrBool && anchor `elem` M.elems anchorMap
400− then uniqueIdent [Str anchor] (Set.fromList $ M.elems anchorMap)
401− else anchor
402− unless inHdrBool
403− (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap})
404− return $ spanWith (newAnchor, ["anchor"], []) mempty
405−parPartToInlines (Drawing fp title alt bs ext) = do
418+ immedPrevAnchor <- gets docxImmedPrevAnchor
419+ case immedPrevAnchor of
420+ Just prevAnchor -> do
421+ unless inHdrBool
422+ (modify $ \s -> s { docxAnchorMap = M.insert anchor prevAnchor anchorMap})
423+ return mempty
424+ Nothing -> do
425+ let newAnchor =
426+ if not inHdrBool && anchor `elem` M.elems anchorMap
427+ then uniqueIdent [Str anchor] (Set.fromList $ M.elems anchorMap)
428+ else anchor
429+ unless inHdrBool
430+ (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap})
431+ return $ spanWith (newAnchor, ["anchor"], []) mempty
432+parPartToInlines' (Drawing fp title alt bs ext) = do
406433 (lift . lift) $ P.insertMedia fp Nothing bs
407434 return $ imageWith (extentToAttr ext) fp title $ text alt
408−parPartToInlines Chart =
435+parPartToInlines' Chart =
409436 return $ spanWith ("", ["chart"], []) $ text "[CHART]"
410−parPartToInlines (InternalHyperLink anchor runs) = do
437+parPartToInlines' (InternalHyperLink anchor runs) = do
411438 ils <- smushInlines <$> mapM runToInlines runs
412439 return $ link ('#' : anchor) "" ils
413−parPartToInlines (ExternalHyperLink target runs) = do
440+parPartToInlines' (ExternalHyperLink target runs) = do
414441 ils <- smushInlines <$> mapM runToInlines runs
415442 return $ link target "" ils
416−parPartToInlines (PlainOMath exps) =
443+parPartToInlines' (PlainOMath exps) =
417444 return $ math $ writeTeX exps
418−parPartToInlines (SmartTag runs) = do
445+parPartToInlines' (SmartTag runs) = do
419446 smushInlines <$> mapM runToInlines runs
420447
421448isAnchorSpan :: Inline -> Bool
test/docx/unused_anchors.native+2−2
@@ -1,3 +1,3 @@
11[Header 1 ("my-section",[],[]) [Str "My",Space,Str "Section"]
2−,Para [Link ("",[],[]) [Str "Here",Space,Str "is",Space,Str "a",Space,Str "link."] ("#Bar","")]
3−,Para [Span ("Bar",["anchor"],[]) [],Str "Here",Space,Str "is",Space,Str "the",Space,Str "target."]]
2+,Para [Link ("",[],[]) [Str "Here",Space,Str "is",Space,Str "a",Space,Str "link."] ("#Foo","")]
3+,Para [Span ("Foo",["anchor"],[]) [],Str "Here",Space,Str "is",Space,Str "the",Space,Str "target."]]