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
This commit is contained in:
Michael Hoffmann 2022-01-12 00:48:41 +01:00 committed by GitHub
parent a25e79b5be
commit 5001fd3f4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 10 deletions

View file

@ -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]

View file

@ -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"]

View file

@ -87,6 +87,11 @@ tests = [ testGroup "inlines"
def
"docx/lists_multiple_initial.native"
"docx/golden/lists_multiple_initial.docx"
, docxTest
"lists with div bullets"
def
"docx/lists_div_bullets.native"
"docx/golden/lists_div_bullets.docx"
, docxTest
"definition lists"
def

Binary file not shown.

View file

@ -0,0 +1,5 @@
[ BulletList
[ [ Div ( "", [], []) [ Para [ Str "one" ], Para [ Str "two" ] ] ]
, [ Div ( "refs", [], []) [ Header 1 ( "" , [] , [] ) [ Str "three" ], Para [ Str "four" ] ] ]
]
]