git.iko.soy/typeable/pandoc
on master help sign in
Docx writer: Fix bookmarks to headers with long titles.

Word has a 40 character limit for bookmark names. In addition, bookmarks must begin with a letter. Since pandoc's auto-generated identifiers may not respect these constraints, some internal links did not work. With this change, pandoc uses a bookmark name based on the SHA1 hash of the identifier when the identifier isn't a legal bookmark name. Closes #5091.

commitd333c283cc7149462491e97e95d3ccfaefab84c2 authorJohn MacFarlane <jgm@berkeley.edu> dateTue 20 Nov 2018, 23:43 (8 years ago) parent2d26591
3 files changed+19−5
Makefile+1−1 src/Text/Pandoc/Writers/Docx.hs+18−4 test/docx/golden/nested_anchors_in_header.docx
Makefile+1−1
@@ -2,7 +2,7 @@ version?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}')
22pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1)
33SOURCEFILES?=$(shell find pandoc.hs src test -name '*.hs')
44BRANCH?=master
5−RESOLVER=lts-12
5+RESOLVER?=lts-12
66GHCOPTS=-fdiagnostics-color=always -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -Wincomplete-uni-patterns -Werror=missing-home-modules -Widentities -Wcpp-undef -fhide-source-paths -j +RTS -A32M -RTS
77# Later:
88# -Wpartial-fields (currently used in Powerpoint writer)
src/Text/Pandoc/Writers/Docx.hs+18−4
@@ -41,19 +41,21 @@ import Control.Monad.Reader
4141import Control.Monad.State.Strict
4242import qualified Data.ByteString as B
4343import qualified Data.ByteString.Lazy as BL
44−import Data.Char (isSpace, ord, toLower)
44+import Data.Char (isSpace, ord, toLower, isLetter)
4545import Data.List (intercalate, isPrefixOf, isSuffixOf)
4646import qualified Data.Map as M
4747import Data.Maybe (fromMaybe, isNothing, mapMaybe, maybeToList)
4848import qualified Data.Set as Set
4949import qualified Data.Text as T
5050import Data.Time.Clock.POSIX
51+import Data.Digest.Pure.SHA (sha1, showDigest)
5152import Skylighting
5253import System.Random (randomR, StdGen, mkStdGen)
5354import Text.Pandoc.BCP47 (getLang, renderLang)
5455import Text.Pandoc.Class (PandocMonad, report, toLang)
5556import qualified Text.Pandoc.Class as P
5657import Data.Time
58+import Text.Pandoc.UTF8 (fromStringLazy)
5759import Text.Pandoc.Definition
5860import Text.Pandoc.Generic
5961import Text.Pandoc.Highlighting (highlight)
@@ -1268,7 +1270,8 @@ inlineToOpenXML' opts (Note bs) = do
12681270-- internal link:
12691271inlineToOpenXML' opts (Link _ txt ('#':xs,_)) = do
12701272 contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
1271− return [ mknode "w:hyperlink" [("w:anchor",xs)] contents ]
1273+ return
1274+ [ mknode "w:hyperlink" [("w:anchor", toBookmarkName xs)] contents ]
12721275-- external link:
12731276inlineToOpenXML' opts (Link _ txt (src,_)) = do
12741277 contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
@@ -1427,7 +1430,18 @@ wrapBookmark :: (PandocMonad m) => String -> [Element] -> WS m [Element]
14271430wrapBookmark [] contents = return contents
14281431wrapBookmark ident contents = do
14291432 id' <- getUniqueId
1430− let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id')
1431− ,("w:name",ident)] ()
1433+ let bookmarkStart = mknode "w:bookmarkStart"
1434+ [("w:id", id')
1435+ ,("w:name", toBookmarkName ident)] ()
14321436 bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
14331437 return $ bookmarkStart : contents ++ [bookmarkEnd]
1438+
1439+-- Word imposes a 40 character limit on bookmark names and requires
1440+-- that they begin with a letter. So we just use a hash of the
1441+-- identifer when otherwise we'd have an illegal bookmark name.
1442+toBookmarkName :: String -> String
1443+toBookmarkName s =
1444+ case s of
1445+ (c:_) | isLetter c
1446+ , length s <= 40 -> s
1447+ _ -> 'X' : drop 1 (showDigest (sha1 (fromStringLazy s)))
test/docx/golden/nested_anchors_in_header.docx

Binary file not shown.