From c113ca6717d00870ec10716897d76a6fa62b1d41 Mon Sep 17 00:00:00 2001
From: Nikolay Yakimov <root@livid.pp.ru>
Date: Sun, 15 Sep 2019 01:40:23 +0300
Subject: [PATCH] [Docx Reader] Use style names, not ids, for assigning
 semantic meaning

Motivating issues: #5523, #5052, #5074

Style name comparisons are case-insensitive, since those are
case-insensitive in Word.

w:styleId will be used as style name if w:name is missing (this should
only happen for malformed docx and is kept as a fallback to avoid
failing altogether on malformed documents)

Block quote detection code moved from Docx.Parser to Readers.Docx

Code styles, i.e. "Source Code" and "Verbatim Char" now honor style
inheritance

Docx Reader now honours "Compact" style (used in Pandoc-generated docx).
The side-effect is that "Compact" style no longer shows up in
docx+styles output. Styles inherited from "Compact" will still
show up.

Removed obsolete list-item style from divsToKeep. That didn't
really do anything for a while now.

Add newtypes to differentiate between style names, ids, and
different style types (that is, paragraph and character styles)

Since docx style names can have spaces in them, and pandoc-markdown
classes can't, anywhere when style name is used as a class name,
spaces are replaced with ASCII dashes `-`.

Get rid of extraneous intermediate types, carrying styleId information.
Instead, styleId is saved with other style data.

Use RunStyle for inline style definitions only (lacking styleId and styleName);
for Character Styles use CharStyle type (which is basicaly RunStyle with styleId
and StyleName bolted onto it).
---
 src/Text/Pandoc/Readers/Docx.hs        | 162 +++++++-------
 src/Text/Pandoc/Readers/Docx/Lists.hs  |  25 ++-
 src/Text/Pandoc/Readers/Docx/Parse.hs  | 283 ++++++++++++++++---------
 test/Tests/Readers/Docx.hs             |   9 +
 test/docx/compact-style-removal.docx   | Bin 0 -> 9951 bytes
 test/docx/compact-style-removal.native |   5 +
 test/docx/lists-compact.docx           | Bin 0 -> 9952 bytes
 test/docx/lists-compact.native         |   5 +
 8 files changed, 306 insertions(+), 183 deletions(-)
 create mode 100644 test/docx/compact-style-removal.docx
 create mode 100644 test/docx/compact-style-removal.native
 create mode 100644 test/docx/lists-compact.docx
 create mode 100644 test/docx/lists-compact.native

diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs
index a26986af2..9d17ab118 100644
--- a/src/Text/Pandoc/Readers/Docx.hs
+++ b/src/Text/Pandoc/Readers/Docx.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE PatternGuards     #-}
 {-# LANGUAGE MultiWayIf        #-}
+{-# LANGUAGE FlexibleContexts  #-}
 {- |
    Module      : Text.Pandoc.Readers.Docx
    Copyright   : Copyright (C) 2014-2019 Jesse Rosenthal
@@ -65,6 +66,7 @@ import Control.Monad.State.Strict
 import qualified Data.ByteString.Lazy as B
 import Data.Default (Default)
 import Data.List (delete, intersect)
+import Data.Char (isSpace)
 import qualified Data.Map as M
 import Data.Maybe (isJust, fromMaybe)
 import Data.Sequence (ViewL (..), viewl)
@@ -133,13 +135,13 @@ evalDocxContext :: PandocMonad m => DocxContext m a -> DEnv -> DState -> m a
 evalDocxContext ctx env st = flip evalStateT st $ runReaderT ctx env
 
 -- This is empty, but we put it in for future-proofing.
-spansToKeep :: [String]
+spansToKeep :: [CharStyleName]
 spansToKeep = []
 
-divsToKeep :: [String]
-divsToKeep = ["list-item", "Definition", "DefinitionTerm"]
+divsToKeep :: [ParaStyleName]
+divsToKeep = ["Definition", "Definition Term"]
 
-metaStyles :: M.Map String String
+metaStyles :: M.Map ParaStyleName String
 metaStyles = M.fromList [ ("Title", "title")
                         , ("Subtitle", "subtitle")
                         , ("Author", "author")
@@ -151,7 +153,7 @@ sepBodyParts = span (\bp -> isMetaPar bp || isEmptyPar bp)
 
 isMetaPar :: BodyPart -> Bool
 isMetaPar (Paragraph pPr _) =
-  not $ null $ intersect (pStyle pPr) (M.keys metaStyles)
+  not $ null $ intersect (getStyleNames $ pStyle pPr) (M.keys metaStyles)
 isMetaPar _ = False
 
 isEmptyPar :: BodyPart -> Bool
@@ -168,7 +170,7 @@ bodyPartsToMeta' :: PandocMonad m => [BodyPart] -> DocxContext m (M.Map String M
 bodyPartsToMeta' [] = return M.empty
 bodyPartsToMeta' (bp : bps)
   | (Paragraph pPr parParts) <- bp
-  , (c : _)<- (pStyle pPr) `intersect` (M.keys metaStyles)
+  , (c : _)<- getStyleNames (pStyle pPr) `intersect` M.keys metaStyles
   , (Just metaField) <- M.lookup c metaStyles = do
     inlines <- smushInlines <$> mapM parPartToInlines parParts
     remaining <- bodyPartsToMeta' bps
@@ -198,11 +200,29 @@ fixAuthors (MetaBlocks blks) =
           g _          = MetaInlines []
 fixAuthors mv = mv
 
-codeStyles :: [String]
-codeStyles = ["VerbatimChar"]
+isInheritedFromStyles :: (Eq (StyleName s), HasStyleName s, HasParentStyle s) => [StyleName s] -> s -> Bool
+isInheritedFromStyles names sty
+  | getStyleName sty `elem` names = True
+  | Just psty <- getParentStyle sty = isInheritedFromStyles names psty
+  | otherwise = False
 
-codeDivs :: [String]
-codeDivs = ["SourceCode"]
+hasStylesInheritedFrom :: [ParaStyleName] -> ParagraphStyle -> Bool
+hasStylesInheritedFrom ns s = any (isInheritedFromStyles ns) $ pStyle s
+
+removeStyleNamed :: ParaStyleName -> ParagraphStyle -> ParagraphStyle
+removeStyleNamed sn ps = ps{pStyle = filter (\psd -> getStyleName psd /= sn) $ pStyle ps}
+
+isCodeCharStyle :: CharStyle -> Bool
+isCodeCharStyle = isInheritedFromStyles ["Verbatim Char"]
+
+isCodeDiv :: ParagraphStyle -> Bool
+isCodeDiv = hasStylesInheritedFrom ["Source Code"]
+
+isBlockQuote :: ParStyle -> Bool
+isBlockQuote =
+  isInheritedFromStyles [
+    "Quote", "Block Text", "Block Quote", "Block Quotation"
+    ]
 
 runElemToInlines :: RunElem -> Inlines
 runElemToInlines (TextRun s)   = text s
@@ -228,57 +248,31 @@ parPartToString (InternalHyperLink _ runs) = concatMap runToString runs
 parPartToString (ExternalHyperLink _ runs) = concatMap runToString runs
 parPartToString _                          = ""
 
-blacklistedCharStyles :: [String]
+blacklistedCharStyles :: [CharStyleName]
 blacklistedCharStyles = ["Hyperlink"]
 
 resolveDependentRunStyle :: PandocMonad m => RunStyle -> DocxContext m RunStyle
 resolveDependentRunStyle rPr
-  | Just (s, _)  <- rStyle rPr, s `elem` blacklistedCharStyles =
+  | Just s  <- rParentStyle rPr
+  , getStyleName s `elem` blacklistedCharStyles =
     return rPr
-  | Just (_, cs) <- rStyle rPr = do
+  | Just s  <- rParentStyle rPr = do
       opts <- asks docxOptions
       if isEnabled Ext_styles opts
         then return rPr
-        else do rPr' <- resolveDependentRunStyle cs
-                return $
-                  RunStyle { isBold = case isBold rPr of
-                               Just bool -> Just bool
-                               Nothing   -> isBold rPr'
-                           , isItalic = case isItalic rPr of
-                               Just bool -> Just bool
-                               Nothing   -> isItalic rPr'
-                           , isSmallCaps = case isSmallCaps rPr of
-                               Just bool -> Just bool
-                               Nothing   -> isSmallCaps rPr'
-                           , isStrike = case isStrike rPr of
-                               Just bool -> Just bool
-                               Nothing   -> isStrike rPr'
-                           , isRTL = case isRTL rPr of
-                               Just bool -> Just bool
-                               Nothing   -> isRTL rPr'
-                           , rVertAlign = case rVertAlign rPr of
-                               Just valign -> Just valign
-                               Nothing     -> rVertAlign rPr'
-                           , rUnderline = case rUnderline rPr of
-                               Just ulstyle -> Just ulstyle
-                               Nothing      -> rUnderline rPr'
-                           , rStyle = rStyle rPr
-                           }
+        else leftBiasedMergeRunStyle rPr <$> resolveDependentRunStyle (cStyleData s)
   | otherwise = return rPr
 
 runStyleToTransform :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines)
 runStyleToTransform rPr
-  | Just (s, _) <- rStyle rPr
-  , s `elem` spansToKeep = do
-      transform <- runStyleToTransform rPr{rStyle = Nothing}
-      return $ spanWith ("", [s], []) . transform
-  | Just (s, _) <- rStyle rPr = do
-      opts <- asks docxOptions
-      let extraInfo = if isEnabled Ext_styles opts
-                      then spanWith ("", [], [("custom-style", s)])
-                      else id
-      transform <- runStyleToTransform rPr{rStyle = Nothing}
-      return $ extraInfo . transform
+  | Just sn <- getStyleName <$> rParentStyle rPr
+  , sn `elem` spansToKeep = do
+      transform <- runStyleToTransform rPr{rParentStyle = Nothing}
+      return $ spanWith ("", [normalizeToClassName sn], []) . transform
+  | Just s <- rParentStyle rPr = do
+      ei <- extraInfo spanWith s
+      transform <- runStyleToTransform rPr{rParentStyle = Nothing}
+      return $ ei . transform
   | Just True <- isItalic rPr = do
       transform <- runStyleToTransform rPr{isItalic = Nothing}
       return $ emph  . transform
@@ -310,8 +304,7 @@ runStyleToTransform rPr
 
 runToInlines :: PandocMonad m => Run -> DocxContext m Inlines
 runToInlines (Run rs runElems)
-  | Just (s, _) <- rStyle rs
-  , s `elem` codeStyles = do
+  | maybe False isCodeCharStyle $ rParentStyle rs = do
       rPr <- resolveDependentRunStyle rs
       let codeString = code $ concatMap runElemToString runElems
       return $ case rVertAlign rPr of
@@ -526,39 +519,49 @@ trimSps (Many ils) = Many $ Seq.dropWhileL isSp $Seq.dropWhileR isSp ils
         isSp LineBreak = True
         isSp _         = False
 
+extraInfo :: (Eq (StyleName a), PandocMonad m, HasStyleName a)
+          => (Attr -> i -> i) -> a -> DocxContext m (i -> i)
+extraInfo f s = do
+  opts <- asks docxOptions
+  return $ if | isEnabled Ext_styles opts
+              -> f ("", [], [("custom-style", fromStyleName $ getStyleName s)])
+              | otherwise -> id
+
 parStyleToTransform :: PandocMonad m => ParagraphStyle -> DocxContext m (Blocks -> Blocks)
 parStyleToTransform pPr
   | (c:cs) <- pStyle pPr
-  , c `elem` divsToKeep = do
+  , getStyleName c `elem` divsToKeep = do
       let pPr' = pPr { pStyle = cs }
       transform <- parStyleToTransform pPr'
-      return $ divWith ("", [c], []) . transform
+      return $ divWith ("", [normalizeToClassName $ getStyleName c], []) . transform
   | (c:cs) <- pStyle pPr,
-    c `elem` listParagraphDivs = do
+    getStyleName c `elem` listParagraphStyles = do
       let pPr' = pPr { pStyle = cs, indentation = Nothing}
       transform <- parStyleToTransform pPr'
-      return $ divWith ("", [c], []) . transform
+      return $ divWith ("", [normalizeToClassName $ getStyleName c], []) . transform
   | (c:cs) <- pStyle pPr = do
-      opts <- asks docxOptions
-      let pPr' = pPr { pStyle = cs}
+      let pPr' = pPr { pStyle = cs }
       transform <- parStyleToTransform pPr'
-      let extraInfo = if isEnabled Ext_styles opts
-                      then divWith ("", [], [("custom-style", c)])
-                      else id
-      return $ extraInfo . (if fromMaybe False (pBlockQuote pPr) then blockQuote else id) . transform
+      ei <- extraInfo divWith c
+      return $ ei . (if isBlockQuote c then blockQuote else id) . transform
   | null (pStyle pPr)
   , Just left <- indentation pPr >>= leftParIndent = do
     let pPr' = pPr { indentation = Nothing }
         hang = fromMaybe 0 $ indentation pPr >>= hangingParIndent
     transform <- parStyleToTransform pPr'
-    return $ if (left - hang) > 0 
+    return $ if (left - hang) > 0
              then blockQuote . transform
              else transform
 parStyleToTransform _ = return id
 
+normalizeToClassName :: (FromStyleName a) => a -> String
+normalizeToClassName = map go . fromStyleName
+  where go c | isSpace c = '-'
+             | otherwise = c
+
 bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks
 bodyPartToBlocks (Paragraph pPr parparts)
-  | not $ null $ codeDivs `intersect` (pStyle pPr) = do
+  | isCodeDiv pPr = do
       transform <- parStyleToTransform pPr
       return $
         transform $
@@ -568,13 +571,16 @@ bodyPartToBlocks (Paragraph pPr parparts)
     ils <-local (\s-> s{docxInHeaderBlock=True})
            (smushInlines <$> mapM parPartToInlines parparts)
     makeHeaderAnchor $
-      headerWith ("", delete style (pStyle pPr), []) n ils
+      headerWith ("", map normalizeToClassName . delete style $ getStyleNames (pStyle pPr), []) n ils
   | otherwise = do
     ils <- trimSps . smushInlines <$> mapM parPartToInlines parparts
     prevParaIls <- gets docxPrevPara
     dropIls <- gets docxDropCap
     let ils' = dropIls <> ils
-    if dropCap pPr
+    let (paraOrPlain, pPr')
+          | hasStylesInheritedFrom ["Compact"] pPr = (plain, removeStyleNamed "Compact" pPr)
+          | otherwise = (para, pPr)
+    if dropCap pPr'
       then do modify $ \s -> s { docxDropCap = ils' }
               return mempty
       else do modify $ \s -> s { docxDropCap = mempty }
@@ -583,41 +589,41 @@ bodyPartToBlocks (Paragraph pPr parparts)
                           ils'
                   handleInsertion = do
                     modify $ \s -> s {docxPrevPara = mempty}
-                    transform <- parStyleToTransform pPr
-                    return $ transform $ para ils''
+                    transform <- parStyleToTransform pPr'
+                    return $ transform $ paraOrPlain ils''
               opts <- asks docxOptions
               if  | isNull ils'' && not (isEnabled Ext_empty_paragraphs opts) ->
                     return mempty
-                  | Just (TrackedChange Insertion _) <- pChange pPr
+                  | Just (TrackedChange Insertion _) <- pChange pPr'
                   , AcceptChanges <- readerTrackChanges opts ->
                       handleInsertion
-                  | Just (TrackedChange Insertion _) <- pChange pPr
+                  | Just (TrackedChange Insertion _) <- pChange pPr'
                   , RejectChanges <- readerTrackChanges opts -> do
                       modify $ \s -> s {docxPrevPara = ils''}
                       return mempty
-                  | Just (TrackedChange Insertion cInfo) <- pChange pPr
+                  | Just (TrackedChange Insertion cInfo) <- pChange pPr'
                   , AllChanges <- readerTrackChanges opts
                   , ChangeInfo _ cAuthor cDate <- cInfo -> do
                       let attr = ("", ["paragraph-insertion"], [("author", cAuthor), ("date", cDate)])
                           insertMark = spanWith attr mempty
-                      transform <- parStyleToTransform pPr
+                      transform <- parStyleToTransform pPr'
                       return $ transform $
-                        para $ ils'' <> insertMark
-                  | Just (TrackedChange Deletion _) <- pChange pPr
+                        paraOrPlain $ ils'' <> insertMark
+                  | Just (TrackedChange Deletion _) <- pChange pPr'
                   , AcceptChanges <- readerTrackChanges opts -> do
                       modify $ \s -> s {docxPrevPara = ils''}
                       return mempty
-                  | Just (TrackedChange Deletion _) <- pChange pPr
+                  | Just (TrackedChange Deletion _) <- pChange pPr'
                   , RejectChanges <- readerTrackChanges opts ->
                       handleInsertion
-                  | Just (TrackedChange Deletion cInfo) <- pChange pPr
+                  | Just (TrackedChange Deletion cInfo) <- pChange pPr'
                   , AllChanges <- readerTrackChanges opts
                   , ChangeInfo _ cAuthor cDate <- cInfo -> do
                       let attr = ("", ["paragraph-deletion"], [("author", cAuthor), ("date", cDate)])
                           insertMark = spanWith attr mempty
-                      transform <- parStyleToTransform pPr
+                      transform <- parStyleToTransform pPr'
                       return $ transform $
-                        para $ ils'' <> insertMark
+                        paraOrPlain $ ils'' <> insertMark
                   | otherwise -> handleInsertion
 bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do
   -- We check whether this current numId has previously been used,
@@ -638,7 +644,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do
   blks <- bodyPartToBlocks (Paragraph pPr parparts)
   return $ divWith ("", ["list-item"], kvs) blks
 bodyPartToBlocks (ListItem pPr _ _ _ parparts) =
-  let pPr' = pPr {pStyle = "ListParagraph": pStyle pPr}
+  let pPr' = pPr {pStyle = constructBogusParStyleData "list-paragraph": pStyle pPr}
   in
     bodyPartToBlocks $ Paragraph pPr' parparts
 bodyPartToBlocks (Tbl _ _ _ []) =
diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs
index cc390f122..eb24640c5 100644
--- a/src/Text/Pandoc/Readers/Docx/Lists.hs
+++ b/src/Text/Pandoc/Readers/Docx/Lists.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
 {- |
    Module      : Text.Pandoc.Readers.Docx.Lists
    Copyright   : Copyright (C) 2014-2019 Jesse Rosenthal
@@ -14,13 +15,16 @@ Functions for converting flat docx paragraphs into nested lists.
 module Text.Pandoc.Readers.Docx.Lists ( blocksToBullets
                                       , blocksToDefinitions
                                       , listParagraphDivs
+                                      , listParagraphStyles
                                       ) where
 
 import Prelude
 import Data.List
 import Data.Maybe
+import Data.String (fromString)
 import Text.Pandoc.Generic (bottomUp)
 import Text.Pandoc.JSON
+import Text.Pandoc.Readers.Docx.Parse (ParaStyleName)
 import Text.Pandoc.Shared (trim, safeRead)
 
 isListItem :: Block -> Bool
@@ -79,7 +83,10 @@ getListType b@(Div (_, _, kvs) _) | isListItem b =
 getListType _ = Nothing
 
 listParagraphDivs :: [String]
-listParagraphDivs = ["ListParagraph"]
+listParagraphDivs = ["list-paragraph"]
+
+listParagraphStyles :: [ParaStyleName]
+listParagraphStyles = map fromString listParagraphDivs
 
 -- This is a first stab at going through and attaching meaning to list
 -- paragraphs, without an item marker, following a list item. We
@@ -160,7 +167,7 @@ blocksToDefinitions' defAcc acc [] =
   reverse $ DefinitionList (reverse defAcc) : acc
 blocksToDefinitions' defAcc acc
   (Div (_, classes1, _) blks1 : Div (ident2, classes2, kvs2) blks2 : blks)
-  | "DefinitionTerm" `elem` classes1 && "Definition"  `elem` classes2 =
+  | "Definition-Term" `elem` classes1 && "Definition"  `elem` classes2 =
     let remainingAttr2 = (ident2, delete "Definition" classes2, kvs2)
         pair = if remainingAttr2 == ("", [], []) then (concatMap plainParaInlines blks1, [blks2]) else (concatMap plainParaInlines blks1, [[Div remainingAttr2 blks2]])
     in
@@ -169,12 +176,12 @@ blocksToDefinitions' ((defTerm, defItems):defs) acc
   (Div (ident2, classes2, kvs2) blks2 : blks)
   | "Definition"  `elem` classes2 =
     let remainingAttr2 = (ident2, delete "Definition" classes2, kvs2)
-        defItems2 = case remainingAttr2 == ("", [], []) of
-          True  -> blks2
-          False -> [Div remainingAttr2 blks2]
-        defAcc' = case null defItems of
-          True -> (defTerm, [defItems2]) : defs
-          False -> (defTerm, init defItems ++ [last defItems ++ defItems2]) : defs
+        defItems2 = if remainingAttr2 == ("", [], [])
+          then blks2
+          else [Div remainingAttr2 blks2]
+        defAcc' = if null defItems
+          then (defTerm, [defItems2]) : defs
+          else (defTerm, init defItems ++ [last defItems ++ defItems2]) : defs
     in
      blocksToDefinitions' defAcc' acc blks
 blocksToDefinitions' [] acc (b:blks) =
@@ -198,7 +205,5 @@ removeListDivs' blk = [blk]
 removeListDivs :: [Block] -> [Block]
 removeListDivs = concatMap removeListDivs'
 
-
-
 blocksToDefinitions :: [Block] -> [Block]
 blocksToDefinitions = blocksToDefinitions' [] []
diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs
index 330c9208f..00c5fb0be 100644
--- a/src/Text/Pandoc/Readers/Docx/Parse.hs
+++ b/src/Text/Pandoc/Readers/Docx/Parse.hs
@@ -1,7 +1,11 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE PatternGuards     #-}
 {-# LANGUAGE ViewPatterns      #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {- |
  Module : Text.Pandoc.Readers.Docx.Parse
  Copyright : Copyright (C) 2014-2019 Jesse Rosenthal
@@ -31,6 +35,8 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..)
                                       , VertAlign(..)
                                       , ParIndentation(..)
                                       , ParagraphStyle(..)
+                                      , ParStyle
+                                      , CharStyle(cStyleData)
                                       , Row(..)
                                       , Cell(..)
                                       , TrackedChange(..)
@@ -38,8 +44,17 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..)
                                       , ChangeInfo(..)
                                       , FieldInfo(..)
                                       , Level(..)
+                                      , ParaStyleName
+                                      , CharStyleName
+                                      , FromStyleName(..)
+                                      , HasStyleName(..)
+                                      , HasParentStyle(..)
                                       , archiveToDocx
                                       , archiveToDocxWithWarnings
+                                      , getStyleNames
+                                      , pHeading
+                                      , constructBogusParStyleData
+                                      , leftBiasedMergeRunStyle
                                       ) where
 import Prelude
 import Codec.Archive.Zip
@@ -49,10 +64,13 @@ import Control.Monad.Reader
 import Control.Monad.State.Strict
 import Data.Bits ((.|.))
 import qualified Data.ByteString.Lazy as B
-import Data.Char (chr, ord, readLitChar)
+import Data.Char (chr, ord, readLitChar, toLower)
 import Data.List
+import Data.Function (on)
+import Data.String (IsString(..))
 import qualified Data.Map as M
 import Data.Maybe
+import Data.Coerce
 import System.FilePath
 import Text.Pandoc.Readers.Docx.Util
 import Text.Pandoc.Readers.Docx.Fields
@@ -160,13 +178,9 @@ newtype Body = Body [BodyPart]
 
 type Media = [(FilePath, B.ByteString)]
 
-type CharStyle = (String, RunStyle)
+type CharStyleMap = M.Map CharStyleId CharStyle
 
-type ParStyle = (String, ParStyleData)
-
-type CharStyleMap = M.Map String RunStyle
-
-type ParStyleMap = M.Map String ParStyleData
+type ParStyleMap = M.Map ParaStyleId ParStyle
 
 data Numbering = Numbering NameSpaces [Numb] [AbstractNumb]
                  deriving Show
@@ -213,12 +227,9 @@ data ChangeInfo = ChangeInfo ChangeId Author ChangeDate
 data TrackedChange = TrackedChange ChangeType ChangeInfo
                    deriving Show
 
-data ParagraphStyle = ParagraphStyle { pStyle      :: [String]
+data ParagraphStyle = ParagraphStyle { pStyle      :: [ParStyle]
                                      , indentation :: Maybe ParIndentation
                                      , dropCap     :: Bool
-                                     , pHeading    :: Maybe (String, Int)
-                                     , pNumInfo    :: Maybe (String, String)
-                                     , pBlockQuote :: Maybe Bool
                                      , pChange     :: Maybe TrackedChange
                                      }
                       deriving Show
@@ -227,9 +238,6 @@ defaultParagraphStyle :: ParagraphStyle
 defaultParagraphStyle = ParagraphStyle { pStyle = []
                                        , indentation = Nothing
                                        , dropCap     = False
-                                       , pHeading    = Nothing
-                                       , pNumInfo    = Nothing
-                                       , pBlockQuote = Nothing
                                        , pChange     = Nothing
                                        }
 
@@ -254,6 +262,49 @@ newtype Row = Row [Cell]
 newtype Cell = Cell [BodyPart]
             deriving Show
 
+newtype CharStyleId   = CharStyleId { fromCharStyleId :: String }
+  deriving (Show, Eq, Ord, FromStyleId)
+newtype ParaStyleId   = ParaStyleId { fromParaStyleId  :: String }
+  deriving (Show, Eq, Ord, FromStyleId)
+
+newtype CharStyleName = CharStyleName { fromCharStyleName :: CIString }
+  deriving (Show, Eq, Ord, IsString, FromStyleName)
+newtype ParaStyleName = ParaStyleName { fromParaStyleName :: CIString }
+  deriving (Show, Eq, Ord, IsString, FromStyleName)
+
+-- Case-insensitive comparisons
+newtype CIString = CIString String deriving (Show, IsString, FromStyleName)
+
+class FromStyleName a where
+  fromStyleName :: a -> String
+
+instance FromStyleName String where
+  fromStyleName = id
+
+class FromStyleId a where
+  fromStyleId :: a -> String
+
+instance FromStyleId String where
+  fromStyleId = id
+
+instance Eq CIString where
+   (==) = (==) `on` map toLower . coerce
+
+instance Ord CIString where
+  compare = compare `on` map toLower . coerce
+
+leftBiasedMergeRunStyle :: RunStyle -> RunStyle -> RunStyle
+leftBiasedMergeRunStyle a b = RunStyle
+    { isBold = isBold a <|> isBold b
+    , isItalic = isItalic a <|> isItalic b
+    , isSmallCaps = isSmallCaps a <|> isSmallCaps b
+    , isStrike = isStrike a <|> isStrike b
+    , isRTL = isRTL a <|> isRTL b
+    , rVertAlign = rVertAlign a <|> rVertAlign b
+    , rUnderline = rUnderline a <|> rUnderline b
+    , rParentStyle = rParentStyle a
+    }
+
 -- (width, height) in EMUs
 type Extent = Maybe (Double, Double)
 
@@ -285,21 +336,28 @@ data RunElem = TextRun String | LnBrk | Tab | SoftHyphen | NoBreakHyphen
 data VertAlign = BaseLn | SupScrpt | SubScrpt
                deriving Show
 
-data RunStyle = RunStyle { isBold      :: Maybe Bool
-                         , isItalic    :: Maybe Bool
-                         , isSmallCaps :: Maybe Bool
-                         , isStrike    :: Maybe Bool
-                         , isRTL       :: Maybe Bool
-                         , rVertAlign  :: Maybe VertAlign
-                         , rUnderline  :: Maybe String
-                         , rStyle      :: Maybe CharStyle
+data CharStyle = CharStyle { cStyleId   :: CharStyleId
+                           , cStyleName :: CharStyleName
+                           , cStyleData :: RunStyle
+                           } deriving (Show)
+
+data RunStyle = RunStyle { isBold       :: Maybe Bool
+                         , isItalic     :: Maybe Bool
+                         , isSmallCaps  :: Maybe Bool
+                         , isStrike     :: Maybe Bool
+                         , isRTL        :: Maybe Bool
+                         , rVertAlign   :: Maybe VertAlign
+                         , rUnderline   :: Maybe String
+                         , rParentStyle :: Maybe CharStyle
                          }
                 deriving Show
 
-data ParStyleData = ParStyleData { headingLev   :: Maybe (String, Int)
-                                 , isBlockQuote :: Maybe Bool
-                                 , numInfo      :: Maybe (String, String)
-                                 , psStyle      :: Maybe ParStyle}
+data ParStyle = ParStyle { headingLev    :: Maybe (ParaStyleName, Int)
+                         , numInfo       :: Maybe (String, String)
+                         , psParentStyle :: Maybe ParStyle
+                         , pStyleName    :: ParaStyleName
+                         , pStyleId      :: ParaStyleId
+                         }
                     deriving Show
 
 defaultRunStyle :: RunStyle
@@ -310,7 +368,7 @@ defaultRunStyle = RunStyle { isBold = Nothing
                            , isRTL = Nothing
                            , rVertAlign = Nothing
                            , rUnderline = Nothing
-                           , rStyle = Nothing
+                           , rParentStyle = Nothing
                            }
 
 type Target = String
@@ -390,7 +448,10 @@ elemToBody ns element | isElem ns "w" "body" element =
 elemToBody _ _ = throwError WrongElem
 
 archiveToStyles :: Archive -> (CharStyleMap, ParStyleMap)
-archiveToStyles zf =
+archiveToStyles = archiveToStyles' getStyleId getStyleId
+archiveToStyles' :: (Ord k1, Ord k2, ElemToStyle a1, ElemToStyle a2) =>
+                    (a1 -> k1) -> (a2 -> k2) -> Archive -> (M.Map k1 a1, M.Map k2 a2)
+archiveToStyles' conv1 conv2 zf =
   let stylesElem = findEntryByPath "word/styles.xml" zf >>=
                    (parseXMLDoc . UTF8.toStringLazy . fromEntry)
   in
@@ -399,19 +460,17 @@ archiveToStyles zf =
      Just styElem ->
        let namespaces = elemToNameSpaces styElem
        in
-        ( M.fromList $ buildBasedOnList namespaces styElem
-            (Nothing :: Maybe CharStyle),
-          M.fromList $ buildBasedOnList namespaces styElem
-            (Nothing :: Maybe ParStyle) )
+        ( M.fromList $ map (\r -> (conv1 r, r)) $ buildBasedOnList namespaces styElem Nothing,
+          M.fromList $ map (\p -> (conv2 p, p)) $ buildBasedOnList namespaces styElem Nothing)
 
-isBasedOnStyle :: (ElemToStyle a) => NameSpaces -> Element -> Maybe a -> Bool
+isBasedOnStyle :: (ElemToStyle a, FromStyleId (StyleId a)) => NameSpaces -> Element -> Maybe a -> Bool
 isBasedOnStyle ns element parentStyle
   | isElem ns "w" "style" element
   , Just styleType <- findAttrByName ns "w" "type" element
   , styleType == cStyleType parentStyle
   , Just basedOnVal <- findChildByName ns "w" "basedOn" element >>=
                        findAttrByName ns "w" "val"
-  , Just ps <- parentStyle = basedOnVal == getStyleId ps
+  , Just ps <- parentStyle = basedOnVal == fromStyleId (getStyleId ps)
   | isElem ns "w" "style" element
   , Just styleType <- findAttrByName ns "w" "type" element
   , styleType == cStyleType parentStyle
@@ -419,30 +478,70 @@ isBasedOnStyle ns element parentStyle
   , Nothing <- parentStyle = True
   | otherwise = False
 
-class ElemToStyle a where
+class HasStyleId a => ElemToStyle a where
   cStyleType  :: Maybe a -> String
   elemToStyle :: NameSpaces -> Element -> Maybe a -> Maybe a
-  getStyleId     :: a -> String
+
+class FromStyleId (StyleId a) => HasStyleId a where
+  type StyleId a
+  getStyleId :: a -> StyleId a
+
+class FromStyleName (StyleName a) => HasStyleName a where
+  type StyleName a
+  getStyleName :: a -> StyleName a
+
+class HasParentStyle a where
+  getParentStyle :: a -> Maybe a
+
+instance HasParentStyle CharStyle where
+  getParentStyle = rParentStyle . cStyleData
+
+instance HasParentStyle ParStyle where
+  getParentStyle = psParentStyle
+
+getStyleNames :: (Functor t, HasStyleName a) => t a -> t (StyleName a)
+getStyleNames = fmap getStyleName
+
+constructBogusParStyleData :: ParaStyleName -> ParStyle
+constructBogusParStyleData stName = ParStyle
+  { headingLev = Nothing
+  , numInfo = Nothing
+  , psParentStyle = Nothing
+  , pStyleName = stName
+  , pStyleId = ParaStyleId . filter (/=' ') . fromStyleName $ stName
+  }
 
 instance ElemToStyle CharStyle where
   cStyleType _ = "character"
   elemToStyle ns element parentStyle
     | isElem ns "w" "style" element
-    , Just "character" <- findAttrByName ns "w" "type" element
-    , Just styleId <- findAttrByName ns "w" "styleId" element =
-      Just (styleId, elemToRunStyle ns element parentStyle)
+    , Just "character" <- findAttrByName ns "w" "type" element =
+      elemToCharStyle ns element parentStyle
     | otherwise = Nothing
-  getStyleId s = fst s
+
+instance HasStyleId CharStyle where
+  type StyleId CharStyle = CharStyleId
+  getStyleId = cStyleId
+
+instance HasStyleName CharStyle where
+  type StyleName CharStyle = CharStyleName
+  getStyleName = cStyleName
 
 instance ElemToStyle ParStyle where
   cStyleType _ = "paragraph"
   elemToStyle ns element parentStyle
     | isElem ns "w" "style" element
     , Just "paragraph" <- findAttrByName ns "w" "type" element
-    , Just styleId <- findAttrByName ns "w" "styleId" element =
-      Just (styleId, elemToParStyleData ns element parentStyle)
+    = elemToParStyleData ns element parentStyle
     | otherwise = Nothing
-  getStyleId s = fst s
+
+instance HasStyleId ParStyle where
+  type StyleId ParStyle = ParaStyleId
+  getStyleId = pStyleId
+
+instance HasStyleName ParStyle where
+  type StyleName ParStyle = ParaStyleName
+  getStyleName = pStyleName
 
 getStyleChildren :: (ElemToStyle a) => NameSpaces -> Element -> Maybe a -> [a]
 getStyleChildren ns element parentStyle
@@ -693,6 +792,12 @@ testBitMask bitMaskS n =
 stringToInteger :: String -> Maybe Integer
 stringToInteger s = listToMaybe $ map fst (reads s :: [(Integer, String)])
 
+pHeading :: ParagraphStyle -> Maybe (ParaStyleName, Int)
+pHeading = getParStyleField headingLev . pStyle
+
+pNumInfo :: ParagraphStyle -> Maybe (String, String)
+pNumInfo = getParStyleField numInfo . pStyle
+
 elemToBodyPart :: NameSpaces -> Element -> D BodyPart
 elemToBodyPart ns element
   | isElem ns "w" "p" element
@@ -1003,20 +1108,18 @@ elemToRun ns element
     return $ Run runStyle runElems
 elemToRun _ _ = throwError WrongElem
 
-getParentStyleValue :: (ParStyleData -> Maybe a) -> ParStyleData -> Maybe a
+getParentStyleValue :: (ParStyle -> Maybe a) -> ParStyle -> Maybe a
 getParentStyleValue field style
   | Just value <- field style = Just value
-  | Just parentStyle <- psStyle style
-                      = getParentStyleValue field (snd parentStyle)
+  | Just parentStyle <- psParentStyle style
+                      = getParentStyleValue field parentStyle
 getParentStyleValue _ _ = Nothing
 
-getParStyleField :: (ParStyleData -> Maybe a) -> ParStyleMap -> [String] ->
-                                                                        Maybe a
-getParStyleField field stylemap styles
-  | x     <- mapMaybe (\x -> M.lookup x stylemap) styles
-  , (y:_) <- mapMaybe (getParentStyleValue field) x
+getParStyleField :: (ParStyle -> Maybe a) -> [ParStyle] -> Maybe a
+getParStyleField field styles
+  | (y:_) <- mapMaybe (getParentStyleValue field) styles
            = Just y
-getParStyleField _ _ _ = Nothing
+getParStyleField _ _ = Nothing
 
 getTrackedChange :: NameSpaces -> Element -> Maybe TrackedChange
 getTrackedChange ns element
@@ -1038,10 +1141,10 @@ elemToParagraphStyle ns element sty
   | Just pPr <- findChildByName ns "w" "pPr" element =
     let style =
           mapMaybe
-          (findAttrByName ns "w" "val")
+          (fmap ParaStyleId . findAttrByName ns "w" "val")
           (findChildrenByName ns "w" "pStyle" pPr)
     in ParagraphStyle
-      {pStyle = style
+      {pStyle = mapMaybe (`M.lookup` sty) style
       , indentation =
           findChildByName ns "w" "ind" pPr >>=
           elemToParIndentation ns
@@ -1053,9 +1156,6 @@ elemToParagraphStyle ns element sty
             Just "none" -> False
             Just _      -> True
             Nothing     -> False
-      , pHeading = getParStyleField headingLev sty style
-      , pNumInfo = getParStyleField numInfo sty style
-      , pBlockQuote = getParStyleField isBlockQuote sty style
       , pChange     = findChildByName ns "w" "rPr" pPr >>=
                       filterChild (\e -> isElem ns "w" "ins" e ||
                                          isElem ns "w" "moveTo" e ||
@@ -1085,16 +1185,20 @@ elemToRunStyleD :: NameSpaces -> Element -> D RunStyle
 elemToRunStyleD ns element
   | Just rPr <- findChildByName ns "w" "rPr" element = do
     charStyles <- asks envCharStyles
-    let parentSty = case
+    let parentSty =
           findChildByName ns "w" "rStyle" rPr >>=
-          findAttrByName ns "w" "val"
-          of
-            Just styName | Just style <- M.lookup styName charStyles ->
-              Just (styName, style)
-            _            -> Nothing
+          findAttrByName ns "w" "val" >>=
+          flip M.lookup charStyles . CharStyleId
     return $ elemToRunStyle ns element parentSty
 elemToRunStyleD _ _ = return defaultRunStyle
 
+elemToCharStyle :: NameSpaces
+                -> Element -> Maybe CharStyle -> Maybe CharStyle
+elemToCharStyle ns element parentStyle
+  = CharStyle <$> (CharStyleId <$> findAttrByName ns "w" "styleId" element)
+              <*> getElementStyleName ns element
+              <*> (Just $ elemToRunStyle ns element parentStyle)
+
 elemToRunStyle :: NameSpaces -> Element -> Maybe CharStyle -> RunStyle
 elemToRunStyle ns element parentStyle
   | Just rPr <- findChildByName ns "w" "rPr" element =
@@ -1117,38 +1221,23 @@ elemToRunStyle ns element parentStyle
       , rUnderline =
           findChildByName ns "w" "u" rPr >>=
           findAttrByName ns "w" "val"
-      , rStyle = parentStyle
+      , rParentStyle = parentStyle
       }
 elemToRunStyle _ _ _ = defaultRunStyle
 
-getHeaderLevel :: NameSpaces -> Element -> Maybe (String,Int)
+getHeaderLevel :: NameSpaces -> Element -> Maybe (ParaStyleName, Int)
 getHeaderLevel ns element
-  | Just styleId <- findAttrByName ns "w" "styleId" element
-  , Just index   <- stripPrefix "Heading" styleId
-  , Just n       <- stringToInteger index
-  , n > 0 = Just (styleId, fromInteger n)
-  | Just styleId <- findAttrByName ns "w" "styleId" element
-  , Just index   <- findChildByName ns "w" "name" element >>=
-                    findAttrByName ns "w" "val" >>=
-                    stripPrefix "heading "
-  , Just n <- stringToInteger index
-  , n > 0 = Just (styleId, fromInteger n)
+  | Just styleName <- getElementStyleName ns element
+  , Just n <- stringToInteger =<<
+              (stripPrefix "heading " . map toLower $
+                fromStyleName styleName)
+  , n > 0 = Just (styleName, fromInteger n)
 getHeaderLevel _ _ = Nothing
 
-blockQuoteStyleIds :: [String]
-blockQuoteStyleIds = ["Quote", "BlockQuote", "BlockQuotation"]
-
-blockQuoteStyleNames :: [String]
-blockQuoteStyleNames = ["Quote", "Block Text"]
-
-getBlockQuote :: NameSpaces -> Element -> Maybe Bool
-getBlockQuote ns element
-  | Just styleId <- findAttrByName ns "w" "styleId" element
-  , styleId `elem` blockQuoteStyleIds = Just True
-  | Just styleName <- findChildByName ns "w" "name" element >>=
-                      findAttrByName ns "w" "val"
-  , styleName `elem` blockQuoteStyleNames = Just True
-getBlockQuote _ _ = Nothing
+getElementStyleName :: Coercible String a => NameSpaces -> Element -> Maybe a
+getElementStyleName ns el = coerce <$>
+  ((findChildByName ns "w" "name" el >>= findAttrByName ns "w" "val")
+  <|> findAttrByName ns "w" "styleId" el)
 
 getNumInfo :: NameSpaces -> Element -> Maybe (String, String)
 getNumInfo ns element = do
@@ -1163,15 +1252,19 @@ getNumInfo ns element = do
   return (numId, lvl)
 
 
-elemToParStyleData :: NameSpaces -> Element -> Maybe ParStyle -> ParStyleData
-elemToParStyleData ns element parentStyle =
-    ParStyleData
+elemToParStyleData :: NameSpaces -> Element -> Maybe ParStyle -> Maybe ParStyle
+elemToParStyleData ns element parentStyle
+  | Just styleId <- findAttrByName ns "w" "styleId" element
+  , Just styleName <- getElementStyleName ns element
+  = Just $ ParStyle
       {
         headingLev = getHeaderLevel ns element
-      , isBlockQuote = getBlockQuote ns element
       , numInfo = getNumInfo ns element
-      , psStyle = parentStyle
-        }
+      , psParentStyle = parentStyle
+      , pStyleName = styleName
+      , pStyleId = ParaStyleId styleId
+      }
+elemToParStyleData _ _ _ = Nothing
 
 elemToRunElem :: NameSpaces -> Element -> D RunElem
 elemToRunElem ns element
diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs
index 9d0913e55..583a6ec18 100644
--- a/test/Tests/Readers/Docx.hs
+++ b/test/Tests/Readers/Docx.hs
@@ -255,6 +255,10 @@ tests = [ testGroup "document"
             "lists"
             "docx/lists.docx"
             "docx/lists.native"
+          , testCompare
+            "compact lists"
+            "docx/lists-compact.docx"
+            "docx/lists-compact.native"
           , testCompare
             "lists with level overrides"
             "docx/lists_level_override.docx"
@@ -425,6 +429,11 @@ tests = [ testGroup "document"
             "custom styles (`+styles`) enabled"
             "docx/custom-style-reference.docx"
             "docx/custom-style-with-styles.native"
+          , testCompareWithOpts
+            def{readerExtensions=extensionsFromList [Ext_styles]}
+            "custom styles (`+styles`): Compact style is removed from output"
+            "docx/compact-style-removal.docx"
+            "docx/compact-style-removal.native"
           ]
         , testGroup "metadata"
           [ testCompareWithOpts def{readerStandalone=True}
diff --git a/test/docx/compact-style-removal.docx b/test/docx/compact-style-removal.docx
new file mode 100644
index 0000000000000000000000000000000000000000..fde0064db444556ada76e08f52ef035ffcdbc206
GIT binary patch
literal 9951
zcmZ{K1yogC*Y%~lLApZ_xO6Gq-5}j5afvJ4-7VeSoze)>AR^r@Ehr%UUHzW-^^yPo
z7~|YC?q$q1_L+O_v)7z!E6c&a;sT%%5djX^3`*uJk;slv0012V0DuDk0CdGb_F!{+
zu%WuUgSoRl(9O=aNn_YPkQKA@iZQMRk9R+>ygZKNJ;{=^f=~~em<b+BeJGRf8WfSb
z1{RG%D@9sb%TG3Hx`r%ZKHAURN|^;-!aBsL#x(YdByXU>76Nz1s{Aug1j>8^9Mxt?
zaBL<*8$7e92`NGO_N<LR_y^-j&>c48x@*CdK*}ryK~&wQ(eyCKc0LoU$HLU1WXd{|
z$D5lMWl2bT$h2^(X>%GrxL@-SNr$3`ALb){D`|r&J^OUp96aOPO6WUADAG}4L83V1
zYdSe+%uGUncoQU<mL=+R?#XvSV<W0KG-ZfcE!d(Ig8DSwL$_I}sw}-~rx)IhU+#f@
zM#e#jiNfhZR@qyT-d{NH)A<;f`s@7;4LgcXz#>1}T7V9!?ej3|8A&3~60s1-@W6ws
z5psu*@jb>@FMeDnm<w01bUBGRmwRv=wz3$7$<(;tUg(BYys~1{p`Qkc>*&A5z#Q<u
zPJ(N+VnRO?&^efet9kUC1}XnFLo)(d1_zLtnT7@cP#||hCv#h87T~YXs)SKR7&cUi
zD=&#{??E+W#5{}lBJbXdm3j#$xskp39=9cW?FlU^tU~zyEBnO7S^Unm$!W_$8ZQQ$
zRjMWzJu>%uN-$CO?C3BvQ+nhzwOw?Q^hs!v#y*m^7)!zlX2bDthTed9-d$sSXZ-m#
zdSV^%G?u~a1=_h1JST#DlM|8KPI2yRO}L`*U^6Mxid6e|-0h(-!*EgV<#;Q%@S{)O
z$rS-`!GmwBC(6V;^VWwHuv2`J{m)y}rrhawqUefLiWc@|eL>^`;f|81i$k2iAK3(4
z?*;b~j(~DM9tph7x-!45^Jeh;O-b7V{fHbSX&K-E04xY4t{^8f7Bi6PTRTYR{z}>L
zq;dNX6sUuzo)KZ5a`hiLCFDG`G^|TBbglqVaqvj2>@<}iq4QqBLc4O012~+cN0#O~
zxA_j6cWBUAuxjxpS&8xl=+T^m5glYM6`u8FATrSfz-be!U?lX`h;sadr>^RO+jCB5
zo7z5cLzL3A*c`V)sM<P2Jzjiwov9`M?t*j~v1d_&2rzbxZSz1Rny^!BSeD>i<*~s&
z{pVG3oTbvS#d^w&%zOcXvo*Y5or;<07(yO#12q|dxYvY7b=ESy6J?bm&mOF5-rYfq
zs$Y`{jiOvaf1IpXn^0686!8e$<<xn_>`4;6f6)>DMp>I4q4)*1R6KB^gpTx?yMg@P
zkBpJ7pNZHE+gOd}%O#~d_|xnPp#6MZ_5OSZXatc1OX`o(LIYcs@9S|fpsEE2&<AF&
z91#Q_(hG2wPaUDgIb^#HI&Wb9W<H5!F%1F2d?PFXK=L2v|2HH5&HwN0Z2Hov_<<F(
z=PawF)*h6JOON;FbCKu)@&**i-Z`botNI$@H8L@|OsWtdS%I(D^+J99f={`BXTjUf
zDLyk9WYi|kw>3*72BRedM9l-^yMzcL4|sDI1*SgHE_`6|E$Tf*m(n1)v9QIIk6N@t
zLW{|<qFv$7uGtk{FHcwsJF+Fd8^o$fvq~>cY2`T2Rh||KY!fU+uDXI*n&X@I&VphL
zwu^U4HQC&Q-kQRViGg_^UW*!}7XIK(bH0Qcu#~pGY(gwp>v28WiV~$A!)zT@tf~&9
z7<0RxYGMm_-6T?r9FXHMqJ3AuT8K7&visqVeuAx8l;|Q0+JqFd+m8KPWVg~8+}{L7
z@AYwlAjEk?F4DgVv;cv?_8_pi^REp4&7eY*2s$(+WPN7`fKPGt2~0!MvJhB3{NNce
zLq+M?$PAN#zN?<T2Hox)<o4+iyu9+yP$O9;X?lCNd{%Od^<BP4^BjmxrIx2pg}n^x
zoI}aK8A=1ccxN8>+B)qfmDz~)vAD;r@iOT65e5|~^!G^FuQZK;katccWLv=g&q(dx
z+L@R;S=(FwUS+AepbS>r!L=)fxFP}WE+b&L?2TsGOye=-Edae0DZwtTt?>4egLC_{
zMJWvRrP(*WJ<y|Kz-0U>xj~V`o9akTHLM;KSd!{ZCYc!^bT@abE2W#3Z<|o1I&F(y
zhV`I$sma^0o-U^EctH{!dkJ`vOnW2q2JFn*)&>{oWb9enFC*PmQl%LvM212WijXU#
z>6V@qSvX#ICQQsMsbTx?U^gk2OXHYUrG){)*|u<v*LyzFFsc-<xs4C(x=y?*ana;#
zkm6B6F_jy~^e?cWB{yGyvt^`ob9Y(&HkUZgrzAAEQn=2N<@un-BWBI*OY-V@13c@b
zyC!D^_v*K!l=6_Wsc-<A%2uo8n>mY4Z(nE7jB)#oCni6Nw6KRt?X@>TZJf3iYtcQc
z96RjCEVvM(Zv2p+U_{g`1UghtW4$?Y=&f;R)62!j>jIIrZXp!Zx3?I8hAT?eUM5zu
z10euE=o1v?1Psm(a)99VHWZ-d#o6-_&Vip|dAm^_V7aZj;P>=tg0$s~w{v9*Zke)l
z_IsC%26t<4UYrzlFudl*<l|o#jWZk9V?zpED4yHFN{V?TQ<T_ZN{Ze5`c5J#2LJ0$
zFDVxC>&_`D24ogdobxMfK2rvvAxI*2b_=+@<RTld>Qzx+LI3F8tH9zc`!P1%CX4P)
zQ_0D=U7Eim;x+@dnOf{<sMW=+l$3BXCJH1)S4)laP$7SH8jOEsfA}Q3ShBK>P!R!u
zT15Z=<3HKu40gBu-}FD$TXI?C#q~aY?RK_mg1V6~(-;|7pTK38$WZA0tQo-7ydA%z
zw|gjcj<zJdY4V(sbhoJPQe<&2lbLkb>gUhvDd%WUX&CWu+65QAwgi|<{UUCTv#nV%
zYgfcMLY=oi9f)e5<HN)A4E8bz*f0@(SIQXgF%ZRoB@e@38{7@@@H>-wZ<TOOvhXcn
zJ`aJ-MyGhePW3DF$57<tAq*lJ5XRDvOX1QjHIok%>&yH+CzCLUJc6AZs1u(kFX1u+
zKbiYmg)e?ZQ&`+AZe;V0$Qguf=yBtP^N;?Nq~>RdhG8tq63Q!#79)&VYwYKTs}~_u
z7)DyF_nptl&8C6X<r@o6JHm<fhl5Jf0^#WQ02Wo;&8wC~{z<**s929#z>j;g)Wc0v
z4*byVJWK|`02Y*NLu7+>7-9+eo=g!SYIe$JULT)Klal<Rq?RGBk9Zv6c!Ox`cZ_wX
zxg5qMyf&%Pt!I}9AylGRo;pnmxuL-LE#b_D4|3Z*hH_ybn6H(Kk7QCevP8DnCJ!w$
zhjc&GrktY$uD$fgm1}c*4C!+4lddzdl;St=PH)@4ezr4X>+cD)PR3?JLzj9CC#2cp
zO7vmZ;_%{L=X1<1{uVzRid%}++ooda7rH@IVcmwfw&RIO8kOv~Yv=TmzSIc*x^l->
z_ny8yoIbzALIh41R)ie-6)M3Q)w`qyR!L4Hp0qkn&iLp$c2xv=6vqkJ@0O$wsEW~H
zg9Vk+VC0is3I82UYdcqGZ97+UnU&t9u@(jsV3OxEvGyU?l4a?RuE0-^z#FS%qHd}&
z{K8^x;jDm@Dn(;9FW!5*k}=z^ev$G_yhdRegZv(lLOAw%N}*kqES_LnxoUo!dXacf
zq*8$iqU|Pj`pcP<Ow?PkDeEsg(ncLW=ld2kXZf|ELor-^R7or~P@Ma8DuQUIrru83
zm9@$6n!Z^uFV%6k8S&ECkWj7AcWcW`P0vhC$o$f9fF{;Vqn=#=>zRdWivy{Z`cS$C
z<J*ZTeS;d_drd-1zB31kt;q{mWn5b?aL?RQ%=9;K;!Gwebaa}z##jKURBjVZSkhYN
zUp+)97V-@VO#QGXUjtHNA}l5^*T8b=G)24+-`}8VBI@`|R!BpR&EC}=YIwSmG~b+X
z*KH@tq|n=NknS3O!_>)LmGZ?xi<hN){$XHQrm;#$zA?+O`>jtN?lF{fV^xaPE&Wu2
zcvXS8JLXOLid?rvxXTahV-i}OgkS&p+<s9n{)$WPhd9erVu83E_HhSXc>d<}QnJ-+
zmZ=k!eA@7A$S+-NSeHMjjz}f6u=9SjrxqYGhS%2M8$Z(bB_MiAVa)>j{0)Kv3lQa~
zj>sjnau6r=P1dY|sfcdVXo=oHW&RXQS{@DLgEwik-jqiUV4!^@VpZ3>xt${FAvxbH
zPP!oDzFZx?vVoJi#PiP#QEDw-KVp{TD7qqW4MSek1k&O!)$2-%Vn?vJnvTu{(@@us
ztDw|Z*b$RVO<Nez*)!8KC{gQ;u!Vdni}-foWv8IW=D#q;EYI}dg{LqQx_w}9l=>Yp
zVr18a`s@2O;j@oa)DIz(k`}<shWTpq%cf+GK7zC6{lxqMC+89>U-J_g-K@1n=K;#7
ze!FFc!fe%d;n7%x$FCG#-*YB0emBq!Y7+4GQOMTNU2TroEta+??)Oe!!TL6XtLl(K
zGwX=gsBBPWrxu*etTDx26s>M%Y#Wo(FIuy|STm~UZj^ULTR*>$^8RE4C|XWWhot#v
z_DMf4+B+yFM$5v?wy@P+S?vbJ6D3+Kn1Nv}w}huB`n-|(nrfxoRtu?FcA~lKE(i=y
zaQ>yM@L7hQ3bXqA`?glmXqe3aiY2$b3|!BBfi6>)AXB^UJD3T|;CW4jbntXXeFag7
zr}V-niO3^Uk*{Jz^SIZQz*8WfWUwD@4Lf^lIfrLvIU^akc&=buAu2yfFE;9n@Ugy&
zSk-5wwtGFP?QQ!y;w;~)M3I|s)VR|GK~c6I&pGDn54z9s$8C5v>pwJ-Fs!~h%#K1}
zE?Sknn)pbJ+v*yh5f``U1rDvDq-FaUhh^*q)*olC`WZaH7b1auEwt%7b~|8Qlx!v#
zi<-b$!@t<hdIy)&Pod`2va5`qa6_G^V7eGQmS!;-#Zx>?NU0!$cEkdJQF}llyyw>p
ze)MaIUDKVBAz2jiXDrI1)R1<S@A+WBvG1(7(;npvPeQU&CkIGWneYoNV7in*HDWk~
zpxnAavo+Ri#Yy1L2F#xY$PUwyl)S8{*UQ~f);|<X(yz%#r&$j5#w!kLCK|Tz@;=8l
zx58HDF5T?2ojtz1<!X1>xek(@OU0{)*$>j&DVoF)vZOG)W-paxYS%+;0CNPqsoo#1
zVG|eXqhLNZ50otjXpX)Sh<z?LHrXr76JceykSY+#7;f+LLRJk*z~OcEHI~$Y1pJpq
zg&N0OJQH2o0)EYKx4DBWqZM;xWtvqrITUsD3egTOs|yd8`*tbG@X?kQj0iL{GQls-
zD@_;-M&i|%B%0PAB_C(J#gXGjs|!6QMOX~9y7}3|U<SmQ{8$u^L;AK$LbvyUTkqZ!
zufQ~sdxk`vo?B2RR^!3dB41>4uvUqTfAQ^sQ@Z65KHUy3it29Vt#-KM&_LcvUWch!
z>jn%&d>2K3>F_)Rm_$y@o*M!4-Tl%a|1$kNSoIn?5h`?q-^Y=upiA^JJAL1<x8xD<
zcUcN3kvJfNC=hv=f0d<wmCjiSy%61j8gj}n@y;_P21aydQJ>BL;O0lKFej}*vTy<o
zQkrdIE%q-rAkn^jysyt%dH8U#FifHS?cKaRay(&8UfqLM_*F~Fo%YdTs6LY};Ib#A
zzVMs+M_KS#UQz<sGBY0yc1x`a4jiEtAw>w4;g^f10{z}V@Yz5jc*KLvco`eDv1(=)
z;NkG`JL-mg$hr<<x`AVQ=1XF$Vi8*SA3H%KSu=>R%zH$7?s@W)mAv;Qylp-wkxM2}
zw;hezdCBs#xe>mVJX)Oraq9f*S8i;TMt6eu8>+Y$GfE>S!#Ta`qJfr&p=a1NboA<|
z^r{Qo@8~5JcoFzR+sD%?U7c*>Ol$5O+YY5$fY+BFL!RZ;V3&74VG@O%AAJc@dO1Og
z{Qs(GQ;^*+5&c_(WKH!(e4xODU-!(Ra#K?9)Ou#UD7TLTCKryf4vM1YpgY;BDRMN%
z&=pmr;&=?@$Of*lYBtcieARWF=gF^MHQKu?z%#tky=MUKtuQbI0%oPEoq8H%s|ZX`
zWo5Yo;l@2tXZLhsVPEbt3Ydu=K<CwsEd;8NW$cOtxEL^@RvOoyX4Spsoc<067cM4Z
z#VG3Xg$-jO5JivEc0k;KSsiTxll`)8f>@cwoKM!oCneRG$$S4(Wo?ceBT}bDeGK@<
zva>f0O;m9p+-FeEIPmQ1u%;%zbo(0N@0?=BGCLuI<dpP3OhBZpvJ=R`nZ*?3Wd2L(
z6eWzlfk;`2t9#^vPG+H8&OVoC%-T)LR<d%gqUkhLvoO{=&wULS6f@!L8Lij~Jol_5
zRXh2=0<WRSleo|r>N9DKg5HhT5}$V&6k(Vxtj_eY84GlH9QG6h#Pz?#Np(P5pnNH5
zjrr;mzuA;=eruh9b5z-y>fk^&$UoWLM~RCfg@ljPuPh^cJ1EIS@;zuR8o)pg&h0hI
zmC*bYiQjwZ&|swgz}wVU5STIBz}t=?GXAmaw4sZrs_rqsr;EQkx}bOjc3To)ZNotD
z;wZOjpEfG~ThG*uwWs2y-O0whE{m9Yb^Ww>BEA{+D`T}a%bfxbFP$1`-Q5AI@zACT
zIZY-l4Ii{Yy%|HlmtFgk_knKk@}!23A(!}nlL=*}C}{>E^9zJbOh|hDmrP>^hhJP~
zsgC}#1v;)6^OwWr+{>NxDKR$zh-D0!%$|u_yv^psu)dS_(;*bR@zW<`6Gs-<T@PRF
z&d}KiPc?TOxmZvfMr^hgHQK5`vB1%$)U%SWF#Wa^c=Qg|8ZLaHw5Bp|KaHwW9>-aG
z9hraRZXV-_R&m&x(Zv8Z40!oWtWyV0=f?I~X`#oAUfmh09pkN}WFJoY`Iu<p-VI1)
z_3t2Aib>2DCt`8Fk)XDNp6G(gKUF5Ja;({ZpI8m&O>nF5>1U^7T>n`>Lmt)9i=aJ9
zIht}l!AynP&Lb{6v15=~$cK=e_0?e_T8?3|s0hJpI{Y6%IKJ0`XS|}={6N`vj&qU2
zD|FJqud`HOec5Ts@-UJ$1y%c-KMP)EM$<<dy(Cr!O>Har%at5Y^jccmOb7BxZOk8S
zYJTqH@=0Yo!TRZ5&D+VFLDp7~Q)^RzO6nsqLAN5@oFuTJK2}Tc`24BoFuWI>69Jit
z8OZg^<Nfc9n7(xegY14!%0w?R#N&ni<k?NNU0o-TL?y}c*%_nRHJliYb0JQ@=>Ap`
z`QDE#)R=4<%<_m!Oyfk*$e;WU7ZX(xIHt22YU3EM?`dU^L`~x&Ooah$-Nrho!@!R6
zGk!!bbXiN@XPacY@{^8bu2h2uVx^+STTKgENyC2f=MazStb04i!dEJGgVa^2iTv+m
z5_pH)NC*!A+(5h%Jjl?0JAGg)b31dE-=A#127j!r09xWE=sJCk`M#yzmho-CONpcT
z<Jn>~>=v=uMsYb?dKHR&nl7|s#~wf9mD&%;N6jB<tAtUGKHsO?k~X7%H6)N2#x_@r
zPHbL^;6jip%-*}-mHXV~wZlubj>#5dz1)-;iH4FpUmzG0SpC+Mv*S};wXKf>p?w&p
z1fn!}jK^pWIar$8HpA12=sc2Bq|S>ck10SDUEAmul}v^Kt1ct@V>+n_i7rJ5)`3n7
z9nKLYtE_}hhVuptm5@pAG*WD>x03Z2LgYho-hCm#E$_Y_N*aB+gR}i9L{bzK%%Nbf
zsV1U>N$AU6^L8UF6|vbcu?wE(yQq)3WnoMN`0|$X2HlPNZ>%hclKlgFbA=pa!;zr`
zlspcZ{Fgq&Ofwd~DLr!3_UPW=UPOUa5VHxdT|K;5zIc(RfxRdfq-a{zIa7(fNZYG_
znb$INTBCtvFQcVPh00P@(}a(RjE7A^&eS_Smdx;i6-Tz3@QgJ46t_va&$j9_J5*h1
zMLXhZfh|4Mi&5ouf-vm>x=)F%f;4VpwVwezm|qNboXHHc<z}Ex*fPVaFI1p+pc&X$
za_+yKFW@*hJ?;<R=O~f!^R_)EhP$AB^jGveTiNp!xPiU9-d@7T=&q!6$ojSvoAYop
ziq9pTN1fw)@$;%AHplmVfqYGfbEAcT<nSj}3?>546xMu!%0Z?G98m*@sw8FqOLIna
z-Y8>P_Fe?4${x@Jg+HAUh6ap#=<r7)af7VY3W84Z`m!Fn?F-g&3RiksfswHo{nrPL
zTJl@Dl0q2=Jpzar(FeCEWn}U=+Dr5FjpX?eW$oe?<nZ*8(1oX8p+xCc^`jWiR(i<_
zG8JK4BSkPwoQ)*in8qq?coUG)3GI@~YQ(Mf(7&pfsMkiLS17m1Q?MaR6k?B8IJ_`2
zYbKHu4_)=6;v{-6WNJYBO>d|WAxxR|8`hrWXJ+n-R4@I@q;%wY%7#*8b7*pcJRXXo
zm*5PtA7TWw7VVBC2kRKu{*_vYE@iAsA{91tq@zJSt%BcOS+Onk&RQHX(<5oUIn2Lz
z8SH<;iZPt2re3Jp(6ySYe<k?1JN0U|w8e}m%Eq)#U=A8T!zqWIyqj3Somg>8KWX;<
zHu<iLwi`5DqqV~bDao%EZ}N1IqTVRY<H!$nrDA~tE2_tfRvwfU%l)bFp5M?U#X5WA
z8TI6Pz(+;I)*)VrrAiCLsA7H7bLkNgZR?QHYU-OAz?Hk2$<$q@qa}$TJ+S;<HNw?^
zSttoLr_rQ}RZ(uE@1g%zF+@)#XKVJWvtiZbtjUWxc)$HC_z4{jo<z?$3ZZBppmBCq
zODaQ2Udf9fLH2PQ!IxDnIBHi>E**JVJjteZEG$6m*HY`HHdXBkbI@e+?`Ory(zZ>L
zr2YAi>EmD>=g!4<xA*0od-pHWZ=8tit0wVik+w{T&sht~UJu~EaNHN2wkz3Ojlw9t
ztj{N{iuqo%Xv|_kpg=c-!^N?~>MK<OU3dRU2-=k0+*4U+b)tN1Q11kG{Mv7r9kd6h
zrT_7oUHfj4&QLIphH`AQtKhY8$m_{qtT%-VmU9HYiiSF%Pxx1lM{+t{=itTj88lh@
zQ`CS)#r9|BRE72Aw3L@2@??lf?A8QvHeyXo2|kaRM3LxC&ytabi3~6!1V{iO=5sid
zVpgxWv$Ltj(_DwV+_w|XW7G)75|17UNU7Lm@y~RKpizP(bcapz{362}6l`osP#;G_
z(=Yj4^Hnu&nTSnv*I*kvjql%9X_*_&;&R(8+rPianA(KT9EyaO7BZtA9HZXBauL5W
z!}B`_P{^H&J~xaQ*w)fnnYx!VzgLqgRMcNx`?{|h=~e;UETQN3?bjFUSy9`B(66Q2
zUROHxSxE!zrSxJg`E@?Su!S3y)WQegs27=<FSjopzYUf<=dO2lzo!*+tuMdQsi@v}
z!+;S59~QL)_*|oAO{3i)ClJH@INm&*xz8)6`GZ-@1Yyn<LdsE>DV|8b_@7+VEfEO2
zLxeW&J400ob<X07Bsth^{6@`Xs8e-DwL*{Xa+=OS8I5ndYk%)0p3BlNGa!XY7t&wK
z!5*w`Y-0Pn%sYOkuN26N8*+NZD6P=pj2n`;F~2G0vL8I*rmsb)U?sQ6zTAD)lZxIP
z#&$Lm&&dE<%g*NKE6JKzbGNW7{VX4W$tM=Y^unag#Dp||ZfA2u>idF`4tzMSr0)8&
zm&%l~(ZeuHvyQTSQ(|dqP&VP}nFK#IY;@_6BKHfTP!xmf^rHrw^9k7;5)`$oRdA~A
zWExe=HLA_zn?k<0HAfC2;aG9EcBC$IGw7I8AeK;iESyVQjGLGag~z48TwjP|5+^WO
zw^_c4GjD0`XHZE`uVK~~6XhLkzJ04ou<&+!tbc#CmNFuqzCb_G!loAYWuL>4C9r9e
zgu%Xw7h$CIIQ)>;N7s75S}|wwm2C+jkg#OYBfsc^i4V)0RiFPOHQ=Dv!Fczgmp}jB
zl~qcB72{nZD{=2N*BF8}y`)BP1I9L5PsSHv#<*2NGn8#&J;2O;VfUa`p7X&5j3{~T
z&EBqHrBolabW6>%3X4^)w=>HpZO>j|a=_!>QUDL$fNe4@!}P;k<7`glyfH5JxG-0I
zqaB2%8!-N!%5S)|ddLs~7zlBY|0`Xb&B0(uXX1aQi>AJFJ}a*Gnqlv^4$Xd$<dSw%
zGsdxEN#iY9p9BY8kW7B%{e`CrG+4K4`uQ;5mEOr~prZW)^2w2~HQjXCQro#6g6a#?
z<m%#+!_j&!Cs4Jo{JC%-8{!)TxH9zZFDvDopTKAVUz3g0WtefM^HpKlWe9@L2buBS
z98^aS2e`E3>JOu;Zey3hvbm{OT&mQ^ff*^d?AE@++recLpeog97gzC|>4T)Js}fh*
zXj}qx+dH=O<LrSNdJI6M8cZ`;)lWo>jIC&KMYGYnDL?sCF_=`vkBJqleR<JcP84J}
zwngA0Oz?_N2t1d8e3)4M8wSLfa%n{RSOh8ZqGG7I>Xtp%##HnO0aH7@vRIU1<(L)I
z7Lyy{g|+;!t!B%9qPL{I>im<3GsWRabnoD^)uB29m3ubJmJX6;bK*6g9Y5;l*)jRb
zea}Z+;U6LWVm%NS$IQb*gB(3(t0&g2lAsk&;@qjm)Rhv^X{?Qi!6Ay(7;v)g+)J(;
zGQ~2MHo?0v*M;Bo@C>xsRX&Rl{npn%pc=^{wuwb7ZL1<_d-hy8rwuo)Z^H&0dsG`z
zYZ@oS_NE#61u~C#WrYE5i6xWA%EH6EuG<HTZsZKfy;oV+RxatMD>qx(SDcUU9ydx`
zzQ!tkFBmZ5<lXY+CK?O5=eX<O(aGG{Tk#hn$=L<9XFb%|n_5%^D2gjH6&(<f#16a#
z_p9937BSIHWKmh8UNDMdmW@Xh%`!nRBj<eE;I5~u(oCEUaOmpis{@O+`t0QrbimO(
zcYl!nQXbP|<8*+U!k8e?Ftf<Xnw*Y6SJ}2IxxsDoMs?Y~1eNaC0;}#P{A(&)E-#7;
zfuP0<BJLB!*T!`(H{#6}&fZH_OnljqFv>C+CFCCvw?hxL+DXauUB9Dt<;B!F(rg5h
zzZq-M`cQ3CXCHmRG+R;EHE_elT|1xKGwZ2$*qYAsGpjZhE?auS9R19D+}Drj5RU$X
z3gQ)2`Rjg-apz(ML_&L9>I(CR?>)YUYAR9P_d<^MPZmxviTXq)q>dhhT)6+Kr(MlW
z)c&=0^kZJ3_pxG%-)8nXDcRCvMx0|Qe5ESvfs(JMNwx)TH(|$2ii(Kveg7e;#?5lt
zj~vewB75S@3>O3?MO-Yq3OYWa$#}^Kvu_e8g~7l^*{hiwup~Rdz#Oc~Ikt|r(<*Fq
z)Bjarli+!iF-b}o3})R?)=MjI_0@TbL;0}NR!8&}8&|A@*U|IF2jj?=2Mf-hbP&i5
z%DZ1(5G%_;L1g9seS8J-NdNl#{~Ts{8sO>aia+Rl$X4;MvldT*PtPFy0X9KK`2U<p
zcnW{oNdE(8fwb0t!~c)A`qM~HJIa3|IY5p_{43I5CfV=)^3zaHTfTon5kWNA?@)g=
zfS=-@Hue7C-5@XO-}pZ*zNg@)#@-)r9mGcX4gTv1p5mX{S%2`ikS*dj{@>=-Q}|O4
z=notYqM83l=6@War|_q)#~-*VWS#vN{*MRp6#Z18|3Pm<RPTS$fAsp(08e%Dp8z?}
z{(1PPs`+V%r{&R~5QmVH9{=$FuVU#b{^_3l2j5Kg5B}df^i%lLH`5>ZDdj)#C+{g`
WIXFmG{xX>`0jQ8g(@FK~+y4Mtd<LEX

literal 0
HcmV?d00001

diff --git a/test/docx/compact-style-removal.native b/test/docx/compact-style-removal.native
new file mode 100644
index 000000000..340878ba0
--- /dev/null
+++ b/test/docx/compact-style-removal.native
@@ -0,0 +1,5 @@
+[OrderedList (1,Decimal,Period)
+ [[Plain [Str "One"]]
+ ,[Plain [Str "Two"]]
+ ,[Plain [Str "Three"]]
+ ,[Plain [Str "Four"]]]]
diff --git a/test/docx/lists-compact.docx b/test/docx/lists-compact.docx
new file mode 100644
index 0000000000000000000000000000000000000000..d7f9e4a062590a4ee7d32cded74f4d13832e60c3
GIT binary patch
literal 9952
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
TW#uoE2^D|{S~Q(xzrOtsH_<7l

literal 0
HcmV?d00001

diff --git a/test/docx/lists-compact.native b/test/docx/lists-compact.native
new file mode 100644
index 000000000..340878ba0
--- /dev/null
+++ b/test/docx/lists-compact.native
@@ -0,0 +1,5 @@
+[OrderedList (1,Decimal,Period)
+ [[Plain [Str "One"]]
+ ,[Plain [Str "Two"]]
+ ,[Plain [Str "Three"]]
+ ,[Plain [Str "Four"]]]]