From 9dbb75dacb26e8482f62fcfaeb7a48ff7b1d7f0e Mon Sep 17 00:00:00 2001 From: Daniel Kessler Date: Sun, 29 Jan 2023 13:38:54 -0800 Subject: [PATCH] ODT reader: fix blockquote indent detection The ODT reader is supposed to detect blockquotes by checking a paragraph style's indentation level. But it's broken for two reasons: * The parser fails on non-integers. So "1in" will get read as 25mm, but "1.0in" fails. By default, the Quotations style is "0.3937in". * The reader doesn't check indentation levels of parent styles. In my test documents, LibreOffice often creates child styles for individual paragraphs, so it's important to check parents (ODT files created by the Pandoc ODT writer don't have this issue though). I added a new test "blockquote2" whose ODT file is generated directly from the corresponding Markdown file with pandoc. Fixes #3437. --- src/Text/Pandoc/Readers/ODT/ContentReader.hs | 26 ++++++++++++------- src/Text/Pandoc/Readers/ODT/StyleReader.hs | 16 ++++++------ test/Tests/Readers/ODT.hs | 3 ++- test/odt/markdown/blockquote2.md | 3 +++ test/odt/odt/blockquote2.odt | Bin 0 -> 9024 bytes 5 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 test/odt/markdown/blockquote2.md create mode 100644 test/odt/odt/blockquote2.odt diff --git a/src/Text/Pandoc/Readers/ODT/ContentReader.hs b/src/Text/Pandoc/Readers/ODT/ContentReader.hs index 97f51d5fa..a1c3e9cf2 100644 --- a/src/Text/Pandoc/Readers/ODT/ContentReader.hs +++ b/src/Text/Pandoc/Readers/ODT/ContentReader.hs @@ -370,16 +370,21 @@ _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_PERCENT_ :: Int _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_MM_ = 5 _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_PERCENT_ = 5 --- | Returns either 'id' or 'blockQuote' depending on the current indentation -getParaModifier :: Style -> ParaModifier -getParaModifier Style{..} | Just props <- paraProperties styleProperties - , isBlockQuote (indentation props) - (margin_left props) - = blockQuote - | otherwise - = id +-- | Returns either 'id' or 'blockQuote' depending if any of the StyleProperties +-- are indented at quote level. +getParaModifier :: [StyleProperties] -> ParaModifier +getParaModifier props | any isBlockQuote props + = blockQuote + | otherwise + = id where - isBlockQuote mIndent mMargin + isBlockQuote SProps {..} | Just paraProps <- paraProperties + , isQuoteWidth (indentation paraProps) + (margin_left paraProps) + = True + | otherwise + = False + isQuoteWidth mIndent mMargin | LengthValueMM indent <- mIndent , indent > _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_MM_ = True @@ -413,7 +418,8 @@ constructPara reader = proc blocks -> do blocks' <- reader -< blocks arr tableCaptionP -< blocks' Right (_, style) -> do - let modifier = getParaModifier style + props <- fromStyles extendedStylePropertyChain -< [style] + let modifier = getParaModifier props blocks' <- reader -< blocks arr modifier -<< blocks' where diff --git a/src/Text/Pandoc/Readers/ODT/StyleReader.hs b/src/Text/Pandoc/Readers/ODT/StyleReader.hs index dadd37dcc..474303bd6 100644 --- a/src/Text/Pandoc/Readers/ODT/StyleReader.hs +++ b/src/Text/Pandoc/Readers/ODT/StyleReader.hs @@ -330,14 +330,14 @@ instance Read XslUnit where -- so I could not really easily calculate anything exact here even if I wanted. -- But I do not care about exactness right now, as I only use measures -- to determine if a paragraph is "indented" or not. -estimateInMillimeter :: Int -> XslUnit -> Int -estimateInMillimeter n XslUnitMM = n -estimateInMillimeter n XslUnitCM = n * 10 -estimateInMillimeter n XslUnitInch = n * 25 -- \* 25.4 -estimateInMillimeter n XslUnitPoints = n `div` 3 -- \* 1/72 * 25.4 -estimateInMillimeter n XslUnitPica = n * 4 -- \* 12 * 1/72 * 25.4 -estimateInMillimeter n XslUnitPixel = n `div`3 -- \* 1/72 * 25.4 -estimateInMillimeter n XslUnitEM = n * 7 -- \* 16 * 1/72 * 25.4 +estimateInMillimeter :: Double -> XslUnit -> Int +estimateInMillimeter n XslUnitMM = round n +estimateInMillimeter n XslUnitCM = round $ n * 10 +estimateInMillimeter n XslUnitInch = round $ n * 25.4 +estimateInMillimeter n XslUnitPoints = round $ n * (1/72) * 25.4 +estimateInMillimeter n XslUnitPica = round $ n * 12 * (1/72) * 25.4 +estimateInMillimeter n XslUnitPixel = round $ n * (1/72) * 25.4 +estimateInMillimeter n XslUnitEM = round $ n * 16 * (1/72) * 25.4 ---- diff --git a/test/Tests/Readers/ODT.hs b/test/Tests/Readers/ODT.hs index 54b9818ec..b48dbe9a3 100644 --- a/test/Tests/Readers/ODT.hs +++ b/test/Tests/Readers/ODT.hs @@ -146,7 +146,8 @@ testMediaBag name odtFile = buildTest $ testMediaBagIO name odtFile namesOfTestsComparingToMarkdown :: [ String ] -namesOfTestsComparingToMarkdown = [ "bold" +namesOfTestsComparingToMarkdown = [ "blockquote2" + , "bold" -- , "citation" , "endnote" , "externalLink" diff --git a/test/odt/markdown/blockquote2.md b/test/odt/markdown/blockquote2.md new file mode 100644 index 000000000..0e666aa98 --- /dev/null +++ b/test/odt/markdown/blockquote2.md @@ -0,0 +1,3 @@ +Paragraph + +> A blockquote. diff --git a/test/odt/odt/blockquote2.odt b/test/odt/odt/blockquote2.odt new file mode 100644 index 0000000000000000000000000000000000000000..b6c922d89532a5fe0da7bb3300cf11ce2ec1b464 GIT binary patch literal 9024 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 literal 0 HcmV?d00001