mirror of
https://github.com/jgm/pandoc.git
synced 2026-08-28 17:20:47 +08:00
Docx reader: key list numbering off abstractNumId.
Word represents "restart numbering" on a style-based list by pointing only the first item of the restarted list at a new `numId` that shares the original list's abstract numbering definition but carries a `w:startOverride`; the remaining items keep using the original `numId`. Pandoc keyed list continuation and grouping on the `numId`, so the restarted items continued the stale count from the earlier list (and were split into a separate ordered list with the wrong start). Key continuation and grouping off the `abstractNumId` instead (the real running counter in Word), and treat `startOverride` as a restart that resets the count. Closes #8367. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -782,15 +782,16 @@ bodyPartToBlocks (Paragraph pPr parparts) = do
|
||||
then (<> horizontalRule)
|
||||
else id)
|
||||
<$> getMainPar
|
||||
bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do
|
||||
-- We check whether this current numId has previously been used,
|
||||
-- since Docx expects us to pick up where we left off.
|
||||
bodyPartToBlocks (ListItem pPr numId lvl isRestart (Just levelInfo) parparts) = do
|
||||
-- We check whether this current abstract numbering id has previously been
|
||||
-- used, since Docx expects us to pick up where we left off -- unless this
|
||||
-- item explicitly restarts numbering (see #8367).
|
||||
listState <- gets docxListState
|
||||
let startFromState = M.lookup (numId, lvl) listState
|
||||
Level _ fmt txt startFromLevelInfo = levelInfo
|
||||
start = case startFromState of
|
||||
Just n -> n + 1
|
||||
Nothing -> fromMaybe 1 startFromLevelInfo
|
||||
Just n | not isRestart -> n + 1
|
||||
_ -> fromMaybe 1 startFromLevelInfo
|
||||
kvs = [ ("level", lvl)
|
||||
, ("num-id", numId)
|
||||
, ("format", fmt)
|
||||
@@ -804,7 +805,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do
|
||||
in M.insert (numId, lvl) start (M.filterWithKey notExpired listState) }
|
||||
blks <- bodyPartToBlocks (Paragraph pPr parparts)
|
||||
return $ divWith ("", ["list-item"], kvs) blks
|
||||
bodyPartToBlocks (ListItem pPr _ _ _ parparts) =
|
||||
bodyPartToBlocks (ListItem pPr _ _ _ _ parparts) =
|
||||
let pPr' = pPr {pStyle = constructBogusParStyleData "list-paragraph": pStyle pPr}
|
||||
in
|
||||
bodyPartToBlocks $ Paragraph pPr' parparts
|
||||
|
||||
@@ -304,7 +304,7 @@ defaultParagraphStyle = ParagraphStyle { pStyle = []
|
||||
data BodyPart = Paragraph ParagraphStyle [ParPart]
|
||||
| Heading Int ParaStyleName ParagraphStyle T.Text T.Text (Maybe Level)
|
||||
[ParPart]
|
||||
| ListItem ParagraphStyle T.Text T.Text (Maybe Level) [ParPart]
|
||||
| ListItem ParagraphStyle T.Text T.Text Bool (Maybe Level) [ParPart]
|
||||
| Tbl (Maybe T.Text) T.Text TblGrid TblLook [Row]
|
||||
| Captioned ParagraphStyle [ParPart] BodyPart
|
||||
| HRule
|
||||
@@ -619,6 +619,28 @@ lookupLevel numId ilvl (Numbering _ numbs absNumbs) = do
|
||||
_ ->
|
||||
lookup ilvl $ map (\l@(Level i _ _ _) -> (i, l)) lvls
|
||||
|
||||
-- | Given a @numId@ and level, return the abstract numbering id (the stable
|
||||
-- identifier shared by all numbering instances based on the same abstract
|
||||
-- numbering definition) together with a flag indicating whether this
|
||||
-- particular instance restarts numbering at this level (via a
|
||||
-- @w:startOverride@). Word represents "restart numbering" by pointing the
|
||||
-- first item of the restarted list at a new numId that shares the abstract
|
||||
-- numbering of the original list but carries a startOverride; the remaining
|
||||
-- items keep using the original numId. Keying list continuation off the
|
||||
-- abstract numbering id (rather than the numId) lets those items be treated
|
||||
-- as one continuous, restarted list. See #8367.
|
||||
lookupNumberingInfo :: T.Text -> T.Text -> Numbering -> (T.Text, Bool)
|
||||
lookupNumberingInfo numId ilvl (Numbering _ numbs _) =
|
||||
case lookup numId $
|
||||
map (\(Numb nid absnumid ovr) -> (nid, (absnumid, ovr))) numbs of
|
||||
Nothing -> (numId, False)
|
||||
Just (absNumId, ovrrides) ->
|
||||
let isRestart = case lookup ilvl $
|
||||
map (\lo@(LevelOverride i _ _) -> (i, lo)) ovrrides of
|
||||
Just (LevelOverride _ (Just _) _) -> True
|
||||
_ -> False
|
||||
in (absNumId, isRestart)
|
||||
|
||||
loElemToLevelOverride :: NameSpaces -> Element -> Maybe LevelOverride
|
||||
loElemToLevelOverride ns element
|
||||
| isElem ns "w" "lvlOverride" element = do
|
||||
@@ -820,8 +842,10 @@ pNumInfo = getParStyleField numInfo . pStyle
|
||||
|
||||
mkListItem :: ParagraphStyle -> Text -> Text -> [ParPart] -> D BodyPart
|
||||
mkListItem parstyle numId lvl parparts = do
|
||||
lvlInfo <- lookupLevel numId lvl <$> asks envNumbering
|
||||
return $ ListItem parstyle numId lvl lvlInfo parparts
|
||||
numbering <- asks envNumbering
|
||||
let lvlInfo = lookupLevel numId lvl numbering
|
||||
(absNumId, isRestart) = lookupNumberingInfo numId lvl numbering
|
||||
return $ ListItem parstyle absNumId lvl isRestart lvlInfo parparts
|
||||
|
||||
pStyleIndentation :: ParagraphStyle -> Maybe ParIndentation
|
||||
pStyleIndentation style = (getParStyleField indent . pStyle) style
|
||||
@@ -877,7 +901,8 @@ elemToBodyPart ns element
|
||||
elemToBodyPart ns element
|
||||
| isElem ns "w" "p" element
|
||||
, Just (numId, lvl) <- getNumInfo ns element = do
|
||||
lvlInfo <- lookupLevel numId lvl <$> asks envNumbering
|
||||
numbering <- asks envNumbering
|
||||
let lvlInfo = lookupLevel numId lvl numbering
|
||||
parstyle <- elemToParagraphStyle ns element
|
||||
<$> asks envParStyles
|
||||
<*> asks envNumbering
|
||||
@@ -885,7 +910,8 @@ elemToBodyPart ns element
|
||||
case pHeading parstyle of
|
||||
Nothing -> mkListItem parstyle numId lvl parparts
|
||||
Just (parstylename, lev)
|
||||
-> return $ Heading lev parstylename parstyle numId lvl lvlInfo parparts
|
||||
-> let (absNumId, _) = lookupNumberingInfo numId lvl numbering
|
||||
in return $ Heading lev parstylename parstyle absNumId lvl lvlInfo parparts
|
||||
elemToBodyPart ns element
|
||||
| isElem ns "w" "p" element
|
||||
, [ppr] <- elChildren element
|
||||
@@ -928,8 +954,10 @@ elemToBodyPart ns element
|
||||
mkListItem parstyle numId lvl parparts
|
||||
Just (parstylename, lev) -> do
|
||||
let (numId, lvl) = fromMaybe ("","") $ pNumInfo parstyle
|
||||
lvlInfo <- lookupLevel numId lvl <$> asks envNumbering
|
||||
return $ Heading lev parstylename parstyle numId lvl lvlInfo parparts
|
||||
numbering <- asks envNumbering
|
||||
let lvlInfo = lookupLevel numId lvl numbering
|
||||
(absNumId, _) = lookupNumberingInfo numId lvl numbering
|
||||
return $ Heading lev parstylename parstyle absNumId lvl lvlInfo parparts
|
||||
Nothing -> return $ Paragraph parstyle parparts
|
||||
elemToBodyPart ns element
|
||||
| isElem ns "w" "tbl" element = do
|
||||
|
||||
@@ -281,6 +281,10 @@ tests = [ testGroup "document"
|
||||
"sublists reset numbering to 1"
|
||||
"docx/lists_sublist_reset.docx"
|
||||
"docx/lists_sublist_reset.native"
|
||||
, testCompare
|
||||
"style-based list restarted on one item (#8367)"
|
||||
"docx/lists_restart_8367.docx"
|
||||
"docx/lists_restart_8367.native"
|
||||
, testCompare
|
||||
"definition lists"
|
||||
"docx/definition_list.docx"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
[Header 1 ("section-1",[],[]) [Str "Section",Space,Str "1"]
|
||||
,OrderedList (1,Decimal,Period)
|
||||
[[Para [Str "Item",Space,Str "1"]]
|
||||
,[Para [Str "Item",Space,Str "2"]]
|
||||
,[Para [Str "Item",Space,Str "3"]]]
|
||||
,Para [Str "Conclusion"]
|
||||
,Header 1 ("section-2",[],[]) [Str "Section",Space,Str "2"]
|
||||
,OrderedList (1,Decimal,Period)
|
||||
[[Para [Str "Item",Space,Str "1"]]
|
||||
,[Para [Str "Item",Space,Str "2"]]
|
||||
,[Para [Str "Item",Space,Str "3"]]
|
||||
,[Para [Str "Item",Space,Str "4"]]]
|
||||
,Para [Str "Conclusion"]]
|
||||
Reference in New Issue
Block a user