From 5001fd3f4d0daee5802a78f6d99d538ff9db4336 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Wed, 12 Jan 2022 00:48:41 +0100 Subject: Docx writer: Handle bullets correctly in lists by not reusing numIds (#7822) Make sure that we only create one bullet per list item in docx. In particular, when a div is a list item, its contained paragraphs will now no longer wrongly get individual bullets. This is accomplished by making sure that for each list, we only use the associated numId once. Any repeated use would add incorrect bullets to the document. Closes #7689 --- src/Text/Pandoc/Writers/Docx.hs | 25 +++++++++++++++---------- src/Text/Pandoc/Writers/Docx/Types.hs | 3 +++ 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index f76a8b86c..6ad91d2bd 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -965,8 +965,7 @@ listItemToOpenXML :: (PandocMonad m) => WriterOptions -> Int -> [Block] -> WS m [Content] -listItemToOpenXML _ _ [] = return [] -listItemToOpenXML opts numid (first:rest) = do +listItemToOpenXML opts numid bs = do oldInList <- gets stInList modify $ \st -> st{ stInList = True } let isListBlock = \case @@ -975,14 +974,15 @@ listItemToOpenXML opts numid (first:rest) = do _ -> False -- Prepend an empty string if the first entry is another -- list. Otherwise the outer bullet will disappear. - let (first', rest') = if isListBlock first - then (Plain [Str ""] , first:rest) - else (first, rest) - first'' <- withNumId numid $ blockToOpenXML opts first' - -- baseListId is the code for no list marker: - rest'' <- withNumId baseListId $ blocksToOpenXML opts rest' + let bs' = case bs of + [] -> [] + first:rest -> if isListBlock first + then Plain [Str ""]:first:rest + else first:rest + modify $ \st -> st{ stNumIdUsed = False } + contents <- withNumId numid $ blocksToOpenXML opts bs' modify $ \st -> st{ stInList = oldInList } - return $ first'' ++ rest'' + return contents -- | Convert a list of inline elements to OpenXML. inlinesToOpenXML :: PandocMonad m => WriterOptions -> [Inline] -> WS m [Content] @@ -1015,9 +1015,14 @@ getParaProps displayMathPara = do props <- asks envParaProperties listLevel <- asks envListLevel numid <- asks envListNumId + numIdUsed <- gets stNumIdUsed + -- clear numId after first use to support multiple paragraphs in the same bullet + -- baseListId is the code for no list marker + let numid' = if numIdUsed then baseListId else numid + modify $ \st -> st{ stNumIdUsed = True } let listPr = [mknode "w:numPr" [] [ mknode "w:ilvl" [("w:val",tshow listLevel)] () - , mknode "w:numId" [("w:val",tshow numid)] () ] | listLevel >= 0 && not displayMathPara] + , mknode "w:numId" [("w:val",tshow numid')] () ] | listLevel >= 0 && not displayMathPara] return $ case listPr ++ squashProps props of [] -> [] ps -> [mknode "w:pPr" [] ps] diff --git a/src/Text/Pandoc/Writers/Docx/Types.hs b/src/Text/Pandoc/Writers/Docx/Types.hs index e367e9cbb..15e16cb93 100644 --- a/src/Text/Pandoc/Writers/Docx/Types.hs +++ b/src/Text/Pandoc/Writers/Docx/Types.hs @@ -111,6 +111,8 @@ data WriterState = WriterState{ , stDelId :: Int , stStyleMaps :: StyleMaps , stFirstPara :: Bool + , stNumIdUsed :: Bool -- ^ True if the current numId (envListNumId) has been used. + -- Should only be used once, for the first paragraph. , stInTable :: Bool , stInList :: Bool , stTocTitle :: [Inline] @@ -133,6 +135,7 @@ defaultWriterState = WriterState{ , stDelId = 1 , stStyleMaps = StyleMaps M.empty M.empty , stFirstPara = False + , stNumIdUsed = False , stInTable = False , stInList = False , stTocTitle = [Str "Table of Contents"] -- cgit v1.2.3