From 3952d4d1a22be1d9b7161f22e0137073455b5ed7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 4 Jun 2024 15:17:43 -0700 Subject: [PATCH] Docx reader: support task lists. This also fixes a small bug in parsing delimiters in numbered lists, which led to the default delimiter being used wrongly in some cases. Closes #8211. --- src/Text/Pandoc/Readers/Docx/Lists.hs | 71 +++++++++++++++++--------- test/Tests/Readers/Docx.hs | 4 ++ test/docx/deep_normalize.native | 4 +- test/docx/golden/lists.docx | Bin 11007 -> 11005 bytes test/docx/lists.native | 2 +- test/docx/task_list.docx | Bin 0 -> 10749 bytes 6 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 test/docx/task_list.docx diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 9870832c438c..56d132a4f947 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Docx.Lists @@ -18,6 +19,7 @@ module Text.Pandoc.Readers.Docx.Lists ( blocksToBullets ) where import Data.List +import Data.Char (isDigit) import Data.Maybe import Data.String (fromString) import qualified Data.Text as T @@ -58,9 +60,9 @@ listStyleMap = [("upperLetter", UpperAlpha), ("decimal", Decimal)] listDelimMap :: [(T.Text, ListNumberDelim)] -listDelimMap = [("%1)", OneParen), - ("(%1)", TwoParens), - ("%1.", Period)] +listDelimMap = [("%)", OneParen), + ("(%)", TwoParens), + ("%.", Period)] getListType :: Block -> Maybe ListType getListType b@(Div (_, _, kvs) _) | isListItem b = @@ -72,7 +74,7 @@ getListType b@(Div (_, _, kvs) _) | isListItem b = case frmt of Just "bullet" -> Just Itemized Just f -> - case txt of + case T.filter (not . isDigit) <$> txt of Just t -> Just $ Enumerated ( fromMaybe 1 (start >>= safeRead) :: Int, fromMaybe DefaultStyle (lookup f listStyleMap), @@ -122,25 +124,48 @@ separateBlocks blks = foldr separateBlocks' [[]] (reverse blks) flatToBullets' :: Integer -> [Block] -> [Block] flatToBullets' _ [] = [] -flatToBullets' num xs@(b : elems) - | getLevelN b == num = b : flatToBullets' num elems - | otherwise = - let bNumId = getNumIdN b - bLevel = getLevelN b - (children, remaining) = - span - (\b' -> - getLevelN b' > bLevel || - (getLevelN b' == bLevel && getNumIdN b' == bNumId)) - xs - in - case getListType b of - Just (Enumerated attr) -> - OrderedList attr (separateBlocks $ flatToBullets' bLevel children) : - flatToBullets' num remaining - _ -> - BulletList (separateBlocks $ flatToBullets' bLevel children) : - flatToBullets' num remaining +flatToBullets' num xs@(b : elems) = + if getLevelN b == num + then (case bCheckmark of + Just checked -> addCheckmark checked b + Nothing -> b) : flatToBullets' num elems + else case getListType b of + Just (Enumerated attr) -> + OrderedList attr (separateBlocks $ flatToBullets' bLevel children) : + flatToBullets' num remaining + _ -> + BulletList (separateBlocks $ flatToBullets' bLevel children) : + flatToBullets' num remaining + where + bNumId = getNumIdN b + bLevel = getLevelN b + isCheckmark (Just "\9744") = Just False + isCheckmark (Just "\9746") = Just True + isCheckmark _ = Nothing + bCheckmark = + case getListType b of + Just Itemized -> isCheckmark (getText b) + _ -> Nothing + addCheckmark checked (Div attrs [Para ils]) = + Div attrs [Para (Str (if checked then "\9746" else "\9744") : Space : ils)] + addCheckmark checked (Div attrs [Plain ils]) = + Div attrs [Plain (Str (if checked then "\9746" else "\9744") : Space : ils)] + addCheckmark _ x = x + (children, remaining) = + span + (\b' -> + getLevelN b' > bLevel || + (getLevelN b' == bLevel && + (getNumIdN b' == bNumId || + (case bCheckmark of + Just _ -> + case getText b' of + Just "" -> True + Just " " -> True + Just x -> isJust (isCheckmark (Just x)) + Nothing -> False + Nothing -> False)))) + xs flatToBullets :: [Block] -> [Block] flatToBullets elems = flatToBullets' (-1) elems diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index b2aa36560390..58818c56f018 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -301,6 +301,10 @@ tests = [ testGroup "document" "definition lists" "docx/definition_list.docx" "docx/definition_list.native" + , testCompare + "task lists" + "docx/task_list.docx" + "docx/task_list.native" , testCompare "custom defined lists in styles" "docx/german_styled_lists.docx" diff --git a/test/docx/deep_normalize.native b/test/docx/deep_normalize.native index d3fd0675567a..72b564f5e981 100644 --- a/test/docx/deep_normalize.native +++ b/test/docx/deep_normalize.native @@ -1,6 +1,6 @@ [OrderedList (1,Decimal,OneParen) [[Para [Str "This",Space,Str "is",Space,Str "at",Space,Str "the",Space,Str "first",Space,Str "level"] - ,OrderedList (1,LowerAlpha,DefaultDelim) + ,OrderedList (1,LowerAlpha,OneParen) [[Para [Str "This",Space,Str "is",Space,Str "at",Space,Str "the",Space,Str "second",Space,Str "level"] - ,OrderedList (1,LowerRoman,DefaultDelim) + ,OrderedList (1,LowerRoman,OneParen) [[Para [Str "This",Space,Str "is",Space,Emph [Str "at",Space,Strong [Str "the",Space,Span ("",["mark"],[]) [Str "th"],Str "i",Span ("",["mark"],[]) [Str "rd"],Space,Str "level"],Str ",",Space,Str "and",Space,Str "I",Space,Str "want",Space,Str "to"],Space,Str "test",Space,Str "normalization",Space,Str "here."]]]]]]]] diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 89a686f11a221657d731fc73a89d1d55bd1889cb..29f7226f7295e70bec822aa5125427e6a0ce8b95 100644 GIT binary patch delta 1695 zcmZWq2{hDe6#vhJL5pdIEMaCeF+*9iyrR@2>lkHu(zEmoEevMH5*p*#o)qG5$&w`| zo);nQDda`=GLn5s5fatB92wsKKj*!3RNpz@xy$#v_kQ<&=iYD4N#L}}-e!Y{G=LNr z2imKvH`O4J?bSEy5nF_`8>xzfYd36{jS$J#snb#Lx0;mTwO$roKxvLlmZ4#J5xp(3{2C34CJdfuf5`hzR#M z*ZH2;yq>RukeOI=-pAfsDaLgjYw^#beTX*Wk~&KwFM%YFEIiA)f>iB4+^w*v--0ua zc{JQkq8+QJ#i2bTrgYBsSDcRc)Q8to(QX^c8nyZuWpu)JE}t{+ny00_>zpxFs`|Cx z*`$g)sXqI}^!`HW(T~Tsm*=KxW}T@%p(P_gX?TtQ@tMtm zzIYaQB+8vnOJ={>?m1bxpfMjhxQ9Bl@T4PpVly?H8uFmC=1tdWNu=EJ^yhI7`;Cuw_Jy9uAomNA_?!z=$Cl=ezS)(<`sk#hmyo-5J z`uVSzre1PZgx$R%*Yu6%I(TZ7j_E3nkNMf~NJA5k;;5q&jLopM9X8X)Iv5^#4y*KG z6~%UVSMWYA{m=Vk-$A~I?rQ5~9dGeQLCOO=jjxOzO953h zH!J!J$V&c5-z@&SLm)4ft2Em%aB%9(h~g3S3lHt4{k?Z8p{ zY15bn(+k%NWwoOF#(ka?uCUI;p-_euCmGBSF*z66qbCJ^RZmw$1>x*4++j+FL`xFz zf_GHg@zp_xo;KaeSXA=MltG8B0W8!XBjNH_nNC+!73%Qd<=CWqU9D4d@9w#!UTbet z@0dF6e#`q=Vq%8WXtNita#_n%sUWCg9-oi!nwTB!Uiq$sa+ieD=cfD}xRQPfmMECs zCb+`G3B(ZFVR4KoArxm-55U@8YKAcJRx^P~iJB!$=GCm$%`a1u5E4dGL}0*7k}?h+ zZ4UwL>1_xAu-O0r7-0Ph1A9mW4vfM1Z@~6LM+_tYY+Vll9?JkgXaJL@}?Pb@c$Xx) z36BL+$v9ye3Sm2w5}-34w6Wh{xL_AqMTp6{UMC@o0)R8S006r8y5$a{)rllHRjC45 zmR{!$07Sqv^&NQO*YT^TTR(JpX^HRa T`+^8A5T(I)>S(b+RoMDp?$yB% delta 1643 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