From 97b70d20bbd266d627a402360b0fd682f53355a3 Mon Sep 17 00:00:00 2001 From: Shin Sang-jae Date: Sat, 28 Nov 2020 02:11:40 +0900 Subject: EPUB: use preserveAspectRatio="xMidYMid" for cover image (#6895) This change affects both the epub2 and the epub3 templates. It avoids distortion of the cover image by requiring that the aspect ratio be preserved. --- data/templates/default.epub2 | 2 +- data/templates/default.epub3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/templates/default.epub2 b/data/templates/default.epub2 index f440134df..685d10208 100644 --- a/data/templates/default.epub2 +++ b/data/templates/default.epub2 @@ -48,7 +48,7 @@ $endif$ $else$ $if(coverpage)$
- +
diff --git a/data/templates/default.epub3 b/data/templates/default.epub3 index 4f5bd6641..fc4fa7620 100644 --- a/data/templates/default.epub3 +++ b/data/templates/default.epub3 @@ -49,7 +49,7 @@ $endif$ $else$ $if(coverpage)$
- +
-- cgit v1.2.3 From 3a2d993165e44103e6cde8a977542e6adedf913d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 27 Nov 2020 18:13:02 +0100 Subject: CONTRIBUTING: add section "How can I help?" (#6892) Also adds a paragraph aimed at highlighting the importance of feature maintenance. --- CONTRIBUTING.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea2bf80e7..b2dc8313d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,64 @@ Contributing to pandoc ====================== +Welcome to pandoc! Very soon after its beginnings in 2006, pandoc +has been influenced, improved, and modified, by users, devs, and +newcomers alike. The project thrives on its active community. It is +great to have you here. + +How can I help? +--------------- + +There are many ways in which you can support pandoc. Here are a few +ideas: + + * Participate in online discussions. The [pandoc-discuss] mailing + list is a good place for this. This gives valuable input and + makes it easier to improve the program. + + * Help with questions. Every request that is answered by the wider + community frees time for programming contributors. This will + speed up development of new features and issue fixes. Don't + underestimate your knowledge, please share it! + + Good places to help are the [pandoc-discuss] mailing list, Q/A + sites like StackOverflow, community forums (e.g. + [RStudio][RStudio Community], [Zettlr][Zettlr Forum]), and, for + technical questions, the GitHub [issue tracker]. + + * Write or improve documentation. If you ran into a problem which + took more time to figure out than expected, please consider to + save other users from the same experience. People writing the + documentation tend to lack an outside few, so please help + provide one. Good documentation is both difficult and extremely + important. + + The official docs are not the only place for documentation. + Pandoc also has a [Wiki][pandoc wiki]. Private blogs can serve + as documentation just as the official manual can. + + * Contribute code. No matter whether it's a small fix in a format + template or a huge lump of Haskell code: help is welcome. It's + usually a good idea to talk about the plans early, as this can + prevent unnecessary work. See below for more information. + + * Last but not least: consider funding the development and + maintenance of pandoc financially. You can find sponsor buttons + on the [pandoc website] and the [GitHub repository][GitHub + repo]. + +A rich ecosystem of libraries, editors, filters, and templates has +developed around pandoc; conversely, pandoc builds and depends on a +large number of libraries. Contributing to any of these projects is +another way that can help to ensure stability, and to keep pushing +the boundaries of what is possible with pandoc. + +[RStudio Community]: https://community.rstudio.com/ +[Zettlr Forum]: https://forum.zettlr.com/ +[pandoc wiki]: https://github.com/jgm/pandoc/wiki +[pandoc website]: https://pandoc.org +[GitHub repo]: https://github.com/jgm/pandoc + Have a question? ---------------- @@ -105,7 +163,12 @@ Explain the rationale for the feature you're requesting. Why would this feature be useful? Consider also any possible drawbacks, including backwards compatibility, new library dependencies, and performance issues. -It is best to discuss a potential new feature on [pandoc-discuss] +Features are very rarely "implement and forget", as all code must be +maintained. This is especially relevant for large or complex +contributions. It is helpful to be sympathetic to that fact, and to +communicate future plans and availability clearly. + +Any potential new feature is best discussed on [pandoc-discuss] before opening an issue. Patches and pull requests @@ -258,10 +321,10 @@ a ghci REPL for working with pandoc. With [stack], use We recommend using the following `.ghci` file (which can be placed in the source directory): - :set -fobject-code - :set -XTypeSynonymInstances - :set -XScopedTypeVariables - :set -XOverloadedStrings + :set -fobject-code + :set -XTypeSynonymInstances + :set -XScopedTypeVariables + :set -XOverloadedStrings Profiling --------- -- cgit v1.2.3 From 8c38390038edcebd55f9dec8359ef983f3813425 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 27 Nov 2020 21:21:25 +0100 Subject: HTML reader tests: improve test coverage of new features --- src/Text/Pandoc/Readers/HTML/Table.hs | 3 +- test/html-reader.html | 76 ++++++++++++++++++++++++ test/html-reader.native | 109 ++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index 5a783988f..91639fa4c 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -214,7 +214,8 @@ normalize :: [ColWidth] -> TableHead -> [TableBody] -> TableFoot -> Either String ([ColSpec], TableHead, [TableBody], TableFoot) normalize widths head' bodies foot = do let rows = headRows head' <> concatMap bodyRows bodies <> footRows foot - let rowLength = length . rowCells + let cellWidth (Cell _ _ _ (ColSpan cs) _) = cs + let rowLength = foldr (\cell acc -> cellWidth cell + acc) 0 . rowCells let ncols = maximum (map rowLength rows) let tblType = tableType (map rowCells rows) -- fail on empty table diff --git a/test/html-reader.html b/test/html-reader.html index ae937af82..4b688cb20 100644 --- a/test/html-reader.html +++ b/test/html-reader.html @@ -487,6 +487,7 @@ An e-mail address: nobody [at] nowhere.net

+

Row headers

@@ -656,6 +657,7 @@ An e-mail address: nobody [at] nowhere.net

+

tbody tags omitted

@@ -669,6 +671,7 @@ An e-mail address: nobody [at] nowhere.net
1

+

empty head

@@ -686,6 +689,7 @@ An e-mail address: nobody [at] nowhere.net

+

explicit body and foot

@@ -702,6 +706,78 @@ An e-mail address: nobody [at] nowhere.net
+

Colspans and Rowspans

+ + + + + + + + +
1 and 23
4, 5, and 6
+
+ + + + + + + + + + + + + + + + + +
Numbers
1 and 423
56
+

Attributes

+ + + + + + + + + + + + + + + + + + + +
Cat X
123
456
+

Tag omission

+

thead, tbody, and tfoot

+ + + + + + + + + + + + + + + + + + + +
XYZ
123
456

Empty Tables

This section should be empty.

diff --git a/test/html-reader.native b/test/html-reader.native index ea74c25e1..04ec55d1e 100644 --- a/test/html-reader.native +++ b/test/html-reader.native @@ -395,6 +395,7 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("titl (TableFoot ("",[],[]) []) ,HorizontalRule +,Para [Str "Row",Space,Str "headers"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) @@ -647,6 +648,7 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("titl (TableFoot ("",[],[]) []) ,HorizontalRule +,Para [Str "tbody",Space,Str "tags",Space,Str "omitted"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) @@ -673,6 +675,7 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("titl (TableFoot ("",[],[]) []) ,HorizontalRule +,Para [Str "empty",Space,Str "head"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) @@ -699,6 +702,7 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("titl (TableFoot ("",[],[]) []) ,HorizontalRule +,Para [Str "explicit",Space,Str "body",Space,Str "and",Space,Str "foot"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) @@ -723,5 +727,110 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("titl [Plain [Str "5"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "6"]]]]) +,Header 2 ("colspans-and-rowspans",[],[]) [Str "Colspans",Space,Str "and",Space,Str "Rowspans"] +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + []) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) + [Plain [Str "1",Space,Str "and",Space,Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 3) + [Plain [Str "4,",Space,Str "5,",Space,Str "and",Space,Str "6"]]]])] + (TableFoot ("",[],[]) + []) +,HorizontalRule +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 3) + [Plain [Str "Numbers"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Str "1",Space,Str "and",Space,Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]]]])] + (TableFoot ("",[],[]) + []) +,Header 2 ("attributes",[],[]) [Str "Attributes"] +,Table ("attrib-test-table",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",["table-head"],[]) + [Row ("",["table-head-row"],[]) + [Cell ("",[],[("abbr","x")]) AlignDefault (RowSpan 1) (ColSpan 3) + [Plain [Str "Cat",Space,Str "X"]]]]) + [(TableBody ("",["main"],[("part","body")]) (RowHeadColumns 0) + [] + [Row ("",[],[("part","row")]) + [Cell ("",[],[("part","cell")]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[("valign","bottom")]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[("style","color: #151950")]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]]]])] + (TableFoot ("",["summary"],[]) + [Row ("",[],[("bgcolor","#ccc")]) + [Cell ("",[],[("square","true")]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]]]]) +,Header 2 ("tag-omission",[],[]) [Str "Tag",Space,Str "omission"] +,Para [Str "thead,",Space,Str "tbody,",Space,Str "and",Space,Str "tfoot"] +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "X"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Y"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Z"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]]]])] + (TableFoot ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]]]]) ,Header 2 ("empty-tables",[],[]) [Str "Empty",Space,Str "Tables"] ,Para [Str "This",Space,Str "section",Space,Str "should",Space,Str "be",Space,Str "empty."]] -- cgit v1.2.3 From 83d63b72e1b9eff9f2aa3b9f36b56d348f0909a2 Mon Sep 17 00:00:00 2001 From: Tassos Manganaris Date: Fri, 27 Nov 2020 23:42:53 -0500 Subject: Fix a tiny Typo in the CSV reader module Header comment in the CSV reader module says "RST" instead of "CSV". --- src/Text/Pandoc/Readers/CSV.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/CSV.hs b/src/Text/Pandoc/Readers/CSV.hs index f0edcaa16..45f4d88d4 100644 --- a/src/Text/Pandoc/Readers/CSV.hs +++ b/src/Text/Pandoc/Readers/CSV.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {- | - Module : Text.Pandoc.Readers.RST + Module : Text.Pandoc.Readers.CSV Copyright : Copyright (C) 2006-2020 John MacFarlane License : GNU GPL, version 2 or above -- cgit v1.2.3 From bff9c129c3579f928a0067759d0a784eb5c07d30 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 29 Nov 2020 10:29:51 -0800 Subject: LaTeX reader: don't parse `\rule` with width 0 as horizontal rule. --- src/Text/Pandoc/Readers/LaTeX.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 2a9bff746..15a1a19fc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1225,6 +1225,16 @@ preamble = mconcat <$> many preambleBlock anyTok return mempty) +rule :: PandocMonad m => LP m Blocks +rule = do + skipopts + width <- T.takeWhile (\c -> isDigit c || c == '.') . stringify <$> tok + _thickness <- tok + -- 0-width rules are used to fix spacing issues: + case safeRead width of + Just (0 :: Double) -> return mempty + _ -> return horizontalRule + paragraph :: PandocMonad m => LP m Blocks paragraph = do x <- trimInlines . mconcat <$> many1 inline @@ -1595,7 +1605,7 @@ blockCommands = M.fromList -- , ("hrule", pure horizontalRule) , ("strut", pure mempty) - , ("rule", skipopts *> tok *> tok $> horizontalRule) + , ("rule", rule) , ("item", looseItem) , ("documentclass", skipopts *> braced *> preamble) , ("centerline", para . trimInlines <$> (skipopts *> tok)) -- cgit v1.2.3 From 0240623edab24aae910529f3331f49f166253d76 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 30 Nov 2020 22:45:12 -0800 Subject: Use doctemplates 0.8.3. Fixes issues with nested for loops in templates. --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 201d61ae0..a23c45374 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,10 +17,10 @@ extra-deps: - jira-wiki-markup-1.3.2 - HsYAML-0.2.1.0 - HsYAML-aeson-0.2.0.0 -- doctemplates-0.8.2 - commonmark-0.1.1.2 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 +- doctemplates-0.8.3 #- citeproc-0.2 - citeproc: git: https://github.com/jgm/citeproc.git -- cgit v1.2.3 From 4ca0d590e2c433c6ce99d53be58df4b982a587f6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 30 Nov 2020 23:02:24 -0800 Subject: Use a more recent stack resolver. --- stack.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/stack.yaml b/stack.yaml index a23c45374..7a849b713 100644 --- a/stack.yaml +++ b/stack.yaml @@ -6,16 +6,12 @@ packages: - '.' extra-deps: - pandoc-types-1.22 -- texmath-0.12.0.3 - rfc5051-0.2 - haddock-library-1.9.0 - skylighting-0.10.1 - skylighting-core-0.10.1 -- doclayout-0.3 -- emojis-0.1 - hslua-1.1.2 - jira-wiki-markup-1.3.2 -- HsYAML-0.2.1.0 - HsYAML-aeson-0.2.0.0 - commonmark-0.1.1.2 - commonmark-extensions-0.2.0.4 @@ -27,7 +23,7 @@ extra-deps: commit: 42b1d154b02435229acbe98ae0f17d01b757ee93 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules -resolver: lts-14.6 +resolver: lts-16.23 nix: packages: [zlib] -- cgit v1.2.3 From 7b11cdee49ec491a33dcade6ea68c3084a425992 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 2 Dec 2020 10:46:23 -0800 Subject: Citeproc: ensure that BCP47 lang codes can be used. We ignore the variants and just use the base lang code and country code when passing off to citeproc. --- src/Text/Pandoc/Citeproc.hs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index a48f97c3b..0de2882ae 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -16,6 +16,7 @@ import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..)) import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText) import Text.Pandoc.Readers.Markdown (yamlToRefs) import Text.Pandoc.Class (setResourcePath, getResourcePath, getUserDataDir) +import qualified Text.Pandoc.BCP47 as BCP47 import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as L import Text.Pandoc.Definition as Pandoc @@ -91,8 +92,9 @@ processCitations (Pandoc meta bs) = do case styleRes of Left err -> throwError $ PandocAppError $ prettyCiteprocError err Right style -> return style{ styleAbbreviations = mbAbbrevs } - let mblang = parseLang <$> - ((lookupMeta "lang" meta <|> lookupMeta "locale" meta) >>= metaValueToText) + mblang <- maybe (return Nothing) bcp47LangToIETF + ((lookupMeta "lang" meta <|> lookupMeta "locale" meta) >>= + metaValueToText) let locale = Citeproc.mergeLocales mblang style let getCiteId (Cite cs _) = Set.fromList $ map B.citationId cs getCiteId _ = mempty @@ -579,3 +581,16 @@ removeFinalPeriod ils = isRightQuote "\8217" = True isRightQuote "\187" = True isRightQuote _ = False + +bcp47LangToIETF :: PandocMonad m => Text -> m (Maybe Lang) +bcp47LangToIETF bcplang = + case BCP47.parseBCP47 bcplang of + Left _ -> do + report $ InvalidLang bcplang + return Nothing + Right lang -> + return $ Just + $ Lang (BCP47.langLanguage lang) + (if T.null (BCP47.langRegion lang) + then Nothing + else Just (BCP47.langRegion lang)) -- cgit v1.2.3 From f1bd176d81872dc587f3c38f3db70fefe9afd7b5 Mon Sep 17 00:00:00 2001 From: Randolf J <34705014+jun-sheaf@users.noreply.github.com> Date: Wed, 2 Dec 2020 21:28:38 +0100 Subject: filters.md: document a new Pandoc filtering framework (#6908) --- doc/filters.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/filters.md b/doc/filters.md index 8b6002467..ce871cb42 100644 --- a/doc/filters.md +++ b/doc/filters.md @@ -253,12 +253,15 @@ repository](https://github.com/jgm/pandocfilters). For a more Pythonic alternative to pandocfilters, see the [panflute](https://pypi.org/project/panflute) library. -Don't like Python? There are also ports of pandocfilters in -[PHP](https://github.com/vinai/pandocfilters-php), -[perl](https://metacpan.org/pod/Pandoc::Filter), -[javascript/node.js](https://github.com/mvhenderson/pandoc-filter-node), -[Groovy](https://github.com/dfrommi/groovy-pandoc), and -[Ruby](https://heerdebeer.org/Software/markdown/paru/). +Don't like Python? There are also ports of pandocfilters in + +- [PHP](https://github.com/vinai/pandocfilters-php), +- [perl](https://metacpan.org/pod/Pandoc::Filter), +- TypeScript/JavaScript via Node.js + - [pandoc-filter](https://github.com/mvhenderson/pandoc-filter-node), + - [node-pandoc-filter](https://github.com/mu-io/node-pandoc-filter), +- [Groovy](https://github.com/dfrommi/groovy-pandoc), and +- [Ruby](https://heerdebeer.org/Software/markdown/paru/). Starting with pandoc 2.0, pandoc includes built-in support for writing filters in lua. The lua interpreter is built in to -- cgit v1.2.3 From aab54c43258e53e4c1fde1b574c65ff8769709d0 Mon Sep 17 00:00:00 2001 From: Kolen Cheung Date: Wed, 2 Dec 2020 21:00:57 -0800 Subject: Add translations zh-Hans.yaml and zh-Hant.yaml Closes #6904, closes #6909. Co-authored-by: taotieren --- data/translations/zh-Hans.yaml | 22 ++++++++++++++++++++++ data/translations/zh-Hant.yaml | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 data/translations/zh-Hans.yaml create mode 100644 data/translations/zh-Hant.yaml diff --git a/data/translations/zh-Hans.yaml b/data/translations/zh-Hans.yaml new file mode 100644 index 000000000..d44ef41e3 --- /dev/null +++ b/data/translations/zh-Hans.yaml @@ -0,0 +1,22 @@ +Abstract: 摘要 +Appendix: 附录 +Bibliography: 文献目录 +Cc: 副本 +Chapter: 章 +Contents: 目录 +Encl: 附件 +Figure: 图 +Glossary: 术语 +Index: 索引 +Listing: 列表 +ListOfFigures: 附图目录 +ListOfTables: 表格索引 +Page: 页 +Part: 段 +Preface: 序 +Proof: 校对 +References: 参考文献 +See: 见 +SeeAlso: 参见 +Table: 表 +To: 到 diff --git a/data/translations/zh-Hant.yaml b/data/translations/zh-Hant.yaml new file mode 100644 index 000000000..60be44201 --- /dev/null +++ b/data/translations/zh-Hant.yaml @@ -0,0 +1,22 @@ +Abstract: 摘要 +Appendix: 附錄 +Bibliography: 文獻目錄 +Cc: 副本 +Chapter: 章 +Contents: 目錄 +Encl: 附件 +Figure: 圖 +Glossary: 術語 +Index: 索引 +Listing: 列表 +ListOfFigures: 附圖目錄 +ListOfTables: 表格索引 +Page: 頁 +Part: 段 +Preface: 序 +Proof: 校對 +References: 參考文獻 +See: 見 +SeeAlso: 參見 +Table: 表 +To: 到 -- cgit v1.2.3 From 5bbd5a9e80c4f4356d71b54315cf75b5ae2f3650 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 3 Dec 2020 09:51:23 -0800 Subject: Docx writer: Support bold and italic in "complex script." Previously bold and italics didn't work properly in LTR text. This commit causes the w:bCs and w:iCs attributes to be used, in addition to w:b and w:i, for bold and italics respectively. Closes #6911. --- src/Text/Pandoc/Writers/Docx.hs | 8 ++++++-- test/docx/golden/custom_style_preserve.docx | Bin 10573 -> 10578 bytes test/docx/golden/definition_list.docx | Bin 9844 -> 9850 bytes test/docx/golden/inline_formatting.docx | Bin 9959 -> 9969 bytes 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index a380fd4fa..4cb879e6a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1272,9 +1272,13 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do $ inlinesToOpenXML opts ils wrapBookmark ident contents inlineToOpenXML' opts (Strong lst) = - withTextProp (mknode "w:b" [] ()) $ inlinesToOpenXML opts lst + withTextProp (mknode "w:b" [] ()) $ + withTextProp (mknode "w:bCs" [] ()) $ -- needed for LTR, #6911 + inlinesToOpenXML opts lst inlineToOpenXML' opts (Emph lst) = - withTextProp (mknode "w:i" [] ()) $ inlinesToOpenXML opts lst + withTextProp (mknode "w:i" [] ()) $ + withTextProp (mknode "w:iCs" [] ()) $ -- needed for LTR, #6911 + inlinesToOpenXML opts lst inlineToOpenXML' opts (Underline lst) = withTextProp (mknode "w:u" [("w:val","single")] ()) $ inlinesToOpenXML opts lst diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index ac595cdd4..17804bb81 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index 005f5dbe3..21629e208 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index ce1d16daa..e12e3b38d 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ -- cgit v1.2.3 From 9c6cc79c11d191c74aae8646279a2b94ae253330 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 3 Dec 2020 21:24:27 -0800 Subject: EPUB writer: add frontmatter type on body element for nav.xhtml. Closes #6918. --- src/Text/Pandoc/Writers/EPUB.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 12004889f..eadd1100f 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -845,7 +845,9 @@ pandocToEPUB version opts doc = do [ unode "ol" landmarkItems ] | not (null landmarkItems)] navData <- lift $ writeHtml opts'{ writerVariables = - Context (M.fromList [("navpage", toVal' "true")]) + Context (M.fromList [("navpage", toVal' "true") + ,("body-type", toVal' "frontmatter") + ]) <> cssvars False <> vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) -- cgit v1.2.3 From 7199d68ba078148ff76a38f2c483da73edd62747 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 3 Dec 2020 21:39:19 -0800 Subject: EPUB writer: include title page in landmarks. Closes #6919. Note that the toc is also included if `--toc` is specified. --- src/Text/Pandoc/Writers/EPUB.hs | 9 +++++++-- test/command/5986.md | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index eadd1100f..5867b7c80 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -825,7 +825,12 @@ pandocToEPUB version opts doc = do [ unode "h1" ! [("id","toc-title")] $ tocTitle , unode "ol" ! [("class","toc")] $ tocBlocks ]] let landmarkItems = if epub3 - then [ unode "li" + then unode "li" + [ unode "a" ! [("href", + "text/title_page.xhtml") + ,("epub:type", "titlepage")] $ + ("Title Page" :: String) ] : + [ unode "li" [ unode "a" ! [("href", "text/cover.xhtml") ,("epub:type", "cover")] $ ("Cover" :: String)] | @@ -834,7 +839,7 @@ pandocToEPUB version opts doc = do [ unode "li" [ unode "a" ! [("href", "#toc") ,("epub:type", "toc")] $ - ("Table of contents" :: String) + ("Table of Contents" :: String) ] | writerTableOfContents opts ] else [] diff --git a/test/command/5986.md b/test/command/5986.md index e24aebb64..ea0ca70c1 100644 --- a/test/command/5986.md +++ b/test/command/5986.md @@ -6,7 +6,8 @@

-- cgit v1.2.3 From 171d3db3848a8ca79480688748d0ffff67ed2039 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 4 Dec 2020 09:47:56 -0800 Subject: HTML writer: Fix handling of nested csl- display spans. Previously inner Spans used to represent CSL display attributes were not rendered as div tags. See #6921. --- src/Text/Pandoc/Writers/HTML.hs | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index c92131d5a..76f17f77a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -695,12 +695,12 @@ blockToHtml opts (Div (ident, "section":dclasses, dkvs) let fragmentClass = case slideVariant of RevealJsSlides -> "fragment" _ -> "incremental" - let inDiv zs = RawBlock (Format "html") ("
fragmentClass <> "\">") : (zs ++ [RawBlock (Format "html") "
"]) let breakOnPauses zs = case splitBy isPause zs of [] -> [] - y:ys -> y ++ concatMap inDiv ys + y:ys -> y ++ concatMap inDiv' ys let (titleBlocks, innerSecs) = if titleSlide -- title slides have no content of their own @@ -783,9 +783,7 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do -- a newline between the column divs, which throws -- off widths! see #4028 mconcat <$> mapM (blockToHtml opts) bs - else if isCslBibEntry - then mconcat <$> mapM (cslEntryToHtml opts') bs - else blockListToHtml opts' bs + else blockListToHtml opts' bs let contents' = nl opts >> contents >> nl opts let (divtag, classes'') = if html5 && "section" `elem` classes' then (H5.section, filter (/= "section") classes') @@ -1213,6 +1211,10 @@ inlineToHtml opts inline = do LineBreak -> return $ do if html5 then H5.br else H.br strToHtml "\n" + (Span ("",[cls],[]) ils) + | cls == "csl-block" || cls == "csl-left-margin" || + cls == "csl-right-inline" || cls == "csl-indent" + -> inlineListToHtml opts ils >>= inDiv cls (Span (id',classes,kvs) ils) -> let spanLikeTag = case classes of @@ -1462,22 +1464,12 @@ blockListToNote opts ref blocks = do _ -> noteItem return $ nl opts >> noteItem' -cslEntryToHtml :: PandocMonad m - => WriterOptions - -> Block - -> StateT WriterState m Html -cslEntryToHtml opts (Para xs) = do +inDiv :: PandocMonad m=> Text -> Html -> StateT WriterState m Html +inDiv cls x = do html5 <- gets stHtml5 - let inDiv :: Text -> Html -> Html - inDiv cls x = (if html5 then H5.div else H.div) - x ! A.class_ (toValue cls) - let go (Span ("",[cls],[]) ils) - | cls == "csl-block" || cls == "csl-left-margin" || - cls == "csl-right-inline" || cls == "csl-indent" - = inDiv cls <$> inlineListToHtml opts ils - go il = inlineToHtml opts il - mconcat <$> mapM go xs -cslEntryToHtml opts x = blockToHtml opts x + return $ + (if html5 then H5.div else H.div) + x ! A.class_ (toValue cls) isMathEnvironment :: Text -> Bool isMathEnvironment s = "\\begin{" `T.isPrefixOf` s && -- cgit v1.2.3 From 68bcddeb2136242aabee716420ecad251fc38920 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 4 Dec 2020 10:14:19 -0800 Subject: LaTeX writer: Fix bug with nested csl- display Spans. See #6921. --- src/Text/Pandoc/Writers/LaTeX.hs | 68 +++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d665269ab..e02cc2833 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -553,9 +553,7 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do Just s -> braces (literal s)) $$ inner $+$ "\\end{CSLReferences}" - else if "csl-entry" `elem` classes - then vcat <$> mapM cslEntryToLaTeX bs - else blockListToLaTeX bs + else blockListToLaTeX bs modify $ \st -> st{ stIncremental = oldIncremental } linkAnchor' <- hypertarget True identifier empty -- see #2704 for the motivation for adding \leavevmode: @@ -1180,23 +1178,6 @@ isQuoted :: Inline -> Bool isQuoted (Quoted _ _) = True isQuoted _ = False -cslEntryToLaTeX :: PandocMonad m - => Block - -> LW m (Doc Text) -cslEntryToLaTeX (Para xs) = - mconcat <$> mapM go xs - where - go (Span ("",["csl-block"],[]) ils) = - (cr <>) . inCmd "CSLBlock" <$> inlineListToLaTeX ils - go (Span ("",["csl-left-margin"],[]) ils) = - inCmd "CSLLeftMargin" <$> inlineListToLaTeX ils - go (Span ("",["csl-right-inline"],[]) ils) = - (cr <>) . inCmd "CSLRightInline" <$> inlineListToLaTeX ils - go (Span ("",["csl-indent"],[]) ils) = - (cr <>) . inCmd "CSLIndent" <$> inlineListToLaTeX ils - go il = inlineToLaTeX il -cslEntryToLaTeX x = blockToLaTeX x - -- | Convert inline element to LaTeX inlineToLaTeX :: PandocMonad m => Inline -- ^ Inline to convert @@ -1204,23 +1185,38 @@ inlineToLaTeX :: PandocMonad m inlineToLaTeX (Span (id',classes,kvs) ils) = do linkAnchor <- hypertarget False id' empty lang <- toLang $ lookup "lang" kvs - let cmds = ["textup" | "csl-no-emph" `elem` classes] ++ - ["textnormal" | "csl-no-strong" `elem` classes || - "csl-no-smallcaps" `elem` classes] ++ - ["RL" | ("dir", "rtl") `elem` kvs] ++ - ["LR" | ("dir", "ltr") `elem` kvs] ++ - (case lang of - Just lng -> let (l, o) = toPolyglossia lng - ops = if T.null o then "" else "[" <> o <> "]" - in ["text" <> l <> ops] - Nothing -> []) + let classToCmd "csl-no-emph" = Just "textup" + classToCmd "csl-no-strong" = Just "textnormal" + classToCmd "csl-no-smallcaps" = Just "textnormal" + classToCmd "csl-block" = Just "CSLBlock" + classToCmd "csl-left-margin" = Just "CSLLeftMargin" + classToCmd "csl-right-inline" = Just "CSLRightInline" + classToCmd "csl-indent" = Just "CSLIndent" + classToCmd _ = Nothing + kvToCmd ("dir","rtl") = Just "RL" + kvToCmd ("dir","ltr") = Just "LR" + kvToCmd _ = Nothing + langCmds = + case lang of + Just lng -> let (l, o) = toPolyglossia lng + ops = if T.null o then "" else "[" <> o <> "]" + in ["text" <> l <> ops] + Nothing -> [] + let cmds = mapMaybe classToCmd classes ++ mapMaybe kvToCmd kvs ++ langCmds contents <- inlineListToLaTeX ils - return $ (if T.null id' - then empty - else "\\protect" <> linkAnchor) <> - (if null cmds - then braces contents - else foldr inCmd contents cmds) + return $ + (case classes of + ["csl-block"] -> (cr <>) + ["csl-left-margin"] -> (cr <>) + ["csl-right-inline"] -> (cr <>) + ["csl-indent"] -> (cr <>) + _ -> id) $ + (if T.null id' + then empty + else "\\protect" <> linkAnchor) <> + (if null cmds + then braces contents + else foldr inCmd contents cmds) inlineToLaTeX (Emph lst) = inCmd "emph" <$> inlineListToLaTeX lst inlineToLaTeX (Underline lst) = inCmd "underline" <$> inlineListToLaTeX lst inlineToLaTeX (Strong lst) = inCmd "textbf" <$> inlineListToLaTeX lst -- cgit v1.2.3 From dc3ef5201f9531bc405ac07e763d9f004bb6bc91 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 4 Dec 2020 10:55:48 -0800 Subject: Markdown writer: ensure that a new csl-block begins on a new line. This just looks better and doesn't affect the semantics. See #6921. --- src/Text/Pandoc/Writers/Markdown.hs | 7 ++++++- test/command/pandoc-citeproc-53.md | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 6aec6b244..5eb47b261 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1058,7 +1058,12 @@ inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) = inlineToMarkdown opts (Span attrs ils) = do variant <- asks envVariant contents <- inlineListToMarkdown opts ils - return $ case variant of + return $ case attrs of + (_,["csl-block"],_) -> (cr <>) + (_,["csl-left-margin"],_) -> (cr <>) + (_,["csl-indent"],_) -> (cr <>) + _ -> id + $ case variant of PlainText -> contents _ | attrs == nullAttr -> contents | isEnabled Ext_bracketed_spans opts -> diff --git a/test/command/pandoc-citeproc-53.md b/test/command/pandoc-citeproc-53.md index 295f52049..fb8d5c35e 100644 --- a/test/command/pandoc-citeproc-53.md +++ b/test/command/pandoc-citeproc-53.md @@ -28,11 +28,13 @@ Doe[^1] Doe[^2] Roe[^3] Roe[^4] Doe[^5] Doe[^6] Roe[^7] Roe[^8] ::: {#refs .references .csl-bib-body} ::: {#ref-a .csl-entry} -[[Doe J.]{.smallcaps} ]{.csl-block}[2000, *Work A*,.]{.csl-left-margin} +[[Doe J.]{.smallcaps} ]{.csl-block} +[2000, *Work A*,.]{.csl-left-margin} ::: ::: {#ref-b .csl-entry} -[[Roe J.]{.smallcaps} ]{.csl-block}[1990, *Work B*,.]{.csl-left-margin} +[[Roe J.]{.smallcaps} ]{.csl-block} +[1990, *Work B*,.]{.csl-left-margin} ::: ::: -- cgit v1.2.3 From 6f35600204132c5a0907bb7cccb2e10ffb27506a Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Dec 2020 06:00:21 +0100 Subject: Docbook writer: add XML namespaces to top-level elements (#6923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we only added xmlns attributes to chapter elements, even when running with --top-level-division=section. Let’s add the namespaces to part and section elements too, when they are the selected top-level divisions. We do not need to add namespaces to documents produced with --standalone flag, since those will already have xmlns attribute on the root element in the template. --- src/Text/Pandoc/Writers/Docbook.hs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 408d8cc0c..3f4c67f10 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -15,6 +15,7 @@ Conversion of 'Pandoc' documents to Docbook XML. module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Control.Monad.Reader import Data.Generics (everywhere, mkT) +import Data.Maybe (isNothing) import Data.Monoid (Any (..)) import Data.Text (Text) import qualified Data.Text as T @@ -40,6 +41,18 @@ data DocBookVersion = DocBook4 | DocBook5 type DB = ReaderT DocBookVersion +-- | Get level of the top-level headers based on the configured top-level division. +-- The header level can then be used to determine appropriate DocBook element +-- for each subdivision associated with a header. +-- The numbering here follows LaTeX's internal numbering +getStartLvl :: WriterOptions -> Int +getStartLvl opts = + case writerTopLevelDivision opts of + TopLevelPart -> -1 + TopLevelChapter -> 0 + TopLevelSection -> 1 + TopLevelDefault -> 1 + -- | Convert list of authors to a docbook section authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines authorToDocbook opts name' = do @@ -79,12 +92,7 @@ writeDocbook opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - -- The numbering here follows LaTeX's internal numbering - let startLvl = case writerTopLevelDivision opts of - TopLevelPart -> -1 - TopLevelChapter -> 0 - TopLevelSection -> 1 - TopLevelDefault -> 1 + let startLvl = getStartLvl opts let fromBlocks = blocksToDocbook opts . makeSections False (Just startLvl) auths' <- mapM (authorToDocbook opts) $ docAuthors meta @@ -170,8 +178,12 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do then "xml:id" else "id" idAttr = [(idName, writerIdentifierPrefix opts <> id') | not (T.null id')] - nsAttr = if version == DocBook5 && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] - else [] + -- We want to add namespaces to the root (top-level) element. + nsAttr = if version == DocBook5 && lvl == getStartLvl opts && isNothing (writerTemplate opts) + -- Though, DocBook 4 does not support namespaces and + -- standalone documents will include them in the template. + then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] + else [] attribs = nsAttr <> idAttr title' <- inlinesToDocbook opts ils contents <- blocksToDocbook opts bs -- cgit v1.2.3 From ddb76cb356a82f6a9e51a6f3626dd154816e9205 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 5 Dec 2020 09:53:39 -0800 Subject: LaTeX reader: don't apply theorem default styling to a figure inside. If we put an image in italics, then when rendering to Markdown we no longer get an implicit figure. Closes #6925. --- src/Text/Pandoc/Readers/LaTeX.hs | 1 + test/command/6925.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 test/command/6925.md diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 15a1a19fc..afe960454 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1812,6 +1812,7 @@ theoremEnvironment name = do _ -> bs italicize :: Block -> Block +italicize x@(Para [Image{}]) = x -- see #6925 italicize (Para ils) = Para [Emph ils] italicize (Plain ils) = Plain [Emph ils] italicize x = x diff --git a/test/command/6925.md b/test/command/6925.md new file mode 100644 index 000000000..458a0b91d --- /dev/null +++ b/test/command/6925.md @@ -0,0 +1,34 @@ +``` +% pandoc -f latex -t markdown +\documentclass{amsart} +\newtheorem{thm}{Theorem}[section] +\theoremstyle{definition} +\newtheorem{thm2}[section]{Theorem} +\begin{document} +\begin{thm} +a +\begin{figure} +\includegraphics[]{1.png} +\end{figure} +\end{thm} + +\begin{thm2} +a +\begin{figure} +\includegraphics[]{1.png} +\end{figure} +\end{thm2} +\end{document} +^D +::: {.thm} +**Theorem 1**. *a* + +![image](1.png) +::: + +::: {.thm2} +**Theorem 1**. a + +![image](1.png) +::: +``` -- cgit v1.2.3 From c161893f442a3e001b64af1421e9f62376d71c92 Mon Sep 17 00:00:00 2001 From: Nils Carlson Date: Sat, 5 Dec 2020 18:00:04 +0000 Subject: OpenDocument writer: Allow references for internal links (#6774) This commit adds two extensions to the OpenDocument writer, `xrefs_name` and `xrefs_number`. Links to headings, figures and tables inside the document are substituted with cross-references that will use the name or caption of the referenced item for `xrefs_name` or the number for `xrefs_number`. For the `xrefs_number` to be useful heading numbers must be enabled in the generated document and table and figure captions must be enabled using for example the `native_numbering` extension. In order for numbers and reference text to be updated the generated document must be refreshed. Co-authored-by: Nils Carlson --- MANUAL.txt | 37 ++++++++++++++ src/Text/Pandoc/Extensions.hs | 4 ++ src/Text/Pandoc/Writers/OpenDocument.hs | 91 ++++++++++++++++++++++++++------- test/command/6774.md | 63 +++++++++++++++++++++++ 4 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 test/command/6774.md diff --git a/MANUAL.txt b/MANUAL.txt index fad885fed..4e1615ff2 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3027,6 +3027,43 @@ This extension can be enabled/disabled for the following formats: output formats : `odt`, `opendocument` +#### Extension: `xrefs_name` #### + +Links to headings, figures and tables inside the document are +substituted with cross-references that will use the name or caption +of the referenced item. The original link text is replaced once +the generated document is refreshed. This extension can be combined +with `xrefs_number` in which case numbers will appear before the +name. + +Text in cross-references is only made consistent with the referenced +item once the document has been refreshed. + +This extension can be enabled/disabled for the following formats: + +output formats +: `odt`, `opendocument` + +#### Extension: `xrefs_number` #### + +Links to headings, figures and tables inside the document are +substituted with cross-references that will use the number +of the referenced item. The original link text is discarded. +This extension can be combined with `xrefs_name` in which case +the name or caption numbers will appear after the number. + +For the `xrefs_number` to be useful heading numbers must be enabled +in the generated document, also table and figure captions must be enabled +using for example the `native_numbering` extension. + +Numbers in cross-references are only visible in the final document once +it has been refreshed. + +This extension can be enabled/disabled for the following formats: + +output formats +: `odt`, `opendocument` + #### Extension: `styles` #### {#ext-styles} When converting from docx, read all docx styles as divs (for diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 646f7abfb..a94e24e2c 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -149,6 +149,8 @@ data Extension = | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] + | Ext_xrefs_name -- ^ Use xrefs with names + | Ext_xrefs_number -- ^ Use xrefs with numbers | Ext_yaml_metadata_block -- ^ YAML metadata block | Ext_gutenberg -- ^ Use Project Gutenberg conventions for plain | Ext_attributes -- ^ Generic attribute syntax @@ -465,6 +467,8 @@ getAllExtensions f = universalExtensions <> getAll f getAll "opendocument" = extensionsFromList [ Ext_empty_paragraphs , Ext_native_numbering + , Ext_xrefs_name + , Ext_xrefs_number ] getAll "odt" = getAll "opendocument" <> autoIdExtensions getAll "muse" = autoIdExtensions <> diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 071a5542f..cf42f2228 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -17,6 +17,7 @@ module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) +import Data.Foldable (find) import Data.List (sortOn, sortBy, foldl') import qualified Data.Map as Map import Data.Maybe (fromMaybe, isNothing) @@ -35,6 +36,7 @@ import Text.DocLayout import Text.Pandoc.Shared (linesToPara, tshow, blocksToInlines) import Text.Pandoc.Templates (renderTemplate) import qualified Text.Pandoc.Translations as Term (Term(Figure, Table)) +import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import qualified Text.Pandoc.Writers.AnnotatedTable as Ann @@ -54,6 +56,11 @@ plainToPara x = x type OD m = StateT WriterState m +data ReferenceType + = HeaderRef + | TableRef + | ImageRef + data WriterState = WriterState { stNotes :: [Doc Text] , stTableStyles :: [Doc Text] @@ -69,6 +76,7 @@ data WriterState = , stImageId :: Int , stTableCaptionId :: Int , stImageCaptionId :: Int + , stIdentTypes :: [(Text,ReferenceType)] } defaultWriterState :: WriterState @@ -86,6 +94,7 @@ defaultWriterState = , stImageId = 1 , stTableCaptionId = 1 , stImageCaptionId = 1 + , stIdentTypes = [] } when :: Bool -> Doc Text -> Doc Text @@ -243,6 +252,12 @@ writeOpenDocument opts (Pandoc meta blocks) = do meta ((body, metadata),s) <- flip runStateT defaultWriterState $ do + let collectInlineIdent (Image (ident,_,_) _ _) = [(ident,ImageRef)] + collectInlineIdent _ = [] + let collectBlockIdent (Header _ (ident,_,_) _) = [(ident,HeaderRef)] + collectBlockIdent (Table (ident,_,_) _ _ _ _ _) = [(ident,TableRef)] + collectBlockIdent _ = [] + modify $ \s -> s{ stIdentTypes = query collectBlockIdent blocks ++ query collectInlineIdent blocks } m <- metaToContext opts (blocksToOpenDocument opts) (fmap chomp . inlinesToOpenDocument opts) @@ -411,7 +426,7 @@ blockToOpenDocument o bs inTags True "text:list" [ ("text:style-name", "L" <> tshow ln)] <$> orderedListToOpenDocument o pn b table :: PandocMonad m => Ann.Table -> OD m (Doc Text) - table (Ann.Table _ (Caption _ c) colspecs thead tbodies _) = do + table (Ann.Table (ident, _, _) (Caption _ c) colspecs thead tbodies _) = do tn <- length <$> gets stTableStyles pn <- length <$> gets stParaStyles let genIds = map chr [65..] @@ -433,7 +448,7 @@ blockToOpenDocument o bs then return empty else inlinesToOpenDocument o (blocksToInlines c) >>= if isEnabled Ext_native_numbering o - then numberedTableCaption + then numberedTableCaption ident else unNumberedCaption "TableCaption" th <- colHeadsToOpenDocument o (map fst paraHStyles) thead tr <- mapM (tableBodyToOpenDocument o (map fst paraStyles)) tbodies @@ -442,36 +457,39 @@ blockToOpenDocument o bs , ("table:style-name", name) ] (vcat columns $$ th $$ vcat tr) return $ captionDoc $$ tableDoc - figure attr caption source title | null caption = + figure attr@(ident, _, _) caption source title | null caption = withParagraphStyle o "Figure" [Para [Image attr caption (source,title)]] | otherwise = do imageDoc <- withParagraphStyle o "FigureWithCaption" [Para [Image attr caption (source,title)]] captionDoc <- inlinesToOpenDocument o caption >>= if isEnabled Ext_native_numbering o - then numberedFigureCaption + then numberedFigureCaption ident else unNumberedCaption "FigureCaption" return $ imageDoc $$ captionDoc -numberedTableCaption :: PandocMonad m => Doc Text -> OD m (Doc Text) -numberedTableCaption caption = do +numberedTableCaption :: PandocMonad m => Text -> Doc Text -> OD m (Doc Text) +numberedTableCaption ident caption = do id' <- gets stTableCaptionId modify (\st -> st{ stTableCaptionId = id' + 1 }) capterm <- translateTerm Term.Table - return $ numberedCaption "TableCaption" capterm "Table" id' caption + return $ numberedCaption "TableCaption" capterm "Table" id' ident caption -numberedFigureCaption :: PandocMonad m => Doc Text -> OD m (Doc Text) -numberedFigureCaption caption = do +numberedFigureCaption :: PandocMonad m => Text -> Doc Text -> OD m (Doc Text) +numberedFigureCaption ident caption = do id' <- gets stImageCaptionId modify (\st -> st{ stImageCaptionId = id' + 1 }) capterm <- translateTerm Term.Figure - return $ numberedCaption "FigureCaption" capterm "Illustration" id' caption + return $ numberedCaption "FigureCaption" capterm "Illustration" id' ident caption -numberedCaption :: Text -> Text -> Text -> Int -> Doc Text -> Doc Text -numberedCaption style term name num caption = +numberedCaption :: Text -> Text -> Text -> Int -> Text -> Doc Text -> Doc Text +numberedCaption style term name num ident caption = let t = text $ T.unpack term r = num - 1 - s = inTags False "text:sequence" [ ("text:ref-name", "ref" <> name <> tshow r), + ident' = case ident of + "" -> "ref" <> name <> tshow r + _ -> ident + s = inTags False "text:sequence" [ ("text:ref-name", ident'), ("text:name", name), ("text:formula", "ooow:" <> name <> "+1"), ("style:num-format", "1") ] $ text $ show num @@ -607,7 +625,9 @@ inlineToOpenDocument o ils else do report $ InlineNotRendered ils return empty - Link _ l (s,t) -> mkLink s t <$> inlinesToOpenDocument o l + Link _ l (s,t) -> do + identTypes <- gets stIdentTypes + mkLink o identTypes s t <$> inlinesToOpenDocument o l Image attr _ (s,t) -> mkImg attr s t Note l -> mkNote l where @@ -619,10 +639,6 @@ inlineToOpenDocument o ils unhighlighted s = inlinedCode $ preformatted s preformatted s = handleSpaces $ escapeStringForXML s inlinedCode s = return $ inTags False "text:span" [("text:style-name", "Source_Text")] s - mkLink s t = inTags False "text:a" [ ("xlink:type" , "simple") - , ("xlink:href" , s ) - , ("office:name", t ) - ] . inSpanTags "Definition" mkImg (_, _, kvs) s _ = do id' <- gets stImageId modify (\st -> st{ stImageId = id' + 1 }) @@ -659,6 +675,45 @@ inlineToOpenDocument o ils addNote nn return nn +mkLink :: WriterOptions -> [(Text,ReferenceType)] -> Text -> Text -> Doc Text -> Doc Text +mkLink o identTypes s t d = + let maybeIdentAndType = case T.uncons s of + Just ('#', ident) -> find ((ident ==) . fst) identTypes + _ -> Nothing + d' = inSpanTags "Definition" d + ref refType format ident = inTags False refType + [ ("text:reference-format", format ), + ("text:ref-name", ident) ] + inlineSpace = selfClosingTag "text:s" [] + bookmarkRef = ref "text:bookmark-ref" + bookmarkRefNumber ident = bookmarkRef "number" ident mempty + bookmarkRefName ident = bookmarkRef "text" ident d + bookmarkRefNameNumber ident = bookmarkRefNumber ident <> inlineSpace <> bookmarkRefName ident + bookmarkRef' + | isEnabled Ext_xrefs_number o && isEnabled Ext_xrefs_name o = bookmarkRefNameNumber + | isEnabled Ext_xrefs_name o = bookmarkRefName + | otherwise = bookmarkRefNumber + sequenceRef = ref "text:sequence-ref" + sequenceRefNumber ident = sequenceRef "value" ident mempty + sequenceRefName ident = sequenceRef "caption" ident d + sequenceRefNameNumber ident = sequenceRefNumber ident <> inlineSpace <> sequenceRefName ident + sequenceRef' + | isEnabled Ext_xrefs_number o && isEnabled Ext_xrefs_name o = sequenceRefNameNumber + | isEnabled Ext_xrefs_name o = sequenceRefName + | otherwise = sequenceRefNumber + link = inTags False "text:a" [ ("xlink:type" , "simple") + , ("xlink:href" , s ) + , ("office:name", t ) + ] d' + linkOrReference = case maybeIdentAndType of + Just (ident, HeaderRef) -> bookmarkRef' ident + Just (ident, TableRef) -> sequenceRef' ident + Just (ident, ImageRef) -> sequenceRef' ident + _ -> link + in if isEnabled Ext_xrefs_name o || isEnabled Ext_xrefs_number o + then linkOrReference + else link + bulletListStyle :: PandocMonad m => Int -> OD m (Int,(Int,[Doc Text])) bulletListStyle l = do let doStyles i = inTags True "text:list-level-style-bullet" diff --git a/test/command/6774.md b/test/command/6774.md new file mode 100644 index 000000000..66549c0f2 --- /dev/null +++ b/test/command/6774.md @@ -0,0 +1,63 @@ +``` +% pandoc -f native -t opendocument --quiet +[Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] +,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")]] +^D +The +Chapter +Chapter 1 references +The +Chapter +``` +``` +% pandoc -f native -t opendocument+xrefs_name --quiet +[Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] +,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] +,Para [Image ("lalune",[],[]) [Str "lalune"] ("lalune.jpg","fig:Voyage dans la Lune")] +,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] +^D +The +Chapter +Chapter 1 references +The +Chapter + +lalune +Image 1 references +La +Lune +``` +``` +% pandoc -f native -t opendocument+xrefs_number --quiet +[Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] +,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] +,Para [Image ("lalune",[],[]) [Str "lalune"] ("lalune.jpg","fig:Voyage dans la Lune")] +,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] +^D +The +Chapter +Chapter 1 references + + +lalune +Image 1 references + +``` +``` +% pandoc -f native -t opendocument+xrefs_number+xrefs_name --quiet +[Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] +,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] +,Para [Image ("lalune",[],[]) [Str "lalune"] ("lalune.jpg","fig:Voyage dans la Lune")] +,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] +^D +The +Chapter +Chapter 1 references +The +Chapter + +lalune +Image 1 references +La +Lune +``` -- cgit v1.2.3 From acf932825bfe40d9a18046c9d304f4f14363a88a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 5 Dec 2020 22:05:37 +0100 Subject: Org reader: preserve targets of spurious links Links with (internal) targets that the reader doesn't know about are converted into emphasized text. Information on the link target is now preserved by wrapping the text in a Span of class `spurious-link`, with an attribute `target` set to the link's original target. This allows to recover and fix broken or unknown links with filters. See: #6916 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 9 ++++----- test/Tests/Readers/Org/Meta.hs | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index b234bee58..0330cf55f 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -477,17 +477,17 @@ linkToInlinesF linkStr = internalLink :: Text -> Inlines -> F Inlines internalLink link title = do - anchorB <- (link `elem`) <$> asksF orgStateAnchorIds - if anchorB + ids <- asksF orgStateAnchorIds + if link `elem` ids then return $ B.link ("#" <> link) "" title - else return $ B.emph title + else let attr' = ("", ["spurious-link"] , [("target", link)]) + in return $ B.spanWith attr' (B.emph title) -- | Parse an anchor like @<>@ and return an empty span with -- @anchor-id@ set as id. Legal anchors in org-mode are defined through -- @org-target-regexp@, which is fairly liberal. Since no link is created if -- @anchor-id@ contains spaces, we are more restrictive in what is accepted as -- an anchor. - anchor :: PandocMonad m => OrgParser m (F Inlines) anchor = try $ do anchorId <- parseAnchor @@ -501,7 +501,6 @@ anchor = try $ do -- | Replace every char but [a-zA-Z0-9_.-:] with a hyphen '-'. This mirrors -- the org function @org-export-solidify-link-text@. - solidify :: Text -> Text solidify = T.map replaceSpecialChar where replaceSpecialChar c diff --git a/test/Tests/Readers/Org/Meta.hs b/test/Tests/Readers/Org/Meta.hs index 041016f64..bc167f2a5 100644 --- a/test/Tests/Readers/Org/Meta.hs +++ b/test/Tests/Readers/Org/Meta.hs @@ -270,7 +270,8 @@ tests = , "Search links are read as emph" =: "[[Wally][Where's Wally?]]" =?> - para (emph $ "Where's" <> space <> "Wally?") + para (spanWith ("", ["spurious-link"], [("target", "Wally")]) + (emph $ "Where's" <> space <> "Wally?")) , "Link to nonexistent anchor" =: T.unlines [ "<> Target." @@ -278,5 +279,6 @@ tests = , "[[link$here][See here!]]" ] =?> (para (spanWith ("link-here", [], []) mempty <> "Target.") <> - para (emph ("See" <> space <> "here!"))) + para (spanWith ("", ["spurious-link"], [("target", "link$here")]) + (emph ("See" <> space <> "here!")))) ] -- cgit v1.2.3 From dc6856530c2cb6ca58ed82721ab895b86cfe0c1c Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 4 Dec 2020 09:16:56 +0100 Subject: Docbook writer: handle admonitions Similarly to https://github.com/jgm/pandoc/commit/d6fdfe6f2bba2a8ed25d6c9f11861774001f7a91, we should handle admonitions. --- src/Text/Pandoc/Writers/Docbook.hs | 42 +++++++++++++++++++++--------- test/Tests/Writers/Docbook.hs | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 3f4c67f10..da111cbc5 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -188,18 +188,36 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do title' <- inlinesToDocbook opts ils contents <- blocksToDocbook opts bs return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents -blockToDocbook opts (Div (ident,_,_) [Para lst]) = - let attribs = [("id", ident) | not (T.null ident)] in - if hasLineBreaks lst - then flush . nowrap . inTags False "literallayout" attribs - <$> inlinesToDocbook opts lst - else inTags True "para" attribs <$> inlinesToDocbook opts lst -blockToDocbook opts (Div (ident,_,_) bs) = do - contents <- blocksToDocbook opts (map plainToPara bs) - return $ - (if T.null ident - then mempty - else selfClosingTag "anchor" [("id", ident)]) $$ contents +blockToDocbook opts (Div (ident,classes,_) bs) = + let identAttribs = [("id", ident) | not (T.null ident)] + admonitions = ["attention","caution","danger","error","hint", + "important","note","tip","warning"] + in case classes of + (l:_) | l `elem` admonitions -> do + let (mTitleBs, bodyBs) = + case bs of + -- Matches AST produced by the Docbook reader. + (Div (_,["title"],_) ts : rest) -> (Just (blocksToDocbook opts ts), rest) + _ -> (Nothing, bs) + admonitionTitle <- case mTitleBs of + Nothing -> return mempty + -- id will be attached to the admonition so let’s pass empty identAttrs. + Just titleBs -> inTags False "title" [] <$> titleBs + admonitionBody <- handleDivBody [] bodyBs + return (inTags True l identAttribs (admonitionTitle $$ admonitionBody)) + _ -> handleDivBody identAttribs bs + where + handleDivBody identAttribs [Para lst] = + if hasLineBreaks lst + then flush . nowrap . inTags False "literallayout" identAttribs + <$> inlinesToDocbook opts lst + else inTags True "para" identAttribs <$> inlinesToDocbook opts lst + handleDivBody identAttribs bodyBs = do + contents <- blocksToDocbook opts (map plainToPara bodyBs) + return $ + (if null identAttribs + then mempty + else selfClosingTag "anchor" identAttribs) $$ contents blockToDocbook _ h@Header{} = do -- should be handled by Div section above, except inside lists/blockquotes report $ BlockNotRendered h diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index f6a047b0b..1d53dcfe7 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -70,6 +70,58 @@ tests = [ testGroup "line blocks" , "" ] ) ] + , testGroup "divs" + [ "admonition" =: divWith ("foo", ["warning"], []) (para "This is a test") + =?> unlines + [ "" + , " " + , " This is a test" + , " " + , "" + ] + , "admonition-with-title" =: + divWith ("foo", ["attention"], []) ( + divWith ("foo", ["title"], []) + (plain (text "This is title")) <> + para "This is a test" + ) + =?> unlines + [ "" + , " This is title" + , " " + , " This is a test" + , " " + , "" + ] + , "single-child" =: + divWith ("foo", [], []) (para "This is a test") + =?> unlines + [ "" + , " This is a test" + , "" + ] + , "single-literal-child" =: + divWith ("foo", [], []) lineblock + =?> unlines + [ "some text" + , "and more lines" + , "and again" + ] + , "multiple-children" =: + divWith ("foo", [], []) ( + para "This is a test" <> + para "This is an another test" + ) + =?> unlines + [ "" + , "" + , " This is a test" + , "" + , "" + , " This is an another test" + , "" + ] + ] , testGroup "compact lists" [ testGroup "bullet" [ "compact" =: bulletList [plain "a", plain "b", plain "c"] -- cgit v1.2.3 From 16ef87745702f69d5aa948fbe6d2101577dee8f4 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 4 Dec 2020 09:28:32 +0100 Subject: Docbook writer: Use correct id attribute consistently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DocBook5 should always use xml:id instead of id so let’s use it everywhere. --- src/Text/Pandoc/Writers/Docbook.hs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index da111cbc5..398920839 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -53,6 +53,13 @@ getStartLvl opts = TopLevelSection -> 1 TopLevelDefault -> 1 +-- | Get correct name for the id attribute based on DocBook version. +-- DocBook 4 used custom id attribute but DocBook 5 adopted the xml:id specification. +-- https://www.w3.org/TR/xml-id/ +idName :: DocBookVersion -> Text +idName DocBook5 = "xml:id" +idName DocBook4 = "id" + -- | Convert list of authors to a docbook section authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines authorToDocbook opts name' = do @@ -174,10 +181,7 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do then "section" else "sect" <> tshow n _ -> "simplesect" - idName = if version == DocBook5 - then "xml:id" - else "id" - idAttr = [(idName, writerIdentifierPrefix opts <> id') | not (T.null id')] + idAttr = [(idName version, writerIdentifierPrefix opts <> id') | not (T.null id')] -- We want to add namespaces to the root (top-level) element. nsAttr = if version == DocBook5 && lvl == getStartLvl opts && isNothing (writerTemplate opts) -- Though, DocBook 4 does not support namespaces and @@ -188,11 +192,12 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do title' <- inlinesToDocbook opts ils contents <- blocksToDocbook opts bs return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents -blockToDocbook opts (Div (ident,classes,_) bs) = - let identAttribs = [("id", ident) | not (T.null ident)] +blockToDocbook opts (Div (ident,classes,_) bs) = do + version <- ask + let identAttribs = [(idName version, ident) | not (T.null ident)] admonitions = ["attention","caution","danger","error","hint", "important","note","tip","warning"] - in case classes of + case classes of (l:_) | l `elem` admonitions -> do let (mTitleBs, bodyBs) = case bs of @@ -371,11 +376,12 @@ inlineToDocbook opts (Quoted _ lst) = inTagsSimple "quote" <$> inlinesToDocbook opts lst inlineToDocbook opts (Cite _ lst) = inlinesToDocbook opts lst -inlineToDocbook opts (Span (ident,_,_) ils) = +inlineToDocbook opts (Span (ident,_,_) ils) = do + version <- ask ((if T.null ident then mempty - else selfClosingTag "anchor" [("id", ident)]) <>) <$> - inlinesToDocbook opts ils + else selfClosingTag "anchor" [(idName version, ident)]) <>) <$> + inlinesToDocbook opts ils inlineToDocbook _ (Code _ str) = return $ inTagsSimple "literal" $ literal (escapeStringForXML str) inlineToDocbook opts (Math t str) -- cgit v1.2.3 From 70c7c5703afcbd1cbf2a80c2be515e038abcd419 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 7 Dec 2020 07:28:39 +0100 Subject: Docbook writer: Handle admonition titles from Markdown reader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docbook reader produces a `Div` with `title` class for `` element within an “admonition” element. Markdown writer then turns this into a fenced div with `title` class attribute. Since fenced divs are block elements, their content is recognized as a paragraph by the Markdown reader. This is an issue for Docbook writer because it would produce an invalid DocBook document from such AST – the `<title>` element can only contain “inline” elements. Let’s handle this invalid special case separately by unwrapping the paragraph before creating the `<title>` element. --- src/Text/Pandoc/Writers/Docbook.hs | 2 ++ test/Tests/Writers/Docbook.hs | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 398920839..affa0de04 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -201,6 +201,8 @@ blockToDocbook opts (Div (ident,classes,_) bs) = do (l:_) | l `elem` admonitions -> do let (mTitleBs, bodyBs) = case bs of + -- Matches AST produced by the DocBook reader → Markdown writer → Markdown reader chain. + (Div (_,["title"],_) [Para ts] : rest) -> (Just (inlinesToDocbook opts ts), rest) -- Matches AST produced by the Docbook reader. (Div (_,["title"],_) ts : rest) -> (Just (blocksToDocbook opts ts), rest) _ -> (Nothing, bs) diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index 1d53dcfe7..621c1280b 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -93,6 +93,20 @@ tests = [ testGroup "line blocks" , " </para>" , "</attention>" ] + , "admonition-with-title-in-para" =: + divWith ("foo", ["attention"], []) ( + divWith ("foo", ["title"], []) + (para "This is title") <> + para "This is a test" + ) + =?> unlines + [ "<attention id=\"foo\">" + , " <title>This is title" + , " " + , " This is a test" + , " " + , "" + ] , "single-child" =: divWith ("foo", [], []) (para "This is a test") =?> unlines -- cgit v1.2.3 From 501ea7f0c4735acdf1457da44fe04d811ac776d7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 12:15:14 -0800 Subject: Dokuwiki reader: handle unknown interwiki links better. DokuWiki lets the user define his own Interwiki links. Previously pandoc reacted to these by emitting a google search link, which is not helpful. Instead, we now just emit the full URL including the wikilink prefix, e.g. `faquk>FAQ-mathml`. This at least gives users the ability to modify the links using filters. Closes #6932. --- src/Text/Pandoc/Readers/DokuWiki.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/DokuWiki.hs b/src/Text/Pandoc/Readers/DokuWiki.hs index 336be09e5..dedc1f03f 100644 --- a/src/Text/Pandoc/Readers/DokuWiki.hs +++ b/src/Text/Pandoc/Readers/DokuWiki.hs @@ -317,7 +317,7 @@ interwikiToUrl "wpes" page = "https://es.wikipedia.org/wiki/" <> page interwikiToUrl "wpfr" page = "https://fr.wikipedia.org/wiki/" <> page interwikiToUrl "wpjp" page = "https://jp.wikipedia.org/wiki/" <> page interwikiToUrl "wppl" page = "https://pl.wikipedia.org/wiki/" <> page -interwikiToUrl _ page = "https://www.google.com/search?q=" <> page <> "&btnI=lucky" +interwikiToUrl unknown page = unknown <> ">" <> page linkText :: PandocMonad m => DWParser m B.Inlines linkText = parseLink fromRaw "[[" "]]" -- cgit v1.2.3 From f2749ba6cd0ec2473332394bfbb6f479b667f35c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 12:56:03 -0800 Subject: Parsing: in nonspaceChar use satisfy instead of oneOf. For efficiency. --- src/Text/Pandoc/Parsing.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 4bae8942b..1d9e182c5 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -443,7 +443,13 @@ spaceChar = satisfy $ \c -> c == ' ' || c == '\t' -- | Parses a nonspace, nonnewline character. nonspaceChar :: Stream s m Char => ParserT s st m Char -nonspaceChar = noneOf ['\t', '\n', ' ', '\r'] +nonspaceChar = satisfy (not . isSpaceChar) + where + isSpaceChar ' ' = True + isSpaceChar '\t' = True + isSpaceChar '\n' = True + isSpaceChar '\r' = True + isSpaceChar _ = False -- | Skips zero or more spaces or tabs. skipSpaces :: Stream s m Char => ParserT s st m () -- cgit v1.2.3 From 2f9b684b3a793896bc28a79a07722415cfdc075e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 13:01:30 -0800 Subject: Bibtex parser: avoid noneOf. --- src/Text/Pandoc/Citeproc/BibTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 552339df0..ed723a11c 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -601,7 +601,7 @@ inBraces :: BibParser Text inBraces = do char '{' res <- manyTill - ( (T.pack <$> many1 (noneOf "{}\\")) + ( take1WhileP (\c -> c /= '{' && c /= '}' && c /= '\\') <|> (char '\\' >> ( (char '{' >> return "\\{") <|> (char '}' >> return "\\}") <|> return "\\")) @@ -616,7 +616,7 @@ inQuotes :: BibParser Text inQuotes = do char '"' T.concat <$> manyTill - ( (T.pack <$> many1 (noneOf "\"\\{")) + ( take1WhileP (\c -> c /= '{' && c /= '"' && c /= '\\') <|> (char '\\' >> T.cons '\\' . T.singleton <$> anyChar) <|> braced <$> inBraces ) (char '"') -- cgit v1.2.3 From ce1791913da713ec35f514006bb532cb9c9c8d22 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 13:24:19 -0800 Subject: Small efficiency improvement in uri parser --- src/Text/Pandoc/Parsing.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 1d9e182c5..1e8518c90 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -654,7 +654,20 @@ uri = try $ do let uri' = scheme <> ":" <> fromEntities str' return (uri', escapeURI uri') where - wordChar = alphaNum <|> oneOf "#$%+/@\\_-&=" + isWordChar '#' = True + isWordChar '$' = True + isWordChar '%' = True + isWordChar '+' = True + isWordChar '/' = True + isWordChar '@' = True + isWordChar '\\' = True + isWordChar '_' = True + isWordChar '-' = True + isWordChar '&' = True + isWordChar '=' = True + isWordChar c = isAlphaNum c + + wordChar = satisfy isWordChar percentEscaped = try $ (:) <$> char '%' <*> many1 hexDigit entity = try $ pure <$> characterReference punct = try $ many1 (char ',') <|> fmap pure (satisfy (\c -> not (isSpace c) && c /= '<' && c /= '>')) -- cgit v1.2.3 From 0fa1023b9ef0f39be013befa9ba3d61b81d95928 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 18:57:09 -0800 Subject: Parsing: More minor performance improvements. --- src/Text/Pandoc/Parsing.hs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 1e8518c90..4ea0d788c 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -190,7 +190,7 @@ where import Control.Monad.Identity import Control.Monad.Reader -import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, +import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isAsciiLower, isPunctuation, isSpace, ord, toLower, toUpper) import Data.Default import Data.Functor (($>)) @@ -444,12 +444,13 @@ spaceChar = satisfy $ \c -> c == ' ' || c == '\t' -- | Parses a nonspace, nonnewline character. nonspaceChar :: Stream s m Char => ParserT s st m Char nonspaceChar = satisfy (not . isSpaceChar) - where - isSpaceChar ' ' = True - isSpaceChar '\t' = True - isSpaceChar '\n' = True - isSpaceChar '\r' = True - isSpaceChar _ = False + +isSpaceChar :: Char -> Bool +isSpaceChar ' ' = True +isSpaceChar '\t' = True +isSpaceChar '\n' = True +isSpaceChar '\r' = True +isSpaceChar _ = False -- | Skips zero or more spaces or tabs. skipSpaces :: Stream s m Char => ParserT s st m () @@ -682,7 +683,9 @@ mathInlineWith :: Stream s m Char => Text -> Text -> ParserT s st m Text mathInlineWith op cl = try $ do textStr op when (op == "$") $ notFollowedBy space - words' <- many1Till (countChar 1 (noneOf " \t\n\\") + words' <- many1Till ( + (T.singleton <$> + satisfy (\c -> not (isSpaceChar c || c == '\\'))) <|> (char '\\' >> -- This next clause is needed because \text{..} can -- contain $, \(\), etc. @@ -840,13 +843,13 @@ defaultNum = do -- | Parses a lowercase letter and returns (LowerAlpha, number). lowerAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) lowerAlpha = do - ch <- oneOf ['a'..'z'] + ch <- satisfy isAsciiLower return (LowerAlpha, ord ch - ord 'a' + 1) -- | Parses an uppercase letter and returns (UpperAlpha, number). upperAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) upperAlpha = do - ch <- oneOf ['A'..'Z'] + ch <- satisfy isAsciiUpper return (UpperAlpha, ord ch - ord 'A' + 1) -- | Parses a roman numeral i or I -- cgit v1.2.3 From 8031ac137f9f84bf6c12d66592b07a3244b049a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 18:57:24 -0800 Subject: LaTeX template: include csquotes package if csquotes variable set. --- data/templates/default.latex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/templates/default.latex b/data/templates/default.latex index 60af51225..169661582 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -391,6 +391,9 @@ $if(csl-refs)$ \newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break} \newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} $endif$ +$if(csquotes)$ +\usepackage{csquotes} +$endif$ $if(title)$ \title{$title$$if(thanks)$\thanks{$thanks$}$endif$} -- cgit v1.2.3 From 5990cbb150f3581ab9b2f3d3a726c73dcadfd793 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 7 Dec 2020 21:34:23 -0800 Subject: Parsing: Small code improvements. --- src/Text/Pandoc/Parsing.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 4ea0d788c..979344f63 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -641,7 +641,7 @@ uri = try $ do scheme <- uriScheme char ':' -- Avoid parsing e.g. "**Notes:**" as a raw URI: - notFollowedBy (oneOf "*_]") + notFollowedBy $ satisfy (\c -> c == '*' || c == '_' || c == ']') -- We allow sentence punctuation except at the end, since -- we don't want the trailing '.' in 'http://google.com.' We want to allow -- http://en.wikipedia.org/wiki/State_of_emergency_(disambiguation) @@ -693,7 +693,7 @@ mathInlineWith op cl = try $ do (("\\text" <>) <$> inBalancedBraces 0 "")) <|> (\c -> T.pack ['\\',c]) <$> anyChar)) <|> do (blankline <* notFollowedBy' blankline) <|> - (oneOf " \t" <* skipMany (oneOf " \t")) + (spaceChar <* skipMany spaceChar) notFollowedBy (char '$') return " " ) (try $ textStr cl) @@ -723,7 +723,8 @@ mathInlineWith op cl = try $ do mathDisplayWith :: Stream s m Char => Text -> Text -> ParserT s st m Text mathDisplayWith op cl = try $ fmap T.pack $ do textStr op - many1Till (noneOf "\n" <|> (newline <* notFollowedBy' blankline)) (try $ textStr cl) + many1Till (satisfy (/= '\n') <|> (newline <* notFollowedBy' blankline)) + (try $ textStr cl) mathDisplay :: (HasReaderOptions st, Stream s m Char) => ParserT s st m Text -- cgit v1.2.3 From d73ef09c84f4be5c5dfe80c574083e4d50ec1a36 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 8 Dec 2020 15:14:34 -0800 Subject: Document that --number-sections works in ms. Closes #6935. --- MANUAL.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 4e1615ff2..0a1300947 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1000,8 +1000,8 @@ header when requesting a document from a URL: `-N`, `--number-sections` -: Number section headings in LaTeX, ConTeXt, HTML, Docx, or EPUB output. - By default, sections are not numbered. Sections with class +: Number section headings in LaTeX, ConTeXt, HTML, Docx, ms, or EPUB + output. By default, sections are not numbered. Sections with class `unnumbered` will never be numbered, even if `--number-sections` is specified. -- cgit v1.2.3 From 0dd228593f0d1e70f975685fabd66f3555d1a70f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 9 Dec 2020 09:34:15 -0800 Subject: Use latest citeproc release. --- pandoc.cabal | 2 +- stack.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index b95446883..c02dfeb38 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -400,7 +400,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.2 && < 0.3, + citeproc >= 0.2.0.1 && < 0.3, commonmark >= 0.1.1.2 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/stack.yaml b/stack.yaml index 7a849b713..ef0bc7a83 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,10 +17,10 @@ extra-deps: - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 - doctemplates-0.8.3 -#- citeproc-0.2 -- citeproc: - git: https://github.com/jgm/citeproc.git - commit: 42b1d154b02435229acbe98ae0f17d01b757ee93 +- citeproc-0.2.0.1 +# - citeproc: +# git: https://github.com/jgm/citeproc.git +# commit: 42b1d154b02435229acbe98ae0f17d01b757ee93 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 -- cgit v1.2.3 From b15191aece326b54db08c737808cf7a01e1df1ad Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 10 Dec 2020 08:11:05 +0100 Subject: cabal.project: disable installation of citeproc from source --- cabal.project | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cabal.project b/cabal.project index ad1bcce55..f6422d81a 100644 --- a/cabal.project +++ b/cabal.project @@ -4,8 +4,8 @@ package pandoc flags: +embed_data_files -trypandoc ghc-options: -j +RTS -A64m -RTS -source-repository-package - type: git - location: https://github.com/jgm/citeproc - tag: 42b1d154b02435229acbe98ae0f17d01b757ee93 +-- source-repository-package +-- type: git +-- location: https://github.com/jgm/citeproc +-- tag: 42b1d154b02435229acbe98ae0f17d01b757ee93 -- cgit v1.2.3 From 8c9010864cd818031d7eff161a57459709751517 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 9 Dec 2020 21:05:40 -0800 Subject: Commonmark reader: refactor specFor, set input name to "". --- src/Text/Pandoc/Readers/CommonMark.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index c1773eaab..d32a38342 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Readers.CommonMark @@ -27,15 +28,20 @@ import Text.Pandoc.Options import Text.Pandoc.Error import Control.Monad.Except import Data.Functor.Identity (runIdentity) +import Data.Typeable -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = do - let res = runIdentity $ - commonmarkWith (foldr ($) defaultSyntaxSpec exts) "input" s + let res = runIdentity $ commonmarkWith (specFor opts) "" s case res of Left err -> throwError $ PandocParsecError s err Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls + +specFor :: (Monad m, Typeable m, Typeable a, + Rangeable (Cm a Inlines), Rangeable (Cm a Blocks)) + => ReaderOptions -> SyntaxSpec m (Cm a Inlines) (Cm a Blocks) +specFor opts = foldr ($) defaultSyntaxSpec exts where exts = [ (hardLineBreaksSpec <>) | isEnabled Ext_hard_line_breaks opts ] ++ [ (smartPunctuationSpec <>) | isEnabled Ext_smart opts ] ++ -- cgit v1.2.3 From a3eb87b2eab9def3e28364b43300043f5e13268d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 9 Dec 2020 21:14:11 -0800 Subject: Add sourcepos extension for commonmarke * Add `Ext_sourcepos` constructor for `Extension`. * Add `sourcepos` extension (only for commonmark). * Bump to 2.11.3 With the `sourcepos` extension set set, `data-pos` attributes are added to the AST by the commonmark reader. No other readers are affected. The `data-pos` attributes are put on elements that accept attributes; for other elements, an enlosing Div or Span is added to hold the attributes. Closes #4565. --- MANUAL.txt | 7 +++++++ pandoc.cabal | 2 +- src/Text/Pandoc/Extensions.hs | 2 ++ src/Text/Pandoc/Options.hs | 1 + src/Text/Pandoc/Readers/CommonMark.hs | 14 +++++++++----- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 0a1300947..461ebf54d 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5127,6 +5127,13 @@ for regular emphasis, add extra blank space around headings. [Project Gutenberg]: https://www.gutenberg.org +#### Extension: `sourcepos` #### + +Include source position attributes when parsing `commonmark`. +For elements that accept attributes, a `data-pos` attribute +is added; other elements are placed in a surrounding +Div or Span elemnet with a `data-pos` attribute. + ## Markdown variants In addition to pandoc's extended Markdown, the following Markdown diff --git a/pandoc.cabal b/pandoc.cabal index c02dfeb38..5829856da 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.11.2 +version: 2.11.3 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index a94e24e2c..9865f897b 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -154,6 +154,7 @@ data Extension = | Ext_yaml_metadata_block -- ^ YAML metadata block | Ext_gutenberg -- ^ Use Project Gutenberg conventions for plain | Ext_attributes -- ^ Generic attribute syntax + | Ext_sourcepos -- ^ Include source position attributes deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. @@ -503,6 +504,7 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_implicit_header_references , Ext_attributes , Ext_fenced_code_attributes + , Ext_sourcepos ] getAll "commonmark_x" = getAll "commonmark" getAll "org" = autoIdExtensions <> diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index c7f1a56fa..ecd65a54d 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -65,6 +65,7 @@ data ReaderOptions = ReaderOptions{ , readerDefaultImageExtension :: Text -- ^ Default extension for images , readerTrackChanges :: TrackChanges -- ^ Track changes setting for docx , readerStripComments :: Bool -- ^ Strip HTML comments instead of parsing as raw HTML + -- (only implemented in commonmark) } deriving (Show, Read, Data, Typeable, Generic) instance HasSyntaxExtensions ReaderOptions where diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index d32a38342..9eef498e1 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -32,11 +32,15 @@ import Data.Typeable -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readCommonMark opts s = do - let res = runIdentity $ commonmarkWith (specFor opts) "" s - case res of - Left err -> throwError $ PandocParsecError s err - Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls +readCommonMark opts s + | isEnabled Ext_sourcepos opts = + case runIdentity (commonmarkWith (specFor opts) "" s) of + Left err -> throwError $ PandocParsecError s err + Right (Cm bls :: Cm SourceRange Blocks) -> return $ B.doc bls + | otherwise = + case runIdentity (commonmarkWith (specFor opts) "" s) of + Left err -> throwError $ PandocParsecError s err + Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls specFor :: (Monad m, Typeable m, Typeable a, Rangeable (Cm a Inlines), Rangeable (Cm a Blocks)) -- cgit v1.2.3 From 1fd642dd30f92ceb6298369f2e1b6b7e9b67c665 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Dec 2020 10:08:24 -0800 Subject: Move executable to app directory. Otherwise we have problems with cabal repl. --- app/pandoc.hs | 22 ++++++++++++++++++++++ pandoc.cabal | 2 +- pandoc.hs | 22 ---------------------- 3 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 app/pandoc.hs delete mode 100644 pandoc.hs diff --git a/app/pandoc.hs b/app/pandoc.hs new file mode 100644 index 000000000..9ed3b9e9f --- /dev/null +++ b/app/pandoc.hs @@ -0,0 +1,22 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{- | + Module : Main + Copyright : Copyright (C) 2006-2020 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Parses command-line options and calls the appropriate readers and +writers. +-} +module Main where +import Prelude +import qualified Control.Exception as E +import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions) +import Text.Pandoc.Error (handleError) + +main :: IO () +main = E.catch (parseOptions options defaultOpts >>= convertWithOpts) + (handleError . Left) diff --git a/pandoc.cabal b/pandoc.cabal index 5829856da..2e447a553 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -683,7 +683,7 @@ executable pandoc build-depends: base-compat >= 0.9 default-language: Haskell2010 other-extensions: NoImplicitPrelude - hs-source-dirs: . + hs-source-dirs: app main-is: pandoc.hs buildable: True other-modules: Paths_pandoc diff --git a/pandoc.hs b/pandoc.hs deleted file mode 100644 index 9ed3b9e9f..000000000 --- a/pandoc.hs +++ /dev/null @@ -1,22 +0,0 @@ -{-# LANGUAGE NoImplicitPrelude #-} -{- | - Module : Main - Copyright : Copyright (C) 2006-2020 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - -Parses command-line options and calls the appropriate readers and -writers. --} -module Main where -import Prelude -import qualified Control.Exception as E -import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions) -import Text.Pandoc.Error (handleError) - -main :: IO () -main = E.catch (parseOptions options defaultOpts >>= convertWithOpts) - (handleError . Left) -- cgit v1.2.3 From 9af56641817c231e6ef4098b44883df551c40436 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Dec 2020 10:08:44 -0800 Subject: RELEASE-CHECKLIST: add suggestion to use policeman. --- RELEASE-CHECKLIST | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-CHECKLIST b/RELEASE-CHECKLIST index 8cf51b3db..ebca152b1 100644 --- a/RELEASE-CHECKLIST +++ b/RELEASE-CHECKLIST @@ -1,7 +1,7 @@ +[ ] use 'policeman' to check API changes and version update [ ] make README.md and commit if needed [ ] make man/pandoc.1 and commit if needed [ ] Finalize changelog - tools/changelog-helper.sh [ ] push release candidate branch rc/VERSION and (if it builds successfully) download artifacts [ ] make macospkg to fetch macos package (built on travis) -- cgit v1.2.3 From 248a2a1db5cb567499a9272a2a2f2390c13d9275 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Dec 2020 10:27:31 -0800 Subject: cabal: remove -Werror=missing-home-modules. It causes problems using cabal repl. --- pandoc.cabal | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 2e447a553..7f8df272f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -477,7 +477,6 @@ library if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths default-language: Haskell2010 @@ -696,7 +695,6 @@ executable pandoc if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths executable trypandoc @@ -723,7 +721,6 @@ executable trypandoc if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths benchmark weigh-pandoc @@ -750,7 +747,6 @@ benchmark weigh-pandoc if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths @@ -857,7 +853,6 @@ test-suite test-pandoc if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths @@ -888,5 +883,4 @@ benchmark benchmark-pandoc if impl(ghc >= 8.4) ghc-options: -Wincomplete-uni-patterns -Widentities - -Werror=missing-home-modules -fhide-source-paths -- cgit v1.2.3 From 0a502e5ff52b251bbf3da69fd1f9a88d5e0fe92c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Dec 2020 15:44:10 -0800 Subject: HTML reader: retain attribute prefixes and avoid duplicates. Previously we stripped attribute prefixes, reading `xml:lang` as `lang` for example. This resulted in two duplicate `lang` attributes when `xml:lang` and `lang` were both used. This commit causes the prefixes to be retained, and also avoids invald duplicate attributes. Closes #6938. --- src/Text/Pandoc/Readers/HTML.hs | 28 +++++++++++----------------- src/Text/Pandoc/Readers/HTML/Parsing.hs | 20 +++++++++++++------- test/command/5986.md | 2 +- test/epub/wasteland.native | 8 ++++---- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index eb78979a3..f870a241d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -74,7 +74,7 @@ readHtml :: PandocMonad m -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readHtml opts inp = do - let tags = stripPrefixes . canonicalizeTags $ + let tags = stripPrefixes $ canonicalizeTags $ parseTagsOptions parseOptions{ optTagPosition = True } (crFilter inp) parseDoc = do @@ -95,6 +95,15 @@ readHtml opts inp = do Right doc -> return doc Left err -> throwError $ PandocParseError $ T.pack $ getError err +-- Strip namespace prefixes on tags (not attributes) +stripPrefixes :: [Tag Text] -> [Tag Text] +stripPrefixes = map stripPrefix + +stripPrefix :: Tag Text -> Tag Text +stripPrefix (TagOpen s as) = TagOpen (T.takeWhileEnd (/=':') s) as +stripPrefix (TagClose s) = TagClose (T.takeWhileEnd (/=':') s) +stripPrefix x = x + replaceNotes :: PandocMonad m => [Block] -> TagParser m [Block] replaceNotes bs = do st <- getState @@ -114,7 +123,7 @@ setInPlain = local (\s -> s {inPlain = True}) pHtml :: PandocMonad m => TagParser m Blocks pHtml = try $ do (TagOpen "html" attr) <- lookAhead pAny - for_ (lookup "lang" attr) $ + for_ (lookup "lang" attr <|> lookup "xml:lang" attr) $ updateState . B.setMeta "lang" . B.text pInTags "html" block @@ -1024,21 +1033,6 @@ htmlTag f = try $ do handleTag tagname _ -> mzero --- Strip namespace prefixes -stripPrefixes :: [Tag Text] -> [Tag Text] -stripPrefixes = map stripPrefix - -stripPrefix :: Tag Text -> Tag Text -stripPrefix (TagOpen s as) = - TagOpen (stripPrefix' s) (map (first stripPrefix') as) -stripPrefix (TagClose s) = TagClose (stripPrefix' s) -stripPrefix x = x - -stripPrefix' :: Text -> Text -stripPrefix' s = - if T.null t then s else T.drop 1 t - where (_, t) = T.span (/= ':') s - -- Utilities -- | Adjusts a url according to the document's base URL. diff --git a/src/Text/Pandoc/Readers/HTML/Parsing.hs b/src/Text/Pandoc/Readers/HTML/Parsing.hs index 2d58319da..e28ebe77b 100644 --- a/src/Text/Pandoc/Readers/HTML/Parsing.hs +++ b/src/Text/Pandoc/Readers/HTML/Parsing.hs @@ -193,14 +193,20 @@ t1 `closes` t2 | _ `closes` _ = False toStringAttr :: [(Text, Text)] -> [(Text, Text)] -toStringAttr = map go +toStringAttr = foldr go [] where - go (x,y) = - case T.stripPrefix "data-" x of - Just x' | x' `Set.notMember` (html5Attributes <> - html4Attributes <> rdfaAttributes) - -> (x',y) - _ -> (x,y) + go :: (Text, Text) -> [(Text, Text)] -> [(Text, Text)] + -- treat xml:lang as lang + go ("xml:lang",y) ats = go ("lang",y) ats + -- prevent duplicate attributes + go (x,y) ats + | any (\(x',_) -> x == x') ats = ats + | otherwise = + case T.stripPrefix "data-" x of + Just x' | x' `Set.notMember` (html5Attributes <> + html4Attributes <> rdfaAttributes) + -> go (x',y) ats + _ -> (x,y):ats -- Unlike fromAttrib from tagsoup, this distinguishes -- between a missing attribute and an attribute with empty content. diff --git a/test/command/5986.md b/test/command/5986.md index ea0ca70c1..ed8dd30c9 100644 --- a/test/command/5986.md +++ b/test/command/5986.md @@ -4,7 +4,7 @@ ^D

-
+ + + + + + + +
+
a1 a2
+
b
c d
+ + +^D +[width="100%",cols="50%,50%",] +|=== +a| +[cols=",",] +!=== +!a1 !a2 +!=== + +|b +|c |d +|=== +``` + +A table within a table within a table cannot be converted because asciidoc only +supports two levels of tables. +The table on level 3 is thus converted to level 2 and a warning is produced +``` +% pandoc -f html -t asciidoc --verbose + + + + + NestedTables + + + + + + + + + + +
+ + + +
a1 +
1 2
+
+
b
c d
+ + +^D +[INFO] Not rendering Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.5),(AlignDefault,ColWidth 0.5)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a1"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]]] (TableFoot ("",[],[]) [])]]]] (TableFoot ("",[],[]) []) +[width="100%",cols="50%,50%",] +|=== +a| +[width="100%",cols="50%,50%",] +!=== +!a1 ! +!=== + +|b +|c |d +|=== +``` -- cgit v1.2.3 From 19d4e43605e957b40193d900c5a28132c5b5b9c0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Dec 2020 22:57:14 -0800 Subject: Require texmath 0.12.1. --- cabal.project | 4 ---- pandoc.cabal | 2 +- stack.yaml | 1 + 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cabal.project b/cabal.project index ead57cd20..348bedc7e 100644 --- a/cabal.project +++ b/cabal.project @@ -6,7 +6,3 @@ package pandoc flags: +embed_data_files -trypandoc ghc-options: -j +RTS -A64m -RTS --- source-repository-package --- type: git --- location: https://github.com/jgm/citeproc --- tag: a8193fe375fa2354049bf9a967ba3bad4b1ba053 diff --git a/pandoc.cabal b/pandoc.cabal index 4a893d672..f3ae5b74e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -444,7 +444,7 @@ library syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, temporary >= 1.1 && < 1.4, - texmath >= 0.12.0.2 && < 0.13, + texmath >= 0.12.1 && < 0.13, text >= 1.1.1.0 && < 1.3, text-conversions >= 0.3 && < 0.4, time >= 1.5 && < 1.10, diff --git a/stack.yaml b/stack.yaml index 9fcc69b8d..80425221d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -18,6 +18,7 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 - citeproc-0.3.0.1 +- texmath-0.12.1 # - citeproc: # git: https://github.com/jgm/citeproc.git # commit: a8193fe375fa2354049bf9a967ba3bad4b1ba053 -- cgit v1.2.3 From 528b67df596feab2f3b80ecce1c9716123a4d8c5 Mon Sep 17 00:00:00 2001 From: Jerry Sky Date: Mon, 28 Dec 2020 08:02:28 +0100 Subject: templates: added the `description` metatag to both html4 and html5 templates (#6982) The `description` meta tag will make the generated HTML documents more complete and SEO-friendly. --- data/templates/default.html4 | 3 +++ data/templates/default.html5 | 3 +++ 2 files changed, 6 insertions(+) diff --git a/data/templates/default.html4 b/data/templates/default.html4 index f0dd8880e..deba57a7b 100644 --- a/data/templates/default.html4 +++ b/data/templates/default.html4 @@ -12,6 +12,9 @@ $if(date-meta)$ $endif$ $if(keywords)$ +$endif$ +$if(description)$ + $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ diff --git a/pandoc.cabal b/pandoc.cabal index ede9af6f0..db8dab491 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -180,8 +180,6 @@ data-files: data/pandoc.List.lua -- bash completion template data/bash_completion.tpl - -- jats csl - data/jats.csl -- citeproc data/default.csl citeproc/biblatex-localization/*.lbx.strings diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 725c76424..437af3257 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -50,10 +50,9 @@ import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts, import Text.Pandoc.App.CommandLineOptions (parseOptions, options) import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings) import Text.Pandoc.BCP47 (Lang (..), parseBCP47) -import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters) import Text.Pandoc.PDF (makePDF) -import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) +import Text.Pandoc.SelfContained (makeSelfContained) import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs, headerShift, isURI, tabFilter, uriPathToPath, filterIpynbOutput, defaultUserDataDirs, tshow, findM) @@ -190,17 +189,6 @@ convertWithOpts opts = do Nothing -> readDataFile "abbreviations" Just f -> readFileStrict f - metadata <- if format == "jats" && - isNothing (lookupMeta "csl" (optMetadata opts)) && - isNothing (lookupMeta "citation-style" - (optMetadata opts)) - then do - jatsCSL <- readDataFile "jats.csl" - let jatsEncoded = makeDataURI - ("application/xml", jatsCSL) - return $ setMeta "csl" jatsEncoded $ optMetadata opts - else return $ optMetadata opts - case lookupMetaString "lang" (optMetadata opts) of "" -> setTranslations $ Lang "en" "" "US" [] l -> case parseBCP47 l of @@ -286,7 +274,7 @@ convertWithOpts opts = do then fillMediaBag else return) >=> return . adjustMetadata (metadataFromFile <>) - >=> return . adjustMetadata (<> metadata) + >=> return . adjustMetadata (<> optMetadata opts) >=> applyTransforms transforms >=> applyFilters readerOpts filters [T.unpack format] >=> maybe return extractMedia (optExtractMedia opts) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index e8d93b8d5..b2266d179 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.JATS - Copyright : Copyright (C) 2017-2021 John MacFarlane + Copyright : 2017-2021 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane @@ -168,13 +168,15 @@ plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- JATS varlistentrys. deflistItemsToJATS :: PandocMonad m - => WriterOptions -> [([Inline],[[Block]])] -> JATS m (Doc Text) + => WriterOptions + -> [([Inline],[[Block]])] -> JATS m (Doc Text) deflistItemsToJATS opts items = vcat <$> mapM (uncurry (deflistItemToJATS opts)) items -- | Convert a term and a list of blocks into a JATS varlistentry. deflistItemToJATS :: PandocMonad m - => WriterOptions -> [Inline] -> [[Block]] -> JATS m (Doc Text) + => WriterOptions + -> [Inline] -> [[Block]] -> JATS m (Doc Text) deflistItemToJATS opts term defs = do term' <- inlinesToJATS opts term def' <- wrappedBlocksToJATS (not . isPara) @@ -186,7 +188,8 @@ deflistItemToJATS opts term defs = do -- | Convert a list of lists of blocks to a list of JATS list items. listItemsToJATS :: PandocMonad m - => WriterOptions -> Maybe [Text] -> [[Block]] -> JATS m (Doc Text) + => WriterOptions + -> Maybe [Text] -> [[Block]] -> JATS m (Doc Text) listItemsToJATS opts markers items = case markers of Nothing -> vcat <$> mapM (listItemToJATS opts Nothing) items @@ -194,12 +197,13 @@ listItemsToJATS opts markers items = -- | Convert a list of blocks into a JATS list item. listItemToJATS :: PandocMonad m - => WriterOptions -> Maybe Text -> [Block] -> JATS m (Doc Text) + => WriterOptions + -> Maybe Text -> [Block] -> JATS m (Doc Text) listItemToJATS opts mbmarker item = do contents <- wrappedBlocksToJATS (not . isParaOrList) opts (walk demoteHeaderAndRefs item) return $ inTagsIndented "list-item" $ - maybe empty (\lbl -> inTagsSimple "label" (text $ T.unpack lbl)) mbmarker + maybe empty (inTagsSimple "label" . text . T.unpack) mbmarker $$ contents imageMimeType :: Text -> [(Text, Text)] -> (Text, Text) @@ -247,7 +251,9 @@ blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do return $ inTags True "sec" attribs $ inTagsSimple "title" title' $$ contents -- Bibliography reference: -blockToJATS opts (Div (T.stripPrefix "ref-" -> Just _,_,_) [Para lst]) = +blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident = + inTags True "ref" [("id", ident)] . + inTagsSimple "mixed-citation" <$> inlinesToJATS opts lst blockToJATS opts (Div ("refs",_,_) xs) = do contents <- blocksToJATS opts xs @@ -470,10 +476,13 @@ inlineToJATS _ (Link _attr [Str t] (T.stripPrefix "mailto:" -> Just email, _)) | escapeURI t == email = return $ inTagsSimple "email" $ literal (escapeStringForXML email) inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do - let attr = [("id", ident) | not (T.null ident)] ++ - [("alt", stringify txt) | not (null txt)] ++ - [("rid", src)] ++ - [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] + let attr = mconcat + [ [("id", ident) | not (T.null ident)] + , [("alt", stringify txt) | not (null txt)] + , [("rid", src)] + , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] + , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src] + ] if null txt then return $ selfClosingTag "xref" attr else do @@ -529,7 +538,7 @@ demoteHeaderAndRefs (Div ("refs",cls,kvs) bs) = demoteHeaderAndRefs x = x parseDate :: Text -> Maybe Day -parseDate s = msum (map (\fs -> parsetimeWith fs $ T.unpack s) formats) :: Maybe Day +parseDate s = msum (map (`parsetimeWith` T.unpack s) formats) where parsetimeWith = parseTimeM True defaultTimeLocale formats = ["%x","%m/%d/%Y", "%D","%F", "%d %b %Y", "%e %B %Y", "%b. %e, %Y", "%B %e, %Y", diff --git a/test/command/7016.md b/test/command/7016.md new file mode 100644 index 000000000..c2d791ce9 --- /dev/null +++ b/test/command/7016.md @@ -0,0 +1,48 @@ +``` +% pandoc --citeproc --to=jats_archiving --standalone +--- +csl: command/apa.csl +references: +- id: doe + type: article + author: + - family: Doe + given: Jane + container-title: Proceedings of the Academy of Test Inputs + doi: 10.x/nope + issued: 2021 + title: Another article +... +Blah [@doe]. +^D + + +
+ + + + + + + + + + + + + + +

Blah (Doe, 2021).

+ + + + + Doe, J. (2021). Another article. Proceedings + of the Academy of Test Inputs. + doi:10.x/nope + + + +
+``` -- cgit v1.2.3 From 45174d7385c4ac09d56dcdd55d1be4bf601a1fa4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 11 Jan 2021 16:12:40 +0100 Subject: MANUAL.txt: update description of `-L`/`--lua-filter`. The example filter was outdated, a reference to the separate Lua filters documentation is added instead. --- MANUAL.txt | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index aa75a32a9..b9c4ef637 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -588,8 +588,8 @@ header when requesting a document from a URL: 3. `$PATH` (executable only) - Filters and Lua-filters are applied in the order specified - on the command line. + Filters, Lua-filters, and citeproc processing are applied in + the order specified on the command line. `-L` *SCRIPT*, `--lua-filter=`*SCRIPT* @@ -603,26 +603,17 @@ header when requesting a document from a URL: The `pandoc` Lua module provides helper functions for element creation. It is always loaded into the script's Lua environment. - The following is an example Lua script for macro-expansion: - - function expand_hello_world(inline) - if inline.c == '{{helloworld}}' then - return pandoc.Emph{ pandoc.Str "Hello, World" } - else - return inline - end - end - - return {{Str = expand_hello_world}} + See the [Lua filters documentation] for further details. In order of preference, pandoc will look for Lua filters in - 1. a specified full or relative path (executable or - non-executable) + 1. a specified full or relative path - 2. `$DATADIR/filters` (executable or non-executable) - where `$DATADIR` is the user data directory (see - `--data-dir`, above). + 2. `$DATADIR/filters` where `$DATADIR` is the user data + directory (see `--data-dir`, above). + + Filters, Lua filters, and citeproc processing are applied in + the order specified on the command line. `-M` *KEY*[`=`*VAL*], `--metadata=`*KEY*[`:`*VAL*] @@ -707,6 +698,7 @@ header when requesting a document from a URL: [PHP]: https://github.com/vinai/pandocfilters-php [perl]: https://metacpan.org/pod/Pandoc::Filter [JavaScript/node.js]: https://github.com/mvhenderson/pandoc-filter-node +[Lua filters documentation]: https://pandoc.org/lua-filters.html ## General writer options {.options} -- cgit v1.2.3 From 4a223e68f42d23b34d0d13c20e8ecf5e4b16f8fe Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 11 Jan 2021 12:23:55 -0800 Subject: Use commonmark 0.1.1.3. --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index db8dab491..ef47163b3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -411,7 +411,7 @@ library bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, citeproc >= 0.3.0.3 && < 0.4, - commonmark >= 0.1.1.2 && < 0.2, + commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, connection >= 0.3.1, diff --git a/stack.yaml b/stack.yaml index 24ef275e2..b8bdd372a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,7 +13,7 @@ extra-deps: - hslua-1.1.2 - jira-wiki-markup-1.3.2 - HsYAML-aeson-0.2.0.0 -- commonmark-0.1.1.2 +- commonmark-0.1.1.3 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 -- cgit v1.2.3 From c451207b08edc36fa5c2f1af5556a8d211e023ed Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 09:49:10 -0800 Subject: Docx writer: handle table header using styles. Instead of hard-coding the border and header cell vertical alignment, we now let this be determined by the Table style, making use of Word's "conditional formatting" for the table's first row. For headerless tables, we use the tblLook element to tell Word not to apply conditional first-row formatting. Closes #7008. --- data/docx/word/styles.xml | 15 +++++++++ src/Text/Pandoc/Writers/Docx.hs | 37 +++++++++++---------- test/docx/golden/block_quotes.docx | Bin 10001 -> 10092 bytes test/docx/golden/codeblock.docx | Bin 9853 -> 9944 bytes test/docx/golden/comments.docx | Bin 10188 -> 10279 bytes test/docx/golden/custom_style_no_reference.docx | Bin 9952 -> 10042 bytes test/docx/golden/custom_style_preserve.docx | Bin 10578 -> 10666 bytes test/docx/golden/definition_list.docx | Bin 9850 -> 9941 bytes .../golden/document-properties-short-desc.docx | Bin 9856 -> 9947 bytes test/docx/golden/document-properties.docx | Bin 10332 -> 10423 bytes test/docx/golden/headers.docx | Bin 9989 -> 10080 bytes test/docx/golden/image.docx | Bin 26667 -> 26758 bytes test/docx/golden/inline_code.docx | Bin 9789 -> 9880 bytes test/docx/golden/inline_formatting.docx | Bin 9969 -> 10060 bytes test/docx/golden/inline_images.docx | Bin 26725 -> 26816 bytes test/docx/golden/link_in_notes.docx | Bin 10010 -> 10101 bytes test/docx/golden/links.docx | Bin 10185 -> 10276 bytes test/docx/golden/lists.docx | Bin 10261 -> 10352 bytes test/docx/golden/lists_continuing.docx | Bin 10052 -> 10143 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10141 -> 10232 bytes test/docx/golden/lists_restarting.docx | Bin 10053 -> 10144 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10148 -> 10239 bytes test/docx/golden/notes.docx | Bin 9955 -> 10046 bytes test/docx/golden/raw-blocks.docx | Bin 9888 -> 9980 bytes test/docx/golden/raw-bookmarks.docx | Bin 10023 -> 10115 bytes test/docx/golden/table_one_row.docx | Bin 9834 -> 9932 bytes test/docx/golden/table_with_list_cell.docx | Bin 10199 -> 10249 bytes test/docx/golden/tables.docx | Bin 10225 -> 10266 bytes test/docx/golden/track_changes_deletion.docx | Bin 9833 -> 9924 bytes test/docx/golden/track_changes_insertion.docx | Bin 9816 -> 9907 bytes test/docx/golden/track_changes_move.docx | Bin 9850 -> 9941 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 9962 -> 10053 bytes test/docx/golden/unicode.docx | Bin 9774 -> 9865 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9822 -> 9913 bytes 34 files changed, 35 insertions(+), 17 deletions(-) diff --git a/data/docx/word/styles.xml b/data/docx/word/styles.xml index 6bb5a3f52..832b1b25b 100644 --- a/data/docx/word/styles.xml +++ b/data/docx/word/styles.xml @@ -350,6 +350,21 @@ + + + + + + + + + + + + + + + diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index a99e13a85..8f498775d 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1023,23 +1023,15 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do _ -> es ++ [Elem $ mknode "w:p" [] ()] headers' <- mapM cellToOpenXML $ zip aligns headers rows' <- mapM (mapM cellToOpenXML . zip aligns) rows - let borderProps = Elem $ mknode "w:tcPr" [] - [ mknode "w:tcBorders" [] - $ mknode "w:bottom" [("w:val","single")] () - , mknode "w:vAlign" [("w:val","bottom")] () ] compactStyle <- pStyleM "Compact" let emptyCell' = [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] - let mkcell border contents = mknode "w:tc" [] - $ [ borderProps | border ] ++ - if null contents - then emptyCell' - else contents - let mkrow border cells = + let mkcell contents = mknode "w:tc" [] + $ if null contents + then emptyCell' + else contents + let mkrow cells = mknode "w:tr" [] $ - [ mknode "w:trPr" [] - [ mknode "w:cnfStyle" [("w:firstRow","1")] ()] - | border] - ++ map (mkcell border) cells + map mkcell cells let textwidth = 7920 -- 5.5 in in twips, 1/20 pt let fullrow = 5000 -- 100% specified in pct let rowwidth = fullrow * sum widths @@ -1047,6 +1039,15 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do [("w:w", show (floor (textwidth * w) :: Integer))] () let hasHeader = not $ all null headers modify $ \s -> s { stInTable = False } + -- for compatibility with Word <= 2007, we include a val with a bitmask + -- 0×0020 Apply first row conditional formatting + -- 0×0040 Apply last row conditional formatting + -- 0×0080 Apply first column conditional formatting + -- 0×0100 Apply last column conditional formatting + -- 0×0200 Do not apply row banding conditional formatting + -- 0×0400 Do not apply column banding conditional formattin + let tblLookVal :: Int + tblLookVal = if hasHeader then 0x20 else 0 return $ caption' ++ [Elem $ @@ -1059,15 +1060,17 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do ,("w:firstColumn","0") ,("w:lastColumn","0") ,("w:noHBand","0") - ,("w:noVBand","0")] () : + ,("w:noVBand","0") + ,("w:val", printf "%04x" tblLookVal) + ] () : [ mknode "w:tblCaption" [("w:val", T.unpack captionStr)] () | not (null caption) ] ) : mknode "w:tblGrid" [] (if all (==0) widths then [] else map mkgridcol widths) - : [ mkrow True headers' | hasHeader ] ++ - map (mkrow False) rows' + : [ mkrow headers' | hasHeader ] ++ + map mkrow rows' )] blockToOpenXML' opts el | BulletList lst <- el = addOpenXMLList BulletMarker lst diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index bbc8d8de9..3e1bf16e7 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index e20efcab4..66f055063 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index f1185da98..fb3a02a0a 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index 83243ab8c..bc6c2702a 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 17804bb81..8c555a5bd 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index 21629e208..c21b3a5b3 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index 5cf8db0b0..92ce144e9 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index 14bfab6d4..d21b67309 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index 416743aa1..3558a47bf 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index ef2940f89..606df92a3 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 479ea65ec..759269cac 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index e12e3b38d..c37777080 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 8bd57bb8c..9450b1a73 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index 2c6a638fc..6f0b830e6 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index 11e52c4b1..e53889cfb 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 7667990c4..5dbe298b7 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index 3e8c6d2b2..194181288 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 05a7cf060..6e0b634f7 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index f5ae4a384..477178e77 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index d02c77271..51110356e 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index f7fdcbe11..b6206cdf5 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index ae7f8f1f0..07b576080 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index 5e433b736..d46095eb7 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index d404878c6..7caba4e93 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 79c395262..6aaa6da61 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index df9680773..5746c5ad0 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index bb73b82f6..5f22dccc6 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index 7df484aaa..ab5c4f56d 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index d717b93ab..085f33162 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index 791182db2..1ac86d5c8 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index b64a7b58e..c2c443b19 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index b5116d1a8..5ea18d32e 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ -- cgit v1.2.3 From 387d3e76ee81138588195ebe8dad7720f2623b02 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 10:20:32 -0800 Subject: Markdown writer: cleaned up raw formats. We now react appropriately to gfm, commonmark, and commonmark_x as raw formats. --- src/Text/Pandoc/Writers/Markdown.hs | 69 +++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index d62727d90..de9075ac4 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -494,25 +494,24 @@ blockToMarkdown' opts b@(RawBlock f str) = do let renderEmpty = mempty <$ report (BlockNotRendered b) case variant of PlainText -> renderEmpty - _ | f `elem` ["markdown", "markdown_github", "markdown_phpextra", - "markdown_mmd", "markdown_strict"] -> - return $ literal str <> literal "\n" - | isEnabled Ext_raw_attribute opts -> rawAttribBlock - | f `elem` ["html", "html5", "html4"] -> - case () of - _ | isEnabled Ext_markdown_attribute opts -> return $ - literal (addMarkdownAttribute str) <> literal "\n" - | isEnabled Ext_raw_html opts -> return $ - literal str <> literal "\n" - | isEnabled Ext_raw_attribute opts -> rawAttribBlock - | otherwise -> renderEmpty - | f `elem` ["latex", "tex"] -> - case () of - _ | isEnabled Ext_raw_tex opts -> return $ - literal str <> literal "\n" - | isEnabled Ext_raw_attribute opts -> rawAttribBlock - | otherwise -> renderEmpty - | otherwise -> renderEmpty + Commonmark + | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"] + -> return $ literal str <> literal "\n" + Markdown + | f `elem` ["markdown", "markdown_github", "markdown_phpextra", + "markdown_mmd", "markdown_strict"] + -> return $ literal str <> literal "\n" + _ | isEnabled Ext_raw_attribute opts -> rawAttribBlock + | f `elem` ["html", "html5", "html4"] + , isEnabled Ext_markdown_attribute opts + -> return $ literal (addMarkdownAttribute str) <> literal "\n" + | f `elem` ["html", "html5", "html4"] + , isEnabled Ext_raw_html opts + -> return $ literal str <> literal "\n" + | f `elem` ["latex", "tex"] + , isEnabled Ext_raw_tex opts + -> return $ literal str <> literal "\n" + _ -> renderEmpty blockToMarkdown' opts HorizontalRule = return $ blankline <> literal (T.replicate (writerColumns opts) "-") <> blankline blockToMarkdown' opts (Header level attr inlines) = do @@ -1250,21 +1249,23 @@ inlineToMarkdown opts il@(RawInline f str) = do let renderEmpty = mempty <$ report (InlineNotRendered il) case variant of PlainText -> renderEmpty - _ | f `elem` ["markdown", "markdown_github", "markdown_phpextra", - "markdown_mmd", "markdown_strict"] -> - return $ literal str - | isEnabled Ext_raw_attribute opts -> rawAttribInline - | f `elem` ["html", "html5", "html4"] -> - case () of - _ | isEnabled Ext_raw_html opts -> return $ literal str - | isEnabled Ext_raw_attribute opts -> rawAttribInline - | otherwise -> renderEmpty - | f `elem` ["latex", "tex"] -> - case () of - _ | isEnabled Ext_raw_tex opts -> return $ literal str - | isEnabled Ext_raw_attribute opts -> rawAttribInline - | otherwise -> renderEmpty - | otherwise -> renderEmpty + Commonmark + | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"] + -> return $ literal str + Markdown + | f `elem` ["markdown", "markdown_github", "markdown_phpextra", + "markdown_mmd", "markdown_strict"] + -> return $ literal str + _ | isEnabled Ext_raw_attribute opts -> rawAttribInline + | f `elem` ["html", "html5", "html4"] + , isEnabled Ext_raw_html opts + -> return $ literal str + | f `elem` ["latex", "tex"] + , isEnabled Ext_raw_tex opts + -> return $ literal str + _ -> renderEmpty + + inlineToMarkdown opts LineBreak = do variant <- asks envVariant if variant == PlainText || isEnabled Ext_hard_line_breaks opts -- cgit v1.2.3 From 91fbea29ddf961654b1c09efaba70d92bfd65cc0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 21:18:17 -0800 Subject: Add files for using nix-shell. 'make nix-shell' enters a nix shell with pandoc dependencies installed. --- Makefile | 8 +++++++- default.nix | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ release.nix | 4 ++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 default.nix create mode 100644 release.nix diff --git a/Makefile b/Makefile index f27272b08..e9607f198 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,12 @@ README.md: README.template MANUAL.txt tools/update-readme.lua pandoc --lua-filter tools/update-readme.lua \ --reference-location=section -t gfm $< -o $@ +default.nix: pandoc.cabal + nix-shell --pure -p cabal2nix --run "cabal2nix ." > $@ + +nix-shell: + nix-shell --attr env release.nix + download_stats: curl https://api.github.com/repos/jgm/pandoc/releases | \ jq -r '.[] | .assets | .[] | "\(.download_count)\t\(.name)"' @@ -124,4 +130,4 @@ sdist-files.txt: .FORCE git-files.txt: .FORCE git ls-tree -r --name-only HEAD | grep '^\(test\|data\)\/' | sort > $@ -.PHONY: .FORCE deps quick full haddock install clean test bench changes_github dist prof download_stats reformat lint weigh doc/lua-filters.md pandoc-templates trypandoc update-website debpkg checkdocs ghcid ghci fix_spacing hlint check check-cabal +.PHONY: .FORCE deps quick full haddock install clean test bench changes_github dist prof download_stats reformat lint weigh doc/lua-filters.md pandoc-templates trypandoc update-website debpkg checkdocs ghcid ghci fix_spacing hlint check check-cabal nix-shell diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..a18be1fa8 --- /dev/null +++ b/default.nix @@ -0,0 +1,55 @@ +{ mkDerivation, aeson, aeson-pretty, attoparsec, base +, base64-bytestring, binary, blaze-html, blaze-markup, bytestring +, case-insensitive, citeproc, commonmark, commonmark-extensions +, commonmark-pandoc, connection, containers, criterion +, data-default, deepseq, Diff, directory, doclayout, doctemplates +, emojis, exceptions, executable-path, file-embed, filepath, Glob +, haddock-library, hslua, hslua-module-system, hslua-module-text +, HsYAML, HTTP, http-client, http-client-tls, http-types, ipynb +, jira-wiki-markup, JuicyPixels, mtl, network, network-uri +, pandoc-types, parsec, process, QuickCheck, random, safe +, scientific, SHA, skylighting, skylighting-core, split, stdenv +, syb, tagsoup, tasty, tasty-golden, tasty-hunit, tasty-lua +, tasty-quickcheck, temporary, texmath, text, text-conversions +, time, unicode-transforms, unix, unordered-containers, weigh, xml +, zip-archive, zlib +}: +mkDerivation { + pname = "pandoc"; + version = "2.11.4"; + src = ./.; + configureFlags = [ "-fhttps" "-f-trypandoc" ]; + isLibrary = true; + isExecutable = true; + enableSeparateDataOutput = true; + libraryHaskellDepends = [ + aeson aeson-pretty attoparsec base base64-bytestring binary + blaze-html blaze-markup bytestring case-insensitive citeproc + commonmark commonmark-extensions commonmark-pandoc connection + containers data-default deepseq directory doclayout doctemplates + emojis exceptions file-embed filepath Glob haddock-library hslua + hslua-module-system hslua-module-text HsYAML HTTP http-client + http-client-tls http-types ipynb jira-wiki-markup JuicyPixels mtl + network network-uri pandoc-types parsec process random safe + scientific SHA skylighting skylighting-core split syb tagsoup + temporary texmath text text-conversions time unicode-transforms + unix unordered-containers xml zip-archive zlib + ]; + executableHaskellDepends = [ base ]; + testHaskellDepends = [ + base base64-bytestring bytestring containers Diff directory + doctemplates exceptions executable-path filepath Glob hslua mtl + pandoc-types process QuickCheck tasty tasty-golden tasty-hunit + tasty-lua tasty-quickcheck temporary text time xml zip-archive + ]; + benchmarkHaskellDepends = [ + base bytestring containers criterion mtl text time weigh + ]; + postInstall = '' + mkdir -p $out/share/man/man1 + mv "man/"*.1 $out/share/man/man1/ + ''; + homepage = "https://pandoc.org"; + description = "Conversion between markup formats"; + license = stdenv.lib.licenses.gpl2Plus; +} diff --git a/release.nix b/release.nix new file mode 100644 index 000000000..e28be9ac3 --- /dev/null +++ b/release.nix @@ -0,0 +1,4 @@ +let + pkgs = import { }; +in + pkgs.haskellPackages.callPackage ./default.nix { } -- cgit v1.2.3 From 526e7f5c5518096c9f108e43d7cc04c88c7c1b8e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 21:50:22 -0800 Subject: Gitignore: add .DS_Store, .orig --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f5a9bc514..d29af79e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *~ +.DS_Store +*.orig deb/.vagrant dist/* MANUAL.* -- cgit v1.2.3 From ea96bc52e48b553dabcdaf6cbade7a332f8ffb1d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 21:53:39 -0800 Subject: CONTRIBUTING: note on GNU xargs --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0749daeac..e87c96694 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -291,6 +291,9 @@ before creating a commit: git diff --diff-filter=MA --cached --name-only | grep '\.hs$' | \ xargs hlint --hint .hlint.yaml +(If you are using GNU `xargs`, add the `-r` option immediately +after `xargs`.) + Saving this to `.git/hooks/pre-commit`, and making the script executable, will prevent accidental introduction of potentially problematic code. -- cgit v1.2.3 From bd3b7c2e2c5ecb08c1ab3cd6c1176c883a124af3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 22:28:57 -0800 Subject: Delete default.nix. It is generated and shouldn't be in repository. --- Makefile | 2 +- default.nix | 55 ------------------------------------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 default.nix diff --git a/Makefile b/Makefile index e9607f198..1f7fd98b4 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ README.md: README.template MANUAL.txt tools/update-readme.lua default.nix: pandoc.cabal nix-shell --pure -p cabal2nix --run "cabal2nix ." > $@ -nix-shell: +nix-shell: default.nix nix-shell --attr env release.nix download_stats: diff --git a/default.nix b/default.nix deleted file mode 100644 index a18be1fa8..000000000 --- a/default.nix +++ /dev/null @@ -1,55 +0,0 @@ -{ mkDerivation, aeson, aeson-pretty, attoparsec, base -, base64-bytestring, binary, blaze-html, blaze-markup, bytestring -, case-insensitive, citeproc, commonmark, commonmark-extensions -, commonmark-pandoc, connection, containers, criterion -, data-default, deepseq, Diff, directory, doclayout, doctemplates -, emojis, exceptions, executable-path, file-embed, filepath, Glob -, haddock-library, hslua, hslua-module-system, hslua-module-text -, HsYAML, HTTP, http-client, http-client-tls, http-types, ipynb -, jira-wiki-markup, JuicyPixels, mtl, network, network-uri -, pandoc-types, parsec, process, QuickCheck, random, safe -, scientific, SHA, skylighting, skylighting-core, split, stdenv -, syb, tagsoup, tasty, tasty-golden, tasty-hunit, tasty-lua -, tasty-quickcheck, temporary, texmath, text, text-conversions -, time, unicode-transforms, unix, unordered-containers, weigh, xml -, zip-archive, zlib -}: -mkDerivation { - pname = "pandoc"; - version = "2.11.4"; - src = ./.; - configureFlags = [ "-fhttps" "-f-trypandoc" ]; - isLibrary = true; - isExecutable = true; - enableSeparateDataOutput = true; - libraryHaskellDepends = [ - aeson aeson-pretty attoparsec base base64-bytestring binary - blaze-html blaze-markup bytestring case-insensitive citeproc - commonmark commonmark-extensions commonmark-pandoc connection - containers data-default deepseq directory doclayout doctemplates - emojis exceptions file-embed filepath Glob haddock-library hslua - hslua-module-system hslua-module-text HsYAML HTTP http-client - http-client-tls http-types ipynb jira-wiki-markup JuicyPixels mtl - network network-uri pandoc-types parsec process random safe - scientific SHA skylighting skylighting-core split syb tagsoup - temporary texmath text text-conversions time unicode-transforms - unix unordered-containers xml zip-archive zlib - ]; - executableHaskellDepends = [ base ]; - testHaskellDepends = [ - base base64-bytestring bytestring containers Diff directory - doctemplates exceptions executable-path filepath Glob hslua mtl - pandoc-types process QuickCheck tasty tasty-golden tasty-hunit - tasty-lua tasty-quickcheck temporary text time xml zip-archive - ]; - benchmarkHaskellDepends = [ - base bytestring containers criterion mtl text time weigh - ]; - postInstall = '' - mkdir -p $out/share/man/man1 - mv "man/"*.1 $out/share/man/man1/ - ''; - homepage = "https://pandoc.org"; - description = "Conversion between markup formats"; - license = stdenv.lib.licenses.gpl2Plus; -} -- cgit v1.2.3 From 2787083b7625e6a0e16f65edb06781da0c9afe84 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 12 Jan 2021 22:32:26 -0800 Subject: Use project.nix instead of default.nix for generated file. --- Makefile | 4 ++-- release.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1f7fd98b4..495539701 100644 --- a/Makefile +++ b/Makefile @@ -91,10 +91,10 @@ README.md: README.template MANUAL.txt tools/update-readme.lua pandoc --lua-filter tools/update-readme.lua \ --reference-location=section -t gfm $< -o $@ -default.nix: pandoc.cabal +project.nix: pandoc.cabal nix-shell --pure -p cabal2nix --run "cabal2nix ." > $@ -nix-shell: default.nix +nix-shell: project.nix nix-shell --attr env release.nix download_stats: diff --git a/release.nix b/release.nix index e28be9ac3..857ea3286 100644 --- a/release.nix +++ b/release.nix @@ -1,4 +1,4 @@ let pkgs = import { }; in - pkgs.haskellPackages.callPackage ./default.nix { } + pkgs.haskellPackages.callPackage ./project.nix { } -- cgit v1.2.3 From 76b36fcb17d9686620032bbfe7eb7f60eadfb057 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 13 Jan 2021 09:51:45 -0800 Subject: Use simple default.nix. Remove nix makefile targets. --- Makefile | 8 +------- default.nix | 13 +++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 default.nix diff --git a/Makefile b/Makefile index 495539701..f27272b08 100644 --- a/Makefile +++ b/Makefile @@ -91,12 +91,6 @@ README.md: README.template MANUAL.txt tools/update-readme.lua pandoc --lua-filter tools/update-readme.lua \ --reference-location=section -t gfm $< -o $@ -project.nix: pandoc.cabal - nix-shell --pure -p cabal2nix --run "cabal2nix ." > $@ - -nix-shell: project.nix - nix-shell --attr env release.nix - download_stats: curl https://api.github.com/repos/jgm/pandoc/releases | \ jq -r '.[] | .assets | .[] | "\(.download_count)\t\(.name)"' @@ -130,4 +124,4 @@ sdist-files.txt: .FORCE git-files.txt: .FORCE git ls-tree -r --name-only HEAD | grep '^\(test\|data\)\/' | sort > $@ -.PHONY: .FORCE deps quick full haddock install clean test bench changes_github dist prof download_stats reformat lint weigh doc/lua-filters.md pandoc-templates trypandoc update-website debpkg checkdocs ghcid ghci fix_spacing hlint check check-cabal nix-shell +.PHONY: .FORCE deps quick full haddock install clean test bench changes_github dist prof download_stats reformat lint weigh doc/lua-filters.md pandoc-templates trypandoc update-website debpkg checkdocs ghcid ghci fix_spacing hlint check check-cabal diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..0aa98f022 --- /dev/null +++ b/default.nix @@ -0,0 +1,13 @@ +# default.nix +let + pkgs = import { }; +in + pkgs.haskellPackages.developPackage { + root = ./.; + modifier = drv: + pkgs.haskell.lib.addBuildTools drv (with pkgs.haskellPackages; + [ cabal-install + ghcid + ]); + } + -- cgit v1.2.3 From 3d6ebc905195592897e3a03ef648384ee01f5155 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 15 Jan 2021 11:51:07 -0800 Subject: Use dev version of citeproc. Change a citation test which had wrong disambiguation (see jgm/citeproc#44). --- cabal.project | 4 ++++ stack.yaml | 8 ++++---- .../pandoc-citeproc-chicago-fullnote-bibliography.md | 20 +++++++++----------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cabal.project b/cabal.project index 348bedc7e..34b3837de 100644 --- a/cabal.project +++ b/cabal.project @@ -6,3 +6,7 @@ package pandoc flags: +embed_data_files -trypandoc ghc-options: -j +RTS -A64m -RTS +source-repository-package + type: git + location: https://github.com/jgm/citeproc + tag: d44e24696ab444090d0e63e321c3a573f68b2e74 diff --git a/stack.yaml b/stack.yaml index b8bdd372a..b2f8cf8fc 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,11 +17,11 @@ extra-deps: - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 -- citeproc-0.3.0.3 - texmath-0.12.1 -# - citeproc: -# git: https://github.com/jgm/citeproc.git -# commit: a8193fe375fa2354049bf9a967ba3bad4b1ba053 +- citeproc: + git: https://github.com/jgm/citeproc.git + commit: d44e24696ab444090d0e63e321c3a573f68b2e74 +# - citeproc-0.3.0.3 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 diff --git a/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md b/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md index 4536342c6..d997d818b 100644 --- a/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md +++ b/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md @@ -107,7 +107,7 @@ Sam Smith. Oxford: Oxford University Press, 2007. [^5]: [30](#ref-item1), with suffix. -[^6]: [*First Book*, 2005](#ref-item1); ["Article," *Journal of Generic +[^6]: [*First Book*](#ref-item1); ["Article," *Journal of Generic Studies* 6 (2006): 30](#ref-item2); see also [John Doe and Jenny Roe, "Why Water Is Wet," in *Third Book*, ed. Sam Smith (Oxford: Oxford University Press, 2007)](#ref-пункт3). @@ -116,22 +116,20 @@ Sam Smith. Oxford: Oxford University Press, 2007. and a citation without locators ([Doe and Roe, "Why Water Is Wet"](#ref-пункт3)). -[^8]: See [Doe, *First Book*, 2005, chap. 3](#ref-item1); also [Doe and - Roe, "Why Water Is Wet," 34--35](#ref-пункт3). +[^8]: See [Doe, *First Book*, chap. 3](#ref-item1); also [Doe and Roe, + "Why Water Is Wet," 34--35](#ref-пункт3). -[^9]: See [Doe, *First Book*, 2005, 34--35](#ref-item1). +[^9]: See [Doe, *First Book*, 34--35](#ref-item1). [^10]: Some citations (see [Doe, chap. 3](#ref-item1); [Doe and Roe, - "Why Water Is Wet"](#ref-пункт3); [Doe, "Article," - 2006](#ref-item2)). + "Why Water Is Wet"](#ref-пункт3); [Doe, "Article"](#ref-item2)). -[^11]: [Doe, *First Book*, 2005, 33, 35--37](#ref-item1), and nowhere - else. +[^11]: [Doe, *First Book*, 33, 35--37](#ref-item1), and nowhere else. -[^12]: [Doe, *First Book*, 2005](#ref-item1) and nowhere else. +[^12]: [Doe, *First Book*](#ref-item1) and nowhere else. [^13]: Like a citation without author: (), and again (), and now Doe - with a locator (["Article," 2006, 44](#ref-item2)). + with a locator (["Article," 44](#ref-item2)). -[^14]: *See* [Doe, *First Book*, 2005, 32](#ref-item1). +[^14]: *See* [Doe, *First Book*, 32](#ref-item1). ``` -- cgit v1.2.3 From 07c98eae50faf311963b91a2bacf053d2ec7972b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 15 Jan 2021 14:17:17 -0800 Subject: Use citeproc >= 0.3.0.4. --- cabal.project | 8 ++++---- pandoc.cabal | 2 +- stack.yaml | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cabal.project b/cabal.project index 34b3837de..c3042cb85 100644 --- a/cabal.project +++ b/cabal.project @@ -6,7 +6,7 @@ package pandoc flags: +embed_data_files -trypandoc ghc-options: -j +RTS -A64m -RTS -source-repository-package - type: git - location: https://github.com/jgm/citeproc - tag: d44e24696ab444090d0e63e321c3a573f68b2e74 +-- source-repository-package +-- type: git +-- location: https://github.com/jgm/citeproc +-- tag: d44e24696ab444090d0e63e321c3a573f68b2e74 diff --git a/pandoc.cabal b/pandoc.cabal index ef47163b3..0d63cbe35 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -410,7 +410,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.3 && < 0.4, + citeproc >= 0.3.0.4 && < 0.4, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/stack.yaml b/stack.yaml index b2f8cf8fc..74367bb8a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -18,10 +18,10 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 - texmath-0.12.1 -- citeproc: - git: https://github.com/jgm/citeproc.git - commit: d44e24696ab444090d0e63e321c3a573f68b2e74 -# - citeproc-0.3.0.3 +- citeproc-0.3.0.4 +# - citeproc: +# git: https://github.com/jgm/citeproc.git +# commit: d44e24696ab444090d0e63e321c3a573f68b2e74 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 -- cgit v1.2.3 From e7ea89928fb11ca882fcb2f3b805eed3ea04147e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 15 Jan 2021 22:41:39 -0800 Subject: Improve .gitignore. --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index d29af79e3..13864c5a2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,10 @@ man/man?/*.html *.o *.hi *.pyc +.hie/ +.policeman-evidence/ +.projectile/ +.vscode/ /COPYING.rtf /COPYRIGHT.txt /cabal-dev/ @@ -28,3 +32,4 @@ data/reference.odt /stack.yaml.lock cabal.project.local /dist-newstyle/ +pandoc-2*/ -- cgit v1.2.3 From 83336a45a78b288dcc0104d2432135bdb3f5e8f1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 16 Jan 2021 11:15:35 -0800 Subject: Recognize more extensions as markdown by default. `mkdn`, `mkd`, `mdwn`, `mdown`, `Rmd`. Closes #7034. --- src/Text/Pandoc/App/FormatHeuristics.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Text/Pandoc/App/FormatHeuristics.hs b/src/Text/Pandoc/App/FormatHeuristics.hs index 17ed30fe9..65a1a7b82 100644 --- a/src/Text/Pandoc/App/FormatHeuristics.hs +++ b/src/Text/Pandoc/App/FormatHeuristics.hs @@ -48,6 +48,11 @@ formatFromFilePath x = ".lhs" -> Just "markdown+lhs" ".ltx" -> Just "latex" ".markdown" -> Just "markdown" + ".mkdn" -> Just "markdown" + ".mkd" -> Just "markdown" + ".mdwn" -> Just "markdown" + ".mdown" -> Just "markdown" + ".Rmd" -> Just "markdown" ".md" -> Just "markdown" ".ms" -> Just "ms" ".muse" -> Just "muse" -- cgit v1.2.3 From 6efd3460a776620fdb93812daa4f6831e6c332ce Mon Sep 17 00:00:00 2001 From: Gautier DI FOLCO Date: Sun, 17 Jan 2021 01:15:33 +0100 Subject: Markdown reader: support GitHub wiki's internal links (#2923) (#6458) Canges overview: * Add a `Ext_markdown_github_wikilink` constructor to `Extension` [API change]. * Add the parser `githubWikiLink` in `Text.Pandoc.Readers.Markdown` * Add tests. --- src/Text/Pandoc/Extensions.hs | 3 +++ src/Text/Pandoc/Readers/Markdown.hs | 25 +++++++++++++++++++++++++ test/Tests/Readers/Markdown.hs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 39c2a0489..69257ecc8 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -118,6 +118,7 @@ data Extension = | Ext_literate_haskell -- ^ Enable literate Haskell conventions | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown iff -- container has attribute 'markdown' + | Ext_wikilinks -- ^ Interpret a markdown wiki link | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] | Ext_mmd_link_attributes -- ^ MMD style reference link attributes @@ -258,6 +259,7 @@ githubMarkdownExtensions = extensionsFromList , Ext_emoji , Ext_fenced_code_blocks , Ext_backtick_code_blocks + , Ext_wikilinks ] -- | Extensions to be used with multimarkdown. @@ -444,6 +446,7 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_tex_math_single_backslash , Ext_tex_math_double_backslash , Ext_markdown_attribute + , Ext_wikilinks , Ext_mmd_title_block , Ext_abbreviations , Ext_autolink_bare_uris diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5c3a21bb7..8fd0b68e2 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -21,6 +21,7 @@ module Text.Pandoc.Readers.Markdown ( import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isAlphaNum, isPunctuation, isSpace) +import Data.Functor (($>)) import Data.List (transpose, elemIndex, sortOn) import qualified Data.Map as M import Data.Maybe @@ -1485,6 +1486,7 @@ inline = choice [ whitespace , note , cite , bracketedSpan + , githubWikiLink , link , image , math @@ -1780,6 +1782,29 @@ source = do linkTitle :: PandocMonad m => MarkdownParser m Text linkTitle = quotedTitle '"' <|> quotedTitle '\'' +-- Github wiki style link, with optional title +-- syntax documented under https://help.github.com/en/github/building-a-strong-community/editing-wiki-content +githubWikiLink :: PandocMonad m => MarkdownParser m (F Inlines) +githubWikiLink = try $ guardEnabled Ext_wikilinks >> wikilink + where + wikilink = try $ do + string "[[" + firstPart <- fmap mconcat . sequence <$> wikiText + (char '|' *> complexWikilink firstPart) + <|> (string "]]" $> (B.link + <$> (stringify <$> firstPart) + <*> return "wikilink" + <*> firstPart)) + + complexWikilink firstPart = do + url <- fmap stringify . sequence <$> wikiText + string "]]" + return $ B.link <$> url + <*> return "wikilink" + <*> firstPart + + wikiText = many (whitespace <|> bareURL <|> str <|> endline <|> escapedChar) + link :: PandocMonad m => MarkdownParser m (F Inlines) link = try $ do st <- getState diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index 18f909583..a2abcb143 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -307,6 +307,36 @@ tests = [ testGroup "inline code" "[https://example.org(](url)" =?> para (link "url" "" (text "https://example.org(")) ] + , testGroup "Github wiki links" + [ test markdownGH "autolink" $ + "[[https://example.org]]" =?> + para (link "https://example.org" "wikilink" (text "https://example.org")) + , test markdownGH "link with title" $ + "[[title|https://example.org]]" =?> + para (link "https://example.org" "wikilink" (text "title")) + , test markdownGH "bad link with title" $ + "[[title|random string]]" =?> + para (link "random-string" "wikilink" (text "title")) + , test markdownGH "autolink not being a link" $ + "[[Name of page]]" =?> + para (link "Name-of-page" "wikilink" (text "Name of page")) + , test markdownGH "autolink not being a link with a square bracket" $ + "[[Name of ]page]]" =?> + para (link "Name-of-]page" "wikilink" (text "Name of ]page")) + , test markdownGH "formatting (strong and emphasis) should not be interpreted" $ + "[[***a**b **c**d*|https://example.org]]" =?> + para (text "[[" <> emph (strong (str "a") <> str "b" <> space + <> strong (str "c") <> str "d") <> text "|https://example.org]]") + , test markdownGH "inlined code should not make a link" $ + "[[ti`|`le|https://example.org]]" =?> + para (text "[[ti" <> code "|" <> text "le|https://example.org]]") + , test markdownGH "link with title and a cut should take the middle part as link" $ + "[[tit|le|https://example.org]]" =?> + para (link "le" "wikilink" (text "tit")) + , test markdownGH "link with inline start should be a link" $ + "[[t`i*t_le|https://example.org]]" =?> + para (link "https://example.org" "wikilink" (text "t`i*t_le")) + ] , testGroup "Headers" [ "blank line before header" =: "\n# Header\n" -- cgit v1.2.3 From c841bcf3b01548b2f9b462b39d8edda4c10be534 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 16 Jan 2021 16:22:04 -0800 Subject: Revert "Markdown reader: support GitHub wiki's internal links (#2923) (#6458)" This reverts commit 6efd3460a776620fdb93812daa4f6831e6c332ce. Since this extension is designed to be used with GitHub markdown (gfm), we need to implement the parser as a commonmark extension (commonmark-extensions), rather than in pandoc's markdown reader. When that is done, we can add it here. --- src/Text/Pandoc/Extensions.hs | 3 --- src/Text/Pandoc/Readers/Markdown.hs | 25 ------------------------- test/Tests/Readers/Markdown.hs | 30 ------------------------------ 3 files changed, 58 deletions(-) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 69257ecc8..39c2a0489 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -118,7 +118,6 @@ data Extension = | Ext_literate_haskell -- ^ Enable literate Haskell conventions | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown iff -- container has attribute 'markdown' - | Ext_wikilinks -- ^ Interpret a markdown wiki link | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] | Ext_mmd_link_attributes -- ^ MMD style reference link attributes @@ -259,7 +258,6 @@ githubMarkdownExtensions = extensionsFromList , Ext_emoji , Ext_fenced_code_blocks , Ext_backtick_code_blocks - , Ext_wikilinks ] -- | Extensions to be used with multimarkdown. @@ -446,7 +444,6 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_tex_math_single_backslash , Ext_tex_math_double_backslash , Ext_markdown_attribute - , Ext_wikilinks , Ext_mmd_title_block , Ext_abbreviations , Ext_autolink_bare_uris diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 8fd0b68e2..5c3a21bb7 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -21,7 +21,6 @@ module Text.Pandoc.Readers.Markdown ( import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isAlphaNum, isPunctuation, isSpace) -import Data.Functor (($>)) import Data.List (transpose, elemIndex, sortOn) import qualified Data.Map as M import Data.Maybe @@ -1486,7 +1485,6 @@ inline = choice [ whitespace , note , cite , bracketedSpan - , githubWikiLink , link , image , math @@ -1782,29 +1780,6 @@ source = do linkTitle :: PandocMonad m => MarkdownParser m Text linkTitle = quotedTitle '"' <|> quotedTitle '\'' --- Github wiki style link, with optional title --- syntax documented under https://help.github.com/en/github/building-a-strong-community/editing-wiki-content -githubWikiLink :: PandocMonad m => MarkdownParser m (F Inlines) -githubWikiLink = try $ guardEnabled Ext_wikilinks >> wikilink - where - wikilink = try $ do - string "[[" - firstPart <- fmap mconcat . sequence <$> wikiText - (char '|' *> complexWikilink firstPart) - <|> (string "]]" $> (B.link - <$> (stringify <$> firstPart) - <*> return "wikilink" - <*> firstPart)) - - complexWikilink firstPart = do - url <- fmap stringify . sequence <$> wikiText - string "]]" - return $ B.link <$> url - <*> return "wikilink" - <*> firstPart - - wikiText = many (whitespace <|> bareURL <|> str <|> endline <|> escapedChar) - link :: PandocMonad m => MarkdownParser m (F Inlines) link = try $ do st <- getState diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index a2abcb143..18f909583 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -307,36 +307,6 @@ tests = [ testGroup "inline code" "[https://example.org(](url)" =?> para (link "url" "" (text "https://example.org(")) ] - , testGroup "Github wiki links" - [ test markdownGH "autolink" $ - "[[https://example.org]]" =?> - para (link "https://example.org" "wikilink" (text "https://example.org")) - , test markdownGH "link with title" $ - "[[title|https://example.org]]" =?> - para (link "https://example.org" "wikilink" (text "title")) - , test markdownGH "bad link with title" $ - "[[title|random string]]" =?> - para (link "random-string" "wikilink" (text "title")) - , test markdownGH "autolink not being a link" $ - "[[Name of page]]" =?> - para (link "Name-of-page" "wikilink" (text "Name of page")) - , test markdownGH "autolink not being a link with a square bracket" $ - "[[Name of ]page]]" =?> - para (link "Name-of-]page" "wikilink" (text "Name of ]page")) - , test markdownGH "formatting (strong and emphasis) should not be interpreted" $ - "[[***a**b **c**d*|https://example.org]]" =?> - para (text "[[" <> emph (strong (str "a") <> str "b" <> space - <> strong (str "c") <> str "d") <> text "|https://example.org]]") - , test markdownGH "inlined code should not make a link" $ - "[[ti`|`le|https://example.org]]" =?> - para (text "[[ti" <> code "|" <> text "le|https://example.org]]") - , test markdownGH "link with title and a cut should take the middle part as link" $ - "[[tit|le|https://example.org]]" =?> - para (link "le" "wikilink" (text "tit")) - , test markdownGH "link with inline start should be a link" $ - "[[t`i*t_le|https://example.org]]" =?> - para (link "https://example.org" "wikilink" (text "t`i*t_le")) - ] , testGroup "Headers" [ "blank line before header" =: "\n# Header\n" -- cgit v1.2.3 From 1c4d14cdcc60c1bfcc579a5b5ab154e8e62e54c7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 18 Jan 2021 11:32:02 -0800 Subject: RST writer: fix #7039. We were losing content from inside spans with a class, due to logic that is meant to avoid nested inline structures that can't be represented in RST. The logic was a bit stricter than necessary. This commit fixes the issue. --- src/Text/Pandoc/Writers/RST.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index a3be1d723..d01e13db4 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -519,8 +519,8 @@ flatten outer (Quoted _ _, _) -> keep f i (_, Quoted _ _) -> keep f i -- spans are not rendered using RST inlines, so we can keep them - (Span ("",[],[]) _, _) -> keep f i - (_, Span ("",[],[]) _) -> keep f i + (Span (_,_,[]) _, _) -> keep f i + (_, Span (_,_,[]) _) -> keep f i -- inlineToRST handles this case properly so it's safe to keep ( Link{}, Image{}) -> keep f i -- parent inlines would prevent links from being correctly -- cgit v1.2.3 From 5f98ac62e3f91a3301c13f17d9081b6ecb39e5a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 19 Jan 2021 20:39:58 -0800 Subject: JATS writer: Ensure that disp-quote is always wrapped in p. Closes #7041. --- src/Text/Pandoc/Writers/JATS.hs | 4 +- test/writer.jats_archiving | 68 +++++++++++++++++------------- test/writer.jats_articleauthoring | 88 +++++++++++++++++++++------------------ test/writer.jats_publishing | 68 +++++++++++++++++------------- 4 files changed, 131 insertions(+), 97 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index b2266d179..c75d40745 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -156,9 +156,11 @@ wrappedBlocksToJATS needsWrap opts = wrappedBlockToJATS b = do inner <- blockToJATS opts b return $ - if needsWrap b + if needsWrap b || isBlockQuote b -- see #7041 then inTags True "p" [("specific-use","wrapper")] inner else inner + isBlockQuote (BlockQuote _) = True + isBlockQuote _ = False -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block diff --git a/test/writer.jats_archiving b/test/writer.jats_archiving index a708e0367..2d06c5cab 100644 --- a/test/writer.jats_archiving +++ b/test/writer.jats_archiving @@ -78,31 +78,39 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

- -

This is a block quote. It is pretty short.

-
- -

Code in a block quote:

- sub status { - print "working"; -} -

A list:

- - -

item one

-
- -

item two

-
-
-

Nested block quotes:

+

-

nested

+

This is a block quote. It is pretty short.

+

+

-

nested

+

Code in a block quote:

+ sub status { + print "working"; +} +

A list:

+ + +

item one

+
+ +

item two

+
+
+

Nested block quotes:

+

+ +

nested

+ +

+

+ +

nested

+ +

- +

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -829,10 +837,12 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

- -

Blockquoted: - http://example.com/

-
+

+ +

Blockquoted: + http://example.com/

+ +

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -856,9 +866,11 @@ These should not be escaped: \$ \\ \> \[ \{ not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

- -

Notes can go in quotes.4

-
+

+ +

Notes can go in quotes.4

+ +

And in list items.5

diff --git a/test/writer.jats_articleauthoring b/test/writer.jats_articleauthoring index 90437992e..65ad5e37c 100644 --- a/test/writer.jats_articleauthoring +++ b/test/writer.jats_articleauthoring @@ -67,39 +67,43 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

- -

This is a block quote. It is pretty short.

-
- -

Code in a block quote:

-

- sub status { +

+ +

This is a block quote. It is pretty short.

+
+

+

+ +

Code in a block quote:

+

+ sub status { print "working"; } -

-

A list:

-

- - -

item one

-
- -

item two

-
-
-

-

Nested block quotes:

-

- -

nested

- -

-

- -

nested

- -

- +

+

A list:

+

+ + +

item one

+ + +

item two

+
+ +

+

Nested block quotes:

+

+ +

nested

+ +

+

+ +

nested

+ +

+ +

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -813,10 +817,12 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

- -

Blockquoted: - http://example.com/

-
+

+ +

Blockquoted: + http://example.com/

+ +

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -854,11 +860,13 @@ These should not be escaped: \$ \\ \> \[ \{ and ] verbatim characters, as well as [bracketed text].

- -

Notes can go in quotes. -

In quote.

-

-
+

+ +

Notes can go in quotes. +

In quote.

+

+ +

And in list items. diff --git a/test/writer.jats_publishing b/test/writer.jats_publishing index 6d5a04057..915471576 100644 --- a/test/writer.jats_publishing +++ b/test/writer.jats_publishing @@ -78,31 +78,39 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

- -

This is a block quote. It is pretty short.

-
- -

Code in a block quote:

- sub status { - print "working"; -} -

A list:

- - -

item one

-
- -

item two

-
-
-

Nested block quotes:

+

-

nested

+

This is a block quote. It is pretty short.

+

+

-

nested

+

Code in a block quote:

+ sub status { + print "working"; +} +

A list:

+ + +

item one

+
+ +

item two

+
+
+

Nested block quotes:

+

+ +

nested

+ +

+

+ +

nested

+ +

- +

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -829,10 +837,12 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

- -

Blockquoted: - http://example.com/

-
+

+ +

Blockquoted: + http://example.com/

+ +

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -856,9 +866,11 @@ These should not be escaped: \$ \\ \> \[ \{ not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

- -

Notes can go in quotes.4

-
+

+ +

Notes can go in quotes.4

+ +

And in list items.5

-- cgit v1.2.3 From 87083bd1d6d715e349313dc6af9cbac82f1bb4ea Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 20 Jan 2021 19:08:02 +0100 Subject: Text.Pandoc.Citeproc: use finer grained imports This allows to import the module in writers without causing a circular dependency. --- src/Text/Pandoc/Citeproc.hs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index 9649c6971..6658c8c0c 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -18,33 +18,36 @@ import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences) import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..)) import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText) import Text.Pandoc.Readers.Markdown (yamlToRefs) -import Text.Pandoc.Class (setResourcePath, getResourcePath, getUserDataDir) import qualified Text.Pandoc.BCP47 as BCP47 -import Data.ByteString (ByteString) -import qualified Data.ByteString.Lazy as L +import Text.Pandoc.Builder (Inlines, Many(..), deleteMeta, setMeta) +import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition as Pandoc -import Text.Pandoc.Walk -import Text.Pandoc.Builder as B -import Text.Pandoc (PandocMonad(..), PandocError(..), - readDataFile, ReaderOptions(..), pandocExtensions, - report, LogMessage(..), fetchItem) +import Text.Pandoc.Class (PandocMonad(..), getResourcePath, getUserDataDir, + fetchItem, readDataFile, report, setResourcePath) +import Text.Pandoc.Error (PandocError(..)) +import Text.Pandoc.Extensions (pandocExtensions) +import Text.Pandoc.Logging (LogMessage(..)) +import Text.Pandoc.Options (ReaderOptions(..)) import Text.Pandoc.Shared (stringify, ordNub, blocksToInlines, tshow) import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Walk (query, walk, walkM) +import Control.Applicative ((<|>)) +import Control.Monad.Except (catchError, throwError) +import Control.Monad.State (State, evalState, get, put, runState) import Data.Aeson (eitherDecode) -import Data.Default -import Data.Ord () +import Data.ByteString (ByteString) +import qualified Data.ByteString.Lazy as L +import Data.Char (isPunctuation, isUpper) +import Data.Default (Default(def)) +import qualified Data.Foldable as Foldable import qualified Data.Map as M +import Data.Maybe (mapMaybe, fromMaybe) +import Data.Ord () +import qualified Data.Sequence as Seq import qualified Data.Set as Set -import Data.Char (isPunctuation, isUpper) import Data.Text (Text) import qualified Data.Text as T -import Control.Monad.State -import qualified Data.Sequence as Seq -import qualified Data.Foldable as Foldable -import System.FilePath -import Control.Applicative -import Control.Monad.Except -import Data.Maybe (mapMaybe, fromMaybe) +import System.FilePath (takeExtension) import Safe (lastMay, initSafe) -- import Debug.Trace as Trace (trace, traceShowId) -- cgit v1.2.3 From fa952c8dbea9ad47ea684729f862a3c6bdd0fecc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 20 Jan 2021 21:08:01 -0800 Subject: Add biblatex, bibtex as output formats (closes #7040). * `biblatex` and `bibtex` are now supported as output as well as input formats. * New module Text.Pandoc.Writers.BibTeX, exporting writeBibTeX and writeBibLaTeX. [API change] * New unexported function `writeBibtexString` in Text.Pandoc.Citeproc.BibTeX. --- MANUAL.txt | 8 +- pandoc.cabal | 1 + src/Text/Pandoc/Citeproc/BibTeX.hs | 254 ++++++++++++++++++++++++++++++++++++- src/Text/Pandoc/Templates.hs | 2 + src/Text/Pandoc/Writers.hs | 5 + src/Text/Pandoc/Writers/BibTeX.hs | 48 +++++++ 6 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 src/Text/Pandoc/Writers/BibTeX.hs diff --git a/MANUAL.txt b/MANUAL.txt index b9c4ef637..436e9d36c 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -281,6 +281,8 @@ header when requesting a document from a URL: ::: {#output-formats} - `asciidoc` ([AsciiDoc]) or `asciidoctor` ([AsciiDoctor]) - `beamer` ([LaTeX beamer][`beamer`] slide show) + - `bibtex` ([BibTeX] bibliography) + - `biblatex` ([BibLaTeX] bibliography) - `commonmark` ([CommonMark] Markdown) - `commonmark_x` ([CommonMark] Markdown with extensions) - `context` ([ConTeXt]) @@ -5258,11 +5260,11 @@ section from a BibTeX, BibLaTeX, or CSL JSON bibliography: pandoc chem.bib -s -f biblatex -t markdown pandoc chem.json -s -f csljson -t markdown -`pandoc` can also be used to produce CSL JSON bibliography -from BibTeX, BibLaTeX, or markdown YAML: +Indeed, `pandoc` can convert between any of these +citation formats: pandoc chem.bib -s -f biblatex -t csljson - pandoc chem.yaml -s -f markdown -t csljson + pandoc chem.yaml -s -f markdown -t biblatex Running pandoc on a bibliography file with the `--citeproc` option will create a formatted bibliography in the format diff --git a/pandoc.cabal b/pandoc.cabal index 0d63cbe35..27c25069b 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -573,6 +573,7 @@ library Text.Pandoc.Writers.Shared, Text.Pandoc.Writers.OOXML, Text.Pandoc.Writers.AnnotatedTable, + Text.Pandoc.Writers.BibTeX, Text.Pandoc.Lua, Text.Pandoc.PDF, Text.Pandoc.UTF8, diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index ed723a11c..10730a1e9 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -1,4 +1,5 @@ {-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} @@ -17,6 +18,7 @@ module Text.Pandoc.Citeproc.BibTeX ( Variant(..) , readBibtexString + , writeBibtexString ) where @@ -24,10 +26,11 @@ import Text.Pandoc.Definition import Text.Pandoc.Builder as B import Text.Pandoc.Readers.LaTeX (readLaTeX) import Text.Pandoc.Extensions (Extension(..), extensionsFromList) -import Text.Pandoc.Options (ReaderOptions(..)) -import Text.Pandoc.Class (runPure) +import Text.Pandoc.Options (ReaderOptions(..), WriterOptions) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Shared (stringify) +import Text.Pandoc.Writers.LaTeX (writeLaTeX) +import Text.Pandoc.Class (runPure) import qualified Text.Pandoc.Walk as Walk import Citeproc.Types import Citeproc.Pandoc () @@ -46,8 +49,9 @@ import qualified Data.Sequence as Seq import Data.Char (isAlphaNum, isDigit, isLetter, isUpper, toLower, toUpper, isLower, isPunctuation) -import Data.List (foldl', intercalate) +import Data.List (foldl', intercalate, intersperse) import Safe (readMay) +import Text.Printf (printf) data Variant = Bibtex | Biblatex deriving (Show, Eq, Ord) @@ -68,6 +72,250 @@ readBibtexString variant locale idpred contents = do Left err -> Left err Right xs -> return xs +-- | Write BibTeX or BibLaTeX given given a 'Reference'. +writeBibtexString :: WriterOptions -- ^ options (for writing LaTex) + -> Variant -- ^ bibtex or biblatex + -> Maybe Lang -- ^ Language + -> Reference Inlines -- ^ Reference to write + -> Text +writeBibtexString opts variant mblang ref = + "@" <> bibtexType <> "{" <> unItemId (referenceId ref) <> ",\n " <> + renderFields fs <> "\n}\n" + + where + bibtexType = + case referenceType ref of + "article-magazine" -> "article" + "article-newspaper" -> "article" + "article-journal" -> "article" + "book" -> "book" + "pamphlet" -> "booklet" + "dataset" | variant == Biblatex -> "dataset" + "webpage" | variant == Biblatex -> "online" + "chapter" -> case getVariable "editor" of + Just _ -> "incollection" + Nothing -> "inbook" + "entry-encyclopedia" | variant == Biblatex -> "inreference" + | otherwise -> "inbook" + "paper-conference" -> "inproceedings" + "thesis" -> case getVariableAsText "genre" of + Just "mathesis" -> "mastersthesis" + _ -> "phdthesis" + "patent" | variant == Biblatex -> "patent" + "report" | variant == Biblatex -> "report" + | otherwise -> "techreport" + "speech" -> "unpublished" + "manuscript" -> "unpublished" + "graphic" | variant == Biblatex -> "artwork" + "song" | variant == Biblatex -> "music" + "legal_case" | variant == Biblatex -> "jurisdictionN" + "legislation" | variant == Biblatex -> "legislation" + "treaty" | variant == Biblatex -> "legal" + "personal_communication" | variant == Biblatex -> "letter" + "motion_picture" | variant == Biblatex -> "movie" + "review" | variant == Biblatex -> "review" + _ -> "misc" + + mbSubtype = + case referenceType ref of + "article-magazine" -> Just "magazine" + "article-newspaper" -> Just "newspaper" + _ -> Nothing + + fs = + case variant of + Biblatex -> + [ "author" + , "editor" + , "translator" + , "publisher" + , "title" + , "booktitle" + , "journal" + , "series" + , "edition" + , "volume" + , "volumes" + , "number" + , "pages" + , "date" + , "eventdate" + , "urldate" + , "address" + , "url" + , "doi" + , "isbn" + , "issn" + , "type" + , "entrysubtype" + , "note" + , "language" + , "abstract" + , "keywords" + ] + Bibtex -> + [ "author" + , "editor" + , "translator" + , "publisher" + , "title" + , "booktitle" + , "journal" + , "series" + , "edition" + , "volume" + , "number" + , "pages" + , "year" + , "month" + , "address" + , "type" + , "note" + ] + + valToInlines (TextVal t) = B.text t + valToInlines (FancyVal ils) = ils + valToInlines (NumVal n) = B.text (T.pack $ show n) + valToInlines (NamesVal names) = + mconcat $ intersperse (B.space <> B.text "and" <> B.space) + $ map renderName names + valToInlines (DateVal date) = B.text $ + case dateLiteral date of + Just t -> t + Nothing -> T.intercalate "/" (map renderDatePart (dateParts date)) <> + (if dateCirca date then "~" else mempty) + + renderDatePart (DateParts xs) = T.intercalate "-" $ + map (T.pack . printf "%02d") xs + + renderName name = + case nameLiteral name of + Just t -> B.text t + Nothing -> spacedMaybes + [ nameNonDroppingParticle name + , nameFamily name + , if nameCommaSuffix name + then (", " <>) <$> nameSuffix name + else nameSuffix name ] + <> + spacedMaybes + [ (", " <>) <$> nameGiven name, + nameDroppingParticle name ] + + titlecase = case mblang of + Just (Lang "en" _) -> titlecase' + Nothing -> titlecase' + _ -> id + + titlecase' = addTextCase mblang TitleCase . + (\ils -> B.fromList + (case B.toList ils of + Str t : xs -> Str t : Walk.walk spanAroundCapitalizedWords xs + xs -> Walk.walk spanAroundCapitalizedWords xs)) + + -- protect capitalized words when we titlecase + spanAroundCapitalizedWords (Str t) + | not (T.all (\c -> isLower c || not (isLetter c)) t) = + Span ("",["nocase"],[]) [Str t] + spanAroundCapitalizedWords x = x + + spacedMaybes = mconcat . intersperse B.space . mapMaybe (fmap B.text) + + toLaTeX x = + case runPure (writeLaTeX opts $ doc (B.plain x)) of + Left _ -> Nothing + Right t -> Just t + + renderField name = (\contents -> name <> " = {" <> contents <> "}") + <$> getContentsFor name + + getVariable v = lookupVariable (toVariable v) ref + + getVariableAsText v = (stringify . valToInlines) <$> getVariable v + + getYear val = + case val of + DateVal date -> + case dateLiteral date of + Just t -> toLaTeX (B.text t) + Nothing -> + case dateParts date of + [DateParts (y1:_), DateParts (y2:_)] -> + Just (T.pack (printf "%04d" y1) <> "--" <> + T.pack (printf "%04d" y2)) + [DateParts (y1:_)] -> + Just (T.pack (printf "%04d" y1)) + _ -> Nothing + _ -> Nothing + + toMonth 1 = "jan" + toMonth 2 = "feb" + toMonth 3 = "mar" + toMonth 4 = "apr" + toMonth 5 = "may" + toMonth 6 = "jun" + toMonth 7 = "jul" + toMonth 8 = "aug" + toMonth 9 = "sep" + toMonth 10 = "oct" + toMonth 11 = "nov" + toMonth 12 = "dec" + toMonth x = T.pack $ show x + + getMonth val = + case val of + DateVal date -> + case dateParts date of + [DateParts (_:m1:_), DateParts (_:m2:_)] -> + Just (toMonth m1 <> "--" <> toMonth m2) + [DateParts (_:m1:_)] -> Just (toMonth m1) + _ -> Nothing + _ -> Nothing + + getContentsFor :: Text -> Maybe Text + getContentsFor "type" = + getVariableAsText "genre" >>= + \case + "mathesis" -> Just "mastersthesis" + "phdthesis" -> Just "phdthesis" + _ -> Nothing + getContentsFor "entrysubtype" = mbSubtype + getContentsFor "journal" + | bibtexType `elem` ["article", "periodical", "suppperiodical", "review"] + = getVariable "container-title" >>= toLaTeX . valToInlines + | otherwise = Nothing + getContentsFor "booktitle" + | bibtexType `elem` + ["inbook","incollection","inproceedings","inreference","bookinbook"] + = (getVariable "volume-title" <|> getVariable "container-title") + >>= toLaTeX . valToInlines + | otherwise = Nothing + getContentsFor "series" = getVariable "collection-title" + >>= toLaTeX . valToInlines + getContentsFor "address" = getVariable "publisher-place" + >>= toLaTeX . valToInlines + getContentsFor "date" = getVariable "issued" >>= toLaTeX . valToInlines + getContentsFor "eventdate" = getVariable "event-date" >>= toLaTeX . valToInlines + getContentsFor "urldate" = getVariable "accessed" >>= toLaTeX . valToInlines + getContentsFor "year" = getVariable "issued" >>= getYear + getContentsFor "month" = getVariable "issued" >>= getMonth + getContentsFor "number" = (getVariable "number" + <|> getVariable "collection-number" + <|> getVariable "issue") >>= toLaTeX . valToInlines + + getContentsFor x = getVariable x >>= + if isURL x + then Just . stringify . valToInlines + else toLaTeX . + (if x == "title" + then titlecase + else id) . + valToInlines + + isURL x = x `elem` ["url","doi","issn","isbn"] + + renderFields = T.intercalate ",\n " . mapMaybe renderField + defaultLang :: Lang defaultLang = Lang "en" (Just "US") diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index e83f26329..3e539bff7 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -81,6 +81,8 @@ getDefaultTemplate writer = do case format of "native" -> return "" "csljson" -> return "" + "bibtex" -> return "" + "biblatex" -> return "" "json" -> return "" "docx" -> return "" "fb2" -> return "" diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 49531d924..95d6270b5 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -21,6 +21,8 @@ module Text.Pandoc.Writers , writeAsciiDoc , writeAsciiDoctor , writeBeamer + , writeBibTeX + , writeBibLaTeX , writeCommonMark , writeConTeXt , writeCustom @@ -85,6 +87,7 @@ import Text.Pandoc.Options import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Error import Text.Pandoc.Writers.AsciiDoc +import Text.Pandoc.Writers.BibTeX import Text.Pandoc.Writers.CommonMark import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.CslJson @@ -185,6 +188,8 @@ writers = [ ,("tei" , TextWriter writeTEI) ,("muse" , TextWriter writeMuse) ,("csljson" , TextWriter writeCslJson) + ,("bibtex" , TextWriter writeBibTeX) + ,("biblatex" , TextWriter writeBibLaTeX) ] -- | Retrieve writer, extensions based on formatSpec (format+extensions). diff --git a/src/Text/Pandoc/Writers/BibTeX.hs b/src/Text/Pandoc/Writers/BibTeX.hs new file mode 100644 index 000000000..e1cb47ca1 --- /dev/null +++ b/src/Text/Pandoc/Writers/BibTeX.hs @@ -0,0 +1,48 @@ +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.Writers.BibTeX + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Writes a BibTeX or BibLaTeX bibliographies based on the +'references' metadata in a Pandoc document. +-} +module Text.Pandoc.Writers.BibTeX + ( writeBibTeX + , writeBibLaTeX + ) +where + +import Text.Pandoc.Options +import Text.Pandoc.Definition +import Data.Text (Text) +import Data.Maybe (mapMaybe) +import Citeproc (parseLang) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Citeproc.BibTeX as BibTeX +import Text.Pandoc.Citeproc.MetaValue (metaValueToReference) +import Text.Pandoc.Writers.Shared (lookupMetaString) + +-- | Write BibTeX based on the references metadata from a Pandoc document. +writeBibTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text +writeBibTeX = writeBibTeX' BibTeX.Bibtex + +-- | Write BibLaTeX based on the references metadata from a Pandoc document. +writeBibLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text +writeBibLaTeX = writeBibTeX' BibTeX.Biblatex + +writeBibTeX' :: PandocMonad m => Variant -> WriterOptions -> Pandoc -> m Text +writeBibTeX' variant opts (Pandoc meta _) = do + let mblang = case lookupMetaString "lang" meta of + "" -> Nothing + t -> Just $ parseLang t + let refs = case lookupMeta "references" meta of + Just (MetaList xs) -> mapMaybe metaValueToReference xs + _ -> [] + return $ mconcat $ + map (BibTeX.writeBibtexString opts variant mblang) refs + -- cgit v1.2.3 From b4b3560191b3699dd4db9d069244925a3c6074db Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 20 Jan 2021 19:09:36 +0100 Subject: JATS writer: allow to use element-citation --- MANUAL.txt | 7 ++ pandoc.cabal | 1 + src/Text/Pandoc/Extensions.hs | 14 +++ src/Text/Pandoc/Writers/JATS.hs | 21 ++-- src/Text/Pandoc/Writers/JATS/References.hs | 160 +++++++++++++++++++++++++++++ src/Text/Pandoc/Writers/JATS/Types.hs | 4 +- test/command/7042.md | 146 ++++++++++++++++++++++++++ 7 files changed, 346 insertions(+), 7 deletions(-) create mode 100644 src/Text/Pandoc/Writers/JATS/References.hs create mode 100644 test/command/7042.md diff --git a/MANUAL.txt b/MANUAL.txt index b9c4ef637..7bf74a8f9 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3093,6 +3093,13 @@ output format. Some aspects of [Pandoc's Markdown citation syntax](#citations) are also accepted in `org` input. +#### Extension: `element_citations` #### + +In the `jats` output formats, this causes reference items to +be replaced with `` elements. These +elements are not influenced by CSL styles, but all information +on the item is included in tags. + #### Extension: `ntb` #### In the `context` output format this enables the use of [Natural Tables diff --git a/pandoc.cabal b/pandoc.cabal index 0d63cbe35..07feb10dd 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -638,6 +638,7 @@ library Text.Pandoc.Readers.Metadata, Text.Pandoc.Readers.Roff, Text.Pandoc.Writers.Docx.StyleMap, + Text.Pandoc.Writers.JATS.References, Text.Pandoc.Writers.JATS.Table, Text.Pandoc.Writers.JATS.Types, Text.Pandoc.Writers.LaTeX.Caption, diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 39c2a0489..7aa32c52c 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -88,6 +88,7 @@ data Extension = -- does not affect readers/writers directly; it causes -- the eastAsianLineBreakFilter to be applied after -- parsing, in Text.Pandoc.App.convertWithOpts. + | Ext_element_citations -- ^ Use element-citation elements for JATS citations | Ext_emoji -- ^ Support emoji like :smile: | Ext_empty_paragraphs -- ^ Allow empty paragraphs | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML @@ -412,6 +413,11 @@ getDefaultExtensions "textile" = extensionsFromList Ext_smart, Ext_raw_html, Ext_auto_identifiers] +getDefaultExtensions "jats" = extensionsFromList + [Ext_auto_identifiers] +getDefaultExtensions "jats_archiving" = getDefaultExtensions "jats" +getDefaultExtensions "jats_publishing" = getDefaultExtensions "jats" +getDefaultExtensions "jats_articleauthoring" = getDefaultExtensions "jats" getDefaultExtensions "opml" = pandocExtensions -- affects notes getDefaultExtensions _ = extensionsFromList [Ext_auto_identifiers] @@ -554,6 +560,14 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_smart , Ext_raw_tex ] + getAll "jats" = + extensionsFromList + [ Ext_auto_identifiers + , Ext_element_citations + ] + getAll "jats_archiving" = getAll "jats" + getAll "jats_publishing" = getAll "jats" + getAll "jats_articleauthoring" = getAll "jats" getAll "opml" = allMarkdownExtensions -- affects notes getAll "twiki" = autoIdExtensions <> extensionsFromList diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index c75d40745..a9369db7a 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -29,6 +29,7 @@ import Data.Maybe (fromMaybe) import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime) import qualified Data.Text as T import Data.Text (Text) +import Text.Pandoc.Citeproc (getReferences) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) @@ -40,6 +41,7 @@ import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.DocTemplates (Context(..), Val(..)) +import Text.Pandoc.Writers.JATS.References (referencesToJATS) import Text.Pandoc.Writers.JATS.Table (tableToJATS) import Text.Pandoc.Writers.JATS.Types import Text.Pandoc.Writers.Math @@ -71,15 +73,19 @@ writeJATS = writeJatsArchiving -- | Convert a @'Pandoc'@ document to JATS. writeJats :: PandocMonad m => JATSTagSet -> WriterOptions -> Pandoc -> m Text -writeJats tagSet opts d = - runReaderT (evalStateT (docToJATS opts d) initialState) - environment - where initialState = JATSState { jatsNotes = [] } - environment = JATSEnv +writeJats tagSet opts d = do + refs <- if extensionEnabled Ext_element_citations $ writerExtensions opts + then getReferences Nothing d + else pure [] + let environment = JATSEnv { jatsTagSet = tagSet , jatsInlinesWriter = inlinesToJATS , jatsBlockWriter = blockToJATS + , jatsReferences = refs } + let initialState = JATSState { jatsNotes = [] } + runReaderT (evalStateT (docToJATS opts d) initialState) + environment -- | Convert Pandoc document to string in JATS format. docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> JATS m Text @@ -258,7 +264,10 @@ blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident = inTagsSimple "mixed-citation" <$> inlinesToJATS opts lst blockToJATS opts (Div ("refs",_,_) xs) = do - contents <- blocksToJATS opts xs + refs <- asks jatsReferences + contents <- if null refs + then blocksToJATS opts xs + else referencesToJATS opts refs return $ inTagsIndented "ref-list" contents blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do contents <- blocksToJATS opts bs diff --git a/src/Text/Pandoc/Writers/JATS/References.hs b/src/Text/Pandoc/Writers/JATS/References.hs new file mode 100644 index 000000000..4ee7eb9dd --- /dev/null +++ b/src/Text/Pandoc/Writers/JATS/References.hs @@ -0,0 +1,160 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.Writers.JATS.References + Copyright : © 2021 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel + Stability : alpha + Portability : portable + +Creation of a bibliography list using @@ elements in +reference items. +-} +module Text.Pandoc.Writers.JATS.References + ( referencesToJATS + , referenceToJATS + ) where + +import Citeproc.Pandoc () +import Citeproc.Types + ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..) + , Val (..) , lookupVariable, valToText + ) +import Data.Text (Text) +import Text.DocLayout (Doc, empty, isEmpty, literal, vcat) +import Text.Pandoc.Class.PandocMonad (PandocMonad) +import Text.Pandoc.Builder (Inlines) +import Text.Pandoc.Options (WriterOptions) +import Text.Pandoc.Shared (tshow) +import Text.Pandoc.Writers.JATS.Types +import Text.Pandoc.XML (inTags) +import qualified Data.Text as T + +referencesToJATS :: PandocMonad m + => WriterOptions + -> [Reference Inlines] + -> JATS m (Doc Text) +referencesToJATS opts = + fmap (inTags True "ref-list" [] . vcat) . mapM (referenceToJATS opts) + +referenceToJATS :: PandocMonad m + => WriterOptions + -> Reference Inlines + -> JATS m (Doc Text) +referenceToJATS _opts ref = do + let refType = referenceType ref + let pubType = [("publication-type", refType) | not (T.null refType)] + let wrap = inTags True "ref" [("id", "ref-" <> unItemId (referenceId ref))] + . inTags True "element-citation" pubType + return . wrap . vcat $ + [ authors + , "title" `varInTag` + if refType == "book" + then "source" + else "article-title" + , if refType == "book" + then empty + else "container-title" `varInTag` "source" + , editors + , "publisher" `varInTag` "publisher-name" + , "publisher-place" `varInTag` "publisher-loc" + , yearTag + , accessed + , "volume" `varInTag` "volume" + , "issue" `varInTag` "issue" + , "page-first" `varInTag` "fpage" + , "page-last" `varInTag` "lpage" + , "pages" `varInTag` "page-range" + , "ISBN" `varInTag` "isbn" + , "ISSN" `varInTag` "issn" + , varInTagWith "doi" "pub-id" [("pub-id-type", "doi")] + , varInTagWith "pmid" "pub-id" [("pub-id-type", "pmid")] + ] + where + varInTag var tagName = varInTagWith var tagName [] + + varInTagWith var tagName tagAttribs = + case lookupVariable var ref >>= valToText of + Nothing -> mempty + Just val -> inTags' tagName tagAttribs $ literal val + + authors = case lookupVariable "author" ref of + Just (NamesVal names) -> + inTags True "person-group" [("person-group-type", "author")] . vcat $ + map toNameElements names + _ -> empty + + editors = case lookupVariable "editor" ref of + Just (NamesVal names) -> + inTags True "person-group" [("person-group-type", "editor")] . vcat $ + map toNameElements names + _ -> empty + + yearTag = + case lookupVariable "issued" ref of + Just (DateVal date) -> toDateElements date + _ -> empty + + accessed = + case lookupVariable "accessed" ref of + Just (DateVal d) -> inTags' "date-in-citation" + [("content-type", "access-date")] + (toDateElements d) + _ -> empty + +toDateElements :: Date -> Doc Text +toDateElements date = + case dateParts date of + dp@(DateParts (y:m:d:_)):_ -> yearElement y dp <> + monthElement m <> + dayElement d + dp@(DateParts (y:m:_)):_ -> yearElement y dp <> monthElement m + dp@(DateParts (y:_)):_ -> yearElement y dp + _ -> empty + +yearElement :: Int -> DateParts -> Doc Text +yearElement year dp = + inTags' "year" [("iso-8601-date", iso8601 dp)] $ literal (fourDigits year) + +monthElement :: Int -> Doc Text +monthElement month = inTags' "month" [] . literal $ twoDigits month + +dayElement :: Int -> Doc Text +dayElement day = inTags' "day" [] . literal $ twoDigits day + +iso8601 :: DateParts -> Text +iso8601 = T.intercalate "-" . \case + DateParts (y:m:d:_) -> [fourDigits y, twoDigits m, twoDigits d] + DateParts (y:m:_) -> [fourDigits y, twoDigits m] + DateParts (y:_) -> [fourDigits y] + _ -> [] + +twoDigits :: Int -> Text +twoDigits n = T.takeEnd 2 $ '0' `T.cons` tshow n + +fourDigits :: Int -> Text +fourDigits n = T.takeEnd 4 $ "000" <> tshow n + +toNameElements :: Name -> Doc Text +toNameElements name = + if not (isEmpty nameTags) + then inTags' "name" [] nameTags + else nameLiteral name `inNameTag` "string-name" + where + inNameTag val tag = maybe empty (inTags' tag [] . literal) val + surnamePrefix = maybe mempty (`T.snoc` ' ') $ + nameNonDroppingParticle name + givenSuffix = maybe mempty (T.cons ' ') $ + nameDroppingParticle name + nameTags = mconcat + [ ((surnamePrefix <>) <$> nameFamily name) `inNameTag` "surname" + , ((<> givenSuffix) <$> nameGiven name) `inNameTag` "given-names" + , nameSuffix name `inNameTag` "suffix" + ] + +-- | Put the supplied contents between start and end tags of tagType, +-- with specified attributes. +inTags' :: Text -> [(Text, Text)] -> Doc Text -> Doc Text +inTags' = inTags False diff --git a/src/Text/Pandoc/Writers/JATS/Types.hs b/src/Text/Pandoc/Writers/JATS/Types.hs index 54ed4a8bd..6fdddc0b5 100644 --- a/src/Text/Pandoc/Writers/JATS/Types.hs +++ b/src/Text/Pandoc/Writers/JATS/Types.hs @@ -17,11 +17,12 @@ module Text.Pandoc.Writers.JATS.Types ) where +import Citeproc.Types (Reference) import Control.Monad.Reader (ReaderT) import Control.Monad.State (StateT) import Data.Text (Text) import Text.DocLayout (Doc) -import Text.Pandoc.Definition (Block, Inline) +import Text.Pandoc.Builder (Block, Inline, Inlines) import Text.Pandoc.Options (WriterOptions) -- | JATS tag set variant @@ -40,6 +41,7 @@ data JATSEnv m = JATSEnv { jatsTagSet :: JATSTagSet , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text) , jatsBlockWriter :: WriterOptions -> Block -> JATS m (Doc Text) + , jatsReferences :: [Reference Inlines] } -- | JATS writer type diff --git a/test/command/7042.md b/test/command/7042.md new file mode 100644 index 000000000..de0294da3 --- /dev/null +++ b/test/command/7042.md @@ -0,0 +1,146 @@ +``` +% pandoc -f markdown -t jats_publishing+element_citations --citeproc -s +--- +nocite: "[@*]" +references: +- author: + - family: Jane + given: Doe + container-title: Public Library of Tests + id: year-month + issued: 1999-08 + title: Year and month + type: article-journal +- accessed: 1999-01-22 + author: + - family: Negidius + given: Numerius + container-title: Public Library of Tests + id: access-date + issued: 1911-10-03 + title: Entry with access date + type: article-journal +- author: + - family: Beethoven + given: Ludwig + dropping-particle: van + - family: Bray + given: Jan + non-dropping-particle: de + container-title: Public Library of Tests + id: name-particles + issued: 1820 + title: Name particles, dropping and non-dropping + type: article-journal +- author: + - 宮水 三葉 + - 立花 瀧 + title: Big Book of Tests + id: book-with-japanese-authors + issued: 2016 + type: book +- author: + - family: Watson + given: J. D. + - family: Crick + given: F. H. C. + container-title: Nature + doi: '10.1038/171737a0' + id: full-journal-article-entry + issue: 4356 + issued: '1953-04-01' + pages: 737-738 + pmid: 13054692 + title: 'Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid' + type: article-journal + volume: 171 +... +^D + + +
+ + + + + + + + + + + + + + + + + + + + + + + JaneDoe + + Year and month + Public Library of Tests + 199908 + + + + + + NegidiusNumerius + + Entry with access date + Public Library of Tests + 19111003 + 19990122 + + + + + + BeethovenLudwig van + de BrayJan + + Name particles, dropping and non-dropping + Public Library of Tests + 1820 + + + + + + 宮水 三葉 + 立花 瀧 + + Big Book of Tests + 2016 + + + + + + WatsonJ. D. + CrickF. H. C. + + Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid + Nature + 19530401 + 171 + 4356 + 737 + 737-738 + 10.1038/171737a0 + 13054692 + + + + + +
+ +``` -- cgit v1.2.3 From f5e3c1dad678732f59d7bd655993cd5ba61a7621 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 22 Jan 2021 10:38:42 -0800 Subject: Use citeproc 0.3.0.5. --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 6806d7c06..f7d48372b 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -410,7 +410,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.4 && < 0.4, + citeproc >= 0.3.0.5 && < 0.4, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/stack.yaml b/stack.yaml index 74367bb8a..8077da570 100644 --- a/stack.yaml +++ b/stack.yaml @@ -18,7 +18,7 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 - texmath-0.12.1 -- citeproc-0.3.0.4 +- citeproc-0.3.0.5 # - citeproc: # git: https://github.com/jgm/citeproc.git # commit: d44e24696ab444090d0e63e321c3a573f68b2e74 -- cgit v1.2.3 From 198ce0cde99731680b62294a7dff1b218b29bd4d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 22 Jan 2021 20:49:41 -0800 Subject: ImageSize: use viewBox for svg if no length, width. This change allows pandoc to extract size information from more SVGs. Closes #7045. --- src/Text/Pandoc/ImageSize.hs | 8 ++++++-- test/command/svg.md | 30 ++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 098c16721..e19958f6a 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -328,12 +328,16 @@ getSize img = svgSize :: WriterOptions -> ByteString -> Maybe ImageSize svgSize opts img = do doc <- Xml.parseXMLDoc $ UTF8.toString img + let viewboxSize = do + vb <- Xml.findAttrBy (== Xml.QName "viewBox" Nothing Nothing) doc + [_,_,w,h] <- mapM safeRead (T.words (T.pack vb)) + return (w,h) let dpi = fromIntegral $ writerDpi opts let dirToInt dir = do dim <- Xml.findAttrBy (== Xml.QName dir Nothing Nothing) doc >>= lengthToDim . T.pack return $ inPixel opts dim - w <- dirToInt "width" - h <- dirToInt "height" + w <- dirToInt "width" <|> (fst <$> viewboxSize) + h <- dirToInt "height" <|> (snd <$> viewboxSize) return ImageSize { pxX = w , pxY = h diff --git a/test/command/svg.md b/test/command/svg.md index 57506570b..4ba836b20 100644 --- a/test/command/svg.md +++ b/test/command/svg.md @@ -36,28 +36,27 @@ % pandoc -f latex -t icml \includegraphics{command/SVG_logo.svg} ^D -[WARNING] Could not determine image size for 'command/SVG_logo.svg': could not determine SVG size - + - - - - + + + + - + $ID/Embedded - + @@ -70,28 +69,27 @@ % pandoc -f latex -t icml \includegraphics{command/SVG_logo-without-xml-declaration.svg} ^D -[WARNING] Could not determine image size for 'command/SVG_logo-without-xml-declaration.svg': could not determine SVG size - + - - - - + + + + - + $ID/Embedded - + -- cgit v1.2.3 From 8e3c44909a8534a88506a06a77a880a3e3a96153 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 22 Jan 2021 21:37:04 -0800 Subject: Update changelog. --- changelog.md | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/changelog.md b/changelog.md index 6fada3e95..34a3002ce 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,154 @@ # Revision history for pandoc +## pandoc 2.11.4 (2021-01-22) + + * Add `biblatex`, `bibtex` as output formats (closes #7040). + + * Recognize more extensions as markdown by default (#7034): + `mkdn`, `mkd`, `mdwn`, `mdown`, `Rmd`. + + * Implement defaults file inheritance (#6924, David Martschenko). + Allow defaults files to inherit options from other defaults files by + specifying them with the following syntax: + `defaults: [list of defaults files or single defaults file]`. + + * Fix infinite HTTP requests when writing epubs from URL source (#7013). + Due to a bug in code added to avoid overwriting the cover image + if it had the form `fileX.YYY`, pandoc made an endless sequence + of HTTP requests when writing epub with input from a URL. + + * Org reader: + + + Allow multiple pipe chars in todo sequences (Albert Krewinkel, #7014). + Additional pipe chars, used to separate "action" state from "no further + action" states, are ignored. E.g., for the following sequence, both + `DONE` and `FINISHED` are states with no further action required: + `#+TODO: UNFINISHED | DONE | FINISHED`. + + Restructure output of captioned code blocks (Albert Krewinkel, #6977). + The Div wrapper of code blocks with captions now has the class + "captioned-content". The caption itself is added as a Plain block + inside a Div of class "caption". This makes it easier to write filters + which match on captioned code blocks. Existing filters will need to be + updated. + + Mark verbatim code with class `verbatim` (Dimitri Sabadie, #6998). + + * LaTeX reader: + + + Handle `filecontents` environment (#7003). + + Put contents of unknown environments in a Div when `raw_tex` is not + enabled (#6997). (When `raw_tex` is enabled, the whole environment is + parsed as a raw block.) The class name is the name of the environment. + Previously, we just included the contents without the surrounding Div, + but having a record of the environment's boundaries and name can be + useful. + + * Mediawiki reader: + + + Allow space around storng/emph delimiters (#6993). + + * New module Text.Pandoc.Writers.BibTeX, exporting + writeBibTeX and writeBibLaTeX. [API change] + + * LaTeX writer: + + + Revert table line height increase in 2.11.3 (#6996). + In 2.11.3 we started adding `\addlinespace`, which produced less dense + tables. This wasn't an intentional change; I misunderstood a comment in + the discussion leading up to the change. This commit restores the earlier + default table appearance. Note that if you want a less dense table, you + can use something like `\def\arraystretch{1.5}` in your header. + + * EPUB writer: + + + Adjust internal links to identifiers defined in raw HTML sections + after splitting into chapters (#7000). + + Recognize `Format "html4"`, `Format "html5"` as raw HTML. + + Adjust internal links to images, links, and tables after splitting into + chapters. Previously we only did this for Div and Span and Header + elements (see #7000). + + * Ms writer: + + + Don't justify text inside table cells. + + * JATS writer: + + + Use `` if `element_citations` + extension is enabled (Albert Krewinkel). + + Fix citations (Albert Krewinkel, #7018). By default + we use formatted citations. + + Ensure that `` is always wrapped in `

` (#7041). + + * Markdown writer: + + + Cleaned up raw formats. We now react appropriately + to `gfm`, `commonmark`, and `commonmark_x` as raw formats. + + * RST writer: + + + Fix bug with dropped content from inside spans with a class in + some cases (#7039). + + * Docx writer: + + + Handle table header using styles (#7008). Instead of hard-coding + the border and header cell vertical alignment, we now let this + be determined by the Table style, making use of Word's + "conditional formatting" for the table's first row. For + headerless tables, we use the tblLook element to tell Word + not to apply conditional first-row formatting. + + * Commonmark writer: + + + Implement start number on ordered lists (#7009). Previously they always + started at 1, but according to the spec the start number is respected. + + * HTML writer: + + + Fix implicit_figure at end of footnotes (#7006). + + * ConTeXt template: Remove `\setupthinrules` from default template. + The width parameter this used is not actually supported, + and the command didn't do anything. + + * Text.Pandoc.Extensions: + + + Add `Ext_element_citations` constructor (Albert Krewinkel). + + * Text.Pandoc.Citeproc.BibTeX: New unexported function + `writeBibtexString`. + + * Text.Pandoc.Citeproc: + + + Use finer grained imports (Albert Krewinkel). + + Factor out and export `getStyle` [API change]. + + Export `getReferences` [API change, #7106]. + + Factor out getLang. + + * Text.Pandoc.Parsing: modify `gridTableWith'` for headerless tables. + If the table lacks a header, the header row should be an empty + list. Previously we got a list of empty cells, which caused + an empty header to be emitted instead of no header. In LaTeX/PDF + output that meant we got a double top line with space between. + + * ImageSize: use `viewBox` for SVG if no length, width attributes (#7045). + This change allows pandoc to extract size information from more SVGs. + + * Add simple default.nix. + + * Use commonmark 0.1.1.3. + + * Use citeproc 0.3.0.5. + + * Update default CSL to use latest chicago-author-date.csl. + + * CONTRIBUTING.md: add note on GNU xargs. + + * MANUAL.txt: + + + Update description of `-L`/`--lua-filter`. + + Document use of citations in note styles (#6828). + ## pandoc 2.11.3.2 (2020-12-29) * HTML reader: use renderTags' from Text.Pandoc.Shared (Albert Krewinkel). -- cgit v1.2.3 From 54d8c6959cc53e78cfea2093b33504672f81ed74 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 22 Jan 2021 21:37:29 -0800 Subject: Update README and man page. --- MANUAL.txt | 2 +- README.md | 2 ++ man/pandoc.1 | 97 +++++++++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 93b7c4a81..d113ea59f 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: December 29, 2020 +date: January 21, 2021 --- # Synopsis diff --git a/README.md b/README.md index 9317438fc..744c52f52 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ It can convert *to* - `asciidoc` ([AsciiDoc](https://www.methods.co.nz/asciidoc/)) or `asciidoctor` ([AsciiDoctor](https://asciidoctor.org/)) - `beamer` ([LaTeX beamer](https://ctan.org/pkg/beamer) slide show) +- `bibtex` ([BibTeX](https://ctan.org/pkg/bibtex) bibliography) +- `biblatex` ([BibLaTeX](https://ctan.org/pkg/biblatex) bibliography) - `commonmark` ([CommonMark](https://commonmark.org) Markdown) - `commonmark_x` ([CommonMark](https://commonmark.org) Markdown with extensions) diff --git a/man/pandoc.1 b/man/pandoc.1 index 8af64c17b..491e24278 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t -.\" Automatically generated by Pandoc 2.11.3.1 +.\" Automatically generated by Pandoc 2.11.4 .\" -.TH "Pandoc User\[cq]s Guide" "" "December 29, 2020" "pandoc 2.11.3.2" "" +.TH "Pandoc User\[cq]s Guide" "" "January 21, 2021" "pandoc 2.11.4" "" .hy .SH NAME pandoc - general markup converter @@ -320,6 +320,10 @@ Specify output format. .IP \[bu] 2 \f[C]beamer\f[R] (LaTeX beamer slide show) .IP \[bu] 2 +\f[C]bibtex\f[R] (BibTeX bibliography) +.IP \[bu] 2 +\f[C]biblatex\f[R] (BibLaTeX bibliography) +.IP \[bu] 2 \f[C]commonmark\f[R] (CommonMark Markdown) .IP \[bu] 2 \f[C]commonmark_x\f[R] (CommonMark Markdown with extensions) @@ -623,8 +627,8 @@ above). .IP "3." 3 \f[C]$PATH\f[R] (executable only) .PP -Filters and Lua-filters are applied in the order specified on the -command line. +Filters, Lua-filters, and citeproc processing are applied in the order +specified on the command line. .RE .TP \f[B]\f[CB]-L\f[B]\f[R] \f[I]SCRIPT\f[R], \f[B]\f[CB]--lua-filter=\f[B]\f[R]\f[I]SCRIPT\f[R] @@ -641,29 +645,17 @@ The \f[C]pandoc\f[R] Lua module provides helper functions for element creation. It is always loaded into the script\[cq]s Lua environment. .PP -The following is an example Lua script for macro-expansion: -.IP -.nf -\f[C] -function expand_hello_world(inline) - if inline.c == \[aq]{{helloworld}}\[aq] then - return pandoc.Emph{ pandoc.Str \[dq]Hello, World\[dq] } - else - return inline - end -end - -return {{Str = expand_hello_world}} -\f[R] -.fi +See the Lua filters documentation for further details. .PP In order of preference, pandoc will look for Lua filters in .IP "1." 3 -a specified full or relative path (executable or non-executable) +a specified full or relative path .IP "2." 3 -\f[C]$DATADIR/filters\f[R] (executable or non-executable) where -\f[C]$DATADIR\f[R] is the user data directory (see \f[C]--data-dir\f[R], -above). +\f[C]$DATADIR/filters\f[R] where \f[C]$DATADIR\f[R] is the user data +directory (see \f[C]--data-dir\f[R], above). +.PP +Filters, Lua filters, and citeproc processing are applied in the order +specified on the command line. .RE .TP \f[B]\f[CB]-M\f[B]\f[R] \f[I]KEY\f[R][\f[B]\f[CB]=\f[B]\f[R]\f[I]VAL\f[R]], \f[B]\f[CB]--metadata=\f[B]\f[R]\f[I]KEY\f[R][\f[B]\f[CB]:\f[B]\f[R]\f[I]VAL\f[R]] @@ -1714,6 +1706,16 @@ input-files: - content.md # or you may use input-file: with a single value +# Include options from the specified defaults files. +# The files will be searched for first in the working directory +# and then in the defaults subdirectory of the user data directory. +# The files are included in the same order in which they appear in +# the list. Options specified in this defaults file always have +# priority over the included ones. +defaults: +- defsA +- defsB + template: letter standalone: true self-contained: false @@ -3437,6 +3439,12 @@ output format. .PP Some aspects of Pandoc\[cq]s Markdown citation syntax are also accepted in \f[C]org\f[R] input. +.SS Extension: \f[C]element_citations\f[R] +.PP +In the \f[C]jats\f[R] output formats, this causes reference items to be +replaced with \f[C]\f[R] elements. +These elements are not influenced by CSL styles, but all information on +the item is included in tags. .SS Extension: \f[C]ntb\f[R] .PP In the \f[C]context\f[R] output format this enables the use of Natural @@ -6137,13 +6145,13 @@ pandoc chem.json -s -f csljson -t markdown \f[R] .fi .PP -\f[C]pandoc\f[R] can also be used to produce CSL JSON bibliography from -BibTeX, BibLaTeX, or markdown YAML: +Indeed, \f[C]pandoc\f[R] can convert between any of these citation +formats: .IP .nf \f[C] pandoc chem.bib -s -f biblatex -t csljson -pandoc chem.yaml -s -f markdown -t csljson +pandoc chem.yaml -s -f markdown -t biblatex \f[R] .fi .PP @@ -6272,6 +6280,41 @@ The format of the file can be illustrated with an example: } \f[R] .fi +.SS Citations in note styles +.PP +Pandoc\[cq]s citation processing is designed to allow you to move +between author-date, numerical, and note styles without modifying the +markdown source. +When you\[cq]re using a note style, avoid inserting footnotes manually. +Instead, insert citations just as you would in an author-date +style\[em]for example, +.IP +.nf +\f[C] +Blah blah [\[at]foo, p. 33]. +\f[R] +.fi +.PP +The footnote will be created automatically. +Pandoc will take care of removing the space and moving the note before +or after the period, depending on the setting of +\f[C]notes-after-punctuation\f[R], as described below in Other relevant +metadata fields. +.PP +In some cases you may need to put a citation inside a regular footnote. +Normal citations in footnotes (such as \f[C][\[at]foo, p. 33]\f[R]) will +be rendered in parentheses. +In-text citations (such as \f[C]\[at]foo [p. 33]\f[R]) will be rendered +without parentheses. +(A comma will be added if appropriate.) Thus: +.IP +.nf +\f[C] +[\[ha]1]: Some studies [\[at]foo; \[at]bar, p. 33] show that +frubulicious zoosnaps are quantical. For a survey +of the literature, see \[at]baz [chap. 1]. +\f[R] +.fi .SS Raw content in a style .PP To include raw content in a prefix, suffix, delimiter, or term, surround @@ -7445,7 +7488,7 @@ To be safe, you should run all the generated HTML through an HTML sanitizer. .SH AUTHORS .PP -Copyright 2006\[en]2020 John MacFarlane (jgm\[at]berkeley.edu). +Copyright 2006\[en]2021 John MacFarlane (jgm\[at]berkeley.edu). Released under the GPL, version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) For a full list -- cgit v1.2.3 From 3f9eee473dfa4eca7001cfe9ec57399de7a28e4a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 23 Jan 2021 15:54:38 -0800 Subject: Update AUTHORS.md. --- AUTHORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index f7be02865..f2f0b7e2e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -62,11 +62,13 @@ - Daniele D'Orazio - David A Roberts - David Lazar +- David Martschenko - David Röthlisberger - Denis Laxalde - Denis Maier - Derek Chen-Becker - Diego Balseiro +- Dimitri Sabadie - Dmitry Pogodin - Dmitry Volodin - Douglas Calvert -- cgit v1.2.3 From 490065f3ed3dd9377a740ad6fcbc441a658889dd Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 26 Jan 2021 14:19:30 +0100 Subject: Lua: always load built-in Lua scripts from default data-dir The Lua modules `pandoc` and `pandoc.List` are now always loaded from the system's default data directory. Loading from a different directory by overriding the default path, e.g. via `--data-dir`, is no longer supported to avoid unexpected behavior and to address security concerns. --- src/Text/Pandoc/Lua/Init.hs | 18 ++++++++++++++---- src/Text/Pandoc/Lua/Module/Pandoc.hs | 8 ++++---- src/Text/Pandoc/Lua/Packages.hs | 33 +++++++-------------------------- src/Text/Pandoc/Lua/PandocLua.hs | 31 +++++++++++++++++++------------ 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index 0a5ce85cb..baa6f0295 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -12,17 +12,18 @@ module Text.Pandoc.Lua.Init ( runLua ) where +import Control.Monad (when) import Control.Monad.Catch (try) import Control.Monad.Trans (MonadIO (..)) import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr) import Foreign.Lua (Lua) import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) +import Text.Pandoc.Class.PandocMonad (readDataFile) import Text.Pandoc.Class.PandocIO (PandocIO) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Packages (installPandocPackageSearcher) -import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, - loadScriptFromDataDir, runPandocLua) - +import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, runPandocLua) +import Text.Pandoc.Lua.Util (throwTopMessageAsError') import qualified Foreign.Lua as Lua import qualified Text.Pandoc.Definition as Pandoc import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc @@ -44,7 +45,7 @@ initLuaState = do liftPandocLua Lua.openlibs installPandocPackageSearcher initPandocModule - loadScriptFromDataDir "init.lua" + loadInitScript "init.lua" where initPandocModule :: PandocLua () initPandocModule = do @@ -61,6 +62,15 @@ initLuaState = do -- assign module to global variable liftPandocLua $ Lua.setglobal "pandoc" + loadInitScript :: FilePath -> PandocLua () + loadInitScript scriptFile = do + script <- readDataFile scriptFile + status <- liftPandocLua $ Lua.dostring script + when (status /= Lua.OK) . liftPandocLua $ + throwTopMessageAsError' + (("Couldn't load '" ++ scriptFile ++ "'.\n") ++) + + -- | AST elements are marshaled via normal constructor functions in the -- @pandoc@ module. However, accessing Lua globals from Haskell is -- expensive (due to error handling). Accessing the Lua registry is much diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index a9ce3866d..a8afecd2e 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -25,7 +25,7 @@ import Text.Pandoc.Definition (Block, Inline) import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..)) import Text.Pandoc.Lua.Marshaling () import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua, - loadScriptFromDataDir) + loadDefaultModule) import Text.Pandoc.Walk (Walkable) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Process (pipeProcess) @@ -38,11 +38,11 @@ import qualified Foreign.Lua as Lua import qualified Text.Pandoc.Lua.Util as LuaUtil import Text.Pandoc.Error --- | Push the "pandoc" on the lua stack. Requires the `list` module to be --- loaded. +-- | Push the "pandoc" package to the Lua stack. Requires the `List` +-- module to be loadable. pushModule :: PandocLua NumResults pushModule = do - loadScriptFromDataDir "pandoc.lua" + loadDefaultModule "pandoc" addFunction "read" readDoc addFunction "pipe" pipeFn addFunction "walk_block" walkBlock diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index d62fb725d..5949a1a7d 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Lua.Packages Copyright : Copyright © 2017-2021 Albert Krewinkel @@ -15,13 +12,9 @@ module Text.Pandoc.Lua.Packages ( installPandocPackageSearcher ) where -import Control.Monad.Catch (try) import Control.Monad (forM_) -import Data.ByteString (ByteString) -import Foreign.Lua (Lua, NumResults) -import Text.Pandoc.Error (PandocError) -import Text.Pandoc.Class.PandocMonad (readDataFile) -import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua) +import Foreign.Lua (NumResults) +import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, loadDefaultModule) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Text @@ -54,24 +47,12 @@ pandocPackageSearcher pkgName = "pandoc.types" -> pushWrappedHsFun Types.pushModule "pandoc.utils" -> pushWrappedHsFun Utils.pushModule "text" -> pushWrappedHsFun Text.pushModule - _ -> searchPureLuaLoader + "pandoc.List" -> pushWrappedHsFun (loadDefaultModule pkgName) + _ -> reportPandocSearcherFailure where pushWrappedHsFun f = liftPandocLua $ do Lua.pushHaskellFunction f return 1 - searchPureLuaLoader = do - let filename = pkgName ++ ".lua" - try (readDataFile filename) >>= \case - Right script -> pushWrappedHsFun (loadStringAsPackage pkgName script) - Left (_ :: PandocError) -> liftPandocLua $ do - Lua.push ("\n\tno file '" ++ filename ++ "' in pandoc's datadir") - return (1 :: NumResults) - -loadStringAsPackage :: String -> ByteString -> Lua NumResults -loadStringAsPackage pkgName script = do - status <- Lua.dostring script - if status == Lua.OK - then return (1 :: NumResults) - else do - msg <- Lua.popValue - Lua.raiseError ("Error while loading `" <> pkgName <> "`.\n" <> msg) + reportPandocSearcherFailure = liftPandocLua $ do + Lua.push ("\n\t" <> pkgName <> "is not one of pandoc's default packages") + return (1 :: NumResults) diff --git a/src/Text/Pandoc/Lua/PandocLua.hs b/src/Text/Pandoc/Lua/PandocLua.hs index 4beac22b7..750e019b6 100644 --- a/src/Text/Pandoc/Lua/PandocLua.hs +++ b/src/Text/Pandoc/Lua/PandocLua.hs @@ -23,24 +23,23 @@ module Text.Pandoc.Lua.PandocLua , runPandocLua , liftPandocLua , addFunction - , loadScriptFromDataDir + , loadDefaultModule ) where -import Control.Monad (when) import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Monad.Except (MonadError (catchError, throwError)) import Control.Monad.IO.Class (MonadIO (liftIO)) import Foreign.Lua (Lua (..), NumResults, Pushable, ToHaskellFunction) import Text.Pandoc.Class.PandocIO (PandocIO) -import Text.Pandoc.Class.PandocMonad (PandocMonad (..), readDataFile) -import Text.Pandoc.Error (PandocError) +import Text.Pandoc.Class.PandocMonad (PandocMonad (..), readDefaultDataFile) +import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.ErrorConversion (errorConversion) import qualified Control.Monad.Catch as Catch +import qualified Data.Text as T import qualified Foreign.Lua as Lua import qualified Text.Pandoc.Class.IO as IO -import qualified Text.Pandoc.Lua.Util as LuaUtil -- | Type providing access to both, pandoc and Lua operations. newtype PandocLua a = PandocLua { unPandocLua :: Lua a } @@ -86,14 +85,22 @@ addFunction name fn = liftPandocLua $ do Lua.pushHaskellFunction fn Lua.rawset (-3) --- | Load a file from pandoc's data directory. -loadScriptFromDataDir :: FilePath -> PandocLua () -loadScriptFromDataDir scriptFile = do - script <- readDataFile scriptFile +-- | Load a pure Lua module included with pandoc. Leaves the result on +-- the stack and returns @NumResults 1@. +-- +-- The script is loaded from the default data directory. We do not load +-- from data directories supplied via command line, as this could cause +-- scripts to be executed even though they had not been passed explicitly. +loadDefaultModule :: String -> PandocLua NumResults +loadDefaultModule name = do + script <- readDefaultDataFile (name <> ".lua") status <- liftPandocLua $ Lua.dostring script - when (status /= Lua.OK) . liftPandocLua $ - LuaUtil.throwTopMessageAsError' - (("Couldn't load '" ++ scriptFile ++ "'.\n") ++) + if status == Lua.OK + then return (1 :: NumResults) + else do + msg <- liftPandocLua Lua.popValue + let err = "Error while loading `" <> name <> "`.\n" <> msg + throwError $ PandocLuaError (T.pack err) -- | Global variables which should always be set. defaultGlobals :: PandocIO [Global] -- cgit v1.2.3 From 12bc6625352aaece955f9f0700f88e9280721ced Mon Sep 17 00:00:00 2001 From: Mauro Bieg Date: Tue, 26 Jan 2021 08:53:26 +0100 Subject: LaTeX writer: change BCP47 lang tag from jp to ja fixes #7047 --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index df922e17b..93603a26e 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1520,7 +1520,7 @@ commonFromBcp47 (Lang l _ _ _) = fromIso l fromIso "ie" = "interlingua" fromIso "is" = "icelandic" fromIso "it" = "italian" - fromIso "jp" = "japanese" + fromIso "ja" = "japanese" fromIso "km" = "khmer" fromIso "kmr" = "kurmanji" fromIso "kn" = "kannada" -- cgit v1.2.3 From 98c2a52b4ee6c833c0a2f2652386cec024e377eb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 26 Jan 2021 22:45:57 -0800 Subject: Clean up BibTeX parsing. Previously there was a messy code path that gave strange results in some cases, not passing through raw tex but trying to extract a string content. This was an artefact of trying to handle some special bibtex-specific commands in the BibTeX reader. Now we just handle these in the LaTeX reader and simplify parsing in the BibTeX reader. This does mean that more raw tex will be passed through (and currently this is not sensitive to the `raw_tex` extension; this should be fixed). Closes #7049. --- src/Text/Pandoc/Citeproc/BibTeX.hs | 33 +-------------------------------- src/Text/Pandoc/Readers/LaTeX.hs | 18 ++++++++++++++++++ test/command/biblatex-cotton.md | 6 +++--- test/command/biblatex-murray.md | 5 +++-- 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 10730a1e9..5b9068378 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -750,41 +750,10 @@ blocksToInlines bs = _ -> B.fromList $ Walk.query (:[]) bs adjustSpans :: Lang -> Inline -> Inline -adjustSpans lang (RawInline (Format "latex") s) - | s == "\\hyphen" || s == "\\hyphen " = Str "-" - | otherwise = parseRawLaTeX lang s +adjustSpans lang (Span ("",[],[("bibstring",s)]) _) = Str $ resolveKey' lang s adjustSpans _ SoftBreak = Space adjustSpans _ x = x -parseRawLaTeX :: Lang -> Text -> Inline -parseRawLaTeX lang t@(T.stripPrefix "\\" -> Just xs) = - case parseLaTeX lang contents of - Right [Para ys] -> f command ys - Right [Plain ys] -> f command ys - Right [] -> f command [] - _ -> RawInline (Format "latex") t - where (command', contents') = T.break (\c -> c =='{' || c =='\\') xs - command = T.strip command' - contents = T.drop 1 $ T.dropEnd 1 contents' - f "mkbibquote" ils = Span nullAttr [Quoted DoubleQuote ils] - f "mkbibemph" ils = Span nullAttr [Emph ils] - f "mkbibitalic" ils = Span nullAttr [Emph ils] - f "mkbibbold" ils = Span nullAttr [Strong ils] - f "mkbibparens" ils = Span nullAttr $ - [Str "("] ++ ils ++ [Str ")"] - f "mkbibbrackets" ils = Span nullAttr $ - [Str "["] ++ ils ++ [Str "]"] - -- ... both should be nestable & should work in year fields - f "autocap" ils = Span nullAttr ils - -- TODO: should work in year fields - f "textnormal" ils = Span ("",["nodecor"],[]) ils - f "bibstring" [Str s] = Str $ resolveKey' lang s - f "adddot" [] = Str "." - f "adddotspace" [] = Span nullAttr [Str ".", Space] - f "addabbrvspace" [] = Space - f _ ils = Span nullAttr ils -parseRawLaTeX _ t = RawInline (Format "latex") t - latex' :: Text -> Bib [Block] latex' t = do lang <- gets localeLang diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f49323996..91c71c000 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -940,6 +940,24 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList , ("uline", underline <$> tok) -- plain tex stuff that should just be passed through as raw tex , ("ifdim", ifdim) + -- bibtex + , ("mkbibquote", spanWith nullAttr . doubleQuoted <$> tok) + , ("mkbibemph", spanWith nullAttr . emph <$> tok) + , ("mkbibitalic", spanWith nullAttr . emph <$> tok) + , ("mkbibbold", spanWith nullAttr . strong <$> tok) + , ("mkbibparens", + spanWith nullAttr . (\x -> str "(" <> x <> str ")") <$> tok) + , ("mkbibbrackets", + spanWith nullAttr . (\x -> str "[" <> x <> str "]") <$> tok) + , ("autocap", spanWith nullAttr <$> tok) + , ("textnormal", spanWith ("",["nodecor"],[]) <$> tok) + , ("bibstring", + (\x -> spanWith ("",[],[("bibstring",x)]) (str x)) . untokenize + <$> braced) + , ("adddot", pure (str ".")) + , ("adddotspace", pure (spanWith nullAttr (str "." <> space))) + , ("addabbrvspace", pure space) + , ("hyphen", pure (str "-")) ] accent :: PandocMonad m => Char -> Maybe Char -> LP m Inlines diff --git a/test/command/biblatex-cotton.md b/test/command/biblatex-cotton.md index 51c3ee2f4..7a7a2e296 100644 --- a/test/command/biblatex-cotton.md +++ b/test/command/biblatex-cotton.md @@ -43,9 +43,9 @@ Cotton, F. A., Wilkinson, G., Murillio, C. A., & Bochmann, M. (1999). --- nocite: "[@*]" references: -- annote: A book entry with author authors and an edition field. By - default, long author and editor lists are automatically truncated. - This is configurable +- annote: A book entry with `\arabic{author}`{=latex} authors and an + edition field. By default, long author and editor lists are + automatically truncated. This is configurable author: - family: Cotton given: Frank Albert diff --git a/test/command/biblatex-murray.md b/test/command/biblatex-murray.md index 78081c6fe..8242e4822 100644 --- a/test/command/biblatex-murray.md +++ b/test/command/biblatex-murray.md @@ -55,8 +55,9 @@ properties as a function of core size. *Langmuir*, *14*(1), 17–30. --- nocite: "[@*]" references: -- annote: An article entry with author authors. By default, long author - and editor lists are automatically truncated. This is configurable +- annote: An article entry with `\arabic{author}`{=latex} authors. By + default, long author and editor lists are automatically truncated. + This is configurable author: - family: Hostetler given: Michael J. -- cgit v1.2.3 From 2ca61a5350003a0327eb9a031c84ae4a2a05807b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 27 Jan 2021 09:22:50 -0800 Subject: Improve docs for cite-method. --- MANUAL.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index d113ea59f..ebed63d2c 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1568,8 +1568,11 @@ data-dir: verbosity: INFO log-file: log.json -# citeproc, natbib, or biblatex +# citeproc, natbib, or biblatex. This only affects LaTeX +# output. If you want to use citeproc to format citations, +# you should also set 'citeproc: true' (see above). cite-method: citeproc + # part, chapter, section, or default: top-level-division: chapter abbreviations: -- cgit v1.2.3 From 300b9b0ea365187240115afbfed0df7fa438a7b3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 29 Jan 2021 09:37:45 +0100 Subject: JATS writer: escape special chars in reference elements. Prevents the generation of invalid markup if a citation element contains an ampersand or another character with a special meaning in XML. --- src/Text/Pandoc/Writers/JATS/References.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS/References.hs b/src/Text/Pandoc/Writers/JATS/References.hs index 4ee7eb9dd..903144128 100644 --- a/src/Text/Pandoc/Writers/JATS/References.hs +++ b/src/Text/Pandoc/Writers/JATS/References.hs @@ -29,7 +29,7 @@ import Text.Pandoc.Builder (Inlines) import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.JATS.Types -import Text.Pandoc.XML (inTags) +import Text.Pandoc.XML (escapeStringForXML, inTags) import qualified Data.Text as T referencesToJATS :: PandocMonad m @@ -78,7 +78,8 @@ referenceToJATS _opts ref = do varInTagWith var tagName tagAttribs = case lookupVariable var ref >>= valToText of Nothing -> mempty - Just val -> inTags' tagName tagAttribs $ literal val + Just val -> inTags' tagName tagAttribs . literal $ + escapeStringForXML val authors = case lookupVariable "author" ref of Just (NamesVal names) -> @@ -143,7 +144,9 @@ toNameElements name = then inTags' "name" [] nameTags else nameLiteral name `inNameTag` "string-name" where - inNameTag val tag = maybe empty (inTags' tag [] . literal) val + inNameTag mVal tag = case mVal of + Nothing -> empty + Just val -> inTags' tag [] . literal $ escapeStringForXML val surnamePrefix = maybe mempty (`T.snoc` ' ') $ nameNonDroppingParticle name givenSuffix = maybe mempty (T.cons ' ') $ -- cgit v1.2.3 From d82fe52a02e43d905f34830162def346d851fc5a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 29 Jan 2021 09:42:25 +0100 Subject: JATS templates: tag author.name as string-name The partitioning the components of a name into surname, given names, etc. is not always possible or not available. Using `author.name` allows to give the full name as a fallback to be used when `author.surname` is not available. --- data/templates/article.jats_publishing | 2 ++ data/templates/default.jats_articleauthoring | 2 ++ doc/jats.md | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/data/templates/article.jats_publishing b/data/templates/article.jats_publishing index 4bdb3f278..15d2e3b08 100644 --- a/data/templates/article.jats_publishing +++ b/data/templates/article.jats_publishing @@ -95,6 +95,8 @@ $if(author.surname)$ $author.surname$ $author.given-names$ +$elseif(author.name)$ +$author.name$ $else$ $author$ $endif$ diff --git a/data/templates/default.jats_articleauthoring b/data/templates/default.jats_articleauthoring index 94c929548..ef2aed769 100644 --- a/data/templates/default.jats_articleauthoring +++ b/data/templates/default.jats_articleauthoring @@ -28,6 +28,8 @@ $if(author.surname)$ $author.surname$ $author.given-names$ +$elseif(author.name)$ +$author.name$ $else$ $author$ $endif$ diff --git a/doc/jats.md b/doc/jats.md index 362ae3bef..0faea8b06 100644 --- a/doc/jats.md +++ b/doc/jats.md @@ -33,6 +33,11 @@ Metadata Values See [``][elem:given-names]. + `name` + : full name of the author; included only as a fallback if + `author.surname` is not available. Tagged with + [``][elem:string-name]. + `email` : the contributor's email address. -- cgit v1.2.3 From 9223788a05fe619d567bcbdf4cb31db63de86f32 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 29 Jan 2021 18:29:17 -0800 Subject: Markdown writer: handle math right before digit. We insert an HTML comment to avoid a `$` right before a digit, which pandoc will not recognize as a math delimiter. --- src/Text/Pandoc/Writers/Markdown.hs | 6 +++++- test/command/7058.md | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/command/7058.md diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index de9075ac4..898905603 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -22,7 +22,7 @@ module Text.Pandoc.Writers.Markdown ( writePlain) where import Control.Monad.Reader import Control.Monad.State.Strict -import Data.Char (isAlphaNum) +import Data.Char (isAlphaNum, isDigit) import Data.Default import Data.List (find, intersperse, sortOn, transpose) import qualified Data.Map as M @@ -987,6 +987,10 @@ inlineListToMarkdown opts lst = do inlist <- asks envInList go (if inlist then avoidBadWrapsInList lst else lst) where go [] = return empty + go (x@Math{}:y@(Str t):zs) + | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058 + = liftM2 (<>) (inlineToMarkdown opts x) + (go (RawInline (Format "html") "" : y : zs)) go (i:is) = case i of Link {} -> case is of -- If a link is followed by another link, or '[', '(' or ':' diff --git a/test/command/7058.md b/test/command/7058.md new file mode 100644 index 000000000..69e5dd445 --- /dev/null +++ b/test/command/7058.md @@ -0,0 +1,6 @@ +``` +% pandoc -f latex -t markdown +5\(-\)8 \(x\) +^D +5$-$``{=html}8 $x$ +``` -- cgit v1.2.3 From a6c7469c85a6239c0148b3dab0b6bb52ce1fe85d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 30 Jan 2021 13:56:43 -0800 Subject: Use latest citeproc. The fix to jgm/citeproc#49 also closes #7060. --- stack.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stack.yaml b/stack.yaml index 8077da570..5ef814487 100644 --- a/stack.yaml +++ b/stack.yaml @@ -18,10 +18,10 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 - texmath-0.12.1 -- citeproc-0.3.0.5 -# - citeproc: -# git: https://github.com/jgm/citeproc.git -# commit: d44e24696ab444090d0e63e321c3a573f68b2e74 +# - citeproc-0.3.0.5 +- citeproc: + git: https://github.com/jgm/citeproc.git + commit: da1f9702fa70d7e1bad9a796f897e920e2d578f7 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 -- cgit v1.2.3 From fe06437ba4ef12782078ac05c6f9c917f32d51f0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 30 Jan 2021 18:01:14 -0800 Subject: Use tasty-bench instead of criterion for benchmarks. It is much lighter-weight. --- Makefile | 1 + benchmark/benchmark-pandoc.hs | 25 ++++++++++++++----------- pandoc.cabal | 3 ++- stack.yaml | 1 + 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index f27272b08..1985a4810 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ RESOLVER?=lts-13 GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 +BENCHARGS?="--timeout=6" quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j +RTS -A64m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index b3e67bb14..2e7600cbc 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -24,8 +24,8 @@ import Control.Monad.Except (throwError, liftIO) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T -import Criterion.Main -import Criterion.Types (Config(..)) +import Test.Tasty.Bench +import Test.Tasty (withResource) import Data.List (intersect) import Data.Maybe (mapMaybe, catMaybes) import System.Environment (getArgs) @@ -59,7 +59,7 @@ readerBench doc name = do <> name return $ case res of Right (readerFun, inp) -> - Just $ bench (T.unpack $ name <> " reader") + Just $ bench (T.unpack name) $ nf (\i -> either (error . show) id $ runPure (readerFun i)) inp Left _ -> Nothing @@ -71,18 +71,18 @@ getImages = do return [("lalune.jpg", "image/jpg", ll) ,("movie.jpg", "image/jpg", mv)] -writerBench :: Pandoc +writerBench :: [(FilePath, MimeType, BL.ByteString)] + -> Pandoc -> T.Text -> Maybe Benchmark -writerBench doc name = +writerBench imgs doc name = case res of Right writerFun -> - Just $ env getImages $ \imgs -> - bench (T.unpack $ name <> " writer") + Just $ bench (T.unpack name) $ nf (\d -> either (error . show) id $ runPure (do mapM_ (\(fp, mt, bs) -> - insertMedia fp (Just mt) bs) + insertMedia fp (Just mt) bs) imgs writerFun d)) doc Left _ -> Nothing @@ -117,6 +117,9 @@ main = do (matchedReaders `intersect` allWriters) -- we need the corresponding writer to generate -- input for the reader - let writerBs = mapMaybe (writerBench doc) matchedWriters - defaultMainWith defaultConfig{ timeLimit = 6.0 } - (writerBs ++ readerBs) + imgs <- getImages + let writerBs = mapMaybe (writerBench imgs doc) matchedWriters + defaultMain + [ bgroup "writers" writerBs + , bgroup "readers" readerBs + ] diff --git a/pandoc.cabal b/pandoc.cabal index f7d48372b..6c8254616 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -880,7 +880,8 @@ benchmark benchmark-pandoc base >= 4.8 && < 5, bytestring, containers, - criterion >= 1.0 && < 1.6, + tasty, + tasty-bench >= 0.1 && <= 0.2, mtl >= 2.2 && < 2.3, text >= 1.1.1.0 && < 1.3, time diff --git a/stack.yaml b/stack.yaml index 5ef814487..85022e91d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -18,6 +18,7 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 - texmath-0.12.1 +- tasty-bench-0.1 # - citeproc-0.3.0.5 - citeproc: git: https://github.com/jgm/citeproc.git -- cgit v1.2.3 From 66959172588409becb42993bb94ea106ac3e5606 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 30 Jan 2021 18:10:22 -0800 Subject: CslJson writer: output `[]` if no references in input, instead of raising a PandocAppError as before. --- src/Text/Pandoc/Writers/CslJson.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Writers/CslJson.hs b/src/Text/Pandoc/Writers/CslJson.hs index 13b95586b..f0defdd38 100644 --- a/src/Text/Pandoc/Writers/CslJson.hs +++ b/src/Text/Pandoc/Writers/CslJson.hs @@ -46,11 +46,11 @@ writeCslJson _opts (Pandoc meta _) = do locale <- case getLocale lang of Left e -> throwError $ PandocCiteprocError e Right l -> return l - case lookupMeta "references" meta of - Just (MetaList rs) -> return $ (UTF8.toText $ - toCslJson locale (mapMaybe metaValueToReference rs)) - <> "\n" - _ -> throwError $ PandocAppError "No references field" + let rs = case lookupMeta "references" meta of + Just (MetaList rs) -> rs + _ -> [] + return $ UTF8.toText + (toCslJson locale (mapMaybe metaValueToReference rs)) <> "\n" fromInlines :: [Inline] -> CslJson Text fromInlines = foldMap fromInline . B.fromList -- cgit v1.2.3 From a9adb2964862fd8074cf9dfdcb8248d3a13665c5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 30 Jan 2021 19:09:08 -0800 Subject: Require citeproc 0.3.0.6. --- pandoc.cabal | 2 +- stack.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 6c8254616..0890318e3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -410,7 +410,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.5 && < 0.4, + citeproc >= 0.3.0.6 && < 0.4, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/stack.yaml b/stack.yaml index 85022e91d..6c8c39fa7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -19,10 +19,10 @@ extra-deps: - doctemplates-0.9 - texmath-0.12.1 - tasty-bench-0.1 -# - citeproc-0.3.0.5 -- citeproc: - git: https://github.com/jgm/citeproc.git - commit: da1f9702fa70d7e1bad9a796f897e920e2d578f7 +- citeproc-0.3.0.6 +#- citeproc: +# git: https://github.com/jgm/citeproc.git +# commit: da1f9702fa70d7e1bad9a796f897e920e2d578f7 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 -- cgit v1.2.3 From 9c8ff53b54836cbe7fa9417784f3204cd041f3ac Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 31 Jan 2021 14:37:47 +0100 Subject: CslJson writer: fix compiler warning --- src/Text/Pandoc/Writers/CslJson.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/CslJson.hs b/src/Text/Pandoc/Writers/CslJson.hs index f0defdd38..a10def95e 100644 --- a/src/Text/Pandoc/Writers/CslJson.hs +++ b/src/Text/Pandoc/Writers/CslJson.hs @@ -47,7 +47,7 @@ writeCslJson _opts (Pandoc meta _) = do Left e -> throwError $ PandocCiteprocError e Right l -> return l let rs = case lookupMeta "references" meta of - Just (MetaList rs) -> rs + Just (MetaList xs) -> xs _ -> [] return $ UTF8.toText (toCslJson locale (mapMaybe metaValueToReference rs)) <> "\n" -- cgit v1.2.3 From d1875b69ec136c19d9b2336ff57e360ff7b7bef6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 31 Jan 2021 12:05:46 -0800 Subject: RST reader: fix handling of header in CSV tables. The interpretation of this line is not affected by the delim option. Closes #7064. --- src/Text/Pandoc/Readers/RST.hs | 9 +++++---- test/command/7064.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 test/command/7064.md diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 707af905f..0f32d993c 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -877,10 +877,11 @@ csvTableDirective top fields rawcsv = do (bs, _) <- fetchItem u return $ UTF8.toText bs Nothing -> return rawcsv - let res = parseCSV opts (case explicitHeader of - Just h -> h <> "\n" <> rawcsv' - Nothing -> rawcsv') - case res of + let header' = case explicitHeader of + Just h -> parseCSV defaultCSVOptions h + Nothing -> Right [] + let res = parseCSV opts rawcsv' + case (<>) <$> header' <*> res of Left e -> throwError $ PandocParsecError "csv table" e Right rawrows -> do diff --git a/test/command/7064.md b/test/command/7064.md new file mode 100644 index 000000000..58b72e363 --- /dev/null +++ b/test/command/7064.md @@ -0,0 +1,32 @@ +``` +% pandoc -f rst -t html +.. csv-table:: Changes + :header: "Version", "Date", "Description" + :widths: 15, 15, 70 + :delim: $ + + 0.1.0 $ 18/02/2013 $ Initial Release +^D + + +++++ + + + + + + + + + + + + + + +
Changes
VersionDateDescription
0.1.018/02/2013Initial Release
+``` -- cgit v1.2.3 From aa60d9b230f3d010d9e2da828d9e2583c0220719 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 31 Jan 2021 12:06:38 -0800 Subject: Add shell.nix. --- shell.nix | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 shell.nix diff --git a/shell.nix b/shell.nix new file mode 100644 index 000000000..d3bf73799 --- /dev/null +++ b/shell.nix @@ -0,0 +1,105 @@ +{nixpkgs ? import {} }: +let + inherit (nixpkgs) pkgs; + inherit (pkgs) haskellPackages; + + haskellDeps = ps: with ps; [ + Diff + Glob + HTTP + HTTP + HsYAML + JuicyPixels + QuickCheck + SHA + aeson + aeson-pretty + attoparsec + base + base-compat + base64-bytestring + binary + blaze-html + blaze-markup + bytestring + case-insensitive + citeproc + commonmark + commonmark-extensions + commonmark-pandoc + conduit-extra + connection + connection + containers + data-default + deepseq + directory + doclayout + doctemplates + emojis + exceptions + file-embed + filepath + haddock-library + hsc2hs + hslua + hslua-module-system + hslua-module-text + http-client + http-client + http-client-tls + http-client-tls + http-types + ipynb + jira-wiki-markup + mtl + network + network + network-uri + pandoc-types + parsec + process + random + safe + scientific + skylighting + skylighting-core + socks + split + streaming-commons + syb + tagsoup + tasty + tasty-golden + tasty-hunit + tasty-lua + tasty-quickcheck + temporary + texmath + text + text-conversions + time + tls + unicode-transforms + unordered-containers + weigh + xml + xml-conduit + zip-archive + zlib + ]; + + ghc = haskellPackages.ghcWithPackages haskellDeps; + + nixPackages = [ + ghc + pkgs.gdb + haskellPackages.ghcid + haskellPackages.cabal2nix + haskellPackages.cabal-install + ]; +in +pkgs.stdenv.mkDerivation { + name = "env"; + buildInputs = nixPackages; +} -- cgit v1.2.3 From 22faea15c249c1f84257d289ed6904f3abe64330 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 31 Jan 2021 18:18:53 -0800 Subject: Makefile: give allocation data in benchmarks. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1985a4810..a61e58270 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ RESOLVER?=lts-13 GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 -BENCHARGS?="--timeout=6" +BENCHARGS?="--timeout=6 +RTS -T -RTS" quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j +RTS -A64m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -39,7 +39,7 @@ ghcid: ghcid -c "stack repl --flag 'pandoc:embed_data_files'" bench: - stack bench --benchmark-arguments='$(BENCHARGS)' --ghc-options '$(GHCOPTS)' + stack bench --benchmark-arguments=$(BENCHARGS) --ghc-options '$(GHCOPTS)' weigh: stack build --ghc-options '$(GHCOPTS)' pandoc:weigh-pandoc && stack exec weigh-pandoc -- cgit v1.2.3 From b239c89a82b66abc55bf7c08e37492938c817c56 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 1 Feb 2021 08:43:26 -0800 Subject: BibTeX writer fixes. Closes #7067. + Require citeproc 0.3.0.7, which correctly titlecases when titles contain non-ASCII characters. + Correctly handle 'pages' (= 'page' in CSL). + Correctly handle BibLaTeX 'langid' (= 'language' in CSL). + In BibTeX output, protect foreign titles since there's no language field. --- pandoc.cabal | 2 +- src/Text/Pandoc/Citeproc/BibTeX.hs | 21 ++++++--- stack.yaml | 4 +- test/command/7067.md | 90 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 test/command/7067.md diff --git a/pandoc.cabal b/pandoc.cabal index 0890318e3..1bdd2a6b3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -410,7 +410,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.6 && < 0.4, + citeproc >= 0.3.0.7 && < 0.4, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 5b9068378..2b43fffb6 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -115,7 +115,7 @@ writeBibtexString opts variant mblang ref = "motion_picture" | variant == Biblatex -> "movie" "review" | variant == Biblatex -> "review" _ -> "misc" - + mbSubtype = case referenceType ref of "article-magazine" -> Just "magazine" @@ -149,7 +149,7 @@ writeBibtexString opts variant mblang ref = , "type" , "entrysubtype" , "note" - , "language" + , "langid" , "abstract" , "keywords" ] @@ -202,12 +202,19 @@ writeBibtexString opts variant mblang ref = [ (", " <>) <$> nameGiven name, nameDroppingParticle name ] - titlecase = case mblang of + mblang' = (parseLang <$> getVariableAsText "language") <|> mblang + + titlecase = case mblang' of Just (Lang "en" _) -> titlecase' Nothing -> titlecase' - _ -> id - - titlecase' = addTextCase mblang TitleCase . + _ -> + case variant of + Bibtex -> B.spanWith nullAttr + -- BibTex lacks a language field, so we wrap non-English + -- titles in {} to protect case. + Biblatex -> id + + titlecase' = addTextCase mblang' TitleCase . (\ils -> B.fromList (case B.toList ils of Str t : xs -> Str t : Walk.walk spanAroundCapitalizedWords xs @@ -299,6 +306,8 @@ writeBibtexString opts variant mblang ref = getContentsFor "urldate" = getVariable "accessed" >>= toLaTeX . valToInlines getContentsFor "year" = getVariable "issued" >>= getYear getContentsFor "month" = getVariable "issued" >>= getMonth + getContentsFor "pages" = getVariable "page" >>= toLaTeX . valToInlines + getContentsFor "langid" = getVariable "language" >>= toLaTeX . valToInlines getContentsFor "number" = (getVariable "number" <|> getVariable "collection-number" <|> getVariable "issue") >>= toLaTeX . valToInlines diff --git a/stack.yaml b/stack.yaml index 6c8c39fa7..dc6fa6517 100644 --- a/stack.yaml +++ b/stack.yaml @@ -19,10 +19,10 @@ extra-deps: - doctemplates-0.9 - texmath-0.12.1 - tasty-bench-0.1 -- citeproc-0.3.0.6 +- citeproc-0.3.0.7 #- citeproc: # git: https://github.com/jgm/citeproc.git -# commit: da1f9702fa70d7e1bad9a796f897e920e2d578f7 +# commit: feb3b7580c6738eec3b23921f7c1739cfba611aa ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-16.23 diff --git a/test/command/7067.md b/test/command/7067.md new file mode 100644 index 000000000..a66e3aa1f --- /dev/null +++ b/test/command/7067.md @@ -0,0 +1,90 @@ +``` +% pandoc -t biblatex +--- +references: +- id: garaud + author: + - family: Garaud + given: Marcel + container-title: Bulletin de la Societé des antiquaires de l’Ouest + collection-title: 4 + issued: + - year: 1967 + language: fr-FR + page: 11-27 + title: Recherches sur les défrichements dans la Gâtine poitevine aux XI^e^ et XII^e^ siècles + type: article-journal + volume: 9 +... +^D +@article{garaud, + author = {Garaud, Marcel}, + title = {Recherches sur les défrichements dans la Gâtine poitevine aux +XI\textsuperscript{e} et XII\textsuperscript{e} siècles}, + journal = {Bulletin de la Societé des antiquaires de l’Ouest}, + series = {4}, + volume = {9}, + pages = {11-27}, + date = {1967}, + langid = {fr-FR} +} +``` + +``` +% pandoc -t bibtex +--- +references: +- id: garaud + author: + - family: Garaud + given: Marcel + container-title: Bulletin de la Société des antiquaires de l’Ouest + collection-title: 4 + issued: + - year: 1967 + language: fr-FR + page: 11-27 + title: Recherches sur les défrichements dans la Gâtine poitevine aux XI^e^ et XII^e^ siècles + type: article-journal + volume: 9 +... +^D +@article{garaud, + author = {Garaud, Marcel}, + title = {{Recherches sur les défrichements dans la Gâtine poitevine aux +XI\textsuperscript{e} et XII\textsuperscript{e} siècles}}, + journal = {Bulletin de la Société des antiquaires de l’Ouest}, + series = {4}, + volume = {9}, + pages = {11-27}, + year = {1967} +} +``` + +This tests the titlecasing of a word with an accented second letter: +``` +% pandoc -t bibtex +--- +references: +- id: garaud + author: + - family: Garaud + given: Marcel + container-title: English Journal + issued: + - year: 1967 + language: en-US + title: Research on the défrichements in the Gâtine poitevine + type: article-journal + volume: 9 +... +^D +@article{garaud, + author = {Garaud, Marcel}, + title = {Research on the Défrichements in the {Gâtine} Poitevine}, + journal = {English Journal}, + volume = {9}, + year = {1967} +} +``` + -- cgit v1.2.3 From 02d3c71e7224853ecabaa9ac4cd947ec2ac1e579 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 1 Feb 2021 18:02:17 -0800 Subject: BibTeX writer: use doclayout and doctemplate. This change allows bibtex/biblatex output to wrap as other formats do, depending on the settings of `--wrap` and `--columns`. It also introduces default templates for bibtex and biblatex, which allow for using the variables `header-include`, `include-before` or `include-after` (or alternatively the command line options `--include-in-header`, `--include-before-body`, `--include-after-body`) to insert content into the generated bibtex/biblatex. This change requires a change in the return type of the unexported `T.P.Citeproc.writeBibTeXString` from `Text` to `Doc Text`. Closes #7068. --- data/templates/default.biblatex | 10 ++++++++++ pandoc.cabal | 2 ++ src/Text/Pandoc/Citeproc/BibTeX.hs | 41 +++++++++++++++++++++----------------- src/Text/Pandoc/Templates.hs | 2 -- src/Text/Pandoc/Writers/BibTeX.hs | 19 +++++++++++++++--- test/command/7067.md | 6 +++--- 6 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 data/templates/default.biblatex diff --git a/data/templates/default.biblatex b/data/templates/default.biblatex new file mode 100644 index 000000000..6bf2632d8 --- /dev/null +++ b/data/templates/default.biblatex @@ -0,0 +1,10 @@ +$for(header-includes)$ +$header-includes$ +$endfor$ +$for(include-before)$ +$include-before$ +$endfor$ +$body$ +$for(include-after)$ +$include-after$ +$endfor$ diff --git a/pandoc.cabal b/pandoc.cabal index 1bdd2a6b3..2f8c255a8 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -56,6 +56,8 @@ data-files: data/templates/default.icml data/templates/default.opml data/templates/default.latex + data/templates/default.bibtex + data/templates/default.biblatex data/templates/default.context data/templates/default.texinfo data/templates/default.jira diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 2b43fffb6..416fe439e 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -52,6 +52,8 @@ import Data.Char (isAlphaNum, isDigit, isLetter, import Data.List (foldl', intercalate, intersperse) import Safe (readMay) import Text.Printf (printf) +import Text.DocLayout (literal, hsep, nest, hang, Doc(..), + braces, ($$), cr) data Variant = Bibtex | Biblatex deriving (Show, Eq, Ord) @@ -77,10 +79,11 @@ writeBibtexString :: WriterOptions -- ^ options (for writing LaTex) -> Variant -- ^ bibtex or biblatex -> Maybe Lang -- ^ Language -> Reference Inlines -- ^ Reference to write - -> Text + -> Doc Text writeBibtexString opts variant mblang ref = - "@" <> bibtexType <> "{" <> unItemId (referenceId ref) <> ",\n " <> - renderFields fs <> "\n}\n" + "@" <> bibtexType <> "{" <> literal (unItemId (referenceId ref)) <> "," + $$ nest 2 (renderFields fs) + $$ "}" <> cr where bibtexType = @@ -231,10 +234,12 @@ writeBibtexString opts variant mblang ref = toLaTeX x = case runPure (writeLaTeX opts $ doc (B.plain x)) of Left _ -> Nothing - Right t -> Just t + Right t -> Just $ hsep . map literal $ T.words t - renderField name = (\contents -> name <> " = {" <> contents <> "}") - <$> getContentsFor name + renderField :: Text -> Maybe (Doc Text) + renderField name = + (((literal name) <>) . hang 2 " = " . braces) + <$> getContentsFor name getVariable v = lookupVariable (toVariable v) ref @@ -248,10 +253,10 @@ writeBibtexString opts variant mblang ref = Nothing -> case dateParts date of [DateParts (y1:_), DateParts (y2:_)] -> - Just (T.pack (printf "%04d" y1) <> "--" <> + Just $ literal (T.pack (printf "%04d" y1) <> "--" <> T.pack (printf "%04d" y2)) [DateParts (y1:_)] -> - Just (T.pack (printf "%04d" y1)) + Just $ literal (T.pack (printf "%04d" y1)) _ -> Nothing _ -> Nothing @@ -274,19 +279,19 @@ writeBibtexString opts variant mblang ref = DateVal date -> case dateParts date of [DateParts (_:m1:_), DateParts (_:m2:_)] -> - Just (toMonth m1 <> "--" <> toMonth m2) - [DateParts (_:m1:_)] -> Just (toMonth m1) + Just $ literal (toMonth m1 <> "--" <> toMonth m2) + [DateParts (_:m1:_)] -> Just $ literal (toMonth m1) _ -> Nothing _ -> Nothing - getContentsFor :: Text -> Maybe Text + getContentsFor :: Text -> Maybe (Doc Text) getContentsFor "type" = getVariableAsText "genre" >>= \case "mathesis" -> Just "mastersthesis" "phdthesis" -> Just "phdthesis" _ -> Nothing - getContentsFor "entrysubtype" = mbSubtype + getContentsFor "entrysubtype" = literal <$> mbSubtype getContentsFor "journal" | bibtexType `elem` ["article", "periodical", "suppperiodical", "review"] = getVariable "container-title" >>= toLaTeX . valToInlines @@ -314,7 +319,7 @@ writeBibtexString opts variant mblang ref = getContentsFor x = getVariable x >>= if isURL x - then Just . stringify . valToInlines + then Just . literal . stringify . valToInlines else toLaTeX . (if x == "title" then titlecase @@ -323,7 +328,7 @@ writeBibtexString opts variant mblang ref = isURL x = x `elem` ["url","doi","issn","isbn"] - renderFields = T.intercalate ",\n " . mapMaybe renderField + renderFields = mconcat . intersperse ("," <> cr) . mapMaybe renderField defaultLang :: Lang defaultLang = Lang "en" (Just "US") @@ -1038,14 +1043,14 @@ getOldDate prefix = do let dateparts = filter (\x -> x /= DateParts []) $ map toDateParts [(year',month',day'), (endyear',endmonth',endday')] - literal <- if null dateparts - then Just <$> getRawField (prefix <> "year") - else return Nothing + literal' <- if null dateparts + then Just <$> getRawField (prefix <> "year") + else return Nothing return $ Date { dateParts = dateparts , dateCirca = False , dateSeason = Nothing - , dateLiteral = literal } + , dateLiteral = literal' } getRawField :: Text -> Bib Text getRawField f = do diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 3e539bff7..e83f26329 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -81,8 +81,6 @@ getDefaultTemplate writer = do case format of "native" -> return "" "csljson" -> return "" - "bibtex" -> return "" - "biblatex" -> return "" "json" -> return "" "docx" -> return "" "fb2" -> return "" diff --git a/src/Text/Pandoc/Writers/BibTeX.hs b/src/Text/Pandoc/Writers/BibTeX.hs index e1cb47ca1..b9ae0c13a 100644 --- a/src/Text/Pandoc/Writers/BibTeX.hs +++ b/src/Text/Pandoc/Writers/BibTeX.hs @@ -25,7 +25,11 @@ import Citeproc (parseLang) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Citeproc.BibTeX as BibTeX import Text.Pandoc.Citeproc.MetaValue (metaValueToReference) -import Text.Pandoc.Writers.Shared (lookupMetaString) +import Text.Pandoc.Writers.Shared (lookupMetaString, defField, + addVariablesToContext) +import Text.DocLayout (render, vcat) +import Text.DocTemplates (Context(..)) +import Text.Pandoc.Templates (renderTemplate) -- | Write BibTeX based on the references metadata from a Pandoc document. writeBibTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text @@ -43,6 +47,15 @@ writeBibTeX' variant opts (Pandoc meta _) = do let refs = case lookupMeta "references" meta of Just (MetaList xs) -> mapMaybe metaValueToReference xs _ -> [] - return $ mconcat $ - map (BibTeX.writeBibtexString opts variant mblang) refs + let main = vcat $ map (BibTeX.writeBibtexString opts variant mblang) refs + let context = defField "body" main + $ addVariablesToContext opts (mempty :: Context Text) + let colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + return $ render colwidth $ + case writerTemplate opts of + Nothing -> main + Just tpl -> renderTemplate tpl context + diff --git a/test/command/7067.md b/test/command/7067.md index a66e3aa1f..34b3bb8ea 100644 --- a/test/command/7067.md +++ b/test/command/7067.md @@ -20,7 +20,7 @@ references: @article{garaud, author = {Garaud, Marcel}, title = {Recherches sur les défrichements dans la Gâtine poitevine aux -XI\textsuperscript{e} et XII\textsuperscript{e} siècles}, + XI\textsuperscript{e} et XII\textsuperscript{e} siècles}, journal = {Bulletin de la Societé des antiquaires de l’Ouest}, series = {4}, volume = {9}, @@ -51,8 +51,8 @@ references: ^D @article{garaud, author = {Garaud, Marcel}, - title = {{Recherches sur les défrichements dans la Gâtine poitevine aux -XI\textsuperscript{e} et XII\textsuperscript{e} siècles}}, + title = {{Recherches sur les défrichements dans la Gâtine poitevine + aux XI\textsuperscript{e} et XII\textsuperscript{e} siècles}}, journal = {Bulletin de la Société des antiquaires de l’Ouest}, series = {4}, volume = {9}, -- cgit v1.2.3 From 84d46e352316b75415f0e9899615f931b6205324 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 1 Feb 2021 18:53:59 -0800 Subject: Add default.bibtex to repository. --- data/templates/default.bibtex | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 data/templates/default.bibtex diff --git a/data/templates/default.bibtex b/data/templates/default.bibtex new file mode 100644 index 000000000..6bf2632d8 --- /dev/null +++ b/data/templates/default.bibtex @@ -0,0 +1,10 @@ +$for(header-includes)$ +$header-includes$ +$endfor$ +$for(include-before)$ +$include-before$ +$endfor$ +$body$ +$for(include-after)$ +$include-after$ +$endfor$ -- cgit v1.2.3 From defad03ce19c9b4d2c28ad29527f353a53cdb86f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 14:46:59 -0800 Subject: Remove superfluous import --- benchmark/benchmark-pandoc.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 2e7600cbc..045573273 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -25,7 +25,6 @@ import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T import Test.Tasty.Bench -import Test.Tasty (withResource) import Data.List (intersect) import Data.Maybe (mapMaybe, catMaybes) import System.Environment (getArgs) -- cgit v1.2.3 From d8e4bbca4cf646afcf36603dca07ad024b2106e1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 14:47:14 -0800 Subject: shell.nix - install zlib --- shell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/shell.nix b/shell.nix index d3bf73799..1fa25de79 100644 --- a/shell.nix +++ b/shell.nix @@ -92,6 +92,7 @@ let ghc = haskellPackages.ghcWithPackages haskellDeps; nixPackages = [ + pkgs.zlib ghc pkgs.gdb haskellPackages.ghcid -- cgit v1.2.3 From af77241b1761814285d63f72f345d60b74fc3d24 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 14:47:27 -0800 Subject: cabal.project - more heap space --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index c3042cb85..c1a89fd74 100644 --- a/cabal.project +++ b/cabal.project @@ -4,7 +4,7 @@ tests: True package pandoc flags: +embed_data_files -trypandoc - ghc-options: -j +RTS -A64m -RTS + ghc-options: -j +RTS -A256m -RTS -- source-repository-package -- type: git -- cgit v1.2.3 From ec8509295a8de19462ecd352a22b2784158e9ec6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 17:00:03 -0800 Subject: Add parseOptionsFromArgs [API change, addition]. Exported by Text.Pandoc.App. --- src/Text/Pandoc/App.hs | 4 +++- src/Text/Pandoc/App/CommandLineOptions.hs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 437af3257..6a071ad5a 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -19,6 +19,7 @@ module Text.Pandoc.App ( , Filter(..) , defaultOpts , parseOptions + , parseOptionsFromArgs , options , applyFilters ) where @@ -47,7 +48,8 @@ import Text.Pandoc import Text.Pandoc.App.FormatHeuristics (formatFromFilePaths) import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts, IpynbOutput (..) ) -import Text.Pandoc.App.CommandLineOptions (parseOptions, options) +import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs, + options) import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings) import Text.Pandoc.BCP47 (Lang (..), parseBCP47) import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters) diff --git a/src/Text/Pandoc/App/CommandLineOptions.hs b/src/Text/Pandoc/App/CommandLineOptions.hs index 307f28b5c..a4c510d97 100644 --- a/src/Text/Pandoc/App/CommandLineOptions.hs +++ b/src/Text/Pandoc/App/CommandLineOptions.hs @@ -17,6 +17,7 @@ Does a pandoc conversion based on command-line options. -} module Text.Pandoc.App.CommandLineOptions ( parseOptions + , parseOptionsFromArgs , options , engines , lookupHighlightStyle @@ -73,9 +74,13 @@ parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt parseOptions options' defaults = do rawArgs <- map UTF8.decodeArg <$> getArgs prg <- getProgName + parseOptionsFromArgs options' defaults prg rawArgs +parseOptionsFromArgs + :: [OptDescr (Opt -> IO Opt)] -> Opt -> String -> [String] -> IO Opt +parseOptionsFromArgs options' defaults prg rawArgs = do let (actions, args, unrecognizedOpts, errors) = - getOpt' Permute options' rawArgs + getOpt' Permute options' (map UTF8.decodeArg rawArgs) let unknownOptionErrors = foldr (handleUnrecognizedOption . takeWhile (/= '=')) [] -- cgit v1.2.3 From 2415b2680a522e89b63abb370c02bfff54b824a2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 17:09:16 -0800 Subject: Test suite: a more robust way of testing the executable. Mmny of our tests require running the pandoc executable. This is problematic for a few different reasons. First, cabal-install will sometimes run the test suite after building the library but before building the executable, which means the executable isn't in place for the tests. One can work around that by first building, then building and running the tests, but that's fragile. Second, we have to find the executable. So far, we've done that using a function findPandoc that attempts to locate it relative to the test executable (which can be located using findExecutablePath). But the logic here is delicate and work with every combination of options. To solve both problems, we add an `--emulate` option to the `test-pandoc` executable. When `--emulate` occurs as the first argument passed to `test-pandoc`, the program simply emulates the regular pandoc executable, using the rest of the arguments (after `--emulate`). Thus, test-pandoc --emulate -f markdown -t latex is just like pandoc -f markdown -t latex Since all the work is done by library functions, implementing this emulation just takes a couple lines of code and should be entirely reliable. With this change, we can test the pandoc executable by running the test program itself (locatable using findExecutablePath) with the `--emulate` option. This removes the need for the fragile `findPandoc` step, and it means we can run our integration tests even when we're just building the library, not the executable. Part of this change involved simplifying some complex handling to set environment variables for dynamic library paths. I have tested a build with `--enable-dynamic-executable`, and it works, but further testing may be needed. --- test/Tests/Command.hs | 61 ++++++++++++++++++++++++++++----------------------- test/Tests/Helpers.hs | 29 ------------------------ test/Tests/Old.hs | 21 ++++++++++-------- test/test-pandoc.hs | 25 ++++++++++++++++----- 4 files changed, 64 insertions(+), 72 deletions(-) diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index b3e2a0509..bbfa62dea 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -1,4 +1,5 @@ {-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE TupleSections #-} {- | Module : Tests.Command Copyright : © 2006-2021 John MacFarlane @@ -10,11 +11,12 @@ Run commands, and test results, defined in markdown files. -} -module Tests.Command (findPandoc, runTest, tests) +module Tests.Command (runTest, tests) where import Prelude import Data.Algorithm.Diff +import System.Environment.Executable (getExecutablePath) import qualified Data.ByteString as BS import qualified Data.Text as T import Data.List (isSuffixOf, intercalate) @@ -34,27 +36,21 @@ import Text.Pandoc import qualified Text.Pandoc.UTF8 as UTF8 -- | Run a test with and return output. -execTest :: FilePath -- ^ Path to pandoc +execTest :: String -- ^ Path to test executable -> String -- ^ Shell command -> String -- ^ Input text -> IO (ExitCode, String) -- ^ Exit code and actual output -execTest pandocpath cmd inp = do +execTest testExePath cmd inp = do mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" - let findDynlibDir [] = Nothing - findDynlibDir ("build":xs) = Just $ joinPath (reverse xs) "build" - findDynlibDir (_:xs) = findDynlibDir xs - let mbDynlibDir = findDynlibDir (reverse $ splitDirectories $ - takeDirectory $ takeWhile (/=' ') cmd) - let dynlibEnv = [("DYLD_LIBRARY_PATH", - intercalate ":" $ catMaybes [mbDynlibDir, mdyldpath]) - ,("LD_LIBRARY_PATH", - intercalate ":" $ catMaybes [mbDynlibDir, mldpath])] - let env' = dynlibEnv ++ [("PATH",takeDirectory pandocpath),("TMP","."), - ("LANG","en_US.UTF-8"), - ("HOME", "./"), - ("pandoc_datadir", "..")] - let pr = (shell cmd){ env = Just env' } + let env' = ("PATH",takeDirectory testExePath) : + ("TMP",".") : + ("LANG","en_US.UTF-8") : + ("HOME", "./") : + ("pandoc_datadir", "..") : + maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ + maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath + let pr = (shell (pandocToEmulate True cmd)){ env = Just env' } (ec, out', err') <- readCreateProcessWithExitCode pr inp -- filter \r so the tests will work on Windows machines let out = filter (/= '\r') $ err' ++ out' @@ -63,15 +59,23 @@ execTest pandocpath cmd inp = do ExitSuccess -> return () return (ec, out) +pandocToEmulate :: Bool -> String -> String +pandocToEmulate True ('p':'a':'n':'d':'o':'c':cs) = + "test-pandoc --emulate" ++ pandocToEmulate False cs +pandocToEmulate False ('|':' ':'p':'a':'n':'d':'o':'c':cs) = + "| " ++ "test-pandoc --emulate" ++ pandocToEmulate False cs +pandocToEmulate _ (c:cs) = c : pandocToEmulate False cs +pandocToEmulate _ [] = [] + -- | Run a test, return True if test passed. -runTest :: String -- ^ Title of test - -> FilePath -- ^ Path to pandoc +runTest :: String -- ^ Path to test executable + -> String -- ^ Title of test -> String -- ^ Shell command -> String -- ^ Input text -> String -- ^ Expected output -> TestTree -runTest testname pandocpath cmd inp norm = testCase testname $ do - (ec, out) <- execTest pandocpath cmd inp +runTest testExePath testname cmd inp norm = testCase testname $ do + (ec, out) <- execTest testExePath cmd inp result <- if ec == ExitSuccess then if out == norm @@ -82,12 +86,13 @@ runTest testname pandocpath cmd inp norm = testCase testname $ do else return $ TestError ec assertBool (show result) (result == TestPassed) -tests :: FilePath -> TestTree +tests :: TestTree {-# NOINLINE tests #-} -tests pandocPath = unsafePerformIO $ do +tests = unsafePerformIO $ do files <- filter (".md" `isSuffixOf`) <$> getDirectoryContents "command" - let cmds = map (extractCommandTest pandocPath) files + testExePath <- getExecutablePath + let cmds = map (extractCommandTest testExePath) files return $ testGroup "Command:" cmds isCodeBlock :: Block -> Bool @@ -103,7 +108,7 @@ dropPercent ('%':xs) = dropWhile (== ' ') xs dropPercent xs = xs runCommandTest :: FilePath -> FilePath -> Int -> String -> TestTree -runCommandTest pandocpath fp num code = +runCommandTest testExePath fp num code = goldenTest testname getExpected getActual compareValues updateGolden where testname = "#" <> show num @@ -116,7 +121,7 @@ runCommandTest pandocpath fp num code = input = unlines inplines norm = unlines normlines getExpected = return norm - getActual = snd <$> execTest pandocpath cmd input + getActual = snd <$> execTest testExePath cmd input compareValues expected actual | actual == expected = return Nothing | otherwise = return $ Just $ "--- test/command/" ++ fp ++ "\n+++ " ++ @@ -132,10 +137,10 @@ runCommandTest pandocpath fp num code = UTF8.writeFile fp' updated extractCommandTest :: FilePath -> FilePath -> TestTree -extractCommandTest pandocpath fp = unsafePerformIO $ do +extractCommandTest testExePath fp = unsafePerformIO $ do contents <- UTF8.toText <$> BS.readFile ("command" fp) Pandoc _ blocks <- runIOorExplode (readMarkdown def{ readerExtensions = pandocExtensions } contents) let codeblocks = map extractCode $ filter isCodeBlock blocks - let cases = zipWith (runCommandTest pandocpath fp) [1..] codeblocks + let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks return $ testGroup fp cases diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index 21898d10e..a4a3c0af5 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -15,7 +15,6 @@ Utility functions for the test suite. module Tests.Helpers ( test , TestResult(..) , showDiff - , findPandoc , (=?>) , purely , ToString(..) @@ -86,34 +85,6 @@ showDiff (l,r) (Second ln : ds) = showDiff (l,r) (Both _ _ : ds) = showDiff (l+1,r+1) ds --- | Find pandoc executable relative to test-pandoc -findPandoc :: IO FilePath -findPandoc = do - testExePath <- getExecutablePath - let pandocDir = - case reverse (splitDirectories (takeDirectory testExePath)) of - -- cabalv2 with --disable-optimization - "test-pandoc" : "build" : "noopt" : "test-pandoc" : "t" : ps - -> joinPath (reverse ps) - "x" "pandoc" "noopt" "build" "pandoc" - -- cabalv2 without --disable-optimization - "test-pandoc" : "build" : "test-pandoc" : "t" : ps - -> joinPath (reverse ps) - "x" "pandoc" "build" "pandoc" - -- cabalv1 - "test-pandoc" : "build" : ps - -> joinPath (reverse ps) "build" "pandoc" - _ -> error "findPandoc: could not find pandoc executable" - let pandocPath = pandocDir "pandoc" -#ifdef _WINDOWS - <.> "exe" -#endif - found <- doesFileExist pandocPath - if found - then return pandocPath - else error $ "findPandoc: could not find pandoc executable at " - ++ pandocPath - vividize :: Diff String -> String vividize (Both s _) = " " ++ s vividize (First s) = "- " ++ s diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index 638620a36..528388c51 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -1,4 +1,5 @@ {-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE TupleSections #-} {- | Module : Tests.Old Copyright : © 2006-2021 John MacFarlane @@ -19,6 +20,7 @@ import Data.Maybe (catMaybes) import System.Exit import System.FilePath (joinPath, splitDirectories, (<.>), ()) import qualified System.Environment as Env +import System.Environment.Executable (getExecutablePath) import Text.Pandoc.Process (pipeProcess) import Test.Tasty (TestTree, testGroup) import Test.Tasty.Golden.Advanced (goldenTest) @@ -322,13 +324,14 @@ testWithNormalize normalizer pandocPath testname opts inp norm = getActual = do mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" - let mbDynlibDir = findDynlibDir (reverse $ - splitDirectories pandocPath) - let dynlibEnv = [("DYLD_LIBRARY_PATH", intercalate ":" $ catMaybes [mbDynlibDir, mdyldpath]) - ,("LD_LIBRARY_PATH", intercalate ":" $ catMaybes [mbDynlibDir, mldpath])] - let env = dynlibEnv ++ - [("TMP","."),("LANG","en_US.UTF-8"),("HOME", "./")] - (ec, out) <- pipeProcess (Just env) pandocPath options mempty + let env = ("TMP",".") : + ("LANG","en_US.UTF-8") : + ("HOME", "./") : + maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ + maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath + + (ec, out) <- pipeProcess (Just env) pandocPath + ("--emulate":options) mempty if ec == ExitSuccess then return $ filter (/='\r') . normalizer $ UTF8.toStringLazy out @@ -339,8 +342,8 @@ testWithNormalize normalizer pandocPath testname opts inp norm = compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String) compareValues norm options expected actual = do - pandocPath <- findPandoc - let cmd = pandocPath ++ " " ++ unwords options + testExePath <- getExecutablePath + let cmd = testExePath ++ " --emulate " ++ unwords options let dash = replicate 72 '-' let diff = getDiff (lines actual) (lines expected) if expected == actual diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs index bb4db90b9..9973dffc8 100644 --- a/test/test-pandoc.hs +++ b/test/test-pandoc.hs @@ -4,6 +4,12 @@ module Main where import Prelude +import System.Environment (getArgs) +import qualified Control.Exception as E +import Text.Pandoc.App (convertWithOpts, defaultOpts, options, + parseOptionsFromArgs) +import Text.Pandoc.Error (handleError) +import System.Environment.Executable (getExecutablePath) import GHC.IO.Encoding import Test.Tasty import qualified Tests.Command @@ -46,12 +52,11 @@ import qualified Tests.Writers.Powerpoint import qualified Tests.Writers.RST import qualified Tests.Writers.AnnotatedTable import qualified Tests.Writers.TEI -import Tests.Helpers (findPandoc) import Text.Pandoc.Shared (inDirectory) tests :: FilePath -> TestTree tests pandocPath = testGroup "pandoc tests" - [ Tests.Command.tests pandocPath + [ Tests.Command.tests , testGroup "Old" (Tests.Old.tests pandocPath) , testGroup "Shared" Tests.Shared.tests , testGroup "Writers" @@ -102,7 +107,15 @@ tests pandocPath = testGroup "pandoc tests" main :: IO () main = do setLocaleEncoding utf8 - inDirectory "test" $ do - fp <- findPandoc - putStrLn $ "Using pandoc executable at " ++ fp - defaultMain $ tests fp + args <- getArgs + case args of + "--emulate":args' -> -- emulate pandoc executable + E.catch + (parseOptionsFromArgs options defaultOpts "pandoc" args' >>= + convertWithOpts) + (handleError . Left) + _ -> inDirectory "test" $ do + fp <- getExecutablePath + -- putStrLn $ "Using pandoc executable at " ++ fp + defaultMain $ tests fp + -- cgit v1.2.3 From 61b108d52789f20fb03c4f8a74719c1d53021c91 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 27 Jan 2021 15:17:39 +0100 Subject: Lua: add module "pandoc.path" The module allows to work with file paths in a convenient and platform-independent manner. Closes: #6001 Closes: #6565 --- doc/lua-filters.md | 172 ++++++++++++++++++++++++++++++++++++++++ pandoc.cabal | 1 + src/Text/Pandoc/Lua/Packages.hs | 2 + stack.yaml | 4 +- test/Tests/Lua/Module.hs | 2 + test/lua/module/pandoc-path.lua | 17 ++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 test/lua/module/pandoc-path.lua diff --git a/doc/lua-filters.md b/doc/lua-filters.md index 787365212..33c0d27bd 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3376,6 +3376,178 @@ methods and convenience functions. `comp`: : Comparison function as described above. +# Module pandoc.path + +Module for file path manipulations. + +## Static Fields {#pandoc.path-fields} + +### separator {#pandoc.path.separator} + +The character that separates directories. + +### search_path_separator {#pandoc.path.search_path_separator} + +The character that is used to separate the entries in the `PATH` +environment variable. + +## Functions {#pandoc.path-functions} + +### directory (filepath) {#pandoc.path.directory} + +Get the directory name; move up one level. + +Parameters: + +filepath +: path (string) + +Returns: + +- The filepath up to the last directory separator. (string) + +### filename (filepath) {#pandoc.path.filename} + +Get the file name. + +Parameters: + +filepath +: path (string) + +Returns: + +- File name part of the input path. (string) + +### is_absolute (filepath) {#pandoc.path.is_absolute} + +Checks whether a path is absolute, i.e. not fixed to a root. + +Parameters: + +filepath +: path (string) + +Returns: + +- `true` iff `filepath` is an absolute path, `false` otherwise. + (boolean) + +### is_relative (filepath) {#pandoc.path.is_relative} + +Checks whether a path is relative or fixed to a root. + +Parameters: + +filepath +: path (string) + +Returns: + +- `true` iff `filepath` is a relative path, `false` otherwise. + (boolean) + +### join (filepaths) {#pandoc.path.join} + +Join path elements back together by the directory separator. + +Parameters: + +filepaths +: path components (list of strings) + +Returns: + +- The joined path. (string) + +### make_relative (path, root[, unsafe]) {#pandoc.path.make_relative} + +Contract a filename, based on a relative path. Note that the +resulting path will usually not introduce `..` paths, as the +presence of symlinks means `../b` may not reach `a/b` if it starts +from `a/c`. For a worked example see [this blog +post](http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html). + +Set `unsafe` to a truthy value to a allow `..` in paths. + +Parameters: + +path +: path to be made relative (string) + +root +: root path (string) + +unsafe +: whether to allow `..` in the result. (boolean) + +Returns: + +- contracted filename (string) + +### normalize (filepath) {#pandoc.path.normalize} + +Normalizes a path. + +- `//` outside of the drive can be made blank +- `/` becomes the `path.separator` +- `./` -\> '' +- an empty path becomes `.` + +Parameters: + +filepath +: path (string) + +Returns: + +- The normalized path. (string) + +### split (filepath) {#pandoc.path.split} + +Splits a path by the directory separator. + +Parameters: + +filepath +: path (string) + +Returns: + +- List of all path components. (list of strings) + +### split_extension (filepath) {#pandoc.path.split_extension} + +Splits the last extension from a file path and returns the parts. The +extension, if present, includes the leading separator; if the path has +no extension, then the empty string is returned as the extension. + +Parameters: + +filepath +: path (string) + +Returns: + +- filepath without extension (string) + +- extension or empty string (string) + +### split_search_path (search_path) {#pandoc.path.split_search_path} + +Takes a string and splits it on the `search_path_separator` character. +Blank items are ignored on Windows, and converted to `.` on Posix. On +Windows path elements are stripped of quotes. + +Parameters: + +search_path +: platform-specific search path (string) + +Returns: + +- list of directories in search path (list of strings) + # Module pandoc.system Access to system information and functionality. diff --git a/pandoc.cabal b/pandoc.cabal index 2f8c255a8..4b808af84 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -429,6 +429,7 @@ library filepath >= 1.1 && < 1.5, haddock-library >= 1.8 && < 1.10, hslua >= 1.1 && < 1.4, + hslua-module-path >= 0.0.1 && < 0.1.0, hslua-module-system >= 0.2 && < 0.3, hslua-module-text >= 0.2.1 && < 0.4, http-client >= 0.4.30 && < 0.8, diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index 5949a1a7d..2f1c139db 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -17,6 +17,7 @@ import Foreign.Lua (NumResults) import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, loadDefaultModule) import qualified Foreign.Lua as Lua +import qualified Foreign.Lua.Module.Path as Path import qualified Foreign.Lua.Module.Text as Text import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag @@ -43,6 +44,7 @@ pandocPackageSearcher pkgName = case pkgName of "pandoc" -> pushWrappedHsFun Pandoc.pushModule "pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule + "pandoc.path" -> pushWrappedHsFun Path.pushModule "pandoc.system" -> pushWrappedHsFun System.pushModule "pandoc.types" -> pushWrappedHsFun Types.pushModule "pandoc.utils" -> pushWrappedHsFun Utils.pushModule diff --git a/stack.yaml b/stack.yaml index dc6fa6517..da7acd8db 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,7 +10,9 @@ extra-deps: - haddock-library-1.9.0 - skylighting-0.10.2 - skylighting-core-0.10.2 -- hslua-1.1.2 +- hslua-1.3.0 +- hslua-module-path-0.0.1 +- hslua-module-text-0.3.0.1 - jira-wiki-markup-1.3.2 - HsYAML-aeson-0.2.0.0 - commonmark-0.1.1.3 diff --git a/test/Tests/Lua/Module.hs b/test/Tests/Lua/Module.hs index d88633cf8..8be445f65 100644 --- a/test/Tests/Lua/Module.hs +++ b/test/Tests/Lua/Module.hs @@ -25,6 +25,8 @@ tests = ("lua" "module" "pandoc-list.lua") , testPandocLua "pandoc.mediabag" ("lua" "module" "pandoc-mediabag.lua") + , testPandocLua "pandoc.path" + ("lua" "module" "pandoc-path.lua") , testPandocLua "pandoc.types" ("lua" "module" "pandoc-types.lua") , testPandocLua "pandoc.util" diff --git a/test/lua/module/pandoc-path.lua b/test/lua/module/pandoc-path.lua new file mode 100644 index 000000000..9a5a3f6c8 --- /dev/null +++ b/test/lua/module/pandoc-path.lua @@ -0,0 +1,17 @@ +local tasty = require 'tasty' +local path = require 'pandoc.path' + +local assert = tasty.assert +local test = tasty.test_case +local group = tasty.test_group + +return { + group 'path separator' { + test('is string', function () + assert.are_same(type(path.separator), 'string') + end), + test('is slash or backslash', function () + assert.is_truthy(path.separator:match '^[/\\]$') + end), + }, +} -- cgit v1.2.3 From f84512228e16e82cfb2197262847974905fb9b29 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 2 Feb 2021 18:21:29 +0100 Subject: Improve docs for directory, normalize --- doc/lua-filters.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/lua-filters.md b/doc/lua-filters.md index 33c0d27bd..0c86d6109 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3395,7 +3395,8 @@ environment variable. ### directory (filepath) {#pandoc.path.directory} -Get the directory name; move up one level. +Gets the directory name, i.e., removes the last directory +separator and everything after from the given path. Parameters: @@ -3489,8 +3490,10 @@ Returns: Normalizes a path. -- `//` outside of the drive can be made blank -- `/` becomes the `path.separator` +- `//` makes sense only as part of a (Windows) network drive; + elsewhere, multiple slashes are reduced to a single + `path.separator` (platform dependent). +- `/` becomes `path.separator` (platform dependent) - `./` -\> '' - an empty path becomes `.` -- cgit v1.2.3 From ebb8f23b6674bb41b68c6654a836ac0e94750180 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 2 Feb 2021 18:29:04 +0100 Subject: Use hslua-module-path 0.1.0 --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 4b808af84..3ab3fae74 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -429,7 +429,7 @@ library filepath >= 1.1 && < 1.5, haddock-library >= 1.8 && < 1.10, hslua >= 1.1 && < 1.4, - hslua-module-path >= 0.0.1 && < 0.1.0, + hslua-module-path >= 0.1.0 && < 0.2.0, hslua-module-system >= 0.2 && < 0.3, hslua-module-text >= 0.2.1 && < 0.4, http-client >= 0.4.30 && < 0.8, -- cgit v1.2.3 From 2ace15964b91be01fa35d9cc361942770c0e5def Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 2 Feb 2021 19:14:19 +0100 Subject: Fix stack config --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index da7acd8db..0e5cec3ea 100644 --- a/stack.yaml +++ b/stack.yaml @@ -11,7 +11,7 @@ extra-deps: - skylighting-0.10.2 - skylighting-core-0.10.2 - hslua-1.3.0 -- hslua-module-path-0.0.1 +- hslua-module-path-0.1.0 - hslua-module-text-0.3.0.1 - jira-wiki-markup-1.3.2 - HsYAML-aeson-0.2.0.0 -- cgit v1.2.3 From e0bf4bfe82a2246b9ea04cfbb97dff072d42f3f6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 2 Feb 2021 22:52:32 +0100 Subject: Check that all documented functions are present. Rely on tests in the module package to check the correctness of each function. --- test/lua/module/pandoc-path.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/lua/module/pandoc-path.lua b/test/lua/module/pandoc-path.lua index 9a5a3f6c8..7f3b21fe2 100644 --- a/test/lua/module/pandoc-path.lua +++ b/test/lua/module/pandoc-path.lua @@ -14,4 +14,23 @@ return { assert.is_truthy(path.separator:match '^[/\\]$') end), }, + group 'module' { + test('check function existence', function () + local functions = { + 'directory', + 'filename', + 'is_absolute', + 'is_relative', + 'join', + 'make_relative', + 'normalize', + 'split', + 'split_extension', + 'split_search_path', + } + for _, f in ipairs(functions) do + assert.are_equal(type(path[f]), 'function') + end + end) + } } -- cgit v1.2.3 From 6f79042502851a1ac50c5c462f4121a1c659b511 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 2 Feb 2021 22:57:16 +0100 Subject: Add tests for search_path_separator --- test/lua/module/pandoc-path.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/lua/module/pandoc-path.lua b/test/lua/module/pandoc-path.lua index 7f3b21fe2..81c11e7b7 100644 --- a/test/lua/module/pandoc-path.lua +++ b/test/lua/module/pandoc-path.lua @@ -14,6 +14,14 @@ return { assert.is_truthy(path.separator:match '^[/\\]$') end), }, + group 'search path separator' { + test('is string', function () + assert.are_same(type(path.search_path_separator), 'string') + end), + test('is colon or semicolon', function () + assert.is_truthy(path.search_path_separator:match '^[:;]$') + end) + }, group 'module' { test('check function existence', function () local functions = { -- cgit v1.2.3 From e6c7fcc59856893a4947925f0dddc3a44b74a48a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 2 Feb 2021 21:09:10 -0800 Subject: Fixed some compiler warnings in tests. --- test/Tests/Command.hs | 5 ++--- test/Tests/Helpers.hs | 3 --- test/Tests/Old.hs | 9 +-------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index bbfa62dea..8118e9759 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -19,12 +19,11 @@ import Data.Algorithm.Diff import System.Environment.Executable (getExecutablePath) import qualified Data.ByteString as BS import qualified Data.Text as T -import Data.List (isSuffixOf, intercalate) -import Data.Maybe (catMaybes) +import Data.List (isSuffixOf) import System.Directory import qualified System.Environment as Env import System.Exit -import System.FilePath (joinPath, splitDirectories, takeDirectory, ()) +import System.FilePath (takeDirectory, ()) import System.IO (hPutStr, stderr) import System.IO.Unsafe (unsafePerformIO) import System.Process diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index a4a3c0af5..813c80adb 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -26,10 +26,7 @@ import Prelude import Data.Algorithm.Diff import qualified Data.Map as M import Data.Text (Text, unpack) -import System.Directory -import System.Environment.Executable (getExecutablePath) import System.Exit -import System.FilePath import Test.Tasty import Test.Tasty.HUnit import Text.Pandoc.Builder (Blocks, Inlines, doc, plain) diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index 528388c51..a031700a6 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -15,10 +15,8 @@ module Tests.Old (tests) where import Prelude import Data.Algorithm.Diff -import Data.List (intercalate) -import Data.Maybe (catMaybes) import System.Exit -import System.FilePath (joinPath, splitDirectories, (<.>), ()) +import System.FilePath ((<.>), ()) import qualified System.Environment as Env import System.Environment.Executable (getExecutablePath) import Text.Pandoc.Process (pipeProcess) @@ -353,8 +351,3 @@ compareValues norm options expected actual = do "\n--- " ++ norm ++ "\n+++ " ++ cmd ++ "\n" ++ showDiff (1,1) diff ++ dash - -findDynlibDir :: [FilePath] -> Maybe FilePath -findDynlibDir [] = Nothing -findDynlibDir ("build":xs) = Just $ joinPath (reverse xs) "build" -findDynlibDir (_:xs) = findDynlibDir xs -- cgit v1.2.3 From 4de9edb8e82d14a0bd8f1e8b77e9019944c1440e Mon Sep 17 00:00:00 2001 From: Andrew Dunning Date: Wed, 3 Feb 2021 16:54:11 +0000 Subject: LaTeX template: Update to iftex package (#7073) Load the iftex package directly rather than via the ifxetex and ifluatex compatibility wrappers, which have been merged into a single package that is part of the LaTeX core. The capitalization of the commands has been changed for compatibility with older versions of TeX Live that have the version of iftex by the Persian TeX Group. This had been removed in for compatibility with BasicTeX, but that is no longer an issue. --- MANUAL.txt | 5 ++--- data/templates/default.latex | 22 +++++++++++----------- test/lhs-test.latex | 6 +++--- test/lhs-test.latex+lhs | 6 +++--- test/writer.latex | 6 +++--- test/writers-lang-and-dir.latex | 12 ++++++------ 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index ebed63d2c..4a82703a3 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -136,7 +136,7 @@ to output the generated LaTeX. You can then test it with `pdflatex test.tex`. When using LaTeX, the following packages need to be available (they are included with all recent versions of [TeX Live]): [`amsfonts`], [`amsmath`], [`lm`], [`unicode-math`], -[`ifxetex`], [`ifluatex`], [`listings`] (if the +[`iftex`], [`listings`] (if the `--listings` option is used), [`fancyvrb`], [`longtable`], [`booktabs`], [`graphicx`] (if the document contains images), [`hyperref`], [`xcolor`], @@ -180,8 +180,7 @@ footnotes in tables). [`graphicx`]: https://ctan.org/pkg/graphicx [`grffile`]: https://ctan.org/pkg/grffile [`hyperref`]: https://ctan.org/pkg/hyperref -[`ifluatex`]: https://ctan.org/pkg/ifluatex -[`ifxetex`]: https://ctan.org/pkg/ifxetex +[`iftex`]: https://ctan.org/pkg/iftex [`listings`]: https://ctan.org/pkg/listings [`lm`]: https://ctan.org/pkg/lm [`longtable`]: https://ctan.org/pkg/longtable diff --git a/data/templates/default.latex b/data/templates/default.latex index c567278e3..c98f53bb6 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -98,14 +98,14 @@ $endif$ $if(linestretch)$ \usepackage{setspace} $endif$ -\usepackage{ifxetex,ifluatex} -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\usepackage{iftex} +\ifPDFTeX \usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols \else % if luatex or xetex $if(mathspec)$ - \ifxetex + \ifXeTeX \usepackage{mathspec} \else \usepackage{unicode-math} @@ -129,7 +129,7 @@ $for(fontfamilies)$ $endfor$ $if(mathfont)$ $if(mathspec)$ - \ifxetex + \ifXeTeX \setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} \else \setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} @@ -139,18 +139,18 @@ $else$ $endif$ $endif$ $if(CJKmainfont)$ - \ifxetex + \ifXeTeX \usepackage{xeCJK} \setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} \fi $endif$ $if(luatexjapresetoptions)$ - \ifluatex + \ifLuaTeX \usepackage[$for(luatexjapresetoptions)$$luatexjapresetoptions$$sep$,$endfor$]{luatexja-preset} \fi $endif$ $if(CJKmainfont)$ - \ifluatex + \ifLuaTeX \usepackage[$for(luatexjafontspecoptions)$$luatexjafontspecoptions$$sep$,$endfor$]{luatexja-fontspec} \setmainjfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} \fi @@ -329,7 +329,7 @@ $for(header-includes)$ $header-includes$ $endfor$ $if(lang)$ -\ifxetex +\ifXeTeX % Load polyglossia as late as possible: uses bidi with RTL langages (e.g. Hebrew, Arabic) \usepackage{polyglossia} \setmainlanguage[$for(polyglossia-lang.options)$$polyglossia-lang.options$$sep$,$endfor$]{$polyglossia-lang.name$} @@ -346,15 +346,15 @@ $if(babel-newcommands)$ $endif$ \fi $endif$ -\ifluatex +\ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi $if(dir)$ -\ifxetex +\ifXeTeX % Load bidi as late as possible as it modifies e.g. graphicx \usepackage{bidi} \fi -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\ifPDFTeX \TeXXeTstate=1 \newcommand{\RL}[1]{\beginR #1\endR} \newcommand{\LR}[1]{\beginL #1\endL} diff --git a/test/lhs-test.latex b/test/lhs-test.latex index 32042b103..fdc344335 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -6,8 +6,8 @@ ]{article} \usepackage{amsmath,amssymb} \usepackage{lmodern} -\usepackage{ifxetex,ifluatex} -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\usepackage{iftex} +\ifPDFTeX \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols @@ -81,7 +81,7 @@ \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} \setcounter{secnumdepth}{-\maxdimen} % remove section numbering -\ifluatex +\ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index 4bc674f4a..f9149ba02 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -6,8 +6,8 @@ ]{article} \usepackage{amsmath,amssymb} \usepackage{lmodern} -\usepackage{ifxetex,ifluatex} -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\usepackage{iftex} +\ifPDFTeX \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols @@ -48,7 +48,7 @@ \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} \setcounter{secnumdepth}{-\maxdimen} % remove section numbering -\ifluatex +\ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi diff --git a/test/writer.latex b/test/writer.latex index 7fc707b27..abf772df4 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -6,8 +6,8 @@ ]{article} \usepackage{amsmath,amssymb} \usepackage{lmodern} -\usepackage{ifxetex,ifluatex} -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\usepackage{iftex} +\ifPDFTeX \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols @@ -63,7 +63,7 @@ \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} \setcounter{secnumdepth}{-\maxdimen} % remove section numbering -\ifluatex +\ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index 1172d23a2..d91f77325 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -7,8 +7,8 @@ ]{article} \usepackage{amsmath,amssymb} \usepackage{lmodern} -\usepackage{ifxetex,ifluatex} -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\usepackage{iftex} +\ifPDFTeX \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols @@ -45,7 +45,7 @@ \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} \setcounter{secnumdepth}{-\maxdimen} % remove section numbering -\ifxetex +\ifXeTeX % Load polyglossia as late as possible: uses bidi with RTL langages (e.g. Hebrew, Arabic) \usepackage{polyglossia} \setmainlanguage[]{english} @@ -69,14 +69,14 @@ \newcommand{\textfrench}[2][]{\foreignlanguage{french}{#2}} \newenvironment{french}[2][]{\begin{otherlanguage}{french}}{\end{otherlanguage}} \fi -\ifluatex +\ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi -\ifxetex +\ifXeTeX % Load bidi as late as possible as it modifies e.g. graphicx \usepackage{bidi} \fi -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex +\ifPDFTeX \TeXXeTstate=1 \newcommand{\RL}[1]{\beginR #1\endR} \newcommand{\LR}[1]{\beginL #1\endL} -- cgit v1.2.3 From b79aba6ea1ecb6a1f619128638369ef47f64d26d Mon Sep 17 00:00:00 2001 From: Nick Berendsen <58348935+Desbeers@users.noreply.github.com> Date: Wed, 3 Feb 2021 18:00:18 +0100 Subject: ePub writer: `belongs-to-collection` metadata (#7063) --- MANUAL.txt | 9 ++++ src/Text/Pandoc/Writers/EPUB.hs | 99 ++++++++++++++++++++++++----------------- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 4a82703a3..e771d3da4 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5931,6 +5931,15 @@ The following fields are recognized: `rights` ~ A string value. + +`belongs-to-collection` + ~ A string value. identifies the name of a collection to which + the EPUB Publication belongs. + +`group-position` + ~ The `group-position` field indicates the numeric position in which + the EPUB Publication belongs relative to other works belonging to + the same `belongs-to-collection` field. `cover-image` ~ A string value (path to cover image). diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 783f190f5..1f16f6772 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -74,26 +74,28 @@ data EPUBState = EPUBState { type E m = StateT EPUBState m data EPUBMetadata = EPUBMetadata{ - epubIdentifier :: [Identifier] - , epubTitle :: [Title] - , epubDate :: [Date] - , epubLanguage :: String - , epubCreator :: [Creator] - , epubContributor :: [Creator] - , epubSubject :: [String] - , epubDescription :: Maybe String - , epubType :: Maybe String - , epubFormat :: Maybe String - , epubPublisher :: Maybe String - , epubSource :: Maybe String - , epubRelation :: Maybe String - , epubCoverage :: Maybe String - , epubRights :: Maybe String - , epubCoverImage :: Maybe String - , epubStylesheets :: [FilePath] - , epubPageDirection :: Maybe ProgressionDirection - , epubIbooksFields :: [(String, String)] - , epubCalibreFields :: [(String, String)] + epubIdentifier :: [Identifier] + , epubTitle :: [Title] + , epubDate :: [Date] + , epubLanguage :: String + , epubCreator :: [Creator] + , epubContributor :: [Creator] + , epubSubject :: [String] + , epubDescription :: Maybe String + , epubType :: Maybe String + , epubFormat :: Maybe String + , epubPublisher :: Maybe String + , epubSource :: Maybe String + , epubRelation :: Maybe String + , epubCoverage :: Maybe String + , epubRights :: Maybe String + , epubBelongsToCollection :: Maybe String + , epubGroupPosition :: Maybe String + , epubCoverImage :: Maybe String + , epubStylesheets :: [FilePath] + , epubPageDirection :: Maybe ProgressionDirection + , epubIbooksFields :: [(String, String)] + , epubCalibreFields :: [(String, String)] } deriving Show data Date = Date{ @@ -235,6 +237,8 @@ addMetadataFromXML e@(Element (QName name _ (Just "dc")) attrs _ _) md | name == "relation" = md { epubRelation = Just $ strContent e } | name == "coverage" = md { epubCoverage = Just $ strContent e } | name == "rights" = md { epubRights = Just $ strContent e } + | name == "belongs-to-collection" = md { epubBelongsToCollection = Just $ strContent e } + | name == "group-position" = md { epubGroupPosition = Just $ strContent e } | otherwise = md where getAttr n = lookupAttr (opfName n) attrs addMetadataFromXML e@(Element (QName "meta" _ _) attrs _ _) md = @@ -313,26 +317,28 @@ simpleList s meta = metadataFromMeta :: WriterOptions -> Meta -> EPUBMetadata metadataFromMeta opts meta = EPUBMetadata{ - epubIdentifier = identifiers - , epubTitle = titles - , epubDate = date - , epubLanguage = language - , epubCreator = creators - , epubContributor = contributors - , epubSubject = subjects - , epubDescription = description - , epubType = epubtype - , epubFormat = format - , epubPublisher = publisher - , epubSource = source - , epubRelation = relation - , epubCoverage = coverage - , epubRights = rights - , epubCoverImage = coverImage - , epubStylesheets = stylesheets - , epubPageDirection = pageDirection - , epubIbooksFields = ibooksFields - , epubCalibreFields = calibreFields + epubIdentifier = identifiers + , epubTitle = titles + , epubDate = date + , epubLanguage = language + , epubCreator = creators + , epubContributor = contributors + , epubSubject = subjects + , epubDescription = description + , epubType = epubtype + , epubFormat = format + , epubPublisher = publisher + , epubSource = source + , epubRelation = relation + , epubCoverage = coverage + , epubRights = rights + , epubBelongsToCollection = belongsToCollection + , epubGroupPosition = groupPosition + , epubCoverImage = coverImage + , epubStylesheets = stylesheets + , epubPageDirection = pageDirection + , epubIbooksFields = ibooksFields + , epubCalibreFields = calibreFields } where identifiers = getIdentifier meta titles = getTitle meta @@ -350,6 +356,8 @@ metadataFromMeta opts meta = EPUBMetadata{ relation = metaValueToString <$> lookupMeta "relation" meta coverage = metaValueToString <$> lookupMeta "coverage" meta rights = metaValueToString <$> lookupMeta "rights" meta + belongsToCollection = metaValueToString <$> lookupMeta "belongs-to-collection" meta + groupPosition = metaValueToString <$> lookupMeta "group-position" meta coverImage = (TS.unpack <$> lookupContext "epub-cover-image" (writerVariables opts)) @@ -931,7 +939,7 @@ metadataElement version md currentTime = ++ descriptionNodes ++ typeNodes ++ formatNodes ++ publisherNodes ++ sourceNodes ++ relationNodes ++ coverageNodes ++ rightsNodes ++ coverImageNodes - ++ modifiedNodes + ++ modifiedNodes ++ belongsToCollectionNodes withIds base f = concat . zipWith f (map (\x -> base ++ ('-' : show x)) ([1..] :: [Int])) identifierNodes = withIds "epub-id" toIdentifierNode $ @@ -970,6 +978,15 @@ metadataElement version md currentTime = $ epubCoverImage md modifiedNodes = [ unode "meta" ! [("property", "dcterms:modified")] $ showDateTimeISO8601 currentTime | version == EPUB3 ] + belongsToCollectionNodes = + maybe [] + (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-id-1")] $ belongsToCollection ) + : + [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: String) ]) + (epubBelongsToCollection md)++ + maybe [] + (\groupPosition -> [unode "meta" ! [("refines", "#epub-id-1"), ("property", "group-position")] $ groupPosition ]) + (epubGroupPosition md) dcTag n s = unode ("dc:" ++ n) s dcTag' n s = [dcTag n s] toIdentifierNode id' (Identifier txt scheme) -- cgit v1.2.3 From 364fe4a03b97f04586194c49e15891ba9a639944 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 4 Feb 2021 15:29:39 +0100 Subject: doc/lua-filters.md: fix, improve docs for `pandoc.mediabag.fetch` --- doc/lua-filters.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/lua-filters.md b/doc/lua-filters.md index 0c86d6109..3b3c03727 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3176,12 +3176,22 @@ Usage: ### fetch {#pandoc.mediabag.fetch} -`fetch (source, base_url)` +`fetch (source)` Fetches the given source from a URL or local file. Returns two values: the contents of the file and the MIME type (or an empty string). +The function will first try to retrieve `source` from the +mediabag; if that fails, it will try to download it or read it +from the local file system while respecting pandoc's "resource +path" setting. + +Parameters: + +`source`: +: path to a resource; either a local file path or URI + Returns: - the entries MIME type, or nil if the file was not found. @@ -3190,7 +3200,7 @@ Returns: Usage: local diagram_url = "https://pandoc.org/diagram.jpg" - local mt, contents = pandoc.mediabag.fetch(diagram_url, ".") + local mt, contents = pandoc.mediabag.fetch(diagram_url) # Module pandoc.List -- cgit v1.2.3 From 57e56ed55cd278e6c7eb8cf2a0f4b9fa4160781e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 4 Feb 2021 19:07:59 +0100 Subject: doc/lua-filters.md: improve docs for `pandoc.mediabag.insert` --- doc/lua-filters.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/lua-filters.md b/doc/lua-filters.md index 3b3c03727..a99dc9008 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3084,7 +3084,8 @@ Clear-out the media bag, deleting all items. `insert (filepath, mime_type, contents)` -Adds a new entry to pandoc's media bag. +Adds a new entry to pandoc's media bag. Replaces any existing +mediabag entry with the same `filepath`. Parameters: @@ -3092,7 +3093,7 @@ Parameters: : filename and path relative to the output folder. `mime_type`: -: the file's MIME type +: the file's MIME type; use `nil` if unknown or unavailable. `contents`: : the binary contents of the file. -- cgit v1.2.3 From a5169f68b251e04b0a68a7d93a30bafcb3f85e78 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 4 Feb 2021 19:07:59 +0100 Subject: Lua filters: use same function names in Haskell and Lua --- .hlint.yaml | 1 + src/Text/Pandoc/Lua/Module/MediaBag.hs | 27 ++++++++++++++------------- src/Text/Pandoc/Lua/Module/Pandoc.hs | 31 ++++++++++++++++--------------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index 6b74014d4..d5ebffd34 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -60,6 +60,7 @@ - Text.Pandoc.Citeproc - Text.Pandoc.Extensions - Text.Pandoc.Lua.Marshaling.Version + - Text.Pandoc.Lua.Module.Pandoc - Text.Pandoc.Lua.Module.Utils - Text.Pandoc.Readers.Odt.ContentReader - Text.Pandoc.Readers.Odt.Namespaces diff --git a/src/Text/Pandoc/Lua/Module/MediaBag.hs b/src/Text/Pandoc/Lua/Module/MediaBag.hs index 715e53885..78b699176 100644 --- a/src/Text/Pandoc/Lua/Module/MediaBag.hs +++ b/src/Text/Pandoc/Lua/Module/MediaBag.hs @@ -13,6 +13,7 @@ module Text.Pandoc.Lua.Module.MediaBag ( pushModule ) where +import Prelude hiding (lookup) import Control.Monad (zipWithM_) import Foreign.Lua (Lua, NumResults, Optional) import Text.Pandoc.Class.CommonState (CommonState (..)) @@ -36,10 +37,10 @@ pushModule = do liftPandocLua Lua.newtable addFunction "delete" delete addFunction "empty" empty - addFunction "insert" insertMediaFn + addFunction "insert" insert addFunction "items" items - addFunction "lookup" lookupMediaFn - addFunction "list" mediaDirectoryFn + addFunction "lookup" lookup + addFunction "list" list addFunction "fetch" fetch return 1 @@ -53,11 +54,11 @@ empty :: PandocLua NumResults empty = 0 <$ modifyCommonState (\st -> st { stMediaBag = mempty }) -- | Insert a new item into the media bag. -insertMediaFn :: FilePath - -> Optional MimeType - -> BL.ByteString - -> PandocLua NumResults -insertMediaFn fp optionalMime contents = do +insert :: FilePath + -> Optional MimeType + -> BL.ByteString + -> PandocLua NumResults +insert fp optionalMime contents = do mb <- getMediaBag setMediaBag $ MB.insertMedia fp (Lua.fromOptional optionalMime) contents mb return (Lua.NumResults 0) @@ -66,9 +67,9 @@ insertMediaFn fp optionalMime contents = do items :: PandocLua NumResults items = getMediaBag >>= liftPandocLua . pushIterator -lookupMediaFn :: FilePath - -> PandocLua NumResults -lookupMediaFn fp = do +lookup :: FilePath + -> PandocLua NumResults +lookup fp = do res <- MB.lookupMedia fp <$> getMediaBag liftPandocLua $ case res of Nothing -> 1 <$ Lua.pushnil @@ -77,8 +78,8 @@ lookupMediaFn fp = do Lua.push contents return 2 -mediaDirectoryFn :: PandocLua NumResults -mediaDirectoryFn = do +list :: PandocLua NumResults +list = do dirContents <- MB.mediaDirectory <$> getMediaBag liftPandocLua $ do Lua.newtable diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index a8afecd2e..8d30f9a0c 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -14,6 +14,7 @@ module Text.Pandoc.Lua.Module.Pandoc ( pushModule ) where +import Prelude hiding (read) import Control.Monad (when) import Control.Monad.Except (throwError) import Data.Default (Default (..)) @@ -43,10 +44,10 @@ import Text.Pandoc.Error pushModule :: PandocLua NumResults pushModule = do loadDefaultModule "pandoc" - addFunction "read" readDoc - addFunction "pipe" pipeFn - addFunction "walk_block" walkBlock - addFunction "walk_inline" walkInline + addFunction "read" read + addFunction "pipe" pipe + addFunction "walk_block" walk_block + addFunction "walk_inline" walk_inline return 1 walkElement :: (Walkable (SingletonsList Inline) a, @@ -54,14 +55,14 @@ walkElement :: (Walkable (SingletonsList Inline) a, => a -> LuaFilter -> PandocLua a walkElement x f = liftPandocLua $ walkInlines f x >>= walkBlocks f -walkInline :: Inline -> LuaFilter -> PandocLua Inline -walkInline = walkElement +walk_inline :: Inline -> LuaFilter -> PandocLua Inline +walk_inline = walkElement -walkBlock :: Block -> LuaFilter -> PandocLua Block -walkBlock = walkElement +walk_block :: Block -> LuaFilter -> PandocLua Block +walk_block = walkElement -readDoc :: T.Text -> Optional T.Text -> PandocLua NumResults -readDoc content formatSpecOrNil = liftPandocLua $ do +read :: T.Text -> Optional T.Text -> PandocLua NumResults +read content formatSpecOrNil = liftPandocLua $ do let formatSpec = fromMaybe "markdown" (Lua.fromOptional formatSpecOrNil) res <- Lua.liftIO . runIO $ getReader formatSpec >>= \(rdr,es) -> @@ -79,11 +80,11 @@ readDoc content formatSpecOrNil = liftPandocLua $ do Left e -> Lua.raiseError $ show e -- | Pipes input through a command. -pipeFn :: String - -> [String] - -> BL.ByteString - -> PandocLua NumResults -pipeFn command args input = liftPandocLua $ do +pipe :: String -- ^ path to executable + -> [String] -- ^ list of arguments + -> BL.ByteString -- ^ input passed to process via stdin + -> PandocLua NumResults +pipe command args input = liftPandocLua $ do (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input case ec of ExitSuccess -> 1 <$ Lua.push output -- cgit v1.2.3 From 51c3c93f0f0e7a225d259795ea225a7496288ef6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 7 Feb 2021 03:09:05 +0100 Subject: CI: use cabal 2.2 when building with GHC 8.0.2. (#7085) --- .github/workflows/ci.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4debe1ebe..c3f29caef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: matrix: versions: - ghc: '8.0.2' - cabal: '2.4' + cabal: '2.2' - ghc: '8.2.2' cabal: '2.4' - ghc: '8.4.4' @@ -88,14 +88,17 @@ jobs: - name: Install dependencies run: | - cabal v2-update - cabal v2-build --dependencies-only --enable-tests --disable-optimization + v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') + cabal $v2-update + cabal $v2-build --dependencies-only --enable-tests --disable-optimization + - name: Build and test run: | - cabal v2-build --enable-tests --disable-optimization 2>&1 | tee build.log + v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') + cabal $v2-build --enable-tests --disable-optimization 2>&1 | tee build.log # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 - cabal v2-test --disable-optimization + cabal $v2-test --disable-optimization windows: -- cgit v1.2.3 From b63d579ba28d07cc0a42fb47fb3eb1213764608b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 4 Feb 2021 16:53:22 -0800 Subject: CI: use haskell/actions/setup. actions/haskell-setup is no longer maintained. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3f29caef..30a0a025a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: # need to install older cabal/ghc versions from ppa repository - name: Install recent cabal/ghc - uses: actions/setup-haskell@v1.1.3 + uses: haskell/actions/setup@v1 with: ghc-version: ${{ matrix.versions.ghc }} cabal-version: ${{ matrix.versions.cabal }} @@ -108,7 +108,7 @@ jobs: - uses: actions/checkout@v2 # We should have ghc 8.10, cabal, stack pre-installed - # - uses: actions/setup-haskell@v1.1.3 + # - uses: haskell/actions/setup@v1 # with: # enable-stack: true # stack-version: 'latest' @@ -157,7 +157,7 @@ jobs: - uses: actions/checkout@v2 - name: Install recent cabal/ghc - uses: actions/setup-haskell@v1.1.3 + uses: haskell/actions/setup@v1 with: ghc-version: ${{ matrix.versions.ghc }} cabal-version: ${{ matrix.versions.cabal }} -- cgit v1.2.3 From 0ab3e4048c44b60fbecd7da3f43435f8c0cde49f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 6 Feb 2021 19:00:22 -0800 Subject: stack.yaml - use commonmark-0.1.1.4 for GHC 9 --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 0e5cec3ea..304dc7b0b 100644 --- a/stack.yaml +++ b/stack.yaml @@ -15,7 +15,7 @@ extra-deps: - hslua-module-text-0.3.0.1 - jira-wiki-markup-1.3.2 - HsYAML-aeson-0.2.0.0 -- commonmark-0.1.1.3 +- commonmark-0.1.1.4 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 - doctemplates-0.9 -- cgit v1.2.3 From 8e9131db4edfda6deafdf94939b907e4dce3cbaa Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 6 Feb 2021 21:52:12 -0800 Subject: Markdown reader: improved handling of mmd link attributes in references. Previously they only worked for links that had titles. Closes #7080. --- src/Text/Pandoc/Readers/Markdown.hs | 2 ++ test/command/7080.md | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/command/7080.md diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5c3a21bb7..e46553dd8 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -334,6 +334,8 @@ referenceKey = try $ do skipMany spaceChar notFollowedBy' referenceTitle notFollowedBy' $ guardEnabled Ext_link_attributes >> attributes + notFollowedBy' $ guardEnabled Ext_mmd_link_attributes >> + try (spnl <* keyValAttr) notFollowedBy' (() <$ reference) many1Char $ notFollowedBy space >> litChar let betweenAngles = try $ char '<' >> manyTillChar litChar (char '>') diff --git a/test/command/7080.md b/test/command/7080.md new file mode 100644 index 000000000..0727c1083 --- /dev/null +++ b/test/command/7080.md @@ -0,0 +1,8 @@ +``` +% pandoc -f markdown_mmd -t native +![][image] + +[image]: image.png width=100px height=150px +^D +[Para [Image ("",[],[("width","100px"),("height","150px")]) [] ("image.png","")]] +``` -- cgit v1.2.3 From bab5d10ea70502a3aa1b2bd2025e3c469f588ee0 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sun, 7 Feb 2021 16:30:34 +0000 Subject: Document no template fallback for absolute path (#7088) See jgm/pandoc#7077 --- MANUAL.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index e771d3da4..5b90d039a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1897,7 +1897,8 @@ ${ styles.html() } ``` (If a partial is not found in the directory of the -template, it will also be sought in the `templates` +template and the template path is given as a relative +path, it will also be sought in the `templates` subdirectory of the user data directory.) Partials may optionally be applied to variables using -- cgit v1.2.3 From f7be8d09649a9337ea443defb1efcf68cf39aa44 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 7 Feb 2021 17:33:43 +0100 Subject: pandoc.cabal: use common stanza to reduce duplication (#7086) --- pandoc.cabal | 169 ++++++++++++++++------------------------------------------- 1 file changed, 45 insertions(+), 124 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 3ab3fae74..7696e22b3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -396,9 +396,43 @@ flag trypandoc Description: Build trypandoc cgi executable. Default: False +common common-options + default-language: Haskell2010 + build-depends: base >= 4.9 && < 5, + text >= 1.1.1.0 && < 1.3 + other-extensions: NoImplicitPrelude + ghc-options: -Wall -fno-warn-unused-do-bind + -Wincomplete-record-updates + -Wnoncanonical-monad-instances + + if impl(ghc < 8.4) + hs-source-dirs: prelude + other-modules: Prelude + build-depends: base-compat >= 0.9 + + if os(windows) + cpp-options: -D_WINDOWS + + -- Later: + -- -Wpartial-fields (currently used in Powerpoint writer) + -- -Wmissing-export-lists (currently some Odt modules violate this) + -- -Wredundant-constraints (problematic if we want to support older base) + if impl(ghc >= 8.2) + ghc-options: -Wcpp-undef + if impl(ghc >= 8.4) + ghc-options: -Wincomplete-uni-patterns + -Widentities + -fhide-source-paths + +common common-executable + import: common-options + build-depends: pandoc + ghc-options: -rtsopts -with-rtsopts=-K16m -threaded + + library - build-depends: base >= 4.9 && < 5, - Glob >= 0.7 && < 0.11, + import: common-options + build-depends: Glob >= 0.7 && < 0.11, HTTP >= 4000.0.5 && < 4000.4, HsYAML >= 0.2 && < 0.3, JuicyPixels >= 3.1.6.1 && < 3.4, @@ -465,35 +499,11 @@ library build-depends: basement >= 0.0.10, foundation >= 0.0.23 -- basement 0.0.9 won't build on 32-bit windows. - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 - if os(windows) - cpp-options: -D_WINDOWS - else + if !os(windows) build-depends: unix >= 2.4 && < 2.8 if flag(embed_data_files) cpp-options: -DEMBED_DATA_FILES other-modules: Text.Pandoc.Data - if os(windows) - cpp-options: -D_WINDOWS - ghc-options: -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - -- Later: - -- -Wpartial-fields (currently used in Powerpoint writer) - -- -Wmissing-export-lists (currently some Odt modules violate this) - -- -Wredundant-constraints (problematic if we want to support older base) - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths - - default-language: Haskell2010 - other-extensions: NoImplicitPrelude hs-source-dirs: src exposed-modules: Text.Pandoc, @@ -694,87 +704,36 @@ library buildable: True executable pandoc - build-depends: pandoc, base >= 4.8 && < 5 - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 - default-language: Haskell2010 - other-extensions: NoImplicitPrelude + import: common-executable hs-source-dirs: app main-is: pandoc.hs buildable: True other-modules: Paths_pandoc - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded - -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths executable trypandoc + import: common-executable main-is: trypandoc.hs hs-source-dirs: trypandoc - default-language: Haskell2010 - other-extensions: NoImplicitPrelude if flag(trypandoc) - build-depends: base, aeson, pandoc, - text, wai-extra, wai >= 0.3, http-types + build-depends: aeson, http-types, wai >= 0.3, wai-extra buildable: True else buildable: False - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded - -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths benchmark weigh-pandoc + import: common-executable type: exitcode-stdio-1.0 main-is: weigh-pandoc.hs hs-source-dirs: benchmark - build-depends: pandoc, - base >= 4.8 && < 5, - mtl >= 2.2 && < 2.3, - text, + build-depends: mtl >= 2.2 && < 2.3, weigh >= 0.0 && < 0.1, - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 - default-language: Haskell2010 - other-extensions: NoImplicitPrelude - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded - -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths - test-suite test-pandoc + import: common-executable type: exitcode-stdio-1.0 main-is: test-pandoc.hs hs-source-dirs: test build-depends: pandoc, - base >= 4.8 && < 5, Diff >= 0.2 && < 0.5, Glob >= 0.7 && < 0.11, QuickCheck >= 2.4 && < 2.15, @@ -796,14 +755,9 @@ test-suite test-pandoc tasty-lua >= 0.2 && < 0.3, tasty-quickcheck >= 0.8 && < 0.11, temporary >= 1.1 && < 1.4, - text >= 1.1.1.0 && < 1.3, time >= 1.5 && < 1.10, xml >= 1.3.12 && < 1.4, zip-archive >= 0.2.3.4 && < 0.5 - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 other-modules: Tests.Old Tests.Command Tests.Helpers @@ -859,48 +813,15 @@ test-suite test-pandoc Tests.Writers.OOXML Tests.Writers.Ms Tests.Writers.AnnotatedTable - if os(windows) - cpp-options: -D_WINDOWS - default-language: Haskell2010 - other-extensions: NoImplicitPrelude - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded - -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths - benchmark benchmark-pandoc + import: common-executable type: exitcode-stdio-1.0 main-is: benchmark-pandoc.hs hs-source-dirs: benchmark - build-depends: pandoc, - base >= 4.8 && < 5, - bytestring, + build-depends: bytestring, containers, tasty, tasty-bench >= 0.1 && <= 0.2, mtl >= 2.2 && < 2.3, - text >= 1.1.1.0 && < 1.3, time - if impl(ghc < 8.4) - hs-source-dirs: prelude - other-modules: Prelude - build-depends: base-compat >= 0.9 - default-language: Haskell2010 - other-extensions: NoImplicitPrelude - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded - -Wall -fno-warn-unused-do-bind - -Wincomplete-record-updates - -Wnoncanonical-monad-instances - if impl(ghc >= 8.2) - ghc-options: -Wcpp-undef - if impl(ghc >= 8.4) - ghc-options: -Wincomplete-uni-patterns - -Widentities - -fhide-source-paths -- cgit v1.2.3 From d202f7eb77242bd2d9395b950b74fc9b22f9ae13 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 7 Feb 2021 19:02:35 +0100 Subject: Avoid unnecessary use of NoImplicitPrelude pragma (#7089) --- app/pandoc.hs | 2 -- benchmark/benchmark-pandoc.hs | 2 -- benchmark/weigh-pandoc.hs | 2 -- pandoc.cabal | 2 +- src/Text/Pandoc/UTF8.hs | 1 - test/Tests/Command.hs | 2 -- test/Tests/Helpers.hs | 2 -- test/Tests/Lua.hs | 2 -- test/Tests/Old.hs | 2 -- test/Tests/Readers/Creole.hs | 2 -- test/Tests/Readers/Docx.hs | 2 -- test/Tests/Readers/DokuWiki.hs | 2 -- test/Tests/Readers/EPUB.hs | 2 -- test/Tests/Readers/FB2.hs | 2 -- test/Tests/Readers/HTML.hs | 2 -- test/Tests/Readers/JATS.hs | 2 -- test/Tests/Readers/Jira.hs | 1 - test/Tests/Readers/LaTeX.hs | 2 -- test/Tests/Readers/Man.hs | 1 - test/Tests/Readers/Markdown.hs | 2 -- test/Tests/Readers/Muse.hs | 2 -- test/Tests/Readers/Odt.hs | 2 -- test/Tests/Readers/Org/Block.hs | 2 -- test/Tests/Readers/Org/Block/CodeBlock.hs | 2 -- test/Tests/Readers/Org/Block/Figure.hs | 2 -- test/Tests/Readers/Org/Block/Header.hs | 2 -- test/Tests/Readers/Org/Block/List.hs | 2 -- test/Tests/Readers/Org/Block/Table.hs | 2 -- test/Tests/Readers/Org/Directive.hs | 2 -- test/Tests/Readers/Org/Inline.hs | 2 -- test/Tests/Readers/Org/Inline/Citation.hs | 2 -- test/Tests/Readers/Org/Inline/Note.hs | 2 -- test/Tests/Readers/Org/Inline/Smart.hs | 2 -- test/Tests/Readers/Org/Meta.hs | 2 -- test/Tests/Readers/Org/Shared.hs | 2 -- test/Tests/Readers/RST.hs | 2 -- test/Tests/Readers/Txt2Tags.hs | 2 -- test/Tests/Shared.hs | 2 -- test/Tests/Writers/AnnotatedTable.hs | 1 - test/Tests/Writers/AsciiDoc.hs | 2 -- test/Tests/Writers/ConTeXt.hs | 2 -- test/Tests/Writers/Docbook.hs | 2 -- test/Tests/Writers/Docx.hs | 2 -- test/Tests/Writers/FB2.hs | 2 -- test/Tests/Writers/HTML.hs | 2 -- test/Tests/Writers/JATS.hs | 2 -- test/Tests/Writers/LaTeX.hs | 2 -- test/Tests/Writers/Markdown.hs | 2 -- test/Tests/Writers/Ms.hs | 2 -- test/Tests/Writers/Muse.hs | 1 - test/Tests/Writers/Native.hs | 2 -- test/Tests/Writers/OOXML.hs | 2 -- test/Tests/Writers/Org.hs | 2 -- test/Tests/Writers/Plain.hs | 2 -- test/Tests/Writers/Powerpoint.hs | 2 -- test/Tests/Writers/RST.hs | 2 -- test/Tests/Writers/TEI.hs | 2 -- test/test-pandoc.hs | 2 -- trypandoc/trypandoc.hs | 2 -- 59 files changed, 1 insertion(+), 112 deletions(-) diff --git a/app/pandoc.hs b/app/pandoc.hs index 162570f18..0e30f45d1 100644 --- a/app/pandoc.hs +++ b/app/pandoc.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {- | Module : Main Copyright : Copyright (C) 2006-2021 John MacFarlane @@ -12,7 +11,6 @@ Parses command-line options and calls the appropriate readers and writers. -} module Main where -import Prelude import qualified Control.Exception as E import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions) import Text.Pandoc.Error (handleError) diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 045573273..496732693 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2012-2021 John MacFarlane @@ -17,7 +16,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -import Prelude import Text.Pandoc import Text.Pandoc.MIME import Control.Monad.Except (throwError, liftIO) diff --git a/benchmark/weigh-pandoc.hs b/benchmark/weigh-pandoc.hs index ad4c83ad7..b77fa9ee9 100644 --- a/benchmark/weigh-pandoc.hs +++ b/benchmark/weigh-pandoc.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Main @@ -11,7 +10,6 @@ Benchmarks to determine resource use of readers and writers. -} -import Prelude import Weigh import Text.Pandoc import Data.Text (Text, unpack) diff --git a/pandoc.cabal b/pandoc.cabal index 7696e22b3..72e7c2da5 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -400,7 +400,6 @@ common common-options default-language: Haskell2010 build-depends: base >= 4.9 && < 5, text >= 1.1.1.0 && < 1.3 - other-extensions: NoImplicitPrelude ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances @@ -409,6 +408,7 @@ common common-options hs-source-dirs: prelude other-modules: Prelude build-depends: base-compat >= 0.9 + other-extensions: NoImplicitPrelude if os(windows) cpp-options: -D_WINDOWS diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index b583dbbdb..567f5abe5 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.UTF8 diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index 8118e9759..07d825f73 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TupleSections #-} {- | Module : Tests.Command @@ -14,7 +13,6 @@ Run commands, and test results, defined in markdown files. module Tests.Command (runTest, tests) where -import Prelude import Data.Algorithm.Diff import System.Environment.Executable (getExecutablePath) import qualified Data.ByteString as BS diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index 813c80adb..31e727a66 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE FlexibleInstances #-} {- | Module : Tests.Helpers @@ -22,7 +21,6 @@ module Tests.Helpers ( test ) where -import Prelude import Data.Algorithm.Diff import qualified Data.Map as M import Data.Text (Text, unpack) diff --git a/test/Tests/Lua.hs b/test/Tests/Lua.hs index 1dfbbd053..31c011900 100644 --- a/test/Tests/Lua.hs +++ b/test/Tests/Lua.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | @@ -14,7 +13,6 @@ Unit and integration tests for pandoc's Lua subsystem. -} module Tests.Lua ( runLuaTest, tests ) where -import Prelude import Control.Monad (when) import System.FilePath (()) import Test.Tasty (TestTree, localOption) diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index a031700a6..17ece49fd 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TupleSections #-} {- | Module : Tests.Old @@ -13,7 +12,6 @@ -} module Tests.Old (tests) where -import Prelude import Data.Algorithm.Diff import System.Exit import System.FilePath ((<.>), ()) diff --git a/test/Tests/Readers/Creole.hs b/test/Tests/Readers/Creole.hs index 1fc0e62d7..3320b78e8 100644 --- a/test/Tests/Readers/Creole.hs +++ b/test/Tests/Readers/Creole.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Creole @@ -14,7 +13,6 @@ Tests for the creole reader. -} module Tests.Readers.Creole (tests) where -import Prelude import Data.Text (Text) import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 12007f502..263e04173 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Docx @@ -13,7 +12,6 @@ Tests for the word docx reader. -} module Tests.Readers.Docx (tests) where -import Prelude import Codec.Archive.Zip import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B diff --git a/test/Tests/Readers/DokuWiki.hs b/test/Tests/Readers/DokuWiki.hs index d5f0c45a9..84ba86d46 100644 --- a/test/Tests/Readers/DokuWiki.hs +++ b/test/Tests/Readers/DokuWiki.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | @@ -14,7 +13,6 @@ Tests for DokuWiki reader. -} module Tests.Readers.DokuWiki (tests) where -import Prelude import Data.Text (Text) import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/EPUB.hs b/test/Tests/Readers/EPUB.hs index 3c75dd08d..13e239911 100644 --- a/test/Tests/Readers/EPUB.hs +++ b/test/Tests/Readers/EPUB.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {- | Module : Tests.Readers.EPUB Copyright : © 2006-2021 John MacFarlane @@ -12,7 +11,6 @@ Tests for the EPUB mediabag. -} module Tests.Readers.EPUB (tests) where -import Prelude import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/FB2.hs b/test/Tests/Readers/FB2.hs index fbb2e2150..42054a235 100644 --- a/test/Tests/Readers/FB2.hs +++ b/test/Tests/Readers/FB2.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {- | Module : Tests.Readers.FB2 Copyright : © 2018-2020 Alexander Krotov @@ -12,7 +11,6 @@ Tests for the EPUB mediabag. -} module Tests.Readers.FB2 (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Test.Tasty.Golden (goldenVsString) diff --git a/test/Tests/Readers/HTML.hs b/test/Tests/Readers/HTML.hs index 578c76860..f23af2cb1 100644 --- a/test/Tests/Readers/HTML.hs +++ b/test/Tests/Readers/HTML.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.HTML @@ -13,7 +12,6 @@ Tests for the HTML reader. -} module Tests.Readers.HTML (tests) where -import Prelude import Data.Text (Text) import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/JATS.hs b/test/Tests/Readers/JATS.hs index 3c61f602f..525499c86 100644 --- a/test/Tests/Readers/JATS.hs +++ b/test/Tests/Readers/JATS.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.JATS @@ -13,7 +12,6 @@ Tests for the JATS reader. -} module Tests.Readers.JATS (tests) where -import Prelude import Data.Text (Text) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Readers/Jira.hs b/test/Tests/Readers/Jira.hs index bf78fe1fe..e170d2aaa 100644 --- a/test/Tests/Readers/Jira.hs +++ b/test/Tests/Readers/Jira.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | diff --git a/test/Tests/Readers/LaTeX.hs b/test/Tests/Readers/LaTeX.hs index 2a52ffd18..77104c853 100644 --- a/test/Tests/Readers/LaTeX.hs +++ b/test/Tests/Readers/LaTeX.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.LaTeX @@ -13,7 +12,6 @@ Tests for the LaTeX reader. -} module Tests.Readers.LaTeX (tests) where -import Prelude import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 diff --git a/test/Tests/Readers/Man.hs b/test/Tests/Readers/Man.hs index 4f3ab5a28..d36151d58 100644 --- a/test/Tests/Readers/Man.hs +++ b/test/Tests/Readers/Man.hs @@ -13,7 +13,6 @@ Tests for the Man reader. -} module Tests.Readers.Man (tests) where -import Prelude import Data.Text (Text) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index 18f909583..0930deae6 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Markdown @@ -13,7 +12,6 @@ Tests for the Markdown reader. -} module Tests.Readers.Markdown (tests) where -import Prelude import Data.Text (Text, unpack) import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/Muse.hs b/test/Tests/Readers/Muse.hs index 4ec1631e0..68bdc87b4 100644 --- a/test/Tests/Readers/Muse.hs +++ b/test/Tests/Readers/Muse.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Muse @@ -13,7 +12,6 @@ Tests for the Muse reader. -} module Tests.Readers.Muse (tests) where -import Prelude import Data.List (intersperse) import Data.Monoid (Any (..)) import Data.Text (Text) diff --git a/test/Tests/Readers/Odt.hs b/test/Tests/Readers/Odt.hs index f5e427ba2..9b5ec6b9e 100644 --- a/test/Tests/Readers/Odt.hs +++ b/test/Tests/Readers/Odt.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Odt @@ -14,7 +13,6 @@ Tests for the ODT reader. -} module Tests.Readers.Odt (tests) where -import Prelude import Control.Monad (liftM) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B diff --git a/test/Tests/Readers/Org/Block.hs b/test/Tests/Readers/Org/Block.hs index 2ce07c4bb..779563794 100644 --- a/test/Tests/Readers/Org/Block.hs +++ b/test/Tests/Readers/Org/Block.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block @@ -13,7 +12,6 @@ Tests parsing of org blocks. -} module Tests.Readers.Org.Block (tests) where -import Prelude import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) diff --git a/test/Tests/Readers/Org/Block/CodeBlock.hs b/test/Tests/Readers/Org/Block/CodeBlock.hs index d40c3bc1d..6b83ec6a9 100644 --- a/test/Tests/Readers/Org/Block/CodeBlock.hs +++ b/test/Tests/Readers/Org/Block/CodeBlock.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.CodeBlock @@ -13,7 +12,6 @@ Test parsing of org code blocks. -} module Tests.Readers.Org.Block.CodeBlock (tests) where -import Prelude import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) diff --git a/test/Tests/Readers/Org/Block/Figure.hs b/test/Tests/Readers/Org/Block/Figure.hs index 8822f5b03..eb5be1c2b 100644 --- a/test/Tests/Readers/Org/Block/Figure.hs +++ b/test/Tests/Readers/Org/Block/Figure.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Figure @@ -13,7 +12,6 @@ Test parsing of org figures. -} module Tests.Readers.Org.Block.Figure (tests) where -import Prelude import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) diff --git a/test/Tests/Readers/Org/Block/Header.hs b/test/Tests/Readers/Org/Block/Header.hs index 887055451..1344ad79b 100644 --- a/test/Tests/Readers/Org/Block/Header.hs +++ b/test/Tests/Readers/Org/Block/Header.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Header @@ -13,7 +12,6 @@ Test parsing of org header blocks. -} module Tests.Readers.Org.Block.Header (tests) where -import Prelude import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep, tagSpan) diff --git a/test/Tests/Readers/Org/Block/List.hs b/test/Tests/Readers/Org/Block/List.hs index ac03c583b..9686b5148 100644 --- a/test/Tests/Readers/Org/Block/List.hs +++ b/test/Tests/Readers/Org/Block/List.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Header @@ -13,7 +12,6 @@ Test parsing of org lists. -} module Tests.Readers.Org.Block.List (tests) where -import Prelude import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) diff --git a/test/Tests/Readers/Org/Block/Table.hs b/test/Tests/Readers/Org/Block/Table.hs index cb38fcc12..ce18e6a5b 100644 --- a/test/Tests/Readers/Org/Block/Table.hs +++ b/test/Tests/Readers/Org/Block/Table.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Table @@ -13,7 +12,6 @@ Test parsing of org tables. -} module Tests.Readers.Org.Block.Table (tests) where -import Prelude import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) diff --git a/test/Tests/Readers/Org/Directive.hs b/test/Tests/Readers/Org/Directive.hs index 00cb9762b..85d1bc088 100644 --- a/test/Tests/Readers/Org/Directive.hs +++ b/test/Tests/Readers/Org/Directive.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Directive @@ -13,7 +12,6 @@ Tests parsing of org directives (like @#+OPTIONS@). -} module Tests.Readers.Org.Directive (tests) where -import Prelude import Data.Time (UTCTime (UTCTime), secondsToDiffTime) import Data.Time.Calendar (Day (ModifiedJulianDay)) import Test.Tasty (TestTree, testGroup) diff --git a/test/Tests/Readers/Org/Inline.hs b/test/Tests/Readers/Org/Inline.hs index 13e9fef21..111d74879 100644 --- a/test/Tests/Readers/Org/Inline.hs +++ b/test/Tests/Readers/Org/Inline.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline @@ -13,7 +12,6 @@ Tests parsing of org inlines. -} module Tests.Readers.Org.Inline (tests) where -import Prelude import Data.List (intersperse) import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) diff --git a/test/Tests/Readers/Org/Inline/Citation.hs b/test/Tests/Readers/Org/Inline/Citation.hs index 87bb3ca75..5538f1ec8 100644 --- a/test/Tests/Readers/Org/Inline/Citation.hs +++ b/test/Tests/Readers/Org/Inline/Citation.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Citation @@ -13,7 +12,6 @@ Test parsing of citations in org input. -} module Tests.Readers.Org.Inline.Citation (tests) where -import Prelude import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) diff --git a/test/Tests/Readers/Org/Inline/Note.hs b/test/Tests/Readers/Org/Inline/Note.hs index 20157d2ae..c37133d54 100644 --- a/test/Tests/Readers/Org/Inline/Note.hs +++ b/test/Tests/Readers/Org/Inline/Note.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Note @@ -13,7 +12,6 @@ Test parsing of footnotes in org input. -} module Tests.Readers.Org.Inline.Note (tests) where -import Prelude import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) diff --git a/test/Tests/Readers/Org/Inline/Smart.hs b/test/Tests/Readers/Org/Inline/Smart.hs index 7fde380af..db96eb2ca 100644 --- a/test/Tests/Readers/Org/Inline/Smart.hs +++ b/test/Tests/Readers/Org/Inline/Smart.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Smart @@ -13,7 +12,6 @@ Test smart parsing of quotes, apostrophe, etc. -} module Tests.Readers.Org.Inline.Smart (tests) where -import Prelude import Data.Text (Text) import Test.Tasty (TestTree) import Tests.Helpers ((=?>), purely, test) diff --git a/test/Tests/Readers/Org/Meta.hs b/test/Tests/Readers/Org/Meta.hs index 3c50f891b..6363d84b0 100644 --- a/test/Tests/Readers/Org/Meta.hs +++ b/test/Tests/Readers/Org/Meta.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Meta @@ -13,7 +12,6 @@ Tests parsing of org meta data (mostly lines starting with @#+@). -} module Tests.Readers.Org.Meta (tests) where -import Prelude import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) diff --git a/test/Tests/Readers/Org/Shared.hs b/test/Tests/Readers/Org/Shared.hs index 4d0848575..c584eff19 100644 --- a/test/Tests/Readers/Org/Shared.hs +++ b/test/Tests/Readers/Org/Shared.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Shared @@ -18,7 +17,6 @@ module Tests.Readers.Org.Shared , tagSpan ) where -import Prelude import Data.List (intersperse) import Data.Text (Text) import Tests.Helpers (ToString, purely, test) diff --git a/test/Tests/Readers/RST.hs b/test/Tests/Readers/RST.hs index 68241b7f9..a12b59fc2 100644 --- a/test/Tests/Readers/RST.hs +++ b/test/Tests/Readers/RST.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | @@ -14,7 +13,6 @@ Tests for the RST reader. -} module Tests.Readers.RST (tests) where -import Prelude import Data.Text (Text) import qualified Data.Text as T import Test.Tasty diff --git a/test/Tests/Readers/Txt2Tags.hs b/test/Tests/Readers/Txt2Tags.hs index 62f336690..013f29d68 100644 --- a/test/Tests/Readers/Txt2Tags.hs +++ b/test/Tests/Readers/Txt2Tags.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Txt2Tags @@ -14,7 +13,6 @@ Tests for the Txt2Tags reader. -} module Tests.Readers.Txt2Tags (tests) where -import Prelude import Data.List (intersperse) import Data.Text (Text) import qualified Data.Text as T diff --git a/test/Tests/Shared.hs b/test/Tests/Shared.hs index 72a59fec0..e415ea153 100644 --- a/test/Tests/Shared.hs +++ b/test/Tests/Shared.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Shared @@ -13,7 +12,6 @@ Tests for functions used in many parts of the library. -} module Tests.Shared (tests) where -import Prelude import System.FilePath.Posix (joinPath) import Test.Tasty import Test.Tasty.HUnit (assertBool, testCase, (@?=)) diff --git a/test/Tests/Writers/AnnotatedTable.hs b/test/Tests/Writers/AnnotatedTable.hs index 7e16cf8e0..53cca80a6 100644 --- a/test/Tests/Writers/AnnotatedTable.hs +++ b/test/Tests/Writers/AnnotatedTable.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Writers.AnnotatedTable diff --git a/test/Tests/Writers/AsciiDoc.hs b/test/Tests/Writers/AsciiDoc.hs index 75f6e5e97..04655635f 100644 --- a/test/Tests/Writers/AsciiDoc.hs +++ b/test/Tests/Writers/AsciiDoc.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.AsciiDoc (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/ConTeXt.hs b/test/Tests/Writers/ConTeXt.hs index c747e5d2f..5c1c98d4e 100644 --- a/test/Tests/Writers/ConTeXt.hs +++ b/test/Tests/Writers/ConTeXt.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.ConTeXt (tests) where -import Prelude import Data.Text (unpack, pack) import Test.Tasty import Test.Tasty.QuickCheck diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index 621c1280b..842aed7ae 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Docbook (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/Docx.hs b/test/Tests/Writers/Docx.hs index 66a5c3d36..2e0f1e3fb 100644 --- a/test/Tests/Writers/Docx.hs +++ b/test/Tests/Writers/Docx.hs @@ -1,7 +1,5 @@ -{-# LANGUAGE NoImplicitPrelude #-} module Tests.Writers.Docx (tests) where -import Prelude import Text.Pandoc import Test.Tasty import Tests.Writers.OOXML diff --git a/test/Tests/Writers/FB2.hs b/test/Tests/Writers/FB2.hs index 7699c58e9..2e10636fa 100644 --- a/test/Tests/Writers/FB2.hs +++ b/test/Tests/Writers/FB2.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.FB2 (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Text.Pandoc diff --git a/test/Tests/Writers/HTML.hs b/test/Tests/Writers/HTML.hs index 6ff0a6e1d..328801e31 100644 --- a/test/Tests/Writers/HTML.hs +++ b/test/Tests/Writers/HTML.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.HTML (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/JATS.hs b/test/Tests/Writers/JATS.hs index 7d98f979b..2f501c890 100644 --- a/test/Tests/Writers/JATS.hs +++ b/test/Tests/Writers/JATS.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.JATS (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/LaTeX.hs b/test/Tests/Writers/LaTeX.hs index 44e23d48e..ae5879099 100644 --- a/test/Tests/Writers/LaTeX.hs +++ b/test/Tests/Writers/LaTeX.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.LaTeX (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/Markdown.hs b/test/Tests/Writers/Markdown.hs index 4b819de24..d4f927ebe 100644 --- a/test/Tests/Writers/Markdown.hs +++ b/test/Tests/Writers/Markdown.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} module Tests.Writers.Markdown (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Tests.Helpers diff --git a/test/Tests/Writers/Ms.hs b/test/Tests/Writers/Ms.hs index d73603314..ad6849633 100644 --- a/test/Tests/Writers/Ms.hs +++ b/test/Tests/Writers/Ms.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Ms (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Text.Pandoc diff --git a/test/Tests/Writers/Muse.hs b/test/Tests/Writers/Muse.hs index d0df0799f..5bddca3af 100644 --- a/test/Tests/Writers/Muse.hs +++ b/test/Tests/Writers/Muse.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Muse (tests) where diff --git a/test/Tests/Writers/Native.hs b/test/Tests/Writers/Native.hs index 905e83b1e..d7771ca19 100644 --- a/test/Tests/Writers/Native.hs +++ b/test/Tests/Writers/Native.hs @@ -1,7 +1,5 @@ -{-# LANGUAGE NoImplicitPrelude #-} module Tests.Writers.Native (tests) where -import Prelude import Data.Text (unpack) import Test.Tasty import Test.Tasty.QuickCheck diff --git a/test/Tests/Writers/OOXML.hs b/test/Tests/Writers/OOXML.hs index 628ea9409..376f02c55 100644 --- a/test/Tests/Writers/OOXML.hs +++ b/test/Tests/Writers/OOXML.hs @@ -1,10 +1,8 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.OOXML (ooxmlTest) where -import Prelude import Text.Pandoc import Test.Tasty import Test.Tasty.Golden.Advanced diff --git a/test/Tests/Writers/Org.hs b/test/Tests/Writers/Org.hs index c99f7344d..9cbe360da 100644 --- a/test/Tests/Writers/Org.hs +++ b/test/Tests/Writers/Org.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Org (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Text.Pandoc diff --git a/test/Tests/Writers/Plain.hs b/test/Tests/Writers/Plain.hs index b8d1f6693..17edc9dbd 100644 --- a/test/Tests/Writers/Plain.hs +++ b/test/Tests/Writers/Plain.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Plain (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Text.Pandoc diff --git a/test/Tests/Writers/Powerpoint.hs b/test/Tests/Writers/Powerpoint.hs index be98fe0e7..87ebe990c 100644 --- a/test/Tests/Writers/Powerpoint.hs +++ b/test/Tests/Writers/Powerpoint.hs @@ -1,7 +1,5 @@ -{-# LANGUAGE NoImplicitPrelude #-} module Tests.Writers.Powerpoint (tests) where -import Prelude import Tests.Writers.OOXML (ooxmlTest) import Text.Pandoc import Test.Tasty diff --git a/test/Tests/Writers/RST.hs b/test/Tests/Writers/RST.hs index a52423fad..94745e9a2 100644 --- a/test/Tests/Writers/RST.hs +++ b/test/Tests/Writers/RST.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.RST (tests) where -import Prelude import Control.Monad.Identity import Test.Tasty import Test.Tasty.HUnit diff --git a/test/Tests/Writers/TEI.hs b/test/Tests/Writers/TEI.hs index 31e970495..fa372909f 100644 --- a/test/Tests/Writers/TEI.hs +++ b/test/Tests/Writers/TEI.hs @@ -1,8 +1,6 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.TEI (tests) where -import Prelude import Test.Tasty import Tests.Helpers import Text.Pandoc diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs index 9973dffc8..4d9da525b 100644 --- a/test/test-pandoc.hs +++ b/test/test-pandoc.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-} module Main where -import Prelude import System.Environment (getArgs) import qualified Control.Exception as E import Text.Pandoc.App (convertWithOpts, defaultOpts, options, diff --git a/trypandoc/trypandoc.hs b/trypandoc/trypandoc.hs index 9a53aa18c..810752afa 100644 --- a/trypandoc/trypandoc.hs +++ b/trypandoc/trypandoc.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Main @@ -12,7 +11,6 @@ Provides a webservice which allows to try pandoc in the browser. -} module Main where -import Prelude import Network.Wai.Handler.CGI import Network.Wai import Control.Applicative ((<$>)) -- cgit v1.2.3 From 69b7401e31216ae52164950970cf88b778d8d198 Mon Sep 17 00:00:00 2001 From: Nils Carlson Date: Mon, 8 Feb 2021 17:36:58 +0000 Subject: DocBook reader: Support informalfigure (#7079) Add support for informalfigure. --- src/Text/Pandoc/Readers/DocBook.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index d3b2dd4d3..ada3e98ec 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -206,7 +206,7 @@ List of all DocBook tags, with [x] indicating implemented, [x] info - A wrapper for information about a component or other block. (DocBook v5) [x] informalequation - A displayed mathematical equation without a title [x] informalexample - A displayed example without a title -[ ] informalfigure - A untitled figure +[x] informalfigure - An untitled figure [ ] informaltable - A table without a title [ ] initializer - The initializer for a FieldSynopsis [x] inlineequation - A mathematical equation or expression occurring inline @@ -669,6 +669,7 @@ blockTags = , "index" , "info" , "informalexample" + , "informalfigure" , "informaltable" , "itemizedlist" , "linegroup" @@ -855,6 +856,7 @@ parseBlock (Elem e) = "variablelist" -> definitionList <$> deflistitems "procedure" -> bulletList <$> steps "figure" -> getFigure e + "informalfigure" -> getFigure e "mediaobject" -> para <$> getMediaobject e "caption" -> skip "info" -> addMetadataFromElement e -- cgit v1.2.3 From 5cd1c1001fbfc768fdb146f14ae056a9a7ba94eb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 8 Feb 2021 09:15:43 -0800 Subject: ODT reader: give more information if zip can't be unpacked. --- src/Text/Pandoc/Readers/Odt.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 7b8bfd4b5..9b66b60ec 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -23,6 +23,8 @@ import System.FilePath import Control.Monad.Except (throwError) +import qualified Data.Text as T + import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition @@ -60,7 +62,8 @@ readOdt' _ bytes = bytesToOdt bytes-- of bytesToOdt :: B.ByteString -> Either PandocError (Pandoc, MediaBag) bytesToOdt bytes = case toArchiveOrFail bytes of Right archive -> archiveToOdt archive - Left _ -> Left $ PandocParseError "Couldn't parse odt file." + Left err -> Left $ PandocParseError + $ "Could not unzip ODT: " <> T.pack err -- archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag) -- cgit v1.2.3 From f70795dc5e431c0132acc8bb2d20bb5dec942de7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 8 Feb 2021 09:39:29 -0800 Subject: ODT reader: finer-grained errors on parse failure. See #7091. --- src/Text/Pandoc/Readers/Odt.hs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 9b66b60ec..9943d3147 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Reader.Odt @@ -67,29 +66,27 @@ bytesToOdt bytes = case toArchiveOrFail bytes of -- archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag) -archiveToOdt archive - | Just contentEntry <- findEntryByPath "content.xml" archive - , Just stylesEntry <- findEntryByPath "styles.xml" archive - , Just contentElem <- entryToXmlElem contentEntry - , Just stylesElem <- entryToXmlElem stylesEntry - , Right styles <- chooseMax (readStylesAt stylesElem ) - (readStylesAt contentElem) - , media <- filteredFilesFromArchive archive filePathIsOdtMedia - , startState <- readerState styles media - , Right pandocWithMedia <- runConverter' read_body - startState - contentElem - - = Right pandocWithMedia - - | otherwise - -- Not very detailed, but I don't think more information would be helpful - = Left $ PandocParseError "Couldn't parse odt file." - where - filePathIsOdtMedia :: FilePath -> Bool +archiveToOdt archive = either (Left. PandocParseError) Right $ do + let onFailure msg Nothing = Left msg + onFailure _ (Just x) = Right x + contentEntry <- onFailure "Could not find content.xml" + (findEntryByPath "content.xml" archive) + stylesEntry <- onFailure "Could not find styles.xml" + (findEntryByPath "styles.xml" archive) + contentElem <- onFailure "Could not find content element" + (entryToXmlElem contentEntry) + stylesElem <- onFailure "Could not find styles element" + (entryToXmlElem stylesEntry) + styles <- either (\_ -> Left "Could not read styles") Right + (chooseMax (readStylesAt stylesElem ) (readStylesAt contentElem)) + let filePathIsOdtMedia :: FilePath -> Bool filePathIsOdtMedia fp = let (dir, name) = splitFileName fp in (dir == "Pictures/") || (dir /= "./" && name == "content.xml") + let media = filteredFilesFromArchive archive filePathIsOdtMedia + let startState = readerState styles media + either (\_ -> Left "Could not convert opendocument") Right + (runConverter' read_body startState contentElem) -- -- cgit v1.2.3 From 9994ad977d03e97baadf680793c58a66ba7e77e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 8 Feb 2021 10:11:06 -0800 Subject: Use lts-17.2 resolver (with ghc 8.10.3). --- stack.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/stack.yaml b/stack.yaml index 304dc7b0b..79e1f2a4b 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,21 +5,12 @@ flags: packages: - '.' extra-deps: -- pandoc-types-1.22 -- rfc5051-0.2 -- haddock-library-1.9.0 -- skylighting-0.10.2 -- skylighting-core-0.10.2 - hslua-1.3.0 - hslua-module-path-0.1.0 -- hslua-module-text-0.3.0.1 - jira-wiki-markup-1.3.2 -- HsYAML-aeson-0.2.0.0 - commonmark-0.1.1.4 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 -- doctemplates-0.9 -- texmath-0.12.1 - tasty-bench-0.1 - citeproc-0.3.0.7 #- citeproc: @@ -27,6 +18,6 @@ extra-deps: # commit: feb3b7580c6738eec3b23921f7c1739cfba611aa ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules -resolver: lts-16.23 +resolver: lts-17.2 nix: packages: [zlib] -- cgit v1.2.3 From 8ca191604dcd13af27c11d2da225da646ebce6fc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 8 Feb 2021 23:35:19 -0800 Subject: Add new unexported module T.P.XMLParser. This exports functions that uses xml-conduit's parser to produce an xml-light Element or [Content]. This allows existing pandoc code to use a better parser without much modification. The new parser is used in all places where xml-light's parser was previously used. Benchmarks show a significant performance improvement in parsing XML-based formats (especially ODT and FB2). Note that the xml-light types use String, so the conversion from xml-conduit types involves a lot of extra allocation. It would be desirable to avoid that in the future by gradually switching to using xml-conduit directly. This can be done module by module. The new parser also reports errors, which we report when possible. A new constructor PandocXMLError has been added to PandocError in T.P.Error [API change]. Closes #7091, which was the main stimulus. These changes revealed the need for some changes in the tests. The docbook-reader.docbook test lacked definitions for the entities it used; these have been added. And the docx golden tests have been updated, because the new parser does not preserve the order of attributes. Add entity defs to docbook-reader.docbook. Update golden tests for docx. --- MANUAL.txt | 1 + pandoc.cabal | 2 + src/Text/Pandoc/Error.hs | 3 + src/Text/Pandoc/ImageSize.hs | 5 +- src/Text/Pandoc/Readers/DocBook.hs | 52 ++++++++++------ src/Text/Pandoc/Readers/Docx/Parse.hs | 21 ++++--- src/Text/Pandoc/Readers/Docx/Parse/Styles.hs | 28 +++++---- src/Text/Pandoc/Readers/EPUB.hs | 17 ++++-- src/Text/Pandoc/Readers/FB2.hs | 10 ++-- src/Text/Pandoc/Readers/JATS.hs | 9 ++- src/Text/Pandoc/Readers/OPML.hs | 10 +++- src/Text/Pandoc/Readers/Odt.hs | 24 ++++---- src/Text/Pandoc/Writers/EPUB.hs | 13 +++- src/Text/Pandoc/Writers/FB2.hs | 11 +++- src/Text/Pandoc/Writers/ODT.hs | 29 +++++---- src/Text/Pandoc/Writers/OOXML.hs | 9 +-- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 4 +- src/Text/Pandoc/XMLParser.hs | 66 +++++++++++++++++++++ test/Tests/Readers/JATS.hs | 1 + test/command/5321.md | 4 +- test/docbook-reader.docbook | 7 ++- test/docx/golden/block_quotes.docx | Bin 10092 -> 10098 bytes test/docx/golden/codeblock.docx | Bin 9944 -> 9950 bytes test/docx/golden/comments.docx | Bin 10279 -> 10285 bytes test/docx/golden/custom_style_no_reference.docx | Bin 10042 -> 10048 bytes test/docx/golden/custom_style_preserve.docx | Bin 10666 -> 10673 bytes test/docx/golden/custom_style_reference.docx | Bin 12434 -> 12434 bytes test/docx/golden/definition_list.docx | Bin 9941 -> 9947 bytes .../golden/document-properties-short-desc.docx | Bin 9947 -> 9953 bytes test/docx/golden/document-properties.docx | Bin 10423 -> 10429 bytes test/docx/golden/headers.docx | Bin 10080 -> 10086 bytes test/docx/golden/image.docx | Bin 26758 -> 26764 bytes test/docx/golden/inline_code.docx | Bin 9880 -> 9886 bytes test/docx/golden/inline_formatting.docx | Bin 10060 -> 10066 bytes test/docx/golden/inline_images.docx | Bin 26816 -> 26822 bytes test/docx/golden/link_in_notes.docx | Bin 10101 -> 10107 bytes test/docx/golden/links.docx | Bin 10276 -> 10282 bytes test/docx/golden/lists.docx | Bin 10352 -> 10358 bytes test/docx/golden/lists_continuing.docx | Bin 10143 -> 10149 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10232 -> 10238 bytes test/docx/golden/lists_restarting.docx | Bin 10144 -> 10150 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10239 -> 10245 bytes test/docx/golden/notes.docx | Bin 10046 -> 10052 bytes test/docx/golden/raw-blocks.docx | Bin 9980 -> 9986 bytes test/docx/golden/raw-bookmarks.docx | Bin 10115 -> 10121 bytes test/docx/golden/table_one_row.docx | Bin 9932 -> 9938 bytes test/docx/golden/table_with_list_cell.docx | Bin 10249 -> 10255 bytes test/docx/golden/tables.docx | Bin 10266 -> 10272 bytes test/docx/golden/track_changes_deletion.docx | Bin 9924 -> 9930 bytes test/docx/golden/track_changes_insertion.docx | Bin 9907 -> 9913 bytes test/docx/golden/track_changes_move.docx | Bin 9941 -> 9947 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 10053 -> 10059 bytes test/docx/golden/unicode.docx | Bin 9865 -> 9871 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9913 -> 9919 bytes test/jats-reader.native | 2 +- test/jats-reader.xml | 1 + test/pptx/code-custom.pptx | Bin 28230 -> 28221 bytes test/pptx/code-custom_templated.pptx | Bin 395524 -> 395516 bytes test/pptx/code.pptx | Bin 28229 -> 28220 bytes test/pptx/code_templated.pptx | Bin 395522 -> 395514 bytes test/pptx/document-properties-short-desc.pptx | Bin 27012 -> 27004 bytes .../document-properties-short-desc_templated.pptx | Bin 394298 -> 394288 bytes test/pptx/document-properties.pptx | Bin 27417 -> 27408 bytes test/pptx/document-properties_templated.pptx | Bin 394701 -> 394691 bytes test/pptx/endnotes.pptx | Bin 26969 -> 26962 bytes test/pptx/endnotes_templated.pptx | Bin 394262 -> 394253 bytes test/pptx/endnotes_toc.pptx | Bin 27892 -> 27789 bytes test/pptx/endnotes_toc_templated.pptx | Bin 395186 -> 395083 bytes test/pptx/images.pptx | Bin 44626 -> 44619 bytes test/pptx/images_templated.pptx | Bin 411916 -> 411909 bytes test/pptx/inline_formatting.pptx | Bin 26156 -> 26148 bytes test/pptx/inline_formatting_templated.pptx | Bin 393447 -> 393438 bytes test/pptx/lists.pptx | Bin 27056 -> 27049 bytes test/pptx/lists_templated.pptx | Bin 394349 -> 394340 bytes test/pptx/raw_ooxml.pptx | Bin 26948 -> 26940 bytes test/pptx/raw_ooxml_templated.pptx | Bin 394240 -> 394231 bytes test/pptx/remove_empty_slides.pptx | Bin 44073 -> 44065 bytes test/pptx/remove_empty_slides_templated.pptx | Bin 411359 -> 411352 bytes test/pptx/slide_breaks.pptx | Bin 28582 -> 28575 bytes test/pptx/slide_breaks_slide_level_1.pptx | Bin 27751 -> 27744 bytes .../pptx/slide_breaks_slide_level_1_templated.pptx | Bin 395045 -> 395038 bytes test/pptx/slide_breaks_templated.pptx | Bin 395875 -> 395868 bytes test/pptx/slide_breaks_toc.pptx | Bin 29539 -> 29532 bytes test/pptx/slide_breaks_toc_templated.pptx | Bin 396833 -> 396826 bytes test/pptx/speaker_notes.pptx | Bin 35444 -> 35436 bytes test/pptx/speaker_notes_after_metadata.pptx | Bin 31683 -> 31675 bytes .../speaker_notes_after_metadata_templated.pptx | Bin 398964 -> 398955 bytes test/pptx/speaker_notes_afterheader.pptx | Bin 30700 -> 30691 bytes test/pptx/speaker_notes_afterheader_templated.pptx | Bin 397988 -> 397979 bytes test/pptx/speaker_notes_afterseps.pptx | Bin 51612 -> 51604 bytes test/pptx/speaker_notes_afterseps_templated.pptx | Bin 418903 -> 418896 bytes test/pptx/speaker_notes_templated.pptx | Bin 402736 -> 402728 bytes test/pptx/start_numbering_at.pptx | Bin 27031 -> 27023 bytes test/pptx/start_numbering_at_templated.pptx | Bin 394323 -> 394314 bytes test/pptx/tables.pptx | Bin 27573 -> 27566 bytes test/pptx/tables_templated.pptx | Bin 394868 -> 394859 bytes test/pptx/two_column.pptx | Bin 26075 -> 26065 bytes test/pptx/two_column_templated.pptx | Bin 393366 -> 393355 bytes 98 files changed, 238 insertions(+), 91 deletions(-) create mode 100644 src/Text/Pandoc/XMLParser.hs diff --git a/MANUAL.txt b/MANUAL.txt index 5b90d039a..dc3b4ca77 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1464,6 +1464,7 @@ Nonzero exit codes have the following meanings: 24 PandocCiteprocError 31 PandocEpubSubdirectoryError 43 PandocPDFError + 44 PandocXMLError 47 PandocPDFProgramNotFoundError 61 PandocHttpError 62 PandocShouldNeverHappenError diff --git a/pandoc.cabal b/pandoc.cabal index 72e7c2da5..e56456c68 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -493,6 +493,7 @@ library unicode-transforms >= 0.3 && < 0.4, unordered-containers >= 0.2 && < 0.3, xml >= 1.3.12 && < 1.4, + xml-conduit >= 1.7 && < 1.10, zip-archive >= 0.2.3.4 && < 0.5, zlib >= 0.5 && < 0.7 if os(windows) && arch(i386) @@ -686,6 +687,7 @@ library Text.Pandoc.Lua.PandocLua, Text.Pandoc.Lua.Util, Text.Pandoc.Lua.Walk, + Text.Pandoc.XMLParser, Text.Pandoc.CSS, Text.Pandoc.CSV, Text.Pandoc.RoffChar, diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 204cf15ca..831405f42 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -48,6 +48,7 @@ data PandocError = PandocIOError Text IOError | PandocFailOnWarningError | PandocPDFProgramNotFoundError Text | PandocPDFError Text + | PandocXMLError Text Text | PandocFilterError Text Text | PandocLuaError Text | PandocCouldNotFindDataFileError Text @@ -103,6 +104,8 @@ handleError (Left e) = PandocPDFProgramNotFoundError pdfprog -> err 47 $ pdfprog <> " not found. Please select a different --pdf-engine or install " <> pdfprog PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" <> logmsg + PandocXMLError fp logmsg -> err 44 $ "Invalid XML" <> + (if T.null fp then "" else " in " <> fp) <> ":\n" <> logmsg PandocFilterError filtername msg -> err 83 $ "Error running filter " <> filtername <> ":\n" <> msg PandocLuaError msg -> err 84 $ "Error running Lua:\n" <> msg diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index e19958f6a..e0a1af8e8 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -45,7 +45,9 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.XML.Light as Xml +import Text.Pandoc.XMLParser (parseXMLElement) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE import Control.Applicative import qualified Data.Attoparsec.ByteString.Char8 as A @@ -327,7 +329,8 @@ getSize img = svgSize :: WriterOptions -> ByteString -> Maybe ImageSize svgSize opts img = do - doc <- Xml.parseXMLDoc $ UTF8.toString img + doc <- either (const mzero) return $ parseXMLElement + $ TL.fromStrict $ UTF8.toText img let viewboxSize = do vb <- Xml.findAttrBy (== Xml.QName "viewBox" Nothing Nothing) doc [_,_,w,h] <- mapM safeRead (T.words (T.pack vb)) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index ada3e98ec..ad0108843 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -12,7 +12,7 @@ Conversion of DocBook XML to 'Pandoc' document. -} module Text.Pandoc.Readers.DocBook ( readDocBook ) where import Control.Monad.State.Strict -import Data.Char (isSpace, toUpper) +import Data.Char (isSpace, toUpper, isLetter) import Data.Default import Data.Either (rights) import Data.Foldable (asum) @@ -21,7 +21,10 @@ import Data.List (intersperse,elemIndex) import Data.Maybe (fromMaybe,mapMaybe) import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import Control.Monad.Except (throwError) import Text.HTML.TagSoup.Entity (lookupEntity) +import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Options @@ -29,6 +32,7 @@ import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) import Text.TeXMath (readMathML, writeTeX) import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLContents) {- @@ -537,22 +541,25 @@ instance Default DBState where readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do - let tree = normalizeTree . parseXML . handleInstructions $ crFilter inp + tree <- either (throwError . PandocXMLError "") (return . normalizeTree) $ + parseXMLContents (TL.fromStrict . handleInstructions $ crFilter inp) (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree return $ Pandoc (dbMeta st') (toList . mconcat $ bs) --- We treat specially (issue #1236), converting it --- to
, since xml-light doesn't parse the instruction correctly. --- Other xml instructions are simply removed from the input stream. +-- We treat certain processing instructions by converting them to tags +-- beginning "pi-". handleInstructions :: Text -> Text -handleInstructions = T.pack . handleInstructions' . T.unpack - -handleInstructions' :: String -> String -handleInstructions' ('<':'?':'a':'s':'c':'i':'i':'d':'o':'c':'-':'b':'r':'?':'>':xs) = '<':'b':'r':'/':'>': handleInstructions' xs -handleInstructions' xs = case break (=='<') xs of - (ys, []) -> ys - ([], '<':zs) -> '<' : handleInstructions' zs - (ys, zs) -> ys ++ handleInstructions' zs +handleInstructions t = + let (x,y) = T.breakOn "" y + in (if T.takeWhile (\c -> isLetter c || c == '-') + (T.drop 2 w) `elem` ["asciidoc-br", "dbfo"] + then x <> " T.drop 2 w <> "/>" + else x <> w <> T.take 2 z) <> + handleInstructions (T.drop 2 z) getFigure :: PandocMonad m => Element -> DB m Blocks getFigure e = do @@ -892,7 +899,11 @@ parseBlock (Elem e) = "subtitle" -> return mempty -- handled in parent element _ -> skip >> getBlocks e where skip = do - lift $ report $ IgnoredElement $ T.pack $ qName (elName e) + let qn = T.pack $ qName $ elName e + let name = if "pi-" `T.isPrefixOf` qn + then " qn <> "?>" + else qn + lift $ report $ IgnoredElement name return mempty codeBlockWithLang = do @@ -964,7 +975,7 @@ parseBlock (Elem e) = cs -> map toAlignment cs let parseWidth s = safeRead (T.filter (\x -> (x >= '0' && x <= '9') || x == '.') s) - let textWidth = case filterChild (named "?dbfo") e of + let textWidth = case filterChild (named "pi-dbfo") e of Just d -> case attrValue "table-width" d of "" -> 1.0 w -> fromMaybe 100.0 (parseWidth w) / 100.0 @@ -1165,12 +1176,15 @@ parseInline (Elem e) = "title" -> return mempty "affiliation" -> skip -- Note: this isn't a real docbook tag; it's what we convert - -- to in handleInstructions, above. A kludge to - -- work around xml-light's inability to parse an instruction. - "br" -> return linebreak + -- to in handleInstructions, above. + "pi-asciidoc-br" -> return linebreak _ -> skip >> innerInlines id where skip = do - lift $ report $ IgnoredElement $ T.pack $ qName (elName e) + let qn = T.pack $ qName $ elName e + let name = if "pi-" `T.isPrefixOf` qn + then " qn <> "?>" + else qn + lift $ report $ IgnoredElement name return mempty innerInlines f = extractSpaces f . mconcat <$> diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index fdcffcc3f..056dab6c2 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -74,6 +74,7 @@ import Text.TeXMath.Readers.OMML (readOMML) import Text.TeXMath.Unicode.Fonts (Font (..), getUnicode, textToFont) import Text.XML.Light import qualified Text.XML.Light.Cursor as XMLC +import Text.Pandoc.XMLParser (parseXMLElement) data ReaderEnv = ReaderEnv { envNotes :: Notes , envComments :: Comments @@ -343,10 +344,16 @@ archiveToDocxWithWarnings archive = do Right doc -> Right (Docx doc, stateWarnings st) Left e -> Left e +parseXMLFromEntry :: Entry -> Maybe Element +parseXMLFromEntry entry = + case parseXMLElement (UTF8.toTextLazy (fromEntry entry)) of + Left _ -> Nothing + Right el -> Just el + getDocumentXmlPath :: Archive -> Maybe FilePath getDocumentXmlPath zf = do entry <- findEntryByPath "_rels/.rels" zf - relsElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry + relsElem <- parseXMLFromEntry entry let rels = filterChildrenName (\n -> qName n == "Relationship") relsElem rel <- find (\e -> findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") @@ -362,7 +369,7 @@ archiveToDocument :: Archive -> D Document archiveToDocument zf = do docPath <- asks envDocXmlPath entry <- maybeToD $ findEntryByPath docPath zf - docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry + docElem <- maybeToD $ parseXMLFromEntry entry let namespaces = elemToNameSpaces docElem bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem let bodyElem' = fromMaybe bodyElem (walkDocument namespaces bodyElem) @@ -401,9 +408,9 @@ constructBogusParStyleData stName = ParStyle archiveToNotes :: Archive -> Notes archiveToNotes zf = let fnElem = findEntryByPath "word/footnotes.xml" zf - >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry) + >>= parseXMLFromEntry enElem = findEntryByPath "word/endnotes.xml" zf - >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry) + >>= parseXMLFromEntry fn_namespaces = maybe [] elemToNameSpaces fnElem en_namespaces = maybe [] elemToNameSpaces enElem ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces @@ -415,7 +422,7 @@ archiveToNotes zf = archiveToComments :: Archive -> Comments archiveToComments zf = let cmtsElem = findEntryByPath "word/comments.xml" zf - >>= (parseXMLDoc . UTF8.toStringLazy . fromEntry) + >>= parseXMLFromEntry cmts_namespaces = maybe [] elemToNameSpaces cmtsElem cmts = elemToComments cmts_namespaces <$> (cmtsElem >>= walkDocument cmts_namespaces) in @@ -445,7 +452,7 @@ filePathToRelationships :: Archive -> FilePath -> FilePath -> [Relationship] filePathToRelationships ar docXmlPath fp | Just relType <- filePathToRelType fp docXmlPath , Just entry <- findEntryByPath fp ar - , Just relElems <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry = + , Just relElems <- parseXMLFromEntry entry = mapMaybe (relElemToRelationship relType) $ elChildren relElems filePathToRelationships _ _ _ = [] @@ -527,7 +534,7 @@ archiveToNumbering' zf = case findEntryByPath "word/numbering.xml" zf of Nothing -> Just $ Numbering [] [] [] Just entry -> do - numberingElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry + numberingElem <- parseXMLFromEntry entry let namespaces = elemToNameSpaces numberingElem numElems = findChildrenByName namespaces "w" "num" numberingElem absNumElems = findChildrenByName namespaces "w" "abstractNum" numberingElem diff --git a/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs b/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs index 236167187..edade8654 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs @@ -53,6 +53,7 @@ import Data.Coerce import Text.Pandoc.Readers.Docx.Util import qualified Text.Pandoc.UTF8 as UTF8 import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLElement) newtype CharStyleId = CharStyleId T.Text deriving (Show, Eq, Ord, IsString, FromStyleId) @@ -135,19 +136,22 @@ defaultRunStyle = RunStyle { isBold = Nothing , rParentStyle = Nothing } -archiveToStyles' :: (Ord k1, Ord k2, ElemToStyle a1, ElemToStyle a2) => - (a1 -> k1) -> (a2 -> k2) -> Archive -> (M.Map k1 a1, M.Map k2 a2) +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 - case stylesElem of - Nothing -> (M.empty, M.empty) - Just styElem -> - let namespaces = elemToNameSpaces styElem - in - ( M.fromList $ map (\r -> (conv1 r, r)) $ buildBasedOnList namespaces styElem Nothing, - M.fromList $ map (\p -> (conv2 p, p)) $ buildBasedOnList namespaces styElem Nothing) + case findEntryByPath "word/styles.xml" zf of + Nothing -> (M.empty, M.empty) + Just entry -> + case parseXMLElement . UTF8.toTextLazy . fromEntry $ entry of + Left _ -> (M.empty, M.empty) + Right styElem -> + let namespaces = elemToNameSpaces styElem + in + ( 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, FromStyleId (StyleId a)) => NameSpaces -> Element -> Maybe a -> Bool isBasedOnStyle ns element parentStyle diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index 5e3326e6d..369c4f0c9 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -17,7 +17,7 @@ module Text.Pandoc.Readers.EPUB (readEPUB) where -import Codec.Archive.Zip (Archive (..), Entry, findEntryByPath, fromEntry, +import Codec.Archive.Zip (Archive (..), Entry(..), findEntryByPath, fromEntry, toArchiveOrFail) import Control.DeepSeq (NFData, deepseq) import Control.Monad (guard, liftM, liftM2, mplus) @@ -41,9 +41,10 @@ import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Shared (addMetaField, collapseFilePath, escapeURI) -import qualified Text.Pandoc.UTF8 as UTF8 (toStringLazy) +import qualified Text.Pandoc.UTF8 as UTF8 (toTextLazy) import Text.Pandoc.Walk (query, walk) import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLElement) type Items = M.Map String (FilePath, MimeType) @@ -181,7 +182,7 @@ renameMeta s = T.pack s getManifest :: PandocMonad m => Archive -> m (String, Element) getManifest archive = do metaEntry <- findEntryByPathE ("META-INF" "container.xml") archive - docElem <- (parseXMLDocE . UTF8.toStringLazy . fromEntry) metaEntry + docElem <- parseXMLDocE metaEntry let namespaces = mapMaybe attrToNSPair (elAttribs docElem) ns <- mkE "xmlns not in namespaces" (lookup "xmlns" namespaces) as <- fmap (map attrToPair . elAttribs) @@ -190,7 +191,7 @@ getManifest archive = do let rootdir = dropFileName manifestFile --mime <- lookup "media-type" as manifest <- findEntryByPathE manifestFile archive - (rootdir,) <$> (parseXMLDocE . UTF8.toStringLazy . fromEntry $ manifest) + (rootdir,) <$> parseXMLDocE manifest -- Fixup @@ -284,8 +285,12 @@ findEntryByPathE :: PandocMonad m => FilePath -> Archive -> m Entry findEntryByPathE (normalise . unEscapeString -> path) a = mkE ("No entry on path: " ++ path) $ findEntryByPath path a -parseXMLDocE :: PandocMonad m => String -> m Element -parseXMLDocE doc = mkE "Unable to parse XML doc" $ parseXMLDoc doc +parseXMLDocE :: PandocMonad m => Entry -> m Element +parseXMLDocE entry = + either (throwError . PandocXMLError fp) return $ parseXMLElement doc + where + doc = UTF8.toTextLazy . fromEntry $ entry + fp = T.pack $ eRelativePath entry findElementE :: PandocMonad m => QName -> Element -> m Element findElementE e x = mkE ("Unable to find element: " ++ show e) $ findElement e x diff --git a/src/Text/Pandoc/Readers/FB2.hs b/src/Text/Pandoc/Readers/FB2.hs index b0d2f092b..b804eab4f 100644 --- a/src/Text/Pandoc/Readers/FB2.hs +++ b/src/Text/Pandoc/Readers/FB2.hs @@ -32,6 +32,7 @@ import Data.List (intersperse) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import Data.Default import Data.Maybe import Text.HTML.TagSoup.Entity (lookupEntity) @@ -42,6 +43,7 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared (crFilter) import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLElement) type FB2 m = StateT FB2State m @@ -64,10 +66,10 @@ instance HasMeta FB2State where readFB2 :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readFB2 _ inp = - case parseXMLDoc $ crFilter inp of - Nothing -> throwError $ PandocParseError "Not an XML document" - Just e -> do - (bs, st) <- runStateT (parseRootElement e) def + case parseXMLElement $ TL.fromStrict $ crFilter inp of + Left msg -> throwError $ PandocXMLError "" msg + Right el -> do + (bs, st) <- runStateT (parseRootElement el) def let authors = if null $ fb2Authors st then id else setMeta "author" (map text $ reverse $ fb2Authors st) diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index c638da519..dfd343b7a 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -14,6 +14,8 @@ Conversion of JATS XML to 'Pandoc' document. module Text.Pandoc.Readers.JATS ( readJATS ) where import Control.Monad.State.Strict +import Control.Monad.Except (throwError) +import Text.Pandoc.Error (PandocError(..)) import Data.Char (isDigit, isSpace, toUpper) import Data.Default import Data.Generics @@ -22,6 +24,7 @@ import qualified Data.Map as Map import Data.Maybe (maybeToList, fromMaybe) import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) @@ -29,6 +32,7 @@ import Text.Pandoc.Options import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) import Text.TeXMath (readMathML, writeTeX) import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLContents) import qualified Data.Set as S (fromList, member) import Data.Set ((\\)) @@ -51,8 +55,9 @@ instance Default JATSState where readJATS :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readJATS _ inp = do - let tree = normalizeTree . parseXML - $ T.unpack $ crFilter inp + tree <- either (throwError . PandocXMLError "") + (return . normalizeTree) $ + parseXMLContents (TL.fromStrict $ crFilter inp) (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree return $ Pandoc (jatsMeta st') (toList . mconcat $ bs) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 5b8996025..bdadc4dd9 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -19,14 +19,18 @@ import Data.Generics import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options +import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) import Text.Pandoc.Shared (crFilter, blocksToInlines') import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLContents) +import Control.Monad.Except (throwError) type OPML m = StateT OPMLState m @@ -49,8 +53,10 @@ instance Default OPMLState where readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readOPML opts inp = do (bs, st') <- runStateT - (mapM parseBlock $ normalizeTree $ - parseXML (T.unpack (crFilter inp))) def{ opmlOptions = opts } + (case parseXMLContents (TL.fromStrict (crFilter inp)) of + Left msg -> throwError $ PandocXMLError "" msg + Right ns -> mapM parseBlock $ normalizeTree ns) + def{ opmlOptions = opts } return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 9943d3147..85308deb1 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -15,6 +15,7 @@ module Text.Pandoc.Readers.Odt ( readOdt ) where import Codec.Archive.Zip import qualified Text.XML.Light as XML +import Text.Pandoc.XMLParser (parseXMLElement) import qualified Data.ByteString.Lazy as B @@ -66,18 +67,18 @@ bytesToOdt bytes = case toArchiveOrFail bytes of -- archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag) -archiveToOdt archive = either (Left. PandocParseError) Right $ do - let onFailure msg Nothing = Left msg +archiveToOdt archive = do + let onFailure msg Nothing = Left $ PandocParseError msg onFailure _ (Just x) = Right x contentEntry <- onFailure "Could not find content.xml" (findEntryByPath "content.xml" archive) stylesEntry <- onFailure "Could not find styles.xml" (findEntryByPath "styles.xml" archive) - contentElem <- onFailure "Could not find content element" - (entryToXmlElem contentEntry) - stylesElem <- onFailure "Could not find styles element" - (entryToXmlElem stylesEntry) - styles <- either (\_ -> Left "Could not read styles") Right + contentElem <- entryToXmlElem contentEntry + stylesElem <- entryToXmlElem stylesEntry + styles <- either + (\_ -> Left $ PandocParseError "Could not read styles") + Right (chooseMax (readStylesAt stylesElem ) (readStylesAt contentElem)) let filePathIsOdtMedia :: FilePath -> Bool filePathIsOdtMedia fp = @@ -85,10 +86,13 @@ archiveToOdt archive = either (Left. PandocParseError) Right $ do in (dir == "Pictures/") || (dir /= "./" && name == "content.xml") let media = filteredFilesFromArchive archive filePathIsOdtMedia let startState = readerState styles media - either (\_ -> Left "Could not convert opendocument") Right + either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right (runConverter' read_body startState contentElem) -- -entryToXmlElem :: Entry -> Maybe XML.Element -entryToXmlElem = XML.parseXMLDoc . UTF8.toStringLazy . fromEntry +entryToXmlElem :: Entry -> Either PandocError XML.Element +entryToXmlElem entry = + case parseXMLElement . UTF8.toTextLazy . fromEntry $ entry of + Right x -> Right x + Left msg -> Left $ PandocXMLError (T.pack $ eRelativePath entry) msg diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 1f16f6772..e99fa2567 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -55,8 +55,9 @@ import Text.Pandoc.Walk (query, walk, walkM) import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) import Text.XML.Light (Attr (..), Element (..), Node (..), QName (..), - add_attrs, lookupAttr, node, onlyElems, parseXML, + add_attrs, lookupAttr, node, onlyElems, ppElement, showElement, strContent, unode, unqual) +import Text.Pandoc.XMLParser (parseXMLContents) import Text.Pandoc.XML (escapeStringForXML) import Text.DocTemplates (FromContext(lookupContext), Context(..), ToContext(toVal), Val(..)) @@ -160,7 +161,12 @@ mkEntry path content = do getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta - let elts = maybe [] (onlyElems . parseXML) $ writerEpubMetadata opts + elts <- case writerEpubMetadata opts of + Nothing -> return [] + Just t -> case parseXMLContents (TL.fromStrict t) of + Left msg -> throwError $ + PandocXMLError "epub metadata" msg + Right ns -> return (onlyElems ns) let md' = foldr addMetadataFromXML md elts let addIdentifier m = if null (epubIdentifier m) @@ -836,7 +842,8 @@ pandocToEPUB version opts doc = do : case subs of [] -> [] (_:_) -> [unode "ol" ! [("class","toc")] $ subs] - where titElements = parseXML titRendered + where titElements = either (const []) id $ + parseXMLContents (TL.fromStrict titRendered) titRendered = case P.runPure (writeHtmlStringForEPUB version opts{ writerTemplate = Nothing diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 25b1f28d1..9334d6e9a 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -19,7 +19,7 @@ FictionBook is an XML-based e-book format. For more information see: module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad (zipWithM) -import Control.Monad.Except (catchError) +import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (StateT, evalStateT, get, gets, lift, liftM, modify) import Data.ByteString.Base64 (encode) import Data.Char (isAscii, isControl, isSpace) @@ -27,16 +27,18 @@ import Data.Either (lefts, rights) import Data.List (intercalate) import Data.Text (Text, pack) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE import Network.HTTP (urlEncode) import Text.XML.Light import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC -import qualified Text.XML.Light.Input as XI +import Text.Pandoc.XMLParser (parseXMLContents) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition +import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) import Text.Pandoc.Shared (capitalize, isURI, orderedListMarkers, @@ -307,7 +309,10 @@ blockToXml (CodeBlock _ s) = return . spaceBeforeAfter . map (el "p" . el "code" . T.unpack) . T.lines $ s blockToXml (RawBlock f str) = if f == Format "fb2" - then return $ XI.parseXML str + then + case parseXMLContents (TL.fromStrict str) of + Left msg -> throwError $ PandocXMLError "" msg + Right nds -> return nds else return [] blockToXml (Div _ bs) = cMapM blockToXml bs blockToXml (BlockQuote bs) = list . el "cite" <$> cMapM blockToXml bs diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 05dfad5eb..a32ff618c 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -13,7 +13,7 @@ Conversion of 'Pandoc' documents to ODT. -} module Text.Pandoc.Writers.ODT ( writeODT ) where import Codec.Archive.Zip -import Control.Monad.Except (catchError) +import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B import Data.Generics (everywhere', mkT) @@ -27,6 +27,7 @@ import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition +import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) @@ -35,10 +36,11 @@ import Text.DocLayout import Text.Pandoc.Shared (stringify, pandocVersion, tshow) import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks, fixDisplayMath) -import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) +import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.XML +import Text.Pandoc.XMLParser (parseXMLElement) import Text.TeXMath import Text.XML.Light @@ -172,17 +174,18 @@ updateStyleWithLang :: PandocMonad m => Maybe Lang -> Archive -> O m Archive updateStyleWithLang Nothing arch = return arch updateStyleWithLang (Just lang) arch = do epochtime <- floor `fmap` lift P.getPOSIXTime - return arch{ zEntries = [if eRelativePath e == "styles.xml" - then case parseXMLDoc - (toStringLazy (fromEntry e)) of - Nothing -> e - Just d -> - toEntry "styles.xml" epochtime - ( fromStringLazy - . ppTopElement - . addLang lang $ d ) - else e - | e <- zEntries arch] } + entries <- mapM (\e -> if eRelativePath e == "styles.xml" + then case parseXMLElement + (toTextLazy (fromEntry e)) of + Left msg -> throwError $ + PandocXMLError "styles.xml" msg + Right d -> return $ + toEntry "styles.xml" epochtime + ( fromStringLazy + . ppTopElement + . addLang lang $ d ) + else return e) (zEntries arch) + return arch{ zEntries = entries } addLang :: Lang -> Element -> Element addLang lang = everywhere' (mkT updateLangAttr) diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs index 3ac007f4e..8f60e70d5 100644 --- a/src/Text/Pandoc/Writers/OOXML.hs +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -35,6 +35,7 @@ import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.UTF8 as UTF8 import Text.XML.Light as XML +import Text.Pandoc.XMLParser (parseXMLElement) mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = @@ -62,10 +63,10 @@ parseXml refArchive distArchive relpath = findEntryByPath relpath distArchive of Nothing -> throwError $ PandocSomeError $ T.pack relpath <> " missing in reference file" - Just e -> case parseXMLDoc . UTF8.toStringLazy . fromEntry $ e of - Nothing -> throwError $ PandocSomeError $ - T.pack relpath <> " corrupt in reference file" - Just d -> return d + Just e -> case parseXMLElement . UTF8.toTextLazy . fromEntry $ e of + Left msg -> + throwError $ PandocXMLError (T.pack relpath) msg + Right d -> return d -- Copied from Util diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 8554db622..cd092969b 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -29,6 +29,7 @@ import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) import Text.XML.Light +import Text.Pandoc.XMLParser (parseXMLElement) import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class.PandocMonad (PandocMonad) @@ -77,7 +78,8 @@ getPresentationSize :: Archive -> Archive -> Maybe (Integer, Integer) getPresentationSize refArchive distArchive = do entry <- findEntryByPath "ppt/presentation.xml" refArchive `mplus` findEntryByPath "ppt/presentation.xml" distArchive - presElement <- parseXMLDoc $ UTF8.toStringLazy $ fromEntry entry + presElement <- either (const Nothing) return $ + parseXMLElement $ UTF8.toTextLazy $ fromEntry entry let ns = elemToNameSpaces presElement sldSize <- findChild (elemName ns "p" "sldSz") presElement cxS <- findAttr (QName "cx" Nothing Nothing) sldSize diff --git a/src/Text/Pandoc/XMLParser.hs b/src/Text/Pandoc/XMLParser.hs new file mode 100644 index 000000000..8ad22a66a --- /dev/null +++ b/src/Text/Pandoc/XMLParser.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.XMLParser + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Bridge to allow using xml-conduit's parser with xml-light's types. +-} +module Text.Pandoc.XMLParser + ( parseXMLElement + , parseXMLContents + , module Text.XML.Light.Types + ) where + +import qualified Control.Exception as E +import qualified Text.XML as Conduit +import Text.XML.Unresolved (InvalidEventStream(..)) +import qualified Text.XML.Light as Light +import Text.XML.Light.Types +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import qualified Data.Map as M +import Data.Maybe (mapMaybe) + +-- Drop in replacement for parseXMLDoc in xml-light. +parseXMLElement :: TL.Text -> Either T.Text Light.Element +parseXMLElement t = + elementToElement . Conduit.documentRoot <$> + either (Left . T.pack . E.displayException) Right + (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t) + +parseXMLContents :: TL.Text -> Either T.Text [Light.Content] +parseXMLContents t = + case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t of + Left e -> + case E.fromException e of + Just (ContentAfterRoot _) -> + elContent <$> parseXMLElement ("" <> t <> "") + _ -> Left . T.pack . E.displayException $ e + Right x -> Right [Light.Elem . elementToElement . Conduit.documentRoot $ x] + +elementToElement :: Conduit.Element -> Light.Element +elementToElement (Conduit.Element name attribMap nodes) = + Light.Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing + where + attrs = map (\(n,v) -> Light.Attr (nameToQname n) (T.unpack v)) $ + M.toList attribMap + nameToQname (Conduit.Name localName mbns mbpref) = + case mbpref of + Nothing | "xmlns:" `T.isPrefixOf` localName -> + Light.QName (T.unpack $ T.drop 6 localName) (T.unpack <$> mbns) + (Just "xmlns") + _ -> Light.QName (T.unpack localName) (T.unpack <$> mbns) + (T.unpack <$> mbpref) + +nodeToContent :: Conduit.Node -> Maybe Light.Content +nodeToContent (Conduit.NodeElement el) = + Just (Light.Elem (elementToElement el)) +nodeToContent (Conduit.NodeContent t) = + Just (Light.Text (Light.CData Light.CDataText (T.unpack t) Nothing)) +nodeToContent _ = Nothing + diff --git a/test/Tests/Readers/JATS.hs b/test/Tests/Readers/JATS.hs index 525499c86..a9c9a0586 100644 --- a/test/Tests/Readers/JATS.hs +++ b/test/Tests/Readers/JATS.hs @@ -88,6 +88,7 @@ tests = [ testGroup "inline code" "

\n\ \ \n\ \ \n\ + \ \n\ \

" =?> para (math "\\sigma|_{\\{x\\}}") , test jats "math ml only" $ diff --git a/test/command/5321.md b/test/command/5321.md index 081abe2a0..83404632a 100644 --- a/test/command/5321.md +++ b/test/command/5321.md @@ -4,7 +4,7 @@

bar

- + ^D [Para [Image ("fig-1",[],[]) [Str "bar"] ("foo.png","fig:")]] @@ -17,7 +17,7 @@ foo

bar

- + ^D [Para [Image ("fig-1",[],[]) [Str "foo",LineBreak,Str "bar"] ("foo.png","fig:")]] diff --git a/test/docbook-reader.docbook b/test/docbook-reader.docbook index 02568d8de..5717d78d0 100644 --- a/test/docbook-reader.docbook +++ b/test/docbook-reader.docbook @@ -1,6 +1,11 @@ +"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" +[ + + + +]>
Pandoc Test Suite diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index 3e1bf16e7..d3b16d0f2 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index 66f055063..6293ef493 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index fb3a02a0a..4205a1516 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index bc6c2702a..adb3f23db 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 8c555a5bd..92c8137fe 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index 5f96cc911..f53470617 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index c21b3a5b3..d6af90a72 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index 92ce144e9..e18dbe853 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index d21b67309..820299043 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index 3558a47bf..ae0f41d12 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 606df92a3..94cd35dfa 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 759269cac..879f2a25b 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index c37777080..93f86478f 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 9450b1a73..967d297f2 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index 6f0b830e6..c5614e2fa 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index e53889cfb..0f39a831f 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 5dbe298b7..07046f223 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index 194181288..3656618e6 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 6e0b634f7..8798253d5 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index 477178e77..0a24d1840 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index 51110356e..52bb7a217 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index b6206cdf5..182c06c64 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index 07b576080..7b69a56a3 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index d46095eb7..3d3a35701 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index 7caba4e93..5ae37b406 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 6aaa6da61..c29aa6716 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 5746c5ad0..664493246 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index 5f22dccc6..b6d15340e 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index ab5c4f56d..f8e1092d2 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index 085f33162..b4cda82f2 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index 1ac86d5c8..ee222efa0 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index c2c443b19..c6f8d9c96 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index 5ea18d32e..ea8146690 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ diff --git a/test/jats-reader.native b/test/jats-reader.native index ab77dd1a0..0715ea8cc 100644 --- a/test/jats-reader.native +++ b/test/jats-reader.native @@ -1,4 +1,4 @@ -Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Space,Str "MacFarlane"]]),("title",MetaInlines [Str "Pandoc",Space,Str "Test",Space,Str "Suite"])]}) +Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Space,Str "MacFarlane"],MetaInlines [Str "Anonymous"]]),("title",MetaInlines [Str "Pandoc",Space,Str "Test",Space,Str "Suite"])]}) [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc.",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber's",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,Header 1 ("headers",[],[]) [Str "Headers"] ,Header 2 ("level-2-with-an-embedded-link",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",SoftBreak,Link ("",[],[]) [Str "embedded",SoftBreak,Str "link"] ("/url","")] diff --git a/test/jats-reader.xml b/test/jats-reader.xml index f75b3e95a..f33cb9ab3 100644 --- a/test/jats-reader.xml +++ b/test/jats-reader.xml @@ -20,6 +20,7 @@ MacFarlane John + Anonymous diff --git a/test/pptx/code-custom.pptx b/test/pptx/code-custom.pptx index aa9b7692a..58070eb3f 100644 Binary files a/test/pptx/code-custom.pptx and b/test/pptx/code-custom.pptx differ diff --git a/test/pptx/code-custom_templated.pptx b/test/pptx/code-custom_templated.pptx index 9aaef4cb5..db9b7e371 100644 Binary files a/test/pptx/code-custom_templated.pptx and b/test/pptx/code-custom_templated.pptx differ diff --git a/test/pptx/code.pptx b/test/pptx/code.pptx index 1737ec757..c7b1ed7d5 100644 Binary files a/test/pptx/code.pptx and b/test/pptx/code.pptx differ diff --git a/test/pptx/code_templated.pptx b/test/pptx/code_templated.pptx index 87fb560ef..6944d92bf 100644 Binary files a/test/pptx/code_templated.pptx and b/test/pptx/code_templated.pptx differ diff --git a/test/pptx/document-properties-short-desc.pptx b/test/pptx/document-properties-short-desc.pptx index 961c31020..ae0d28429 100644 Binary files a/test/pptx/document-properties-short-desc.pptx and b/test/pptx/document-properties-short-desc.pptx differ diff --git a/test/pptx/document-properties-short-desc_templated.pptx b/test/pptx/document-properties-short-desc_templated.pptx index 894738ef7..37c74c69a 100644 Binary files a/test/pptx/document-properties-short-desc_templated.pptx and b/test/pptx/document-properties-short-desc_templated.pptx differ diff --git a/test/pptx/document-properties.pptx b/test/pptx/document-properties.pptx index 188e8d826..324e443a1 100644 Binary files a/test/pptx/document-properties.pptx and b/test/pptx/document-properties.pptx differ diff --git a/test/pptx/document-properties_templated.pptx b/test/pptx/document-properties_templated.pptx index 253e8c0a7..c81b983e3 100644 Binary files a/test/pptx/document-properties_templated.pptx and b/test/pptx/document-properties_templated.pptx differ diff --git a/test/pptx/endnotes.pptx b/test/pptx/endnotes.pptx index e230420d2..30ce33db6 100644 Binary files a/test/pptx/endnotes.pptx and b/test/pptx/endnotes.pptx differ diff --git a/test/pptx/endnotes_templated.pptx b/test/pptx/endnotes_templated.pptx index 49384fd65..d6c604968 100644 Binary files a/test/pptx/endnotes_templated.pptx and b/test/pptx/endnotes_templated.pptx differ diff --git a/test/pptx/endnotes_toc.pptx b/test/pptx/endnotes_toc.pptx index cdf1be4ad..000e17ecd 100644 Binary files a/test/pptx/endnotes_toc.pptx and b/test/pptx/endnotes_toc.pptx differ diff --git a/test/pptx/endnotes_toc_templated.pptx b/test/pptx/endnotes_toc_templated.pptx index c4fcbad45..fdcd2e29b 100644 Binary files a/test/pptx/endnotes_toc_templated.pptx and b/test/pptx/endnotes_toc_templated.pptx differ diff --git a/test/pptx/images.pptx b/test/pptx/images.pptx index 4a13b5b7f..e73126376 100644 Binary files a/test/pptx/images.pptx and b/test/pptx/images.pptx differ diff --git a/test/pptx/images_templated.pptx b/test/pptx/images_templated.pptx index 7a6e9700e..e3f968e9e 100644 Binary files a/test/pptx/images_templated.pptx and b/test/pptx/images_templated.pptx differ diff --git a/test/pptx/inline_formatting.pptx b/test/pptx/inline_formatting.pptx index 926c8ff3f..eadb9372e 100644 Binary files a/test/pptx/inline_formatting.pptx and b/test/pptx/inline_formatting.pptx differ diff --git a/test/pptx/inline_formatting_templated.pptx b/test/pptx/inline_formatting_templated.pptx index 16f48e182..8ca6bab2b 100644 Binary files a/test/pptx/inline_formatting_templated.pptx and b/test/pptx/inline_formatting_templated.pptx differ diff --git a/test/pptx/lists.pptx b/test/pptx/lists.pptx index f47b17a74..ae188ee68 100644 Binary files a/test/pptx/lists.pptx and b/test/pptx/lists.pptx differ diff --git a/test/pptx/lists_templated.pptx b/test/pptx/lists_templated.pptx index 88109a95e..60301fa50 100644 Binary files a/test/pptx/lists_templated.pptx and b/test/pptx/lists_templated.pptx differ diff --git a/test/pptx/raw_ooxml.pptx b/test/pptx/raw_ooxml.pptx index 84020708f..17124a50d 100644 Binary files a/test/pptx/raw_ooxml.pptx and b/test/pptx/raw_ooxml.pptx differ diff --git a/test/pptx/raw_ooxml_templated.pptx b/test/pptx/raw_ooxml_templated.pptx index a2f77e945..19ae7dd4e 100644 Binary files a/test/pptx/raw_ooxml_templated.pptx and b/test/pptx/raw_ooxml_templated.pptx differ diff --git a/test/pptx/remove_empty_slides.pptx b/test/pptx/remove_empty_slides.pptx index 48bf7bc8a..b650b7585 100644 Binary files a/test/pptx/remove_empty_slides.pptx and b/test/pptx/remove_empty_slides.pptx differ diff --git a/test/pptx/remove_empty_slides_templated.pptx b/test/pptx/remove_empty_slides_templated.pptx index 23b134a5f..0ab029614 100644 Binary files a/test/pptx/remove_empty_slides_templated.pptx and b/test/pptx/remove_empty_slides_templated.pptx differ diff --git a/test/pptx/slide_breaks.pptx b/test/pptx/slide_breaks.pptx index d6eebeffb..2a6e35080 100644 Binary files a/test/pptx/slide_breaks.pptx and b/test/pptx/slide_breaks.pptx differ diff --git a/test/pptx/slide_breaks_slide_level_1.pptx b/test/pptx/slide_breaks_slide_level_1.pptx index a6c76a187..a7bcf6a4b 100644 Binary files a/test/pptx/slide_breaks_slide_level_1.pptx and b/test/pptx/slide_breaks_slide_level_1.pptx differ diff --git a/test/pptx/slide_breaks_slide_level_1_templated.pptx b/test/pptx/slide_breaks_slide_level_1_templated.pptx index 1fbde815b..21b018c25 100644 Binary files a/test/pptx/slide_breaks_slide_level_1_templated.pptx and b/test/pptx/slide_breaks_slide_level_1_templated.pptx differ diff --git a/test/pptx/slide_breaks_templated.pptx b/test/pptx/slide_breaks_templated.pptx index cb3af4aa1..4ec4772a4 100644 Binary files a/test/pptx/slide_breaks_templated.pptx and b/test/pptx/slide_breaks_templated.pptx differ diff --git a/test/pptx/slide_breaks_toc.pptx b/test/pptx/slide_breaks_toc.pptx index dff386885..5983657b6 100644 Binary files a/test/pptx/slide_breaks_toc.pptx and b/test/pptx/slide_breaks_toc.pptx differ diff --git a/test/pptx/slide_breaks_toc_templated.pptx b/test/pptx/slide_breaks_toc_templated.pptx index 43b125f5e..dd54c7082 100644 Binary files a/test/pptx/slide_breaks_toc_templated.pptx and b/test/pptx/slide_breaks_toc_templated.pptx differ diff --git a/test/pptx/speaker_notes.pptx b/test/pptx/speaker_notes.pptx index 3314a1c65..b3e5ed5b9 100644 Binary files a/test/pptx/speaker_notes.pptx and b/test/pptx/speaker_notes.pptx differ diff --git a/test/pptx/speaker_notes_after_metadata.pptx b/test/pptx/speaker_notes_after_metadata.pptx index 27a136838..1078854bb 100644 Binary files a/test/pptx/speaker_notes_after_metadata.pptx and b/test/pptx/speaker_notes_after_metadata.pptx differ diff --git a/test/pptx/speaker_notes_after_metadata_templated.pptx b/test/pptx/speaker_notes_after_metadata_templated.pptx index 7aa3b6a87..5116c6c4e 100644 Binary files a/test/pptx/speaker_notes_after_metadata_templated.pptx and b/test/pptx/speaker_notes_after_metadata_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterheader.pptx b/test/pptx/speaker_notes_afterheader.pptx index d43709ca7..0c8e49bd9 100644 Binary files a/test/pptx/speaker_notes_afterheader.pptx and b/test/pptx/speaker_notes_afterheader.pptx differ diff --git a/test/pptx/speaker_notes_afterheader_templated.pptx b/test/pptx/speaker_notes_afterheader_templated.pptx index 793ea10f6..68695939d 100644 Binary files a/test/pptx/speaker_notes_afterheader_templated.pptx and b/test/pptx/speaker_notes_afterheader_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterseps.pptx b/test/pptx/speaker_notes_afterseps.pptx index 2f4d3b820..7ed9b946d 100644 Binary files a/test/pptx/speaker_notes_afterseps.pptx and b/test/pptx/speaker_notes_afterseps.pptx differ diff --git a/test/pptx/speaker_notes_afterseps_templated.pptx b/test/pptx/speaker_notes_afterseps_templated.pptx index 94a221398..79fc82345 100644 Binary files a/test/pptx/speaker_notes_afterseps_templated.pptx and b/test/pptx/speaker_notes_afterseps_templated.pptx differ diff --git a/test/pptx/speaker_notes_templated.pptx b/test/pptx/speaker_notes_templated.pptx index 22040c88c..9f943c279 100644 Binary files a/test/pptx/speaker_notes_templated.pptx and b/test/pptx/speaker_notes_templated.pptx differ diff --git a/test/pptx/start_numbering_at.pptx b/test/pptx/start_numbering_at.pptx index 18477380b..ac72d8ced 100644 Binary files a/test/pptx/start_numbering_at.pptx and b/test/pptx/start_numbering_at.pptx differ diff --git a/test/pptx/start_numbering_at_templated.pptx b/test/pptx/start_numbering_at_templated.pptx index 4b9d0ba4d..15c7b5469 100644 Binary files a/test/pptx/start_numbering_at_templated.pptx and b/test/pptx/start_numbering_at_templated.pptx differ diff --git a/test/pptx/tables.pptx b/test/pptx/tables.pptx index 1c5b54185..926c5e699 100644 Binary files a/test/pptx/tables.pptx and b/test/pptx/tables.pptx differ diff --git a/test/pptx/tables_templated.pptx b/test/pptx/tables_templated.pptx index 1314f4de4..a37e72d2c 100644 Binary files a/test/pptx/tables_templated.pptx and b/test/pptx/tables_templated.pptx differ diff --git a/test/pptx/two_column.pptx b/test/pptx/two_column.pptx index 9018be36e..7f86533fe 100644 Binary files a/test/pptx/two_column.pptx and b/test/pptx/two_column.pptx differ diff --git a/test/pptx/two_column_templated.pptx b/test/pptx/two_column_templated.pptx index 35e93af67..89e3db0ab 100644 Binary files a/test/pptx/two_column_templated.pptx and b/test/pptx/two_column_templated.pptx differ -- cgit v1.2.3 From acc9afaf6f6afa14cbe7cd06798275728e78a529 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 11 Feb 2021 09:16:25 -0800 Subject: Correctly parse "raw" date value in markdown references metadata. See jgm/citeproc#53. --- src/Text/Pandoc/Citeproc/MetaValue.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Citeproc/MetaValue.hs b/src/Text/Pandoc/Citeproc/MetaValue.hs index f5a49f49e..b43ca7314 100644 --- a/src/Text/Pandoc/Citeproc/MetaValue.hs +++ b/src/Text/Pandoc/Citeproc/MetaValue.hs @@ -135,12 +135,13 @@ metaValueToVal k v MetaMap _ -> TextVal mempty metaValueToDate :: MetaValue -> Date -metaValueToDate (MetaMap m) = - Date +metaValueToDate (MetaMap m) = fromMaybe + (Date { dateParts = dateparts , dateCirca = circa , dateSeason = season - , dateLiteral = literal } + , dateLiteral = literal }) + rawdate where dateparts = case M.lookup "date-parts" m of Just (MetaList xs) -> @@ -152,6 +153,7 @@ metaValueToDate (MetaMap m) = M.lookup "circa" m >>= metaValueToBool season = M.lookup "season" m >>= metaValueToInt literal = M.lookup "literal" m >>= metaValueToText + rawdate = M.lookup "raw" m >>= metaValueToText >>= rawDateEDTF metaValueToDate (MetaList xs) = Date{ dateParts = mapMaybe metaValueToDateParts xs , dateCirca = False -- cgit v1.2.3 From 3c4a58bad03ef56ae9c82b7e7a6ae027514e2bd6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 10 Feb 2021 23:01:08 -0800 Subject: T.P.Class: Add getTimestamp [API change]. This attempts to read the SOURCE_DATE_EPOCH environment variable and parse a UTC time from it (treating it as a unix date stamp, see https://reproducible-builds.org/specs/source-date-epoch/). If the variable is not set or can't be parsed as a unix date stamp, then the function returns the current date. --- src/Text/Pandoc/Class/PandocMonad.hs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 374da161b..86c8de79e 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -51,6 +51,7 @@ module Text.Pandoc.Class.PandocMonad , setTranslations , translateTerm , makeCanonical + , getTimestamp ) where import Codec.Archive.Zip @@ -59,7 +60,8 @@ import Control.Monad.Except (MonadError (catchError, throwError), import Data.Digest.Pure.SHA (sha1, showDigest) import Data.Maybe (fromMaybe) import Data.Time (UTCTime) -import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds) +import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds, + posixSecondsToUTCTime) import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime) import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, @@ -74,7 +76,7 @@ import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Pandoc.MediaBag (MediaBag, lookupMedia) -import Text.Pandoc.Shared (uriPathToPath) +import Text.Pandoc.Shared (uriPathToPath, safeRead) import Text.Pandoc.Translations (Term(..), Translations, lookupTerm, readTranslations) import Text.Pandoc.Walk (walkM) @@ -175,6 +177,21 @@ report msg = do when (level <= verbosity) $ logOutput msg modifyCommonState $ \st -> st{ stLog = msg : stLog st } +-- | Get the time from the @SOURCE_DATE_EPOCH@ +-- environment variable. The variable should contain a +-- unix time stamp, the number of seconds since midnight Jan 01 +-- 1970 UTC. If the variable is not set or cannot be +-- parsed as a unix time stamp, the current time is returned. +-- This function is designed to make possible reproducible +-- builds in formats that include a creation timestamp. +getTimestamp :: PandocMonad m => m UTCTime +getTimestamp = do + mbSourceDateEpoch <- lookupEnv "SOURCE_DATE_EPOCH" + case mbSourceDateEpoch >>= safeRead of + Just (epoch :: Integer) -> + return $ posixSecondsToUTCTime $ fromIntegral epoch + Nothing -> getCurrentTime + -- | Determine whether tracing is enabled. This affects -- the behavior of 'trace'. If tracing is not enabled, -- 'trace' does nothing. -- cgit v1.2.3 From 390d5e65b2d66078b2f9b9db142dbe2167ea29a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 10 Feb 2021 23:13:33 -0800 Subject: Use getTimestamp instead of getCurrentTime in writers. Setting SOURCE_DATE_EPOCH will allow reproducible builds. Partially addresses #7093. This does not suffice to fully enable reproducible in EPUB, since a unique id is being generated for each build. --- src/Text/Pandoc/Readers/RST.hs | 4 ++-- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 0f32d993c..29f81b046 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -28,7 +28,7 @@ import qualified Data.Text as T import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, - readFileFromDirs, getCurrentTime) + readFileFromDirs, getTimestamp) import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error @@ -685,7 +685,7 @@ directive' = do "replace" -> B.para <$> -- consumed by substKey parseInlineFromText (trim top) "date" -> B.para <$> do -- consumed by substKey - t <- getCurrentTime + t <- getTimestamp let format = case T.unpack (T.strip top) of [] -> "%Y-%m-%d" x -> x diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 8f498775d..da990e4d3 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -217,7 +217,7 @@ writeDocx opts doc = do let doc' = Pandoc meta blocks' username <- P.lookupEnv "USERNAME" - utctime <- P.getCurrentTime + utctime <- P.getTimestamp oldUserDataDir <- P.getUserDataDir P.setUserDataDir Nothing res <- P.readDefaultDataFile "reference.docx" diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index e99fa2567..171ffe582 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -191,7 +191,7 @@ getEPUBMetadata opts meta = do let fixDate m = if null (epubDate m) then do - currentTime <- lift P.getCurrentTime + currentTime <- lift P.getTimestamp return $ m{ epubDate = [ Date{ dateText = showDateTimeISO8601 currentTime , dateEvent = Nothing } ] } @@ -709,7 +709,7 @@ pandocToEPUB version opts doc = do uuid <- case epubIdentifier metadata of (x:_) -> return $ identifierText x -- use first identifier as UUID [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen - currentTime <- lift P.getCurrentTime + currentTime <- lift P.getTimestamp let contentsData = UTF8.fromStringLazy $ ppTopElement $ unode "package" ! ([("version", case version of diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index a32ff618c..06369b4db 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -68,7 +68,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let title = docTitle meta let authors = docAuthors meta - utctime <- P.getCurrentTime + utctime <- P.getTimestamp lang <- toLang (getLang opts meta) refArchive <- case writerReferenceDoc opts of diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index cd092969b..0a7060895 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -312,7 +312,7 @@ presentationToArchive opts pres = do Nothing -> toArchive . BL.fromStrict <$> P.readDataFile "reference.pptx" - utctime <- P.getCurrentTime + utctime <- P.getTimestamp presSize <- case getPresentationSize refArchive distArchive of Just sz -> return sz -- cgit v1.2.3 From 8c2618ed8150d2b738b3baa4ba23c2d957450105 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 11 Feb 2021 15:17:56 -0800 Subject: Add MANUAL section on reproducible builds. --- MANUAL.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/MANUAL.txt b/MANUAL.txt index dc3b4ca77..486cee0b1 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -6309,6 +6309,21 @@ subdirectory of your user data directory (see [Templates]). [Lua]: https://www.lua.org +# Reproducible builds + +Some of the document formats pandoc targets (such as EPUB, +docx, and ODT) include build timestamps in the generated document. +That means that the files generated on successive builds will +differ, even if the source does not. To avoid this, set the +`SOURCE_DATE_EPOCH` environment variable, and the timestamp will +be taken from it instead of the current time. +`SOURCE_DATE_EPOCH` should contain an integer unix timestamp +(specifying the number of second since midnight UTC January 1, 1970). + +Some document formats also include a unique identifier. For +EPUB, this can be set explicitly by setting the `identifier` +metadata field (see [EPUB Metadata], above). + # A note on security If you use pandoc to convert user-contributed content in a web -- cgit v1.2.3 From 8ffd4159d6f0ca4f13a84b27e3f8654f01c63d59 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 12 Feb 2021 16:38:00 +0100 Subject: Jira: require jira-wiki-markup 1.3.3 * Modified the Doc parser to skip leading blank lines. This fixes parsing of documents which start with multiple blank lines. (#7095) * Prevent URLs within link aliases to be treated as autolinks. (#6944) Fixes: #7095 Fixes: #6944 --- pandoc.cabal | 2 +- stack.yaml | 2 +- test/Tests/Readers/Jira.hs | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index e56456c68..63b1e6cd9 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -470,7 +470,7 @@ library http-client-tls >= 0.2.4 && < 0.4, http-types >= 0.8 && < 0.13, ipynb >= 0.1 && < 0.2, - jira-wiki-markup >= 1.3.2 && < 1.4, + jira-wiki-markup >= 1.3.3 && < 1.4, mtl >= 2.2 && < 2.3, network >= 2.6, network-uri >= 2.6 && < 2.7, diff --git a/stack.yaml b/stack.yaml index 79e1f2a4b..dcb1c9742 100644 --- a/stack.yaml +++ b/stack.yaml @@ -7,7 +7,7 @@ packages: extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 -- jira-wiki-markup-1.3.2 +- jira-wiki-markup-1.3.3 - commonmark-0.1.1.4 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 diff --git a/test/Tests/Readers/Jira.hs b/test/Tests/Readers/Jira.hs index e170d2aaa..189b504f7 100644 --- a/test/Tests/Readers/Jira.hs +++ b/test/Tests/Readers/Jira.hs @@ -34,6 +34,9 @@ tests = [ testGroup "para" [ "Simple sentence" =: "Hello, World!" =?> para "Hello, World!" + + , "leading blank lines" =: + "\n\ntext" =?> para "text" ] , testGroup "header" @@ -131,6 +134,10 @@ tests = "[Example|https://example.org]" =?> para (link "https://example.org" "" "Example") + , "URL in alias" =: + "[See https://example.com|https://example.com]" =?> + para (link "https://example.com" "" "See https://example.com") + , "email" =: "[mailto:me@example.org]" =?> para (link "mailto:me@example.org" "" "me@example.org") -- cgit v1.2.3 From 59875185b3da8f57174f1b3da42c750f4c3ee910 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 12 Feb 2021 17:43:00 -0800 Subject: Add command test for #7092 --- test/command/7092.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/command/7092.md diff --git a/test/command/7092.md b/test/command/7092.md new file mode 100644 index 000000000..e7f127244 --- /dev/null +++ b/test/command/7092.md @@ -0,0 +1,8 @@ +``` +% pandoc -flatex+raw_tex -t native +\newcommand{\em}[1]{\emph{#1}} + +\em{\parseMe{foo}} +^D +[Para [Emph [RawInline (Format "latex") "\\parseMe{foo}"]]] +``` -- cgit v1.2.3 From 3be066b7d308ca7f103c7fabd80238b350bf4f94 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 12 Feb 2021 17:43:34 -0800 Subject: Fix command test 5686 --- test/command/5686.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/command/5686.md b/test/command/5686.md index 67b4579ef..1c18109c3 100644 --- a/test/command/5686.md +++ b/test/command/5686.md @@ -4,6 +4,6 @@ FOO\t0BAR This part does not make it to the html output. ^D -[Para [Str "FOO",RawInline (Format "tex") "\\t0BAR"] +[Para [Str "FOO",RawInline (Format "tex") "\\t0",Str "BAR"] ,Para [Str "This",Space,Str "part",Space,Str "does",Space,Str "not",Space,Str "make",Space,Str "it",Space,Str "to",Space,Str "the",Space,Str "html",Space,Str "output."]] ``` -- cgit v1.2.3 From d9322629a36ad50035912ee56df876c345039225 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 12 Feb 2021 17:45:05 -0800 Subject: LaTeX reader improvements. * Rewrote `withRaw` so it doesn't rely on fragile assumptions about token positions (which break when macros are expanded). This requires the addition of `sEnableWithRaw` and `sRawTokens` in `LaTeXState`, and a new combinator `disablingWithRaw` to disable collecting of raw tokens in certain contexts. * Add `parseFromToks` to T.P.Readers.LaTeX.Parsing. * Fix parsing of single character tokens so it doesn't mess up the new raw token collecting. * These changes slightly increase allocations and have a small performance impact, but it's minor. Closes #7092. --- src/Text/Pandoc/Readers/LaTeX.hs | 6 +-- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 84 +++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 91c71c000..0a66b7f39 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1054,7 +1054,7 @@ romanNumeralArg = spaces *> (parser <|> inBraces) symbol '}' return res parser = do - Tok _ Word s <- satisfyTok isWordTok + s <- untokenize <$> many1 (satisfyTok isWordTok) let (digits, rest) = T.span isDigit s unless (T.null rest) $ Prelude.fail "Non-digits in argument to \\Rn or \\RN" @@ -2208,9 +2208,7 @@ parseTableRow envname prefsufs = do option [] (count 1 amp) return $ map (setpos prefpos) pref ++ contents ++ map (setpos suffpos) suff rawcells <- mapM celltoks prefsufs - oldInput <- getInput - cells <- mapM (\ts -> setInput ts >> parseTableCell) rawcells - setInput oldInput + cells <- mapM (parseFromToks parseTableCell) rawcells spaces return $ Row nullAttr cells diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 12a3ba2f6..313aa6c51 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -32,6 +32,8 @@ module Text.Pandoc.Readers.LaTeX.Parsing , totoks , toksToString , satisfyTok + , parseFromToks + , disablingWithRaw , doMacros , doMacros' , setpos @@ -87,13 +89,15 @@ import Control.Monad.Trans (lift) import Data.Char (chr, isAlphaNum, isDigit, isLetter, ord) import Data.Default import Data.List (intercalate) +import qualified Data.IntMap as IntMap import qualified Data.Map as M import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad, report) -import Text.Pandoc.Error (PandocError (PandocMacroLoop)) +import Text.Pandoc.Error + (PandocError (PandocMacroLoop,PandocShouldNeverHappenError)) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, @@ -153,6 +157,8 @@ data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sToggles :: M.Map Text Bool , sExpanded :: Bool , sFileContents :: M.Map Text Text + , sEnableWithRaw :: Bool + , sRawTokens :: IntMap.IntMap [Tok] } deriving Show @@ -179,6 +185,8 @@ defaultLaTeXState = LaTeXState{ sOptions = def , sToggles = M.empty , sExpanded = False , sFileContents = M.empty + , sEnableWithRaw = True + , sRawTokens = IntMap.empty } instance PandocMonad m => HasQuoteContext LaTeXState m where @@ -404,11 +412,31 @@ untoken t = untokenAccum t mempty toksToString :: [Tok] -> String toksToString = T.unpack . untokenize +parseFromToks :: PandocMonad m => LP m a -> [Tok] -> LP m a +parseFromToks parser toks = do + oldInput <- getInput + setInput toks + result <- disablingWithRaw parser + setInput oldInput + return result + +disablingWithRaw :: PandocMonad m => LP m a -> LP m a +disablingWithRaw parser = do + oldEnableWithRaw <- sEnableWithRaw <$> getState + updateState $ \st -> st{ sEnableWithRaw = False } + result <- parser + updateState $ \st -> st{ sEnableWithRaw = oldEnableWithRaw } + return result + satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok satisfyTok f = do doMacros -- apply macros on remaining input stream res <- tokenPrim (T.unpack . untoken) updatePos matcher - updateState $ \st -> st{ sExpanded = False } + updateState $ \st -> st{ sExpanded = False + , sRawTokens = + if sEnableWithRaw st + then IntMap.map (res:) $ sRawTokens st + else sRawTokens st } return res where matcher t | f t = Just t | otherwise = Nothing @@ -594,18 +622,22 @@ isCommentTok _ = False anyTok :: PandocMonad m => LP m Tok anyTok = satisfyTok (const True) +singleCharTok :: PandocMonad m => LP m Tok +singleCharTok = + satisfyTok $ \case + Tok _ Word t -> T.length t == 1 + Tok _ Symbol t -> not (T.any (`Set.member` specialChars) t) + _ -> False + singleChar :: PandocMonad m => LP m Tok -singleChar = try $ do - Tok pos toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) - guard $ not $ toktype == Symbol && - T.any (`Set.member` specialChars) t - if T.length t > 1 - then do - let (t1, t2) = (T.take 1 t, T.drop 1 t) - inp <- getInput - setInput $ Tok (incSourceColumn pos 1) toktype t2 : inp - return $ Tok pos toktype t1 - else return $ Tok pos toktype t +singleChar = singleCharTok <|> singleCharFromWord + where + singleCharFromWord = do + Tok pos toktype t <- disablingWithRaw $ satisfyTok isWordTok + let (t1, t2) = (T.take 1 t, T.drop 1 t) + inp <- getInput + setInput $ Tok pos toktype t1 : Tok (incSourceColumn pos 1) toktype t2 : inp + anyTok specialChars :: Set.Set Char specialChars = Set.fromList "#$%&~_^\\{}" @@ -725,11 +757,23 @@ ignore raw = do withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok]) withRaw parser = do - inp <- getInput + rawTokensMap <- sRawTokens <$> getState + let key = case IntMap.lookupMax rawTokensMap of + Nothing -> 0 + Just (n,_) -> n + 1 + -- insert empty list at key + updateState $ \st -> st{ sRawTokens = + IntMap.insert key [] $ sRawTokens st } result <- parser - nxtpos <- option Nothing ((\(Tok pos' _ _) -> Just pos') <$> lookAhead anyTok) - let raw = takeWhile (\(Tok pos _ _) -> maybe True - (\p -> sourceName p /= sourceName pos || pos < p) nxtpos) inp + mbRevToks <- IntMap.lookup key . sRawTokens <$> getState + raw <- case mbRevToks of + Just revtoks -> do + updateState $ \st -> st{ sRawTokens = + IntMap.delete key $ sRawTokens st} + return $ reverse revtoks + Nothing -> + throwError $ PandocShouldNeverHappenError $ + "sRawTokens has nothing at key " <> T.pack (show key) return (result, raw) keyval :: PandocMonad m => LP m (Text, Text) @@ -794,7 +838,7 @@ getRawCommand name txt = do (_, rawargs) <- withRaw $ case name of "write" -> do - void $ satisfyTok isWordTok -- digits + void $ many $ satisfyTok isDigitTok -- digits void braced "titleformat" -> do void braced @@ -809,6 +853,10 @@ getRawCommand name txt = do void $ many braced return $ txt <> untokenize rawargs +isDigitTok :: Tok -> Bool +isDigitTok (Tok _ Word t) = T.all isDigit t +isDigitTok _ = False + skipopts :: PandocMonad m => LP m () skipopts = skipMany (void overlaySpecification <|> void rawopt) -- cgit v1.2.3 From eb0c63b00263263e3dc0f74c0bf19e93c7afeb13 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 12 Feb 2021 19:29:48 -0800 Subject: Avoid an unnecessary withRaw. --- src/Text/Pandoc/Readers/LaTeX.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 0a66b7f39..2a949cbdc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1869,9 +1869,12 @@ rawEnv name = do pos1 <- getPosition (bs, raw) <- withRaw $ env name blocks if parseRaw - then return $ rawBlock "latex" + then do + (_, raw) <- withRaw $ env name blocks + return $ rawBlock "latex" $ beginCommand <> untokenize raw else do + bs <- env name blocks report $ SkippedContent beginCommand pos1 pos2 <- getPosition report $ SkippedContent ("\\end{" <> name <> "}") pos2 -- cgit v1.2.3 From 1954e894b403a8b0f209e834a619c5893de1d22b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Feb 2021 00:14:49 -0800 Subject: Clean up benchmark code. Now we can do patterns using `-p blah'. --- Makefile | 5 +- benchmark/benchmark-pandoc.hs | 111 +++++++++++++++--------------------------- 2 files changed, 41 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index a61e58270..bcf09172a 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,10 @@ version?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}') pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1) SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$") BRANCH?=master -RESOLVER?=lts-13 GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 -BENCHARGS?="--timeout=6 +RTS -T -RTS" +BENCHARGS?=--timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j +RTS -A64m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -39,7 +38,7 @@ ghcid: ghcid -c "stack repl --flag 'pandoc:embed_data_files'" bench: - stack bench --benchmark-arguments=$(BENCHARGS) --ghc-options '$(GHCOPTS)' + stack bench --benchmark-arguments='$(BENCHARGS)' --ghc-options '$(GHCOPTS)' weigh: stack build --ghc-options '$(GHCOPTS)' pandoc:weigh-pandoc && stack exec weigh-pandoc diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 496732693..96810a477 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -18,48 +18,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} import Text.Pandoc import Text.Pandoc.MIME -import Control.Monad.Except (throwError, liftIO) +import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T import Test.Tasty.Bench -import Data.List (intersect) -import Data.Maybe (mapMaybe, catMaybes) -import System.Environment (getArgs) import qualified Data.ByteString.Lazy as BL - -data Input = InputText {unInputText :: T.Text} - | InputBS {unInputBS :: BL.ByteString} +import Data.Maybe (mapMaybe) readerBench :: Pandoc -> T.Text - -> IO (Maybe Benchmark) -readerBench doc name = do - let (rdr, rexts) = either (error . show) id . runPure $ getReader name - res <- runIO $ do - (wtr, wexts) <- getWriter name - case (rdr, wtr) of - (TextReader r, TextWriter w) -> do - setResourcePath ["./test"] - inp <- w def{ writerWrapText = WrapAuto - , writerExtensions = wexts } doc - return (r def{ readerExtensions = rexts } . unInputText, InputText inp) - (ByteStringReader r, ByteStringWriter w) -> do - setResourcePath ["./test"] - tmpl <- Just <$> compileDefaultTemplate name - inp <- w def{ writerWrapText = WrapAuto - , writerExtensions = wexts - , writerTemplate = tmpl } doc - liftIO $ BL.writeFile "/tmp/test.odt" inp - return (r def{ readerExtensions = rexts } . unInputBS, InputBS inp) - _ -> throwError $ PandocSomeError $ "text/bytestring format mismatch: " - <> name - return $ case res of - Right (readerFun, inp) -> - Just $ bench (T.unpack name) - $ nf (\i -> either (error . show) id $ runPure (readerFun i)) - inp - Left _ -> Nothing + -> Maybe Benchmark +readerBench doc name = either (const Nothing) Just $ + runPure $ do + (rdr, rexts) <- getReader name + (wtr, wexts) <- getWriter name + case (rdr, wtr) of + (TextReader r, TextWriter w) -> do + inp <- w def{ writerWrapText = WrapAuto + , writerExtensions = wexts } doc + return $ bench (T.unpack name) $ + nf (either (error . show) id . runPure . r def) inp + (ByteStringReader r, ByteStringWriter w) -> do + tmpl <- Just <$> compileDefaultTemplate name + inp <- w def{ writerWrapText = WrapAuto + , writerExtensions = wexts + , writerTemplate = tmpl } doc + return $ bench (T.unpack name) $ + nf (either (error . show) id . + runPure . r def{readerExtensions = rexts}) inp + _ -> throwError $ PandocSomeError $ "text/bytestring format mismatch: " + <> name getImages :: IO [(FilePath, MimeType, BL.ByteString)] getImages = do @@ -72,51 +61,29 @@ writerBench :: [(FilePath, MimeType, BL.ByteString)] -> Pandoc -> T.Text -> Maybe Benchmark -writerBench imgs doc name = - case res of - Right writerFun -> - Just $ bench (T.unpack name) +writerBench imgs doc name = either (const Nothing) Just $ + runPure $ do + (wtr, wexts) <- getWriter name + case wtr of + TextWriter writerFun -> + return $ bench (T.unpack name) $ nf (\d -> either (error . show) id $ - runPure (do mapM_ - (\(fp, mt, bs) -> - insertMedia fp (Just mt) bs) - imgs - writerFun d)) doc - Left _ -> Nothing - where res = runPure $ do - (wtr, wexts) <- getWriter name - case wtr of - TextWriter w -> - return $ w def{ writerExtensions = wexts } - _ -> throwError $ PandocSomeError - $ "could not get text writer for " <> name + runPure $ do + mapM_ (\(fp,mt,bs) -> insertMedia fp (Just mt) bs) imgs + writerFun def{ writerExtensions = wexts} d) + doc + _ -> throwError $ PandocSomeError + $ "could not get text writer for " <> name main :: IO () main = do - args <- filter (\x -> T.take 1 x /= "-") . fmap T.pack <$> getArgs - print args - let matchReader (n, _) = - null args || ("reader" `elem` args && n `elem` args) - matchWriter (n, TextWriter _) = - null args || ("writer" `elem` args && n `elem` args) - matchWriter _ = False - allWriters = map fst (writers :: [(T.Text, Writer PandocPure)]) - matchedReaders = map fst (filter matchReader readers - :: [(T.Text, Reader PandocPure)]) - matchedWriters = map fst (filter matchWriter writers - :: [(T.Text, Writer PandocPure)]) inp <- UTF8.toText <$> B.readFile "test/testsuite.txt" let opts = def let doc = either (error . show) id $ runPure $ readMarkdown opts inp - readerBs <- fmap catMaybes - $ mapM (readerBench doc) - $ filter (/="haddock") - (matchedReaders `intersect` allWriters) - -- we need the corresponding writer to generate - -- input for the reader imgs <- getImages - let writerBs = mapMaybe (writerBench imgs doc) matchedWriters defaultMain - [ bgroup "writers" writerBs - , bgroup "readers" readerBs + [ bgroup "writers" $ mapMaybe (writerBench imgs doc . fst) + (writers :: [(T.Text, Writer PandocPure)]) + , bgroup "readers" $ mapMaybe (readerBench doc . fst) + (readers :: [(T.Text, Reader PandocPure)]) ] -- cgit v1.2.3 From 25b7df7c2a1798b5861b7ddc084e74b926299630 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Feb 2021 00:18:40 -0800 Subject: Remove Ext_fenced_code_attributes from allowed commonmark attributes. This attribute was listed as allowed, but it didn't actually do anything. Use `attributes` for code attributes and more. Closes #7097. --- src/Text/Pandoc/Extensions.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 7aa32c52c..82eb0e957 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -378,7 +378,6 @@ getDefaultExtensions "commonmark_x" = extensionsFromList , Ext_raw_attribute , Ext_implicit_header_references , Ext_attributes - , Ext_fenced_code_attributes ] getDefaultExtensions "org" = extensionsFromList [Ext_citations, @@ -509,7 +508,6 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_raw_attribute , Ext_implicit_header_references , Ext_attributes - , Ext_fenced_code_attributes , Ext_sourcepos ] getAll "commonmark_x" = getAll "commonmark" -- cgit v1.2.3 From 6323250bad5e0bf9ef0ec007e16c869d99828080 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Feb 2021 00:22:22 -0800 Subject: LaTeX reader: remove unnecessary line --- src/Text/Pandoc/Readers/LaTeX.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 2a949cbdc..7d8dfab0e 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1867,7 +1867,6 @@ rawEnv name = do rawOptions <- mconcat <$> many rawopt let beginCommand = "\\begin{" <> name <> "}" <> rawOptions pos1 <- getPosition - (bs, raw) <- withRaw $ env name blocks if parseRaw then do (_, raw) <- withRaw $ env name blocks -- cgit v1.2.3 From 2d60a5127cc28bb6b55c19309d6e8fb6e81fbe66 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 13 Feb 2021 09:36:19 +0100 Subject: T.P.Shared: export `handleTaskListItem`. [API change] --- src/Text/Pandoc/Shared.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index b908a0172..6d5d4c97d 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -70,6 +70,7 @@ module Text.Pandoc.Shared ( isTightList, taskListItemFromAscii, taskListItemToAscii, + handleTaskListItem, addMetaField, makeMeta, eastAsianLineBreakFilter, -- cgit v1.2.3 From a3beed9db874517fa57b55380658f4e019e809b2 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 13 Feb 2021 09:37:43 +0100 Subject: Org: support task_lists extension The tasks lists extension is now supported by the org reader and writer; the extension is turned on by default. Closes: #6336 --- src/Text/Pandoc/Extensions.hs | 2 ++ src/Text/Pandoc/Readers/Org/Blocks.hs | 41 +++++++++++++++++++++++-- src/Text/Pandoc/Writers/Org.hs | 16 ++++++++-- test/Tests/Readers/Org/Block/List.hs | 13 ++++++++ test/Tests/Writers/Org.hs | 57 ++++++++++++++++++++++++++++------- 5 files changed, 113 insertions(+), 16 deletions(-) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 82eb0e957..3b96f9e04 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -381,6 +381,7 @@ getDefaultExtensions "commonmark_x" = extensionsFromList ] getDefaultExtensions "org" = extensionsFromList [Ext_citations, + Ext_task_lists, Ext_auto_identifiers] getDefaultExtensions "html" = extensionsFromList [Ext_auto_identifiers, @@ -515,6 +516,7 @@ getAllExtensions f = universalExtensions <> getAll f extensionsFromList [ Ext_citations , Ext_smart + , Ext_task_lists ] getAll "html" = autoIdExtensions <> extensionsFromList diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 6bd046e04..d1aff701e 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {- | Module : Text.Pandoc.Readers.Org.Blocks Copyright : Copyright (C) 2014-2021 Albert Krewinkel @@ -850,16 +851,52 @@ definitionListItem parseIndentedMarker = try $ do definitionMarker = spaceChar *> string "::" <* (spaceChar <|> lookAhead newline) +-- | Checkbox for tasks. +data Checkbox + = UncheckedBox + | CheckedBox + | SemicheckedBox + +-- | Parses a checkbox in a plain list. +checkbox :: PandocMonad m + => OrgParser m Checkbox +checkbox = do + guardEnabled Ext_task_lists + try (char '[' *> status <* char ']') "checkbox" + where + status = choice + [ UncheckedBox <$ char ' ' + , CheckedBox <$ char 'X' + , SemicheckedBox <$ char '-' + ] + +checkboxToInlines :: Checkbox -> Inline +checkboxToInlines = B.Str . \case + UncheckedBox -> "☐" + SemicheckedBox -> "☐" + CheckedBox -> "☒" + -- | parse raw text for one list item listItem :: PandocMonad m => OrgParser m Int -> OrgParser m (F Blocks) listItem parseIndentedMarker = try . withContext ListItemState $ do markerLength <- try parseIndentedMarker + box <- optionMaybe checkbox firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- T.concat <$> many (listContinuation markerLength) - parseFromString blocks $ firstLine <> blank <> rest + contents <- parseFromString blocks $ firstLine <> blank <> rest + return (maybe id (prependInlines . checkboxToInlines) box <$> contents) + +-- | Prepend inlines to blocks, adding them to the first paragraph or +-- creating a new Plain element if necessary. +prependInlines :: Inline -> Blocks -> Blocks +prependInlines inlns = B.fromList . prepend . B.toList + where + prepend (Plain is : bs) = Plain (inlns : Space : is) : bs + prepend (Para is : bs) = Para (inlns : Space : is) : bs + prepend bs = Plain [inlns, Space] : bs -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 43ebf1807..8dfc2749c 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -213,25 +213,35 @@ blockToOrg (DefinitionList items) = do -- | Convert bullet list item (list of blocks) to Org. bulletListItemToOrg :: PandocMonad m => [Block] -> Org m (Doc Text) bulletListItemToOrg items = do - contents <- blockListToOrg items + exts <- gets $ writerExtensions . stOptions + contents <- blockListToOrg (taskListItemToOrg exts items) return $ hang 2 "- " contents $$ if endsWithPlain items then cr else blankline - -- | Convert ordered list item (a list of blocks) to Org. orderedListItemToOrg :: PandocMonad m => Text -- ^ marker for list item -> [Block] -- ^ list item (list of blocks) -> Org m (Doc Text) orderedListItemToOrg marker items = do - contents <- blockListToOrg items + exts <- gets $ writerExtensions . stOptions + contents <- blockListToOrg (taskListItemToOrg exts items) return $ hang (T.length marker + 1) (literal marker <> space) contents $$ if endsWithPlain items then cr else blankline +-- | Convert a list item containing text starting with @U+2610 BALLOT BOX@ +-- or @U+2612 BALLOT BOX WITH X@ to org checkbox syntax (e.g. @[X]@). +taskListItemToOrg :: Extensions -> [Block] -> [Block] +taskListItemToOrg = handleTaskListItem toOrg + where + toOrg (Str "☐" : Space : is) = Str "[ ]" : Space : is + toOrg (Str "☒" : Space : is) = Str "[X]" : Space : is + toOrg is = is + -- | Convert definition list item (label, list of blocks) to Org. definitionListItemToOrg :: PandocMonad m => ([Inline], [[Block]]) -> Org m (Doc Text) diff --git a/test/Tests/Readers/Org/Block/List.hs b/test/Tests/Readers/Org/Block/List.hs index 9686b5148..2ee37081e 100644 --- a/test/Tests/Readers/Org/Block/List.hs +++ b/test/Tests/Readers/Org/Block/List.hs @@ -118,6 +118,19 @@ tests = ] =?> bulletList [ plain "", plain "" ] + , "Task list" =: + T.unlines [ "- [ ] nope" + , "- [X] yup" + , "- [-] started" + , " 1. [X] sure" + , " 2. [ ] nuh-uh" + ] =?> + bulletList [ plain "☐ nope", plain "☒ yup" + , mconcat [ plain "☐ started" + , orderedList [plain "☒ sure", plain "☐ nuh-uh"] + ] + ] + , "Simple Ordered List" =: ("1. Item1\n" <> "2. Item2\n") =?> diff --git a/test/Tests/Writers/Org.hs b/test/Tests/Writers/Org.hs index 9cbe360da..bd6c9b7ab 100644 --- a/test/Tests/Writers/Org.hs +++ b/test/Tests/Writers/Org.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Org (tests) where +import Data.Text as T import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -9,17 +10,51 @@ import Text.Pandoc.Builder infix 4 =: (=:) :: (ToString a, ToPandoc a) - => String -> (a, String) -> TestTree -(=:) = test (purely (writeOrg def . toPandoc)) + => String -> (a, Text) -> TestTree +(=:) = test org + +defopts :: WriterOptions +defopts = def + { writerExtensions = getDefaultExtensions "org" + } + +org :: (ToPandoc a) => a -> Text +org = orgWithOpts defopts + +orgWithOpts :: (ToPandoc a) => WriterOptions -> a -> Text +orgWithOpts opts x = purely (writeOrg opts) $ toPandoc x + tests :: [TestTree] -tests = [ testGroup "links" - -- See http://orgmode.org/manual/Internal-links.html#Internal-links - [ "simple link" - =: link "/url" "" "foo" - =?> "[[/url][foo]]" - , "internal link to anchor" - =: link "#my-custom-id" "" "#my-custom-id" - =?> "[[#my-custom-id]]" +tests = + [ testGroup "links" + -- See http://orgmode.org/manual/Internal-links.html#Internal-links + [ "simple link" + =: link "/url" "" "foo" + =?> "[[/url][foo]]" + , "internal link to anchor" + =: link "#my-custom-id" "" "#my-custom-id" + =?> "[[#my-custom-id]]" + ] + + , testGroup "lists" + [ "bullet task list" + =: bulletList [plain "☐ a", plain "☒ b"] + =?> T.unlines + [ "- [ ] a" + , "- [X] b" + ] + , "ordered task list" + =: orderedList [plain ("☐" <> space <> "a"), plain "☒ b"] + =?> T.unlines + [ "1. [ ] a" + , "2. [X] b" + ] + , test (orgWithOpts def) "bullet without task_lists" $ + bulletList [plain "☐ a", plain "☒ b"] + =?> T.unlines + [ "- ☐ a" + , "- ☒ b" ] - ] + ] + ] -- cgit v1.2.3 From 6e73273916a55448c1a12ece343454ef139648a8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Feb 2021 13:05:17 -0800 Subject: T.P.Error: export `renderError`. Refactor `handleError` to use `renderError`. This allows us render error messages without exiting. --- src/Text/Pandoc/Error.hs | 105 ++++++++++++++++++++++++++++++++--------------- test/command/7099.md | 8 ++++ 2 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 test/command/7099.md diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 831405f42..50ad3c0e3 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -16,9 +16,10 @@ encountered during parsing. -} module Text.Pandoc.Error ( PandocError(..), + renderError, handleError) where -import Control.Exception (Exception) +import Control.Exception (Exception, displayException) import Data.Typeable (Typeable) import Data.Word (Word8) import Data.Text (Text) @@ -68,19 +69,17 @@ data PandocError = PandocIOError Text IOError instance Exception PandocError --- | Handle PandocError by exiting with an error message. -handleError :: Either PandocError a -> IO a -handleError (Right r) = return r -handleError (Left e) = +renderError :: PandocError -> Text +renderError e = case e of - PandocIOError _ err' -> ioError err' - PandocHttpError u err' -> err 61 $ + PandocIOError _ err' -> T.pack $ displayException err' + PandocHttpError u err' -> "Could not fetch " <> u <> "\n" <> tshow err' - PandocShouldNeverHappenError s -> err 62 $ + PandocShouldNeverHappenError s -> "Something we thought was impossible happened!\n" <> "Please report this to pandoc's developers: " <> s - PandocSomeError s -> err 63 s - PandocParseError s -> err 64 s + PandocSomeError s -> s + PandocParseError s -> s PandocParsecError input err' -> let errPos = errorPos err' errLine = sourceLine errPos @@ -91,41 +90,41 @@ handleError (Left e) = ,"\n", T.replicate (errColumn - 1) " " ,"^"] else "" - in err 65 $ "\nError at " <> tshow err' <> + in "\nError at " <> tshow err' <> -- if error comes from a chunk or included file, -- then we won't get the right text this way: if sourceName errPos == "source" then errorInFile else "" - PandocMakePDFError s -> err 66 s - PandocOptionError s -> err 6 s - PandocSyntaxMapError s -> err 67 s - PandocFailOnWarningError -> err 3 "Failing because there were warnings." - PandocPDFProgramNotFoundError pdfprog -> err 47 $ + PandocMakePDFError s -> s + PandocOptionError s -> s + PandocSyntaxMapError s -> s + PandocFailOnWarningError -> "Failing because there were warnings." + PandocPDFProgramNotFoundError pdfprog -> pdfprog <> " not found. Please select a different --pdf-engine or install " <> pdfprog - PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" <> logmsg - PandocXMLError fp logmsg -> err 44 $ "Invalid XML" <> + PandocPDFError logmsg -> "Error producing PDF.\n" <> logmsg + PandocXMLError fp logmsg -> "Invalid XML" <> (if T.null fp then "" else " in " <> fp) <> ":\n" <> logmsg - PandocFilterError filtername msg -> err 83 $ "Error running filter " <> + PandocFilterError filtername msg -> "Error running filter " <> filtername <> ":\n" <> msg - PandocLuaError msg -> err 84 $ "Error running Lua:\n" <> msg - PandocCouldNotFindDataFileError fn -> err 97 $ + PandocLuaError msg -> "Error running Lua:\n" <> msg + PandocCouldNotFindDataFileError fn -> "Could not find data file " <> fn - PandocResourceNotFound fn -> err 99 $ + PandocResourceNotFound fn -> "File " <> fn <> " not found in resource path" - PandocTemplateError s -> err 5 $ "Error compiling template " <> s - PandocAppError s -> err 4 s - PandocEpubSubdirectoryError s -> err 31 $ + PandocTemplateError s -> "Error compiling template " <> s + PandocAppError s -> s + PandocEpubSubdirectoryError s -> "EPUB subdirectory name '" <> s <> "' contains illegal characters" - PandocMacroLoop s -> err 91 $ + PandocMacroLoop s -> "Loop encountered in expanding macro " <> s - PandocUTF8DecodingError f offset w -> err 92 $ + PandocUTF8DecodingError f offset w -> "UTF-8 decoding error in " <> f <> " at byte offset " <> tshow offset <> " (" <> T.pack (printf "%2x" w) <> ").\n" <> "The input must be a UTF-8 encoded text." - PandocIpynbDecodingError w -> err 93 $ + PandocIpynbDecodingError w -> "ipynb decoding error: " <> w - PandocUnknownReaderError r -> err 21 $ + PandocUnknownReaderError r -> "Unknown input format " <> r <> case r of "doc" -> "\nPandoc can convert from DOCX, but not from DOC." <> @@ -133,7 +132,7 @@ handleError (Left e) = " and convert that with pandoc." "pdf" -> "\nPandoc can convert to PDF, but not from PDF." _ -> "" - PandocUnknownWriterError w -> err 22 $ + PandocUnknownWriterError w -> "Unknown output format " <> w <> case w of "pdf" -> "To create a pdf using pandoc, use" <> @@ -142,14 +141,54 @@ handleError (Left e) = ".pdf extension (-o filename.pdf)." "doc" -> "\nPandoc can convert to DOCX, but not to DOC." _ -> "" - PandocUnsupportedExtensionError ext f -> err 23 $ + PandocUnsupportedExtensionError ext f -> "The extension " <> ext <> " is not supported " <> "for " <> f - PandocCiteprocError e' -> err 24 $ + PandocCiteprocError e' -> prettyCiteprocError e' - PandocBibliographyError fp msg -> err 25 $ + PandocBibliographyError fp msg -> "Error reading bibliography file " <> fp <> ":\n" <> msg + +-- | Handle PandocError by exiting with an error message. +handleError :: Either PandocError a -> IO a +handleError (Right r) = return r +handleError (Left e) = + case e of + PandocIOError _ err' -> ioError err' + _ -> err exitCode (renderError e) + where + exitCode = + case e of + PandocIOError{} -> 1 + PandocHttpError{} -> 61 + PandocShouldNeverHappenError{} -> 62 + PandocSomeError{} -> 63 + PandocParseError{} -> 64 + PandocParsecError{} -> 65 + PandocMakePDFError{} -> 66 + PandocOptionError{} -> 6 + PandocSyntaxMapError{} -> 67 + PandocFailOnWarningError{} -> 3 + PandocPDFProgramNotFoundError{} -> 47 + PandocPDFError{} -> 43 + PandocXMLError{} -> 44 + PandocFilterError{} -> 83 + PandocLuaError{} -> 84 + PandocCouldNotFindDataFileError{} -> 97 + PandocResourceNotFound fn -> 99 + PandocTemplateError{} -> 5 + PandocAppError{} -> 4 + PandocEpubSubdirectoryError{} -> 31 + PandocMacroLoop s -> 91 + PandocUTF8DecodingError{} -> 92 + PandocIpynbDecodingError{} -> 93 + PandocUnknownReaderError{} -> 21 + PandocUnknownWriterError{} -> 22 + PandocUnsupportedExtensionError{} -> 23 + PandocCiteprocError{} -> 24 + PandocBibliographyError{} -> 25 + err :: Int -> Text -> IO a err exitCode msg = do UTF8.hPutStrLn stderr (T.unpack msg) diff --git a/test/command/7099.md b/test/command/7099.md new file mode 100644 index 000000000..d9ff8e5ff --- /dev/null +++ b/test/command/7099.md @@ -0,0 +1,8 @@ +``` +% pandoc -f html -t native --verbose + +^D +[INFO] Fetching ... +[INFO] Skipped '' at input line 1 column 1 +[] +``` -- cgit v1.2.3 From d84a6041e12547331f3a252ea4e8b4d229ba1159 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Feb 2021 13:06:22 -0800 Subject: HTML reader: fix bad handling of empty src attribute in iframe. - If src is empty, we simply skip the iframe. - If src is invalid or cannot be fetched, we issue a warning and skip instead of failing with an error. - Closes #7099. --- src/Text/Pandoc/Readers/HTML.hs | 18 ++++++++++++------ test/command/7099.md | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index afc7a3e25..cc60b5501 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -26,7 +26,7 @@ module Text.Pandoc.Readers.HTML ( readHtml import Control.Applicative ((<|>)) import Control.Monad (guard, msum, mzero, unless, void) -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader (ask, asks, lift, local, runReaderT) import Data.ByteString.Base64 (encode) import Data.Char (isAlphaNum, isLetter) @@ -393,11 +393,17 @@ pIframe = try $ do tag <- pSatisfy (tagOpen (=="iframe") (isJust . lookup "src")) pCloses "iframe" <|> eof url <- canonicalizeUrl $ fromAttrib "src" tag - (bs, _) <- openURL url - let inp = UTF8.toText bs - opts <- readerOpts <$> getState - Pandoc _ contents <- readHtml opts inp - return $ B.divWith ("",["iframe"],[]) $ B.fromList contents + if T.null url + then ignore $ renderTags' [tag, TagClose "iframe"] + else catchError + (do (bs, _) <- openURL url + let inp = UTF8.toText bs + opts <- readerOpts <$> getState + Pandoc _ contents <- readHtml opts inp + return $ B.divWith ("",["iframe"],[]) $ B.fromList contents) + (\e -> do + logMessage $ CouldNotFetchResource url (renderError e) + ignore $ renderTags' [tag, TagClose "iframe"]) pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do diff --git a/test/command/7099.md b/test/command/7099.md index d9ff8e5ff..33ac8aea1 100644 --- a/test/command/7099.md +++ b/test/command/7099.md @@ -2,7 +2,17 @@ % pandoc -f html -t native --verbose ^D -[INFO] Fetching ... -[INFO] Skipped '' at input line 1 column 1 +[INFO] Skipped '' at input line 1 column 16 +[] +``` + +``` +% pandoc -f html -t native --verbose + +^D +[INFO] Fetching h:invalid@url... +[WARNING] Could not fetch resource 'h:invalid@url': Could not fetch h:invalid@url + InvalidUrlException "h:invalid@url" "Invalid scheme" +[INFO] Skipped '' at input line 1 column 29 [] ``` -- cgit v1.2.3 From 1942dc561118a72fbbd78c1ee8d8784f47b88f4c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 14 Feb 2021 14:24:16 +0100 Subject: Allow tasty 1.4.* --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 63b1e6cd9..3c7063f6c 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -751,7 +751,7 @@ test-suite test-pandoc mtl >= 2.2 && < 2.3, pandoc-types >= 1.22 && < 1.23, process >= 1.2.3 && < 1.7, - tasty >= 0.11 && < 1.4, + tasty >= 0.11 && < 1.5, tasty-golden >= 2.3 && < 2.4, tasty-hunit >= 0.9 && < 0.11, tasty-lua >= 0.2 && < 0.3, -- cgit v1.2.3 From 8621ed600af6bdc984fe2095dac400887c3cda78 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 14 Feb 2021 15:49:12 +0100 Subject: T.P.Error: remove unused variables --- src/Text/Pandoc/Error.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 50ad3c0e3..94c013cdb 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -176,11 +176,11 @@ handleError (Left e) = PandocFilterError{} -> 83 PandocLuaError{} -> 84 PandocCouldNotFindDataFileError{} -> 97 - PandocResourceNotFound fn -> 99 + PandocResourceNotFound{} -> 99 PandocTemplateError{} -> 5 PandocAppError{} -> 4 PandocEpubSubdirectoryError{} -> 31 - PandocMacroLoop s -> 91 + PandocMacroLoop{} -> 91 PandocUTF8DecodingError{} -> 92 PandocIpynbDecodingError{} -> 93 PandocUnknownReaderError{} -> 21 -- cgit v1.2.3 From 2c99e0e3588d5645e901346572e1b04e8fb4fa3e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 15 Feb 2021 13:15:14 +0100 Subject: JATS writer: replace attribute "pub-type" with "publication-format". The former attribute is deprecated. --- data/templates/article.jats_publishing | 6 +++--- doc/jats.md | 11 +++++++---- test/writer.jats_archiving | 2 +- test/writer.jats_publishing | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/data/templates/article.jats_publishing b/data/templates/article.jats_publishing index 15d2e3b08..3a3a8f8cf 100644 --- a/data/templates/article.jats_publishing +++ b/data/templates/article.jats_publishing @@ -30,10 +30,10 @@ $if(journal.abbrev-title)$ $endif$ $if(journal.pissn)$ -$journal.pissn$ +$journal.pissn$ $endif$ $if(journal.eissn)$ -$journal.eissn$ +$journal.eissn$ $endif$ $-- At least one issn element is required; use empty issn as fallback $if(journal.pissn)$ @@ -130,7 +130,7 @@ $endif$ $endif$ $if(date)$ - + $if(date.day)$ $date.day$ $endif$ diff --git a/doc/jats.md b/doc/jats.md index 0faea8b06..0e717d0d8 100644 --- a/doc/jats.md +++ b/doc/jats.md @@ -173,7 +173,8 @@ Metadata Values The publication date is recorded in the document via the [``][elem:pub-date] element and its sub-elements. The - [`pub-type`][attr:pub-type] attribute is always set to `epub`. + [`publication-format`][attr:publication-format] attribute is + always set to `electronic`. `iso-8601` : ISO-8601 representation of the publication date. Used as the @@ -300,12 +301,14 @@ Metadata Values `pissn` : ISSN identifier of the publication's print version. Used as content of the [``][elem:issn] element with the - [`pub-type`][attr:pub-type] attribute set to `ppub`. + [`publication-format`][attr:publication-format] attribute + set to `print`. `eissn` : ISSN identifier of the publication's electronic version. Used as content of the [``][elem:issn] element with - the [`pub-type`][attr:pub-type] attribute set to `epub`. + the [`publication-format`][attr:publication-format] + attribute set to `electronic`. `publisher-name` : name of the publishing entity (person, company, or other). @@ -352,7 +355,7 @@ Required metadata values: [attr:kwd-group-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/kwd-group-type.html [attr:license-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/license-type.html [attr:pub-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/pub-id-type.html -[attr:pub-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/pub-type.html +[attr:publication-format]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/publication-format.html [attr:ref-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/ref-type.html [attr:rid]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/rid.html [attr:subj-group-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/subj-group-type.html diff --git a/test/writer.jats_archiving b/test/writer.jats_archiving index 2d06c5cab..941f5e87d 100644 --- a/test/writer.jats_archiving +++ b/test/writer.jats_archiving @@ -24,7 +24,7 @@ Anonymous - + 17 7 2006 diff --git a/test/writer.jats_publishing b/test/writer.jats_publishing index 915471576..f7d4a4720 100644 --- a/test/writer.jats_publishing +++ b/test/writer.jats_publishing @@ -24,7 +24,7 @@ Anonymous - + 17 7 2006 -- cgit v1.2.3 From b5b576184c3c1668aad0c904e186136b81a0dd54 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 15 Feb 2021 13:15:14 +0100 Subject: JATS writer: add date-type to pub-date elements --- data/templates/article.jats_publishing | 2 +- doc/jats.md | 13 ++++++++++--- test/writer.jats_archiving | 2 +- test/writer.jats_publishing | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/data/templates/article.jats_publishing b/data/templates/article.jats_publishing index 3a3a8f8cf..47fa4ac62 100644 --- a/data/templates/article.jats_publishing +++ b/data/templates/article.jats_publishing @@ -130,7 +130,7 @@ $endif$ $endif$ $if(date)$ - + $if(date.day)$ $date.day$ $endif$ diff --git a/doc/jats.md b/doc/jats.md index 0e717d0d8..95151d9f1 100644 --- a/doc/jats.md +++ b/doc/jats.md @@ -172,13 +172,13 @@ Metadata Values pass these components directly. The publication date is recorded in the document via the - [``][elem:pub-date] element and its sub-elements. The + [``] element and its sub-elements. The [`publication-format`][attr:publication-format] attribute is always set to `electronic`. `iso-8601` : ISO-8601 representation of the publication date. Used as the - value of the [`pub-date`][elem:pub-date] element's + value of the [``] element's [`iso-8601-date`][attr:iso-8601-date] attribute. This value is set automatically if pandoc can parse the @@ -192,6 +192,12 @@ Metadata Values The values are set automatically if pandoc can parse the `date` value as a date. + `type` + : The type of event marked by this date. The value is set as + the [`date-type`][attr:date-type] attribute on the + [``] element and defaults to "pub" if not + specified. + `article` : information concerning the article that identifies or describes it. The key-value pairs within this map are typically used @@ -348,6 +354,7 @@ Required metadata values: [Ringgold]: https://ringgold.com/ [attr:content-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/content-type.html +[attr:date-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/date-type.html [attr:fn-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/fn-type.html [attr:institution-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/institution-id-type.html [attr:iso-8601-date]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/iso-8601-date.html @@ -380,7 +387,6 @@ Required metadata values: [elem:license]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/license.html [elem:notes]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/notes.html [elem:permissions]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/permissions.html -[elem:pub-date]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/pub-date.html [elem:publisher-loc]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/publisher-loc.html [elem:publisher-name]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/publisher-name.html [elem:string-name]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/string-name.html @@ -396,3 +402,4 @@ Required metadata values: [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-id.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-wrap.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution.html +[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/pub-date.html diff --git a/test/writer.jats_archiving b/test/writer.jats_archiving index 941f5e87d..a6ff94bcd 100644 --- a/test/writer.jats_archiving +++ b/test/writer.jats_archiving @@ -24,7 +24,7 @@ Anonymous - + 17 7 2006 diff --git a/test/writer.jats_publishing b/test/writer.jats_publishing index f7d4a4720..eb3f8faab 100644 --- a/test/writer.jats_publishing +++ b/test/writer.jats_publishing @@ -24,7 +24,7 @@ Anonymous - + 17 7 2006 -- cgit v1.2.3 From 967e7f5fb990b29de48b37be1db40fb149a8cf55 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 14 Feb 2021 22:29:21 -0800 Subject: Rename Text.Pandoc.XMLParser -> Text.Pandoc.XML.Light... ..and add new definitions isomorphic to xml-light's, but with Text instead of String. This allows us to keep most of the code in existing readers that use xml-light, but avoid lots of unnecessary allocation. We also add versions of the functions from xml-light's Text.XML.Light.Output and Text.XML.Light.Proc that operate on our modified XML types, and functions that convert xml-light types to our types (since some of our dependencies, like texmath, use xml-light). Update golden tests for docx and pptx. OOXML test: Use `showContent` instead of `ppContent` in `displayDiff`. Docx: Do a manual traversal to unwrap sdt and smartTag. This is faster, and needed to pass the tests. Benchmarks: A = prior to 8ca191604dcd13af27c11d2da225da646ebce6fc (Feb 8) B = as of 8ca191604dcd13af27c11d2da225da646ebce6fc (Feb 8) C = this commit | Reader | A | B | C | | ------- | ----- | ------ | ----- | | docbook | 18 ms | 12 ms | 10 ms | | opml | 65 ms | 62 ms | 35 ms | | jats | 15 ms | 11 ms | 9 ms | | docx | 72 ms | 69 ms | 44 ms | | odt | 78 ms | 41 ms | 28 ms | | epub | 64 ms | 61 ms | 56 ms | | fb2 | 14 ms | 5 ms | 4 ms | --- .hlint.yaml | 1 + pandoc.cabal | 2 +- src/Text/Pandoc/ImageSize.hs | 9 +- src/Text/Pandoc/Readers/DocBook.hs | 76 +-- src/Text/Pandoc/Readers/Docx/Parse.hs | 163 +++--- src/Text/Pandoc/Readers/Docx/Parse/Styles.hs | 31 +- src/Text/Pandoc/Readers/Docx/Util.hs | 27 +- src/Text/Pandoc/Readers/EPUB.hs | 65 +-- src/Text/Pandoc/Readers/FB2.hs | 93 ++-- src/Text/Pandoc/Readers/JATS.hs | 58 +- src/Text/Pandoc/Readers/OPML.hs | 29 +- src/Text/Pandoc/Readers/Odt.hs | 5 +- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 13 +- src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs | 3 +- src/Text/Pandoc/Readers/Odt/Generic/Utils.hs | 33 +- .../Pandoc/Readers/Odt/Generic/XMLConverter.hs | 23 +- src/Text/Pandoc/Readers/Odt/Namespaces.hs | 11 +- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 23 +- src/Text/Pandoc/Writers/Docx.hs | 263 +++++---- src/Text/Pandoc/Writers/EPUB.hs | 356 ++++++------- src/Text/Pandoc/Writers/FB2.hs | 127 ++--- src/Text/Pandoc/Writers/ODT.hs | 16 +- src/Text/Pandoc/Writers/OOXML.hs | 35 +- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 201 +++---- src/Text/Pandoc/XML/Light.hs | 586 +++++++++++++++++++++ src/Text/Pandoc/XMLParser.hs | 66 --- test/Tests/Writers/OOXML.hs | 3 +- test/docx/golden/block_quotes.docx | Bin 10098 -> 10071 bytes test/docx/golden/codeblock.docx | Bin 9950 -> 9920 bytes test/docx/golden/comments.docx | Bin 10285 -> 10258 bytes test/docx/golden/custom_style_no_reference.docx | Bin 10048 -> 10021 bytes test/docx/golden/custom_style_preserve.docx | Bin 10673 -> 10650 bytes test/docx/golden/custom_style_reference.docx | Bin 12434 -> 12403 bytes test/docx/golden/definition_list.docx | Bin 9947 -> 9920 bytes .../golden/document-properties-short-desc.docx | Bin 9953 -> 9925 bytes test/docx/golden/document-properties.docx | Bin 10429 -> 10404 bytes test/docx/golden/headers.docx | Bin 10086 -> 10059 bytes test/docx/golden/image.docx | Bin 26764 -> 26736 bytes test/docx/golden/inline_code.docx | Bin 9886 -> 9859 bytes test/docx/golden/inline_formatting.docx | Bin 10066 -> 10038 bytes test/docx/golden/inline_images.docx | Bin 26822 -> 26793 bytes test/docx/golden/link_in_notes.docx | Bin 10107 -> 10081 bytes test/docx/golden/links.docx | Bin 10282 -> 10251 bytes test/docx/golden/lists.docx | Bin 10358 -> 10332 bytes test/docx/golden/lists_continuing.docx | Bin 10149 -> 10123 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10238 -> 10210 bytes test/docx/golden/lists_restarting.docx | Bin 10150 -> 10122 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10245 -> 10216 bytes test/docx/golden/notes.docx | Bin 10052 -> 10028 bytes test/docx/golden/raw-blocks.docx | Bin 9986 -> 9960 bytes test/docx/golden/raw-bookmarks.docx | Bin 10121 -> 10094 bytes test/docx/golden/table_one_row.docx | Bin 9938 -> 9908 bytes test/docx/golden/table_with_list_cell.docx | Bin 10255 -> 10227 bytes test/docx/golden/tables.docx | Bin 10272 -> 10244 bytes test/docx/golden/track_changes_deletion.docx | Bin 9930 -> 9903 bytes test/docx/golden/track_changes_insertion.docx | Bin 9913 -> 9886 bytes test/docx/golden/track_changes_move.docx | Bin 9947 -> 9920 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 10059 -> 10032 bytes test/docx/golden/unicode.docx | Bin 9871 -> 9845 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9919 -> 9892 bytes test/pptx/code-custom.pptx | Bin 28221 -> 28184 bytes test/pptx/code-custom_templated.pptx | Bin 395516 -> 395477 bytes test/pptx/code.pptx | Bin 28220 -> 28183 bytes test/pptx/code_templated.pptx | Bin 395514 -> 395477 bytes test/pptx/document-properties-short-desc.pptx | Bin 27004 -> 26967 bytes .../document-properties-short-desc_templated.pptx | Bin 394288 -> 394253 bytes test/pptx/document-properties.pptx | Bin 27408 -> 27375 bytes test/pptx/document-properties_templated.pptx | Bin 394691 -> 394656 bytes test/pptx/endnotes.pptx | Bin 26962 -> 26928 bytes test/pptx/endnotes_templated.pptx | Bin 394253 -> 394219 bytes test/pptx/endnotes_toc.pptx | Bin 27789 -> 27747 bytes test/pptx/endnotes_toc_templated.pptx | Bin 395083 -> 395041 bytes test/pptx/images.pptx | Bin 44619 -> 44579 bytes test/pptx/images_templated.pptx | Bin 411909 -> 411870 bytes test/pptx/inline_formatting.pptx | Bin 26148 -> 26121 bytes test/pptx/inline_formatting_templated.pptx | Bin 393438 -> 393412 bytes test/pptx/lists.pptx | Bin 27049 -> 27015 bytes test/pptx/lists_templated.pptx | Bin 394340 -> 394307 bytes test/pptx/raw_ooxml.pptx | Bin 26940 -> 26908 bytes test/pptx/raw_ooxml_templated.pptx | Bin 394231 -> 394198 bytes test/pptx/remove_empty_slides.pptx | Bin 44065 -> 44025 bytes test/pptx/remove_empty_slides_templated.pptx | Bin 411352 -> 411311 bytes test/pptx/slide_breaks.pptx | Bin 28575 -> 28531 bytes test/pptx/slide_breaks_slide_level_1.pptx | Bin 27744 -> 27705 bytes .../pptx/slide_breaks_slide_level_1_templated.pptx | Bin 395038 -> 395000 bytes test/pptx/slide_breaks_templated.pptx | Bin 395868 -> 395825 bytes test/pptx/slide_breaks_toc.pptx | Bin 29532 -> 29481 bytes test/pptx/slide_breaks_toc_templated.pptx | Bin 396826 -> 396776 bytes test/pptx/speaker_notes.pptx | Bin 35436 -> 35360 bytes test/pptx/speaker_notes_after_metadata.pptx | Bin 31675 -> 31636 bytes .../speaker_notes_after_metadata_templated.pptx | Bin 398955 -> 398915 bytes test/pptx/speaker_notes_afterheader.pptx | Bin 30691 -> 30657 bytes test/pptx/speaker_notes_afterheader_templated.pptx | Bin 397979 -> 397943 bytes test/pptx/speaker_notes_afterseps.pptx | Bin 51604 -> 51548 bytes test/pptx/speaker_notes_afterseps_templated.pptx | Bin 418896 -> 418834 bytes test/pptx/speaker_notes_templated.pptx | Bin 402728 -> 402650 bytes test/pptx/start_numbering_at.pptx | Bin 27023 -> 26991 bytes test/pptx/start_numbering_at_templated.pptx | Bin 394314 -> 394283 bytes test/pptx/tables.pptx | Bin 27566 -> 27532 bytes test/pptx/tables_templated.pptx | Bin 394859 -> 394827 bytes test/pptx/two_column.pptx | Bin 26065 -> 26038 bytes test/pptx/two_column_templated.pptx | Bin 393355 -> 393327 bytes 102 files changed, 1388 insertions(+), 930 deletions(-) create mode 100644 src/Text/Pandoc/XML/Light.hs delete mode 100644 src/Text/Pandoc/XMLParser.hs diff --git a/.hlint.yaml b/.hlint.yaml index d5ebffd34..e482b2b37 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -14,6 +14,7 @@ - ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained - ignore: {name: "Use &&&"} - ignore: {name: "Use String"} +- ignore: {name: "Use camelCase"} - ignore: {name: "Use fmap"} # specific for GHC 7.8 compat - ignore: {name: "Use isDigit"} diff --git a/pandoc.cabal b/pandoc.cabal index 3c7063f6c..22aebd55e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -687,7 +687,7 @@ library Text.Pandoc.Lua.PandocLua, Text.Pandoc.Lua.Util, Text.Pandoc.Lua.Walk, - Text.Pandoc.XMLParser, + Text.Pandoc.XML.Light, Text.Pandoc.CSS, Text.Pandoc.CSV, Text.Pandoc.RoffChar, diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index e0a1af8e8..bb1aa6351 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -44,8 +44,7 @@ import Numeric (showFFloat) import Text.Pandoc.Definition import Text.Pandoc.Options import qualified Text.Pandoc.UTF8 as UTF8 -import qualified Text.XML.Light as Xml -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light hiding (Attr) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE @@ -332,12 +331,12 @@ svgSize opts img = do doc <- either (const mzero) return $ parseXMLElement $ TL.fromStrict $ UTF8.toText img let viewboxSize = do - vb <- Xml.findAttrBy (== Xml.QName "viewBox" Nothing Nothing) doc - [_,_,w,h] <- mapM safeRead (T.words (T.pack vb)) + vb <- findAttrBy (== QName "viewBox" Nothing Nothing) doc + [_,_,w,h] <- mapM safeRead (T.words vb) return (w,h) let dpi = fromIntegral $ writerDpi opts let dirToInt dir = do - dim <- Xml.findAttrBy (== Xml.QName dir Nothing Nothing) doc >>= lengthToDim . T.pack + dim <- findAttrBy (== QName dir Nothing Nothing) doc >>= lengthToDim return $ inPixel opts dim w <- dirToInt "width" <|> (fst <$> viewboxSize) h <- dirToInt "height" <|> (snd <$> viewboxSize) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index ad0108843..e201b54fe 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -31,8 +31,7 @@ import Text.Pandoc.Options import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) import Text.TeXMath (readMathML, writeTeX) -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLContents) +import Text.Pandoc.XML.Light {- @@ -578,26 +577,27 @@ normalizeTree = everywhere (mkT go) where go :: [Content] -> [Content] go (Text (CData CDataRaw _ _):xs) = xs go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 ++ s2) z):xs + Text (CData CDataText (s1 <> s2) z):xs go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 ++ convertEntity r) z):xs + Text (CData CDataText (s1 <> convertEntity r) z):xs go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r ++ s1) z):xs + Text (CData CDataText (convertEntity r <> s1) z):xs go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs + Text (CData CDataText (convertEntity r1 <> + convertEntity r2) Nothing):xs go xs = xs -convertEntity :: String -> String -convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) +convertEntity :: Text -> Text +convertEntity e = maybe (T.map toUpper e) T.pack (lookupEntity $ T.unpack e) -- convenience function to get an attribute value, defaulting to "" -attrValue :: String -> Element -> Text +attrValue :: Text -> Element -> Text attrValue attr elt = - maybe "" T.pack (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) + fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) -- convenience function named :: Text -> Element -> Bool -named s e = qName (elName e) == T.unpack s +named s e = qName (elName e) == s -- @@ -634,7 +634,7 @@ isBlockElement :: Content -> Bool isBlockElement (Elem e) = qName (elName e) `elem` blockTags isBlockElement _ = False -blockTags :: [String] +blockTags :: [Text] blockTags = [ "abstract" , "ackno" @@ -721,7 +721,7 @@ blockTags = , "variablelist" ] ++ admonitionTags -admonitionTags :: [String] +admonitionTags :: [Text] admonitionTags = ["important","caution","note","tip","warning"] -- Trim leading and trailing newline characters @@ -779,10 +779,10 @@ getBlocks e = mconcat <$> parseBlock :: PandocMonad m => Content -> DB m Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE -parseBlock (Text (CData _ s _)) = if all isSpace s +parseBlock (Text (CData _ s _)) = if T.all isSpace s then return mempty - else return $ plain $ trimInlines $ text $ T.pack s -parseBlock (CRef x) = return $ plain $ str $ T.toUpper $ T.pack x + else return $ plain $ trimInlines $ text s +parseBlock (CRef x) = return $ plain $ str $ T.toUpper x parseBlock (Elem e) = case qName (elName e) of "toc" -> skip -- skip TOC, since in pandoc it's autogenerated @@ -837,7 +837,7 @@ parseBlock (Elem e) = "refsect2" -> sect 2 "refsect3" -> sect 3 "refsection" -> gets dbSectionLevel >>= sect . (+1) - l | l `elem` admonitionTags -> parseAdmonition $ T.pack l + l | l `elem` admonitionTags -> parseAdmonition l "area" -> skip "areaset" -> skip "areaspec" -> skip @@ -899,7 +899,7 @@ parseBlock (Elem e) = "subtitle" -> return mempty -- handled in parent element _ -> skip >> getBlocks e where skip = do - let qn = T.pack $ qName $ elName e + let qn = qName $ elName e let name = if "pi-" `T.isPrefixOf` qn then " qn <> "?>" else qn @@ -911,7 +911,7 @@ parseBlock (Elem e) = "" -> [] x -> [x] return $ codeBlockWith (attrValue "id" e, classes', []) - $ trimNl $ T.pack $ strContentRecursive e + $ trimNl $ strContentRecursive e parseBlockquote = do attrib <- case filterChild (named "attribution") e of Nothing -> return mempty @@ -965,7 +965,7 @@ parseBlock (Elem e) = w <- findAttr (unqual "colwidth") c n <- safeRead $ "0" <> T.filter (\x -> (x >= '0' && x <= '9') - || x == '.') (T.pack w) + || x == '.') w if n > 0 then Just n else Nothing let numrows = case bodyrows of [] -> 0 @@ -1048,12 +1048,12 @@ parseMixed container conts = do x <- parseMixed container rs return $ p <> b <> x -parseRow :: PandocMonad m => [String] -> Element -> DB m [Cell] +parseRow :: PandocMonad m => [Text] -> Element -> DB m [Cell] parseRow cn = do let isEntry x = named "entry" x || named "td" x || named "th" x mapM (parseEntry cn) . filterChildren isEntry -parseEntry :: PandocMonad m => [String] -> Element -> DB m Cell +parseEntry :: PandocMonad m => [Text] -> Element -> DB m Cell parseEntry cn el = do let colDistance sa ea = do let iStrt = elemIndex sa cn @@ -1075,7 +1075,7 @@ getInlines :: PandocMonad m => Element -> DB m Inlines getInlines e' = trimInlines . mconcat <$> mapM parseInline (elContent e') -strContentRecursive :: Element -> String +strContentRecursive :: Element -> Text strContentRecursive = strContent . (\e' -> e'{ elContent = map elementToStr $ elContent e' }) @@ -1084,9 +1084,9 @@ elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing elementToStr x = x parseInline :: PandocMonad m => Content -> DB m Inlines -parseInline (Text (CData _ s _)) = return $ text $ T.pack s +parseInline (Text (CData _ s _)) = return $ text s parseInline (CRef ref) = - return $ text $ maybe (T.toUpper $ T.pack ref) T.pack $ lookupEntity ref + return $ text $ maybe (T.toUpper ref) T.pack $ lookupEntity (T.unpack ref) parseInline (Elem e) = case qName (elName e) of "anchor" -> do @@ -1138,7 +1138,7 @@ parseInline (Elem e) = "userinput" -> codeWithLang "systemitem" -> codeWithLang "varargs" -> return $ code "(...)" - "keycap" -> return (str $ T.pack $ strContent e) + "keycap" -> return (str $ strContent e) "keycombo" -> keycombo <$> mapM parseInline (elContent e) "menuchoice" -> menuchoice <$> @@ -1150,17 +1150,17 @@ parseInline (Elem e) = let title = case attrValue "endterm" e of "" -> maybe "???" xrefTitleByElem (findElementById linkend content) - endterm -> maybe "???" (T.pack . strContent) + endterm -> maybe "???" strContent (findElementById endterm content) return $ link ("#" <> linkend) "" (text title) - "email" -> return $ link ("mailto:" <> T.pack (strContent e)) "" - $ str $ T.pack $ strContent e - "uri" -> return $ link (T.pack $ strContent e) "" $ str $ T.pack $ strContent e + "email" -> return $ link ("mailto:" <> strContent e) "" + $ str $ strContent e + "uri" -> return $ link (strContent e) "" $ str $ strContent e "ulink" -> innerInlines (link (attrValue "url" e) "") "link" -> do ils <- innerInlines id let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of - Just h -> T.pack h + Just h -> h _ -> "#" <> attrValue "linkend" e let ils' = if ils == mempty then str href else ils let attr = (attrValue "id" e, T.words $ attrValue "role" e, []) @@ -1180,7 +1180,7 @@ parseInline (Elem e) = "pi-asciidoc-br" -> return linebreak _ -> skip >> innerInlines id where skip = do - let qn = T.pack $ qName $ elName e + let qn = qName $ elName e let name = if "pi-" `T.isPrefixOf` qn then " qn <> "?>" else qn @@ -1193,7 +1193,7 @@ parseInline (Elem e) = let classes' = case attrValue "language" e of "" -> [] l -> [l] - return $ codeWith (attrValue "id" e,classes',[]) $ T.pack $ strContentRecursive e + return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e simpleList = mconcat . intersperse (str "," <> space) <$> mapM getInlines (filterChildren (named "member") e) segmentedList = do @@ -1234,10 +1234,10 @@ parseInline (Elem e) = "sect5" -> descendantContent "title" el "cmdsynopsis" -> descendantContent "command" el "funcsynopsis" -> descendantContent "function" el - _ -> T.pack $ qName (elName el) ++ "_title" + _ -> qName (elName el) <> "_title" where xrefLabel = attrValue "xreflabel" el - descendantContent name = maybe "???" (T.pack . strContent) + descendantContent name = maybe "???" strContent . filterElementName (\n -> qName n == name) -- | Extract a math equation from an element @@ -1258,7 +1258,7 @@ equation e constructor = mathMLEquations :: [Text] mathMLEquations = map writeTeX $ rights $ readMath (\x -> qName (elName x) == "math" && qPrefix (elName x) == Just "mml") - (readMathML . T.pack . showElement) + (readMathML . showElement) latexEquations :: [Text] latexEquations = readMath (\x -> qName (elName x) == "mathphrase") @@ -1272,8 +1272,8 @@ equation e constructor = -- | Get the actual text stored in a CData block. 'showContent' -- returns the text still surrounded by the [[CDATA]] tags. showVerbatimCData :: Content -> Text -showVerbatimCData (Text (CData _ d _)) = T.pack d -showVerbatimCData c = T.pack $ showContent c +showVerbatimCData (Text (CData _ d _)) = d +showVerbatimCData c = showContent c -- | Set the prefix of a name to 'Nothing' diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 056dab6c2..c76f3c171 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -63,6 +63,7 @@ import Data.Char (chr, ord, readLitChar) import Data.List import qualified Data.Map as M import qualified Data.Text as T +import Data.Text (Text) import Data.Maybe import System.FilePath import Text.Pandoc.Readers.Docx.Util @@ -72,9 +73,7 @@ import qualified Text.Pandoc.UTF8 as UTF8 import Text.TeXMath (Exp) import Text.TeXMath.Readers.OMML (readOMML) import Text.TeXMath.Unicode.Fonts (Font (..), getUnicode, textToFont) -import Text.XML.Light -import qualified Text.XML.Light.Cursor as XMLC -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light data ReaderEnv = ReaderEnv { envNotes :: Notes , envComments :: Comments @@ -128,37 +127,23 @@ mapD f xs = in concatMapM handler xs -unwrap :: NameSpaces -> Content -> [Content] -unwrap ns (Elem element) +unwrapElement :: NameSpaces -> Element -> [Element] +unwrapElement ns element | isElem ns "w" "sdt" element , Just sdtContent <- findChildByName ns "w" "sdtContent" element - = concatMap (unwrap ns . Elem) (elChildren sdtContent) + = concatMap (unwrapElement ns) (elChildren sdtContent) | isElem ns "w" "smartTag" element - = concatMap (unwrap ns . Elem) (elChildren element) -unwrap _ content = [content] + = concatMap (unwrapElement ns) (elChildren element) + | otherwise + = [element{ elContent = concatMap (unwrapContent ns) (elContent element) }] -unwrapChild :: NameSpaces -> Content -> Content -unwrapChild ns (Elem element) = - Elem $ element { elContent = concatMap (unwrap ns) (elContent element) } -unwrapChild _ content = content +unwrapContent :: NameSpaces -> Content -> [Content] +unwrapContent ns (Elem element) = map Elem $ unwrapElement ns element +unwrapContent _ content = [content] -walkDocument' :: NameSpaces -> XMLC.Cursor -> XMLC.Cursor -walkDocument' ns cur = - let modifiedCur = XMLC.modifyContent (unwrapChild ns) cur - in - case XMLC.nextDF modifiedCur of - Just cur' -> walkDocument' ns cur' - Nothing -> XMLC.root modifiedCur - -walkDocument :: NameSpaces -> Element -> Maybe Element +walkDocument :: NameSpaces -> Element -> Element walkDocument ns element = - let cur = XMLC.fromContent (Elem element) - cur' = walkDocument' ns cur - in - case XMLC.toTree cur' of - Elem element' -> Just element' - _ -> Nothing - + element{ elContent = concatMap (unwrapContent ns) (elContent element) } newtype Docx = Docx Document deriving Show @@ -361,9 +346,9 @@ getDocumentXmlPath zf = do fp <- findAttr (QName "Target" Nothing Nothing) rel -- sometimes there will be a leading slash, which windows seems to -- have trouble with. - return $ case fp of + return $ case T.unpack fp of '/' : fp' -> fp' - _ -> fp + fp' -> fp' archiveToDocument :: Archive -> D Document archiveToDocument zf = do @@ -372,7 +357,7 @@ archiveToDocument zf = do docElem <- maybeToD $ parseXMLFromEntry entry let namespaces = elemToNameSpaces docElem bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem - let bodyElem' = fromMaybe bodyElem (walkDocument namespaces bodyElem) + let bodyElem' = walkDocument namespaces bodyElem body <- elemToBody namespaces bodyElem' return $ Document namespaces body @@ -414,8 +399,8 @@ archiveToNotes zf = fn_namespaces = maybe [] elemToNameSpaces fnElem en_namespaces = maybe [] elemToNameSpaces enElem ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces - fn = fnElem >>= walkDocument ns >>= elemToNotes ns "footnote" - en = enElem >>= walkDocument ns >>= elemToNotes ns "endnote" + fn = fnElem >>= elemToNotes ns "footnote" . walkDocument ns + en = enElem >>= elemToNotes ns "endnote" . walkDocument ns in Notes ns fn en @@ -424,7 +409,8 @@ archiveToComments zf = let cmtsElem = findEntryByPath "word/comments.xml" zf >>= parseXMLFromEntry cmts_namespaces = maybe [] elemToNameSpaces cmtsElem - cmts = elemToComments cmts_namespaces <$> (cmtsElem >>= walkDocument cmts_namespaces) + cmts = elemToComments cmts_namespaces . walkDocument cmts_namespaces <$> + cmtsElem in case cmts of Just c -> Comments cmts_namespaces c @@ -443,8 +429,8 @@ filePathToRelType path docXmlPath = relElemToRelationship :: DocumentLocation -> Element -> Maybe Relationship relElemToRelationship relType element | qName (elName element) == "Relationship" = do - relId <- findAttrText (QName "Id" Nothing Nothing) element - target <- findAttrText (QName "Target" Nothing Nothing) element + relId <- findAttr (QName "Id" Nothing Nothing) element + target <- findAttr (QName "Target" Nothing Nothing) element return $ Relationship relType relId target relElemToRelationship _ _ = Nothing @@ -485,10 +471,10 @@ lookupLevel numId ilvl (Numbering _ numbs absNumbs) = do loElemToLevelOverride :: NameSpaces -> Element -> Maybe LevelOverride loElemToLevelOverride ns element | isElem ns "w" "lvlOverride" element = do - ilvl <- findAttrTextByName ns "w" "ilvl" element + ilvl <- findAttrByName ns "w" "ilvl" element let startOverride = findChildByName ns "w" "startOverride" element >>= findAttrByName ns "w" "val" - >>= (\s -> listToMaybe (map fst (reads s :: [(Integer, String)]))) + >>= stringToInteger lvl = findChildByName ns "w" "lvl" element >>= levelElemToLevel ns return $ LevelOverride ilvl startOverride lvl @@ -497,9 +483,9 @@ loElemToLevelOverride _ _ = Nothing numElemToNum :: NameSpaces -> Element -> Maybe Numb numElemToNum ns element | isElem ns "w" "num" element = do - numId <- findAttrTextByName ns "w" "numId" element + numId <- findAttrByName ns "w" "numId" element absNumId <- findChildByName ns "w" "abstractNumId" element - >>= findAttrTextByName ns "w" "val" + >>= findAttrByName ns "w" "val" let lvlOverrides = mapMaybe (loElemToLevelOverride ns) (findChildrenByName ns "w" "lvlOverride" element) @@ -509,7 +495,7 @@ numElemToNum _ _ = Nothing absNumElemToAbsNum :: NameSpaces -> Element -> Maybe AbstractNumb absNumElemToAbsNum ns element | isElem ns "w" "abstractNum" element = do - absNumId <- findAttrTextByName ns "w" "abstractNumId" element + absNumId <- findAttrByName ns "w" "abstractNumId" element let levelElems = findChildrenByName ns "w" "lvl" element levels = mapMaybe (levelElemToLevel ns) levelElems return $ AbstractNumb absNumId levels @@ -518,14 +504,14 @@ absNumElemToAbsNum _ _ = Nothing levelElemToLevel :: NameSpaces -> Element -> Maybe Level levelElemToLevel ns element | isElem ns "w" "lvl" element = do - ilvl <- findAttrTextByName ns "w" "ilvl" element + ilvl <- findAttrByName ns "w" "ilvl" element fmt <- findChildByName ns "w" "numFmt" element - >>= findAttrTextByName ns "w" "val" + >>= findAttrByName ns "w" "val" txt <- findChildByName ns "w" "lvlText" element - >>= findAttrTextByName ns "w" "val" + >>= findAttrByName ns "w" "val" let start = findChildByName ns "w" "start" element >>= findAttrByName ns "w" "val" - >>= (\s -> listToMaybe (map fst (reads s :: [(Integer, String)]))) + >>= stringToInteger return (Level ilvl fmt txt start) levelElemToLevel _ _ = Nothing @@ -546,11 +532,11 @@ archiveToNumbering :: Archive -> Numbering archiveToNumbering archive = fromMaybe (Numbering [] [] []) (archiveToNumbering' archive) -elemToNotes :: NameSpaces -> String -> Element -> Maybe (M.Map T.Text Element) +elemToNotes :: NameSpaces -> Text -> Element -> Maybe (M.Map T.Text Element) elemToNotes ns notetype element | isElem ns "w" (notetype <> "s") element = let pairs = mapMaybe - (\e -> findAttrTextByName ns "w" "id" e >>= + (\e -> findAttrByName ns "w" "id" e >>= (\a -> Just (a, e))) (findChildrenByName ns "w" notetype element) in @@ -562,7 +548,7 @@ elemToComments :: NameSpaces -> Element -> M.Map T.Text Element elemToComments ns element | isElem ns "w" "comments" element = let pairs = mapMaybe - (\e -> findAttrTextByName ns "w" "id" e >>= + (\e -> findAttrByName ns "w" "id" e >>= (\a -> Just (a, e))) (findChildrenByName ns "w" "comment" element) in @@ -622,12 +608,12 @@ elemToParIndentation ns element | isElem ns "w" "ind" element = stringToInteger , hangingParIndent = findAttrByName ns "w" "hanging" element >>= - stringToInteger} + stringToInteger } elemToParIndentation _ _ = Nothing -testBitMask :: String -> Int -> Bool +testBitMask :: Text -> Int -> Bool testBitMask bitMaskS n = - case (reads ("0x" ++ bitMaskS) :: [(Int, String)]) of + case (reads ("0x" ++ T.unpack bitMaskS) :: [(Int, String)]) of [] -> False ((n', _) : _) -> (n' .|. n) /= 0 @@ -642,7 +628,7 @@ elemToBodyPart ns element | isElem ns "w" "p" element , (c:_) <- findChildrenByName ns "m" "oMathPara" element = do - expsLst <- eitherToD $ readOMML $ T.pack $ showElement c + expsLst <- eitherToD $ readOMML $ showElement c return $ OMathPara expsLst elemToBodyPart ns element | isElem ns "w" "p" element @@ -666,7 +652,7 @@ elemToBodyPart ns element | isElem ns "w" "tbl" element = do let caption' = findChildByName ns "w" "tblPr" element >>= findChildByName ns "w" "tblCaption" - >>= findAttrTextByName ns "w" "val" + >>= findAttrByName ns "w" "val" caption = fromMaybe "" caption' grid' = case findChildByName ns "w" "tblGrid" element of Just g -> elemToTblGrid ns g @@ -705,8 +691,8 @@ getTitleAndAlt :: NameSpaces -> Element -> (T.Text, T.Text) getTitleAndAlt ns element = let mbDocPr = findChildByName ns "wp" "inline" element >>= findChildByName ns "wp" "docPr" - title = fromMaybe "" (mbDocPr >>= findAttrTextByName ns "" "title") - alt = fromMaybe "" (mbDocPr >>= findAttrTextByName ns "" "descr") + title = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "title") + alt = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "descr") in (title, alt) elemToParPart :: NameSpaces -> Element -> D ParPart @@ -718,7 +704,7 @@ elemToParPart ns element = let (title, alt) = getTitleAndAlt ns drawingElem a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main" drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem - >>= findAttrTextByName ns "r" "embed" + >>= findAttrByName ns "r" "embed" in case drawing of Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem) @@ -728,7 +714,7 @@ elemToParPart ns element | isElem ns "w" "r" element , Just _ <- findChildByName ns "w" "pict" element = let drawing = findElement (elemName ns "v" "imagedata") element - >>= findAttrTextByName ns "r" "id" + >>= findAttrByName ns "r" "id" in case drawing of -- Todo: check out title and attr for deprecated format. @@ -797,7 +783,7 @@ elemToParPart ns element fldCharState <- gets stateFldCharState case fldCharState of FldCharOpen -> do - info <- eitherToD $ parseFieldInfo $ T.pack $ strContent instrText + info <- eitherToD $ parseFieldInfo $ strContent instrText modify $ \st -> st{stateFldCharState = FldCharFieldInfo info} return NullParPart _ -> return NullParPart @@ -818,48 +804,48 @@ elemToParPart ns element return $ ChangedRuns change runs elemToParPart ns element | isElem ns "w" "bookmarkStart" element - , Just bmId <- findAttrTextByName ns "w" "id" element - , Just bmName <- findAttrTextByName ns "w" "name" element = + , Just bmId <- findAttrByName ns "w" "id" element + , Just bmName <- findAttrByName ns "w" "name" element = return $ BookMark bmId bmName elemToParPart ns element | isElem ns "w" "hyperlink" element - , Just relId <- findAttrTextByName ns "r" "id" element = do + , Just relId <- findAttrByName ns "r" "id" element = do location <- asks envLocation runs <- mapD (elemToRun ns) (elChildren element) rels <- asks envRelationships case lookupRelationship location relId rels of Just target -> - case findAttrTextByName ns "w" "anchor" element of + case findAttrByName ns "w" "anchor" element of Just anchor -> return $ ExternalHyperLink (target <> "#" <> anchor) runs Nothing -> return $ ExternalHyperLink target runs Nothing -> return $ ExternalHyperLink "" runs elemToParPart ns element | isElem ns "w" "hyperlink" element - , Just anchor <- findAttrTextByName ns "w" "anchor" element = do + , Just anchor <- findAttrByName ns "w" "anchor" element = do runs <- mapD (elemToRun ns) (elChildren element) return $ InternalHyperLink anchor runs elemToParPart ns element | isElem ns "w" "commentRangeStart" element - , Just cmtId <- findAttrTextByName ns "w" "id" element = do + , Just cmtId <- findAttrByName ns "w" "id" element = do (Comments _ commentMap) <- asks envComments case M.lookup cmtId commentMap of Just cmtElem -> elemToCommentStart ns cmtElem Nothing -> throwError WrongElem elemToParPart ns element | isElem ns "w" "commentRangeEnd" element - , Just cmtId <- findAttrTextByName ns "w" "id" element = + , Just cmtId <- findAttrByName ns "w" "id" element = return $ CommentEnd cmtId elemToParPart ns element | isElem ns "m" "oMath" element = - fmap PlainOMath (eitherToD $ readOMML $ T.pack $ showElement element) + fmap PlainOMath (eitherToD $ readOMML $ showElement element) elemToParPart _ _ = throwError WrongElem elemToCommentStart :: NameSpaces -> Element -> D ParPart elemToCommentStart ns element | isElem ns "w" "comment" element - , Just cmtId <- findAttrTextByName ns "w" "id" element - , Just cmtAuthor <- findAttrTextByName ns "w" "author" element - , cmtDate <- findAttrTextByName ns "w" "date" element = do + , Just cmtId <- findAttrByName ns "w" "id" element + , Just cmtAuthor <- findAttrByName ns "w" "author" element + , cmtDate <- findAttrByName ns "w" "date" element = do bps <- mapD (elemToBodyPart ns) (elChildren element) return $ CommentStart cmtId cmtAuthor cmtDate bps elemToCommentStart _ _ = throwError WrongElem @@ -878,7 +864,7 @@ elemToExtent drawingElem = where wp_ns = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" getDim at = findElement (QName "extent" (Just wp_ns) (Just "wp")) drawingElem - >>= findAttr (QName at Nothing Nothing) >>= safeRead . T.pack + >>= findAttr (QName at Nothing Nothing) >>= safeRead childElemToRun :: NameSpaces -> Element -> D Run @@ -889,7 +875,7 @@ childElemToRun ns element = let (title, alt) = getTitleAndAlt ns element a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main" drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem - >>= findAttrText (QName "embed" (lookup "r" ns) (Just "r")) + >>= findAttr (QName "embed" (lookup "r" ns) (Just "r")) in case drawing of Just s -> expandDrawingId s >>= @@ -902,7 +888,7 @@ childElemToRun ns element = return InlineChart childElemToRun ns element | isElem ns "w" "footnoteReference" element - , Just fnId <- findAttrTextByName ns "w" "id" element = do + , Just fnId <- findAttrByName ns "w" "id" element = do notes <- asks envNotes case lookupFootnote fnId notes of Just e -> do bps <- local (\r -> r {envLocation=InFootnote}) $ mapD (elemToBodyPart ns) (elChildren e) @@ -910,7 +896,7 @@ childElemToRun ns element Nothing -> return $ Footnote [] childElemToRun ns element | isElem ns "w" "endnoteReference" element - , Just enId <- findAttrTextByName ns "w" "id" element = do + , Just enId <- findAttrByName ns "w" "id" element = do notes <- asks envNotes case lookupEndnote enId notes of Just e -> do bps <- local (\r -> r {envLocation=InEndnote}) $ mapD (elemToBodyPart ns) (elChildren e) @@ -963,15 +949,15 @@ getParStyleField _ _ = Nothing getTrackedChange :: NameSpaces -> Element -> Maybe TrackedChange getTrackedChange ns element | isElem ns "w" "ins" element || isElem ns "w" "moveTo" element - , Just cId <- findAttrTextByName ns "w" "id" element - , Just cAuthor <- findAttrTextByName ns "w" "author" element - , mcDate <- findAttrTextByName ns "w" "date" element = + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , mcDate <- findAttrByName ns "w" "date" element = Just $ TrackedChange Insertion (ChangeInfo cId cAuthor mcDate) getTrackedChange ns element | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element - , Just cId <- findAttrTextByName ns "w" "id" element - , Just cAuthor <- findAttrTextByName ns "w" "author" element - , mcDate <- findAttrTextByName ns "w" "date" element = + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , mcDate <- findAttrByName ns "w" "date" element = Just $ TrackedChange Deletion (ChangeInfo cId cAuthor mcDate) getTrackedChange _ _ = Nothing @@ -980,7 +966,7 @@ elemToParagraphStyle ns element sty | Just pPr <- findChildByName ns "w" "pPr" element = let style = mapMaybe - (fmap ParaStyleId . findAttrTextByName ns "w" "val") + (fmap ParaStyleId . findAttrByName ns "w" "val") (findChildrenByName ns "w" "pStyle" pPr) in ParagraphStyle {pStyle = mapMaybe (`M.lookup` sty) style @@ -1012,7 +998,7 @@ elemToRunStyleD ns element charStyles <- asks envCharStyles let parentSty = findChildByName ns "w" "rStyle" rPr >>= - findAttrTextByName ns "w" "val" >>= + findAttrByName ns "w" "val" >>= flip M.lookup charStyles . CharStyleId return $ elemToRunStyle ns element parentSty elemToRunStyleD _ _ = return defaultRunStyle @@ -1022,7 +1008,7 @@ elemToRunElem ns element | isElem ns "w" "t" element || isElem ns "w" "delText" element || isElem ns "m" "t" element = do - let str = T.pack $ strContent element + let str = strContent element font <- asks envFont case font of Nothing -> return $ TextRun str @@ -1044,14 +1030,14 @@ getSymChar :: NameSpaces -> Element -> RunElem getSymChar ns element | Just s <- lowerFromPrivate <$> getCodepoint , Just font <- getFont = - case readLitChar ("\\x" ++ s) of + case readLitChar ("\\x" ++ T.unpack s) of [(char, _)] -> TextRun . maybe "" T.singleton $ getUnicode font char _ -> TextRun "" where getCodepoint = findAttrByName ns "w" "char" element - getFont = textToFont . T.pack =<< findAttrByName ns "w" "font" element - lowerFromPrivate ('F':xs) = '0':xs - lowerFromPrivate xs = xs + getFont = textToFont =<< findAttrByName ns "w" "font" element + lowerFromPrivate t | "F" `T.isPrefixOf` t = "0" <> T.drop 1 t + | otherwise = t getSymChar _ _ = TextRun "" elemToRunElems :: NameSpaces -> Element -> D [RunElem] @@ -1061,8 +1047,9 @@ elemToRunElems ns element let qualName = elemName ns "w" let font = do fontElem <- findElement (qualName "rFonts") element - textToFont . T.pack =<< - foldr ((<|>) . (flip findAttr fontElem . qualName)) Nothing ["ascii", "hAnsi"] + textToFont =<< + foldr ((<|>) . (flip findAttr fontElem . qualName)) + Nothing ["ascii", "hAnsi"] local (setFont font) (mapD (elemToRunElem ns) (elChildren element)) elemToRunElems _ _ = throwError WrongElem diff --git a/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs b/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs index edade8654..0d7271d6a 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse/Styles.hs @@ -48,12 +48,13 @@ import Data.Function (on) import Data.String (IsString(..)) import qualified Data.Map as M import qualified Data.Text as T +import qualified Data.Text.Read +import Data.Text (Text) import Data.Maybe import Data.Coerce import Text.Pandoc.Readers.Docx.Util import qualified Text.Pandoc.UTF8 as UTF8 -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light newtype CharStyleId = CharStyleId T.Text deriving (Show, Eq, Ord, IsString, FromStyleId) @@ -109,7 +110,7 @@ data RunStyle = RunStyle { isBold :: Maybe Bool , isRTL :: Maybe Bool , isForceCTL :: Maybe Bool , rVertAlign :: Maybe VertAlign - , rUnderline :: Maybe String + , rUnderline :: Maybe Text , rParentStyle :: Maybe CharStyle } deriving Show @@ -159,7 +160,7 @@ isBasedOnStyle ns element parentStyle , Just styleType <- findAttrByName ns "w" "type" element , styleType == cStyleType parentStyle , Just basedOnVal <- findChildByName ns "w" "basedOn" element >>= - findAttrTextByName ns "w" "val" + findAttrByName ns "w" "val" , Just ps <- parentStyle = basedOnVal == fromStyleId (getStyleId ps) | isElem ns "w" "style" element , Just styleType <- findAttrByName ns "w" "type" element @@ -169,7 +170,7 @@ isBasedOnStyle ns element parentStyle | otherwise = False class HasStyleId a => ElemToStyle a where - cStyleType :: Maybe a -> String + cStyleType :: Maybe a -> Text elemToStyle :: NameSpaces -> Element -> Maybe a -> Maybe a class FromStyleId (StyleId a) => HasStyleId a where @@ -226,8 +227,10 @@ buildBasedOnList ns element rootStyle = stys -> stys ++ concatMap (buildBasedOnList ns element . Just) stys -stringToInteger :: String -> Maybe Integer -stringToInteger s = listToMaybe $ map fst (reads s :: [(Integer, String)]) +stringToInteger :: Text -> Maybe Integer +stringToInteger s = case Data.Text.Read.decimal s of + Right (x,_) -> Just x + Left _ -> Nothing checkOnOff :: NameSpaces -> Element -> QName -> Maybe Bool checkOnOff ns rPr tag @@ -247,7 +250,7 @@ checkOnOff _ _ _ = Nothing elemToCharStyle :: NameSpaces -> Element -> Maybe CharStyle -> Maybe CharStyle elemToCharStyle ns element parentStyle - = CharStyle <$> (CharStyleId <$> findAttrTextByName ns "w" "styleId" element) + = CharStyle <$> (CharStyleId <$> findAttrByName ns "w" "styleId" element) <*> getElementStyleName ns element <*> Just (elemToRunStyle ns element parentStyle) @@ -281,7 +284,7 @@ elemToRunStyle _ _ _ = defaultRunStyle getHeaderLevel :: NameSpaces -> Element -> Maybe (ParaStyleName, Int) getHeaderLevel ns element | Just styleName <- getElementStyleName ns element - , Just n <- stringToInteger . T.unpack =<< + , Just n <- stringToInteger =<< (T.stripPrefix "heading " . T.toLower $ fromStyleName styleName) , n > 0 = Just (styleName, fromInteger n) @@ -289,8 +292,8 @@ getHeaderLevel _ _ = Nothing getElementStyleName :: Coercible T.Text a => NameSpaces -> Element -> Maybe a getElementStyleName ns el = coerce <$> - ((findChildByName ns "w" "name" el >>= findAttrTextByName ns "w" "val") - <|> findAttrTextByName ns "w" "styleId" el) + ((findChildByName ns "w" "name" el >>= findAttrByName ns "w" "val") + <|> findAttrByName ns "w" "styleId" el) getNumInfo :: NameSpaces -> Element -> Maybe (T.Text, T.Text) getNumInfo ns element = do @@ -298,15 +301,15 @@ getNumInfo ns element = do findChildByName ns "w" "numPr" lvl = fromMaybe "0" (numPr >>= findChildByName ns "w" "ilvl" >>= - findAttrTextByName ns "w" "val") + findAttrByName ns "w" "val") numId <- numPr >>= findChildByName ns "w" "numId" >>= - findAttrTextByName ns "w" "val" + findAttrByName ns "w" "val" return (numId, lvl) elemToParStyleData :: NameSpaces -> Element -> Maybe ParStyle -> Maybe ParStyle elemToParStyleData ns element parentStyle - | Just styleId <- findAttrTextByName ns "w" "styleId" element + | Just styleId <- findAttrByName ns "w" "styleId" element , Just styleName <- getElementStyleName ns element = Just $ ParStyle { diff --git a/src/Text/Pandoc/Readers/Docx/Util.hs b/src/Text/Pandoc/Readers/Docx/Util.hs index f9c9a8e26..21df03d9e 100644 --- a/src/Text/Pandoc/Readers/Docx/Util.hs +++ b/src/Text/Pandoc/Readers/Docx/Util.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Docx.StyleMaps Copyright : © 2014-2020 Jesse Rosenthal , @@ -18,51 +19,45 @@ module Text.Pandoc.Readers.Docx.Util ( , elemToNameSpaces , findChildByName , findChildrenByName - , findAttrText , findAttrByName - , findAttrTextByName ) where import Data.Maybe (mapMaybe) import qualified Data.Text as T -import Text.XML.Light +import Data.Text (Text) +import Text.Pandoc.XML.Light -type NameSpaces = [(String, String)] +type NameSpaces = [(Text, Text)] elemToNameSpaces :: Element -> NameSpaces elemToNameSpaces = mapMaybe attrToNSPair . elAttribs -attrToNSPair :: Attr -> Maybe (String, String) +attrToNSPair :: Attr -> Maybe (Text, Text) attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val) attrToNSPair _ = Nothing -elemName :: NameSpaces -> String -> String -> QName +elemName :: NameSpaces -> Text -> Text -> QName elemName ns prefix name = - QName name (lookup prefix ns) (if null prefix then Nothing else Just prefix) + QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix) -isElem :: NameSpaces -> String -> String -> Element -> Bool +isElem :: NameSpaces -> Text -> Text -> Element -> Bool isElem ns prefix name element = let ns' = ns ++ elemToNameSpaces element in qName (elName element) == name && qURI (elName element) == lookup prefix ns' -findChildByName :: NameSpaces -> String -> String -> Element -> Maybe Element +findChildByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element findChildByName ns pref name el = let ns' = ns ++ elemToNameSpaces el in findChild (elemName ns' pref name) el -findChildrenByName :: NameSpaces -> String -> String -> Element -> [Element] +findChildrenByName :: NameSpaces -> Text -> Text -> Element -> [Element] findChildrenByName ns pref name el = let ns' = ns ++ elemToNameSpaces el in findChildren (elemName ns' pref name) el -findAttrText :: QName -> Element -> Maybe T.Text -findAttrText x = fmap T.pack . findAttr x - -findAttrByName :: NameSpaces -> String -> String -> Element -> Maybe String +findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text findAttrByName ns pref name el = let ns' = ns ++ elemToNameSpaces el in findAttr (elemName ns' pref name) el -findAttrTextByName :: NameSpaces -> String -> String -> Element -> Maybe T.Text -findAttrTextByName a b c = fmap T.pack . findAttrByName a b c diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index 369c4f0c9..eb8d2405d 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -23,8 +23,8 @@ import Control.DeepSeq (NFData, deepseq) import Control.Monad (guard, liftM, liftM2, mplus) import Control.Monad.Except (throwError) import qualified Data.ByteString.Lazy as BL (ByteString) -import Data.List (isInfixOf) import qualified Data.Text as T +import Data.Text (Text) import qualified Data.Map as M (Map, elems, fromList, lookup) import Data.Maybe (mapMaybe) import qualified Data.Text.Lazy as TL @@ -40,13 +40,12 @@ import Text.Pandoc.Extensions (Extension (Ext_raw_html), enableExtension) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Readers.HTML (readHtml) -import Text.Pandoc.Shared (addMetaField, collapseFilePath, escapeURI) +import Text.Pandoc.Shared (addMetaField, collapseFilePath, escapeURI, tshow) import qualified Text.Pandoc.UTF8 as UTF8 (toTextLazy) import Text.Pandoc.Walk (query, walk) -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light -type Items = M.Map String (FilePath, MimeType) +type Items = M.Map Text (FilePath, MimeType) readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc readEPUB opts bytes = case toArchiveOrFail bytes of @@ -126,26 +125,27 @@ imageToPandoc s = B.doc . B.para $ B.image (T.pack s) "" mempty imageMimes :: [MimeType] imageMimes = ["image/gif", "image/jpeg", "image/png"] -type CoverId = String +type CoverId = Text type CoverImage = FilePath -parseManifest :: (PandocMonad m) => Element -> Maybe CoverId -> m (Maybe CoverImage, Items) +parseManifest :: (PandocMonad m) + => Element -> Maybe CoverId -> m (Maybe CoverImage, Items) parseManifest content coverId = do manifest <- findElementE (dfName "manifest") content let items = findChildren (dfName "item") manifest r <- mapM parseItem items let cover = findAttr (emptyName "href") =<< filterChild findCover manifest - return (cover `mplus` coverId, M.fromList r) + return (T.unpack <$> (cover `mplus` coverId), M.fromList r) where - findCover e = maybe False (isInfixOf "cover-image") + findCover e = maybe False (T.isInfixOf "cover-image") (findAttr (emptyName "properties") e) || Just True == liftM2 (==) coverId (findAttr (emptyName "id") e) parseItem e = do uid <- findAttrE (emptyName "id") e href <- findAttrE (emptyName "href") e mime <- findAttrE (emptyName "media-type") e - return (uid, (href, T.pack mime)) + return (uid, (T.unpack href, mime)) parseSpine :: PandocMonad m => Items -> Element -> m [(FilePath, MimeType)] parseSpine is e = do @@ -173,11 +173,11 @@ parseMeta content = do -- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-metadata-elem parseMetaItem :: Element -> Meta -> Meta parseMetaItem e@(stripNamespace . elName -> field) meta = - addMetaField (renameMeta field) (B.str $ T.pack $ strContent e) meta + addMetaField (renameMeta field) (B.str $ strContent e) meta -renameMeta :: String -> T.Text +renameMeta :: Text -> Text renameMeta "creator" = "author" -renameMeta s = T.pack s +renameMeta s = s getManifest :: PandocMonad m => Archive -> m (String, Element) getManifest archive = do @@ -187,7 +187,7 @@ getManifest archive = do ns <- mkE "xmlns not in namespaces" (lookup "xmlns" namespaces) as <- fmap (map attrToPair . elAttribs) (findElementE (QName "rootfile" (Just ns) Nothing) docElem) - manifestFile <- mkE "Root not found" (lookup "full-path" as) + manifestFile <- T.unpack <$> mkE "Root not found" (lookup "full-path" as) let rootdir = dropFileName manifestFile --mime <- lookup "media-type" as manifest <- findEntryByPathE manifestFile archive @@ -201,7 +201,8 @@ fixInternalReferences pathToFile = . walk (fixBlockIRs filename) . walk (fixInlineIRs filename) where - (root, T.unpack . escapeURI . T.pack -> filename) = splitFileName pathToFile + (root, T.unpack . escapeURI . T.pack -> filename) = + splitFileName pathToFile fixInlineIRs :: String -> Inline -> Inline fixInlineIRs s (Span as v) = @@ -214,7 +215,7 @@ fixInlineIRs s (Link as is t) = Link (fixAttrs s as) is t fixInlineIRs _ v = v -prependHash :: [T.Text] -> Inline -> Inline +prependHash :: [Text] -> Inline -> Inline prependHash ps l@(Link attr is (url, tit)) | or [s `T.isPrefixOf` url | s <- ps] = Link attr is ("#" <> url, tit) @@ -231,16 +232,17 @@ fixBlockIRs s (CodeBlock as code) = fixBlockIRs _ b = b fixAttrs :: FilePath -> B.Attr -> B.Attr -fixAttrs s (ident, cs, kvs) = (addHash s ident, filter (not . T.null) cs, removeEPUBAttrs kvs) +fixAttrs s (ident, cs, kvs) = + (addHash s ident, filter (not . T.null) cs, removeEPUBAttrs kvs) -addHash :: String -> T.Text -> T.Text +addHash :: FilePath -> Text -> Text addHash _ "" = "" addHash s ident = T.pack (takeFileName s) <> "#" <> ident -removeEPUBAttrs :: [(T.Text, T.Text)] -> [(T.Text, T.Text)] +removeEPUBAttrs :: [(Text, Text)] -> [(Text, Text)] removeEPUBAttrs kvs = filter (not . isEPUBAttr) kvs -isEPUBAttr :: (T.Text, a) -> Bool +isEPUBAttr :: (Text, a) -> Bool isEPUBAttr (k, _) = "epub:" `T.isPrefixOf` k -- Library @@ -257,33 +259,33 @@ uncurry3 f (a, b, c) = f a b c -- Utility -stripNamespace :: QName -> String +stripNamespace :: QName -> Text stripNamespace (QName v _ _) = v -attrToNSPair :: Attr -> Maybe (String, String) +attrToNSPair :: Attr -> Maybe (Text, Text) attrToNSPair (Attr (QName "xmlns" _ _) val) = Just ("xmlns", val) attrToNSPair _ = Nothing -attrToPair :: Attr -> (String, String) +attrToPair :: Attr -> (Text, Text) attrToPair (Attr (QName name _ _) val) = (name, val) -defaultNameSpace :: Maybe String +defaultNameSpace :: Maybe Text defaultNameSpace = Just "http://www.idpf.org/2007/opf" -dfName :: String -> QName +dfName :: Text -> QName dfName s = QName s defaultNameSpace Nothing -emptyName :: String -> QName +emptyName :: Text -> QName emptyName s = QName s Nothing Nothing -- Convert Maybe interface to Either -findAttrE :: PandocMonad m => QName -> Element -> m String +findAttrE :: PandocMonad m => QName -> Element -> m Text findAttrE q e = mkE "findAttr" $ findAttr q e findEntryByPathE :: PandocMonad m => FilePath -> Archive -> m Entry findEntryByPathE (normalise . unEscapeString -> path) a = - mkE ("No entry on path: " ++ path) $ findEntryByPath path a + mkE ("No entry on path: " <> T.pack path) $ findEntryByPath path a parseXMLDocE :: PandocMonad m => Entry -> m Element parseXMLDocE entry = @@ -293,7 +295,8 @@ parseXMLDocE entry = fp = T.pack $ eRelativePath entry findElementE :: PandocMonad m => QName -> Element -> m Element -findElementE e x = mkE ("Unable to find element: " ++ show e) $ findElement e x +findElementE e x = + mkE ("Unable to find element: " <> tshow e) $ findElement e x -mkE :: PandocMonad m => String -> Maybe a -> m a -mkE s = maybe (throwError . PandocParseError $ T.pack s) return +mkE :: PandocMonad m => Text -> Maybe a -> m a +mkE s = maybe (throwError . PandocParseError $ s) return diff --git a/src/Text/Pandoc/Readers/FB2.hs b/src/Text/Pandoc/Readers/FB2.hs index b804eab4f..66e390bd7 100644 --- a/src/Text/Pandoc/Readers/FB2.hs +++ b/src/Text/Pandoc/Readers/FB2.hs @@ -25,7 +25,6 @@ TODO: module Text.Pandoc.Readers.FB2 ( readFB2 ) where import Control.Monad.Except (throwError) import Control.Monad.State.Strict -import Data.ByteString.Lazy.Char8 ( pack ) import Data.ByteString.Base64.Lazy import Data.Functor import Data.List (intersperse) @@ -42,8 +41,8 @@ import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared (crFilter) -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light +import qualified Text.Pandoc.UTF8 as UTF8 type FB2 m = StateT FB2State m @@ -85,12 +84,12 @@ removeHash t = case T.uncons t of Just ('#', xs) -> xs _ -> t -convertEntity :: String -> Text -convertEntity e = maybe (T.toUpper $ T.pack e) T.pack $ lookupEntity e +convertEntity :: Text -> Text +convertEntity e = maybe (T.toUpper e) T.pack $ lookupEntity (T.unpack e) parseInline :: PandocMonad m => Content -> FB2 m Inlines parseInline (Elem e) = - case T.pack $ qName $ elName e of + case qName $ elName e of "strong" -> strong <$> parseStyleType e "emphasis" -> emph <$> parseStyleType e "style" -> parseNamedStyle e @@ -98,12 +97,12 @@ parseInline (Elem e) = "strikethrough" -> strikeout <$> parseStyleType e "sub" -> subscript <$> parseStyleType e "sup" -> superscript <$> parseStyleType e - "code" -> pure $ code $ T.pack $ strContent e + "code" -> pure $ code $ strContent e "image" -> parseInlineImageElement e name -> do report $ IgnoredElement name pure mempty -parseInline (Text x) = pure $ text $ T.pack $ cdData x +parseInline (Text x) = pure $ text $ cdData x parseInline (CRef r) = pure $ str $ convertEntity r parseSubtitle :: PandocMonad m => Element -> FB2 m Blocks @@ -113,7 +112,7 @@ parseSubtitle e = headerWith ("", ["unnumbered"], []) <$> gets fb2SectionLevel < parseRootElement :: PandocMonad m => Element -> FB2 m Blocks parseRootElement e = - case T.pack $ qName $ elName e of + case qName $ elName e of "FictionBook" -> do -- Parse notes before parsing the rest of the content. case filterChild isNotesBody e of @@ -146,7 +145,7 @@ parseNote e = Just sectionId -> do content <- mconcat <$> mapM parseSectionChild (dropTitle $ elChildren e) oldNotes <- gets fb2Notes - modify $ \s -> s { fb2Notes = M.insert ("#" <> T.pack sectionId) content oldNotes } + modify $ \s -> s { fb2Notes = M.insert ("#" <> sectionId) content oldNotes } pure () where isTitle x = qName (elName x) == "title" @@ -158,7 +157,7 @@ parseNote e = -- | Parse a child of @\@ element. parseFictionBookChild :: PandocMonad m => Element -> FB2 m Blocks parseFictionBookChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "stylesheet" -> pure mempty -- stylesheet is ignored "description" -> mempty <$ mapM_ parseDescriptionChild (elChildren e) "body" -> if isNotesBody e @@ -170,7 +169,7 @@ parseFictionBookChild e = -- | Parse a child of @\@ element. parseDescriptionChild :: PandocMonad m => Element -> FB2 m () parseDescriptionChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "title-info" -> mapM_ parseTitleInfoChild (elChildren e) "src-title-info" -> pure () -- ignore "document-info" -> pure () @@ -184,7 +183,7 @@ parseDescriptionChild e = -- | Parse a child of @\@ element. parseBodyChild :: PandocMonad m => Element -> FB2 m Blocks parseBodyChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "image" -> parseImageElement e "title" -> header <$> gets fb2SectionLevel <*> parseTitleType (elContent e) "epigraph" -> parseEpigraph e @@ -198,7 +197,10 @@ parseBinaryElement e = (Nothing, _) -> report $ IgnoredElement "binary without id attribute" (Just _, Nothing) -> report $ IgnoredElement "binary without content-type attribute" - (Just filename, contentType) -> insertMedia filename (T.pack <$> contentType) (decodeLenient (pack (strContent e))) + (Just filename, contentType) -> + insertMedia (T.unpack filename) contentType + (decodeLenient + (UTF8.fromTextLazy . TL.fromStrict . strContent $ e)) -- * Type parsers @@ -208,13 +210,13 @@ parseAuthor e = T.unwords . catMaybes <$> mapM parseAuthorChild (elChildren e) parseAuthorChild :: PandocMonad m => Element -> FB2 m (Maybe Text) parseAuthorChild e = - case T.pack $ qName $ elName e of - "first-name" -> pure $ Just $ T.pack $ strContent e - "middle-name" -> pure $ Just $ T.pack $ strContent e - "last-name" -> pure $ Just $ T.pack $ strContent e - "nickname" -> pure $ Just $ T.pack $ strContent e - "home-page" -> pure $ Just $ T.pack $ strContent e - "email" -> pure $ Just $ T.pack $ strContent e + case qName $ elName e of + "first-name" -> pure $ Just $ strContent e + "middle-name" -> pure $ Just $ strContent e + "last-name" -> pure $ Just $ strContent e + "nickname" -> pure $ Just $ strContent e + "home-page" -> pure $ Just $ strContent e + "email" -> pure $ Just $ strContent e name -> do report $ IgnoredElement $ name <> " in author" pure Nothing @@ -238,13 +240,13 @@ parseTitleContent _ = pure Nothing parseImageElement :: PandocMonad m => Element -> FB2 m Blocks parseImageElement e = case href of - Just src -> pure $ para $ imageWith (imgId, [], []) (removeHash $ T.pack src) title alt + Just src -> pure $ para $ imageWith (imgId, [], []) (removeHash src) title alt Nothing -> do report $ IgnoredElement " image without href" pure mempty - where alt = maybe mempty (str . T.pack) $ findAttr (unqual "alt") e - title = maybe "" T.pack $ findAttr (unqual "title") e - imgId = maybe "" T.pack $ findAttr (unqual "id") e + where alt = maybe mempty str $ findAttr (unqual "alt") e + title = fromMaybe "" $ findAttr (unqual "title") e + imgId = fromMaybe "" $ findAttr (unqual "id") e href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e -- | Parse @pType@ @@ -258,7 +260,7 @@ parseCite e = blockQuote . mconcat <$> mapM parseCiteChild (elChildren e) -- | Parse @citeType@ child parseCiteChild :: PandocMonad m => Element -> FB2 m Blocks parseCiteChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "p" -> para <$> parsePType e "poem" -> parsePoem e "empty-line" -> pure horizontalRule @@ -273,13 +275,13 @@ parsePoem e = mconcat <$> mapM parsePoemChild (elChildren e) parsePoemChild :: PandocMonad m => Element -> FB2 m Blocks parsePoemChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "title" -> parseTitle e "subtitle" -> parseSubtitle e "epigraph" -> parseEpigraph e "stanza" -> parseStanza e "text-author" -> para <$> parsePType e - "date" -> pure $ para $ text $ T.pack $ strContent e + "date" -> pure $ para $ text $ strContent e name -> report (UnexpectedXmlElement name "poem") $> mempty parseStanza :: PandocMonad m => Element -> FB2 m Blocks @@ -292,7 +294,7 @@ joinLineBlocks [] = [] parseStanzaChild :: PandocMonad m => Element -> FB2 m Blocks parseStanzaChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "title" -> parseTitle e "subtitle" -> parseSubtitle e "v" -> lineBlock . (:[]) <$> parsePType e @@ -302,11 +304,11 @@ parseStanzaChild e = parseEpigraph :: PandocMonad m => Element -> FB2 m Blocks parseEpigraph e = divWith (divId, ["epigraph"], []) . mconcat <$> mapM parseEpigraphChild (elChildren e) - where divId = maybe "" T.pack $ findAttr (unqual "id") e + where divId = fromMaybe "" $ findAttr (unqual "id") e parseEpigraphChild :: PandocMonad m => Element -> FB2 m Blocks parseEpigraphChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "p" -> para <$> parsePType e "poem" -> parsePoem e "cite" -> parseCite e @@ -320,7 +322,7 @@ parseAnnotation e = mconcat <$> mapM parseAnnotationChild (elChildren e) parseAnnotationChild :: PandocMonad m => Element -> FB2 m Blocks parseAnnotationChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "p" -> para <$> parsePType e "poem" -> parsePoem e "cite" -> parseCite e @@ -334,14 +336,14 @@ parseSection :: PandocMonad m => Element -> FB2 m Blocks parseSection e = do n <- gets fb2SectionLevel modify $ \st -> st{ fb2SectionLevel = n + 1 } - let sectionId = maybe "" T.pack $ findAttr (unqual "id") e + let sectionId = fromMaybe "" $ findAttr (unqual "id") e bs <- divWith (sectionId, ["section"], []) . mconcat <$> mapM parseSectionChild (elChildren e) modify $ \st -> st{ fb2SectionLevel = n } pure bs parseSectionChild :: PandocMonad m => Element -> FB2 m Blocks parseSectionChild e = - case T.pack $ qName $ elName e of + case qName $ elName e of "title" -> parseBodyChild e "epigraph" -> parseEpigraph e "image" -> parseImageElement e @@ -363,16 +365,16 @@ parseStyleType e = mconcat <$> mapM parseInline (elContent e) parseNamedStyle :: PandocMonad m => Element -> FB2 m Inlines parseNamedStyle e = do content <- mconcat <$> mapM parseNamedStyleChild (elContent e) - let lang = maybeToList $ ("lang",) . T.pack <$> findAttr (QName "lang" Nothing (Just "xml")) e + let lang = maybeToList $ ("lang",) <$> findAttr (QName "lang" Nothing (Just "xml")) e case findAttr (unqual "name") e of - Just name -> pure $ spanWith ("", [T.pack name], lang) content + Just name -> pure $ spanWith ("", [name], lang) content Nothing -> do report $ IgnoredElement "link without required name" pure mempty parseNamedStyleChild :: PandocMonad m => Content -> FB2 m Inlines parseNamedStyleChild (Elem e) = - case T.pack $ qName (elName e) of + case qName (elName e) of "strong" -> strong <$> parseStyleType e "emphasis" -> emph <$> parseStyleType e "style" -> parseNamedStyle e @@ -380,7 +382,7 @@ parseNamedStyleChild (Elem e) = "strikethrough" -> strikeout <$> parseStyleType e "sub" -> subscript <$> parseStyleType e "sup" -> superscript <$> parseStyleType e - "code" -> pure $ code $ T.pack $ strContent e + "code" -> pure $ code $ strContent e "image" -> parseInlineImageElement e name -> do report $ IgnoredElement $ name <> " in style" @@ -392,7 +394,7 @@ parseLinkType :: PandocMonad m => Element -> FB2 m Inlines parseLinkType e = do content <- mconcat <$> mapM parseStyleLinkType (elContent e) notes <- gets fb2Notes - case T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of + case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of Just href -> case findAttr (QName "type" Nothing Nothing) e of Just "note" -> case M.lookup href notes of Nothing -> pure $ link href "" content @@ -419,15 +421,14 @@ parseTable _ = pure mempty -- TODO: tables are not supported yet -- | Parse @title-infoType@ parseTitleInfoChild :: PandocMonad m => Element -> FB2 m () parseTitleInfoChild e = - case T.pack $ qName (elName e) of + case qName (elName e) of "genre" -> pure () "author" -> parseAuthor e >>= \author -> modify (\st -> st {fb2Authors = author:fb2Authors st}) - "book-title" -> modify (setMeta "title" (text $ T.pack $ strContent e)) + "book-title" -> modify (setMeta "title" (text $ strContent e)) "annotation" -> parseAnnotation e >>= modify . setMeta "abstract" "keywords" -> modify (setMeta "keywords" (map (MetaString . trim) $ T.splitOn "," - $ T.pack $ strContent e)) - "date" -> modify (setMeta "date" (text $ T.pack $ strContent e)) + "date" -> modify (setMeta "date" (text $ strContent e)) "coverpage" -> parseCoverPage e "lang" -> pure () "src-lang" -> pure () @@ -441,7 +442,7 @@ parseCoverPage e = Just img -> case href of Just src -> modify (setMeta "cover-image" (MetaString $ removeHash src)) Nothing -> pure () - where href = T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) img + where href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) img Nothing -> pure () -- | Parse @inlineImageType@ element @@ -454,5 +455,5 @@ parseInlineImageElement e = Nothing -> do report $ IgnoredElement "inline image without href" pure mempty - where alt = maybe mempty (str . T.pack) $ findAttr (unqual "alt") e - href = T.pack <$> findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e + where alt = maybe mempty str $ findAttr (unqual "alt") e + href = findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index dfd343b7a..5353f2001 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -16,7 +16,7 @@ module Text.Pandoc.Readers.JATS ( readJATS ) where import Control.Monad.State.Strict import Control.Monad.Except (throwError) import Text.Pandoc.Error (PandocError(..)) -import Data.Char (isDigit, isSpace, toUpper) +import Data.Char (isDigit, isSpace) import Data.Default import Data.Generics import Data.List (foldl', intersperse) @@ -31,8 +31,7 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) import Text.TeXMath (readMathML, writeTeX) -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLContents) +import Text.Pandoc.XML.Light import qualified Data.Set as S (fromList, member) import Data.Set ((\\)) @@ -67,29 +66,29 @@ normalizeTree = everywhere (mkT go) where go :: [Content] -> [Content] go (Text (CData CDataRaw _ _):xs) = xs go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 ++ s2) z):xs + Text (CData CDataText (s1 <> s2) z):xs go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 ++ convertEntity r) z):xs + Text (CData CDataText (s1 <> convertEntity r) z):xs go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r ++ s1) z):xs + Text (CData CDataText (convertEntity r <> s1) z):xs go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs + Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs go xs = xs -convertEntity :: String -> String -convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) +convertEntity :: Text -> Text +convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity $ T.unpack e) -- convenience function to get an attribute value, defaulting to "" -attrValue :: String -> Element -> Text +attrValue :: Text -> Element -> Text attrValue attr = fromMaybe "" . maybeAttrValue attr -maybeAttrValue :: String -> Element -> Maybe Text +maybeAttrValue :: Text -> Element -> Maybe Text maybeAttrValue attr elt = - T.pack <$> lookupAttrBy (\x -> qName x == attr) (elAttribs elt) + lookupAttrBy (\x -> qName x == attr) (elAttribs elt) -- convenience function -named :: String -> Element -> Bool +named :: Text -> Element -> Bool named s e = qName (elName e) == s -- @@ -155,10 +154,10 @@ getBlocks e = mconcat <$> parseBlock :: PandocMonad m => Content -> JATS m Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE -parseBlock (Text (CData _ s _)) = if all isSpace s +parseBlock (Text (CData _ s _)) = if T.all isSpace s then return mempty - else return $ plain $ trimInlines $ text $ T.pack s -parseBlock (CRef x) = return $ plain $ str $ T.toUpper $ T.pack x + else return $ plain $ trimInlines $ text s +parseBlock (CRef x) = return $ plain $ str $ T.toUpper x parseBlock (Elem e) = case qName (elName e) of "p" -> parseMixed para (elContent e) @@ -207,7 +206,7 @@ parseBlock (Elem e) = "" -> [] x -> [x] return $ codeBlockWith (attrValue "id" e, classes', []) - $ trimNl $ textContentRecursive e + $ trimNl $ strContentRecursive e parseBlockquote = do attrib <- case filterChild (named "attribution") e of Nothing -> return mempty @@ -271,7 +270,7 @@ parseBlock (Elem e) = Just "center" -> AlignCenter _ -> AlignDefault let toWidth c = do - w <- findAttrText (unqual "colwidth") c + w <- findAttr (unqual "colwidth") c n <- safeRead $ "0" <> T.filter (\x -> isDigit x || x == '.') w if n > 0 then Just n else Nothing let numrows = foldl' max 0 $ map length bodyrows @@ -442,16 +441,10 @@ parseRef e = do Nothing -> return $ Map.insert "id" (toMetaValue refId) mempty -- TODO handle mixed-citation -findAttrText :: QName -> Element -> Maybe Text -findAttrText x = fmap T.pack . findAttr x - textContent :: Element -> Text -textContent = T.pack . strContent - -textContentRecursive :: Element -> Text -textContentRecursive = T.pack . strContentRecursive +textContent = strContent -strContentRecursive :: Element -> String +strContentRecursive :: Element -> Text strContentRecursive = strContent . (\e' -> e'{ elContent = map elementToStr $ elContent e' }) @@ -460,9 +453,8 @@ elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing elementToStr x = x parseInline :: PandocMonad m => Content -> JATS m Inlines -parseInline (Text (CData _ s _)) = return $ text $ T.pack s -parseInline (CRef ref) = - return . text . maybe (T.toUpper $ T.pack ref) T.pack $ lookupEntity ref +parseInline (Text (CData _ s _)) = return $ text s +parseInline (CRef ref) = return . text . convertEntity $ ref parseInline (Elem e) = case qName (elName e) of "italic" -> innerInlines emph @@ -507,9 +499,9 @@ parseInline (Elem e) = else linkWith attr ("#" <> rid) "" ils "ext-link" -> do ils <- innerInlines id - let title = fromMaybe "" $ findAttrText (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e + let title = fromMaybe "" $ findAttr (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of - Just h -> T.pack h + Just h -> h _ -> "#" <> attrValue "rid" e let ils' = if ils == mempty then str href else ils let attr = (attrValue "id" e, [], []) @@ -529,7 +521,7 @@ parseInline (Elem e) = where innerInlines f = extractSpaces f . mconcat <$> mapM parseInline (elContent e) mathML x = - case readMathML . T.pack . showElement $ everywhere (mkT removePrefix) x of + case readMathML . showElement $ everywhere (mkT removePrefix) x of Left _ -> mempty Right m -> writeTeX m formula constructor = do @@ -547,4 +539,4 @@ parseInline (Elem e) = let classes' = case attrValue "language" e of "" -> [] l -> [l] - return $ codeWith (attrValue "id" e,classes',[]) $ textContentRecursive e + return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index bdadc4dd9..184d5a63f 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -13,7 +13,6 @@ Conversion of OPML to 'Pandoc' document. module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State.Strict -import Data.Char (toUpper) import Data.Default import Data.Generics import Data.Maybe (fromMaybe) @@ -28,8 +27,7 @@ import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) import Text.Pandoc.Shared (crFilter, blocksToInlines') -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLContents) +import Text.Pandoc.XML.Light import Control.Monad.Except (throwError) type OPML m = StateT OPMLState m @@ -69,25 +67,22 @@ normalizeTree = everywhere (mkT go) where go :: [Content] -> [Content] go (Text (CData CDataRaw _ _):xs) = xs go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 ++ s2) z):xs + Text (CData CDataText (s1 <> s2) z):xs go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 ++ convertEntity r) z):xs + Text (CData CDataText (s1 <> convertEntity r) z):xs go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r ++ s1) z):xs + Text (CData CDataText (convertEntity r <> s1) z):xs go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs + Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs go xs = xs -convertEntity :: String -> String -convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) +convertEntity :: Text -> Text +convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity (T.unpack e)) -- convenience function to get an attribute value, defaulting to "" -attrValue :: String -> Element -> Text +attrValue :: Text -> Element -> Text attrValue attr elt = - maybe "" T.pack (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) - -textContent :: Element -> Text -textContent = T.pack . strContent + fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) -- exceptT :: PandocMonad m => Either PandocError a -> OPML m a -- exceptT = either throwError return @@ -111,11 +106,11 @@ parseBlock :: PandocMonad m => Content -> OPML m Blocks parseBlock (Elem e) = case qName (elName e) of "ownerName" -> mempty <$ modify (\st -> - st{opmlDocAuthors = [text $ textContent e]}) + st{opmlDocAuthors = [text $ strContent e]}) "dateModified" -> mempty <$ modify (\st -> - st{opmlDocDate = text $ textContent e}) + st{opmlDocDate = text $ strContent e}) "title" -> mempty <$ modify (\st -> - st{opmlDocTitle = text $ textContent e}) + st{opmlDocTitle = text $ strContent e}) "outline" -> gets opmlSectionLevel >>= sect . (+1) "?xml" -> return mempty _ -> getBlocks e diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 85308deb1..c274b6fd4 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -14,8 +14,7 @@ Entry point to the odt reader. module Text.Pandoc.Readers.Odt ( readOdt ) where import Codec.Archive.Zip -import qualified Text.XML.Light as XML -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light import qualified Data.ByteString.Lazy as B @@ -91,7 +90,7 @@ archiveToOdt archive = do -- -entryToXmlElem :: Entry -> Either PandocError XML.Element +entryToXmlElem :: Entry -> Either PandocError Element entryToXmlElem entry = case parseXMLElement . UTF8.toTextLazy . fromEntry $ entry of Right x -> Right x diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 43c44e7e9..df90880fa 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -29,14 +29,14 @@ import Control.Monad ((<=<)) import qualified Data.ByteString.Lazy as B import Data.Foldable (fold) -import Data.List (find, stripPrefix) +import Data.List (find) import qualified Data.Map as M import qualified Data.Text as T import Data.Maybe import Data.Semigroup (First(..), Option(..)) import Text.TeXMath (readMathML, writeTeX) -import qualified Text.XML.Light as XML +import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.Builder hiding (underline) import Text.Pandoc.MediaBag (MediaBag, insertMedia) @@ -557,7 +557,7 @@ read_plain_text = fst ^&&& read_plain_text' >>% recover >>?% mappend -- extractText :: XML.Content -> Fallible T.Text - extractText (XML.Text cData) = succeedWith (T.pack $ XML.cdData cData) + extractText (XML.Text cData) = succeedWith (XML.cdData cData) extractText _ = failEmpty read_text_seq :: InlineMatcher @@ -777,14 +777,14 @@ read_frame_img = "" -> returnV mempty -< () src' -> do let exts = extensionsFromList [Ext_auto_identifiers] - resource <- lookupResource -< src' + resource <- lookupResource -< T.unpack src' _ <- updateMediaWithResource -< resource w <- findAttrText' NsSVG "width" -< () h <- findAttrText' NsSVG "height" -< () titleNodes <- matchChildContent' [ read_frame_title ] -< () alt <- matchChildContent [] read_plain_text -< () arr (firstMatch . uncurry4 imageWith) -< - (image_attributes w h, T.pack src', inlineListToIdentifier exts (toList titleNodes), alt) + (image_attributes w h, src', inlineListToIdentifier exts (toList titleNodes), alt) read_frame_title :: InlineMatcher read_frame_title = matchingElement NsSVG "title" (matchChildContent [] read_plain_text) @@ -804,7 +804,8 @@ read_frame_mathml = case fold src of "" -> returnV mempty -< () src' -> do - let path = fromMaybe src' (stripPrefix "./" src') ++ "/content.xml" + let path = T.unpack $ + fromMaybe src' (T.stripPrefix "./" src') <> "/content.xml" (_, mathml) <- lookupResource -< path case readMathML (UTF8.toText $ B.toStrict mathml) of Left _ -> returnV mempty -< () diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs b/src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs index 77174c793..78a7fc0b2 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Namespaces.hs @@ -14,9 +14,10 @@ typesafe Haskell namespace identifiers and unsafe "real world" namespaces. module Text.Pandoc.Readers.Odt.Generic.Namespaces where import qualified Data.Map as M +import Data.Text (Text) -- -type NameSpaceIRI = String +type NameSpaceIRI = Text -- type NameSpaceIRIs nsID = M.Map nsID NameSpaceIRI diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs index 6dc56a0d9..edefe3c70 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs @@ -20,7 +20,6 @@ module Text.Pandoc.Readers.Odt.Generic.Utils , reverseComposition , tryToRead , Lookupable(..) -, readLookupables , readLookupable , readPercent , findBy @@ -30,11 +29,11 @@ module Text.Pandoc.Readers.Odt.Generic.Utils import Control.Category (Category, (<<<), (>>>)) import qualified Control.Category as Cat (id) -import Control.Monad (msum) - +import Data.Char (isSpace) import qualified Data.Foldable as F (Foldable, foldr) import Data.Maybe - +import Data.Text (Text) +import qualified Data.Text as T -- | Equivalent to -- > foldr (.) id @@ -76,8 +75,8 @@ swing = flip.(.flip id) -- (nobody wants that) while the latter returns "to much" for simple purposes. -- This function instead applies 'reads' and returns the first match (if any) -- in a 'Maybe'. -tryToRead :: (Read r) => String -> Maybe r -tryToRead = reads >>> listToMaybe >>> fmap fst +tryToRead :: (Read r) => Text -> Maybe r +tryToRead = (reads . T.unpack) >>> listToMaybe >>> fmap fst -- | A version of 'reads' that requires a '%' sign after the number readPercent :: ReadS Int @@ -88,26 +87,12 @@ readPercent s = [ (i,s') | (i , r ) <- reads s -- | Data that can be looked up. -- This is mostly a utility to read data with kind *. class Lookupable a where - lookupTable :: [(String, a)] - --- | The idea is to use this function as if there was a declaration like --- --- > instance (Lookupable a) => (Read a) where --- > readsPrec _ = readLookupables --- . --- But including this code in this form would need UndecideableInstances. --- That is a bad idea. Luckily 'readLookupable' (without the s at the end) --- can be used directly in almost any case. -readLookupables :: (Lookupable a) => String -> [(a,String)] -readLookupables s = [ (a,rest) | (word,rest) <- lex s, - a <- maybeToList (lookup word lookupTable) - ] + lookupTable :: [(Text, a)] -- | Very similar to a simple 'lookup' in the 'lookupTable', but with a lexer. -readLookupable :: (Lookupable a) => String -> Maybe a -readLookupable s = msum - $ map ((`lookup` lookupTable).fst) - $ lex s +readLookupable :: (Lookupable a) => Text -> Maybe a +readLookupable s = + lookup (T.takeWhile (not . isSpace) $ T.dropWhile isSpace s) lookupTable uncurry3 :: (a->b->c -> z) -> (a,b,c ) -> z uncurry4 :: (a->b->c->d -> z) -> (a,b,c,d ) -> z diff --git a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs index 00c636a0d..0d921e23b 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} @@ -60,11 +61,11 @@ import Control.Arrow import Data.Bool ( bool ) import Data.Either ( rights ) import qualified Data.Map as M -import qualified Data.Text as T +import Data.Text (Text) import Data.Default import Data.Maybe -import qualified Text.XML.Light as XML +import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.Readers.Odt.Arrows.State import Text.Pandoc.Readers.Odt.Arrows.Utils @@ -78,13 +79,13 @@ import Text.Pandoc.Readers.Odt.Generic.Fallible -------------------------------------------------------------------------------- -- -type ElementName = String -type AttributeName = String -type AttributeValue = String -type TextAttributeValue = T.Text +type ElementName = Text +type AttributeName = Text +type AttributeValue = Text +type TextAttributeValue = Text -- -type NameSpacePrefix = String +type NameSpacePrefix = Text -- type NameSpacePrefixes nsID = M.Map nsID NameSpacePrefix @@ -461,7 +462,7 @@ lookupDefaultingAttr :: (NameSpaceID nsID, Lookupable a, Default a) lookupDefaultingAttr nsID attrName = lookupAttrWithDefault nsID attrName def --- | Return value as a (Maybe String) +-- | Return value as a (Maybe Text) findAttr' :: (NameSpaceID nsID) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe AttributeValue) @@ -477,7 +478,6 @@ findAttrText' nsID attrName = qualifyName nsID attrName &&& getCurrentElement >>% XML.findAttr - >>^ fmap T.pack -- | Return value as string or fail findAttr :: (NameSpaceID nsID) @@ -492,7 +492,6 @@ findAttrText :: (NameSpaceID nsID) -> FallibleXMLConverter nsID extraState x TextAttributeValue findAttrText nsID attrName = findAttr' nsID attrName - >>^ fmap T.pack >>> maybeToChoice -- | Return value as string or return provided default value @@ -511,7 +510,7 @@ findAttrTextWithDefault :: (NameSpaceID nsID) -> XMLConverter nsID extraState x TextAttributeValue findAttrTextWithDefault nsID attrName deflt = findAttr' nsID attrName - >>^ maybe deflt T.pack + >>^ fromMaybe deflt -- | Read and return value or fail readAttr :: (NameSpaceID nsID, Read attrValue) @@ -748,7 +747,7 @@ matchContent lookups fallback -- Internals -------------------------------------------------------------------------------- -stringToBool' :: String -> Maybe Bool +stringToBool' :: Text -> Maybe Bool stringToBool' val | val `elem` trueValues = Just True | val `elem` falseValues = Just False | otherwise = Nothing diff --git a/src/Text/Pandoc/Readers/Odt/Namespaces.hs b/src/Text/Pandoc/Readers/Odt/Namespaces.hs index 3a24a1162..70741c28d 100644 --- a/src/Text/Pandoc/Readers/Odt/Namespaces.hs +++ b/src/Text/Pandoc/Readers/Odt/Namespaces.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Reader.Odt.Namespaces Copyright : Copyright (C) 2015 Martin Linnemann @@ -13,10 +14,10 @@ Namespaces used in odt files. module Text.Pandoc.Readers.Odt.Namespaces ( Namespace (..) ) where -import Data.List (isPrefixOf) import qualified Data.Map as M (empty, insert) import Data.Maybe (fromMaybe, listToMaybe) - +import Data.Text (Text) +import qualified Data.Text as T import Text.Pandoc.Readers.Odt.Generic.Namespaces @@ -30,7 +31,7 @@ instance NameSpaceID Namespace where findID :: NameSpaceIRI -> Maybe Namespace -findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `isPrefixOf` iri] +findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `T.isPrefixOf` iri] nsIDmap :: NameSpaceIRIs Namespace nsIDmap = foldr (uncurry $ flip M.insert) M.empty nsIDs @@ -54,12 +55,12 @@ data Namespace = -- Open Document core -- Core XML (basically only for the 'id'-attribute) | NsXML -- Fallback - | NsOther String + | NsOther Text deriving ( Eq, Ord, Show ) -- | Not the actual iri's, but large prefixes of them - this way there are -- less versioning problems and the like. -nsIDs :: [(String,Namespace)] +nsIDs :: [(Text, Namespace)] nsIDs = [ ("urn:oasis:names:tc:opendocument:xmlns:animation" , NsAnim ), ("urn:oasis:names:tc:opendocument:xmlns:chart" , NsChart ), diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 46a777df1..5e10f896c 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -2,6 +2,7 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} +{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Odt.StyleReader Copyright : Copyright (C) 2015 Martin Linnemann @@ -46,11 +47,13 @@ import qualified Data.Foldable as F import Data.List (unfoldr) import qualified Data.Map as M import Data.Maybe +import Data.Text (Text) +import qualified Data.Text as T import qualified Data.Set as S -import qualified Text.XML.Light as XML +import qualified Text.Pandoc.XML.Light as XML -import Text.Pandoc.Shared (safeRead) +import Text.Pandoc.Shared (safeRead, tshow) import Text.Pandoc.Readers.Odt.Arrows.Utils @@ -90,7 +93,7 @@ instance Default FontPitch where -- -- Thus, we want -type FontFaceName = String +type FontFaceName = Text type FontPitches = M.Map FontFaceName FontPitch @@ -151,7 +154,7 @@ findPitch = ( lookupAttr NsStyle "font-pitch" -- Definitions of main data -------------------------------------------------------------------------------- -type StyleName = String +type StyleName = Text -- | There are two types of styles: named styles with a style family and an -- optional style parent, and default styles for each style family, @@ -355,8 +358,8 @@ getListLevelStyle level ListStyle{..} = -- \^ simpler, but in general less efficient data ListLevelStyle = ListLevelStyle { listLevelType :: ListLevelType - , listItemPrefix :: Maybe String - , listItemSuffix :: Maybe String + , listItemPrefix :: Maybe Text + , listItemSuffix :: Maybe Text , listItemFormat :: ListItemNumberFormat , listItemStart :: Int } @@ -366,9 +369,9 @@ instance Show ListLevelStyle where show ListLevelStyle{..} = " listItemPrefix) ++ show listItemFormat - ++ maybeToString listItemSuffix + ++ maybeToString (T.unpack <$> listItemSuffix) ++ ">" where maybeToString = fromMaybe "" @@ -471,7 +474,7 @@ readTextProperties = ) where isFontEmphasised = [("normal",False),("italic",True),("oblique",True)] isFontBold = ("normal",False):("bold",True) - :map ((,True).show) ([100,200..900]::[Int]) + :map ((,True) . tshow) ([100,200..900]::[Int]) readUnderlineMode :: StyleReaderSafe _x (Maybe UnderlineMode) readUnderlineMode = readLineMode "text-underline-mode" @@ -481,7 +484,7 @@ readStrikeThroughMode :: StyleReaderSafe _x (Maybe UnderlineMode) readStrikeThroughMode = readLineMode "text-line-through-mode" "text-line-through-style" -readLineMode :: String -> String -> StyleReaderSafe _x (Maybe UnderlineMode) +readLineMode :: Text -> Text -> StyleReaderSafe _x (Maybe UnderlineMode) readLineMode modeAttr styleAttr = proc x -> do isUL <- searchAttr NsStyle styleAttr False isLinePresent -< x mode <- lookupAttr' NsStyle modeAttr -< x diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index da990e4d3..89c71d773 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -31,6 +31,7 @@ import qualified Data.Map as M import Data.Maybe (fromMaybe, isNothing, mapMaybe, maybeToList) import qualified Data.Set as Set import qualified Data.Text as T +import Data.Text (Text) import qualified Data.Text.Lazy as TL import Data.Time.Clock.POSIX import Data.Digest.Pure.SHA (sha1, showDigest) @@ -57,19 +58,19 @@ import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Printf (printf) import Text.TeXMath -import Text.XML.Light as XML -import Text.XML.Light.Cursor as XMLC import Text.Pandoc.Writers.OOXML +import Text.Pandoc.XML.Light as XML +import Data.Generics (mkT, everywhere) data ListMarker = NoMarker | BulletMarker | NumberMarker ListNumberStyle ListNumberDelim Int deriving (Show, Read, Eq, Ord) -listMarkerToId :: ListMarker -> String +listMarkerToId :: ListMarker -> Text listMarkerToId NoMarker = "990" listMarkerToId BulletMarker = "991" -listMarkerToId (NumberMarker sty delim n) = +listMarkerToId (NumberMarker sty delim n) = T.pack $ '9' : '9' : styNum : delimNum : show n where styNum = case sty of DefaultStyle -> '2' @@ -106,8 +107,8 @@ data WriterEnv = WriterEnv{ envTextProperties :: EnvProps , envListLevel :: Int , envListNumId :: Int , envInDel :: Bool - , envChangesAuthor :: T.Text - , envChangesDate :: T.Text + , envChangesAuthor :: Text + , envChangesDate :: Text , envPrintWidth :: Integer } @@ -125,9 +126,9 @@ defaultWriterEnv = WriterEnv{ envTextProperties = mempty data WriterState = WriterState{ stFootnotes :: [Element] - , stComments :: [([(T.Text, T.Text)], [Inline])] - , stSectionIds :: Set.Set T.Text - , stExternalLinks :: M.Map String String + , stComments :: [([(Text, Text)], [Inline])] + , stSectionIds :: Set.Set Text + , stExternalLinks :: M.Map Text Text , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString) , stLists :: [ListMarker] , stInsId :: Int @@ -164,18 +165,18 @@ defaultWriterState = WriterState{ type WS m = ReaderT WriterEnv (StateT WriterState m) -renumIdMap :: Int -> [Element] -> M.Map String String +renumIdMap :: Int -> [Element] -> M.Map Text Text renumIdMap _ [] = M.empty renumIdMap n (e:es) | Just oldId <- findAttr (QName "Id" Nothing Nothing) e = - M.insert oldId ("rId" ++ show n) (renumIdMap (n+1) es) + M.insert oldId ("rId" <> tshow n) (renumIdMap (n+1) es) | otherwise = renumIdMap n es -replaceAttr :: (QName -> Bool) -> String -> [XML.Attr] -> [XML.Attr] +replaceAttr :: (QName -> Bool) -> Text -> [XML.Attr] -> [XML.Attr] replaceAttr f val = map $ \a -> if f (attrKey a) then XML.Attr (attrKey a) val else a -renumId :: (QName -> Bool) -> M.Map String String -> Element -> Element +renumId :: (QName -> Bool) -> M.Map Text Text -> Element -> Element renumId f renumMap e | Just oldId <- findAttrBy f e , Just newId <- M.lookup oldId renumMap = @@ -184,18 +185,12 @@ renumId f renumMap e e { elAttribs = attrs' } | otherwise = e -renumIds :: (QName -> Bool) -> M.Map String String -> [Element] -> [Element] +renumIds :: (QName -> Bool) -> M.Map Text Text -> [Element] -> [Element] renumIds f renumMap = map (renumId f renumMap) -findAttrTextBy :: (QName -> Bool) -> Element -> Maybe T.Text -findAttrTextBy x = fmap T.pack . findAttrBy x - -lookupAttrTextBy :: (QName -> Bool) -> [XML.Attr] -> Maybe T.Text -lookupAttrTextBy x = fmap T.pack . lookupAttrBy x - -- | Certain characters are invalid in XML even if escaped. -- See #1992 -stripInvalidChars :: T.Text -> T.Text +stripInvalidChars :: Text -> Text stripInvalidChars = T.filter isValidChar -- | See XML reference @@ -234,11 +229,11 @@ writeDocx opts doc = do -- Gets the template size let mbpgsz = mbsectpr >>= filterElementName (wname (=="pgSz")) - let mbAttrSzWidth = mbpgsz >>= lookupAttrTextBy ((=="w") . qName) . elAttribs + let mbAttrSzWidth = mbpgsz >>= lookupAttrBy ((=="w") . qName) . elAttribs let mbpgmar = mbsectpr >>= filterElementName (wname (=="pgMar")) - let mbAttrMarLeft = mbpgmar >>= lookupAttrTextBy ((=="left") . qName) . elAttribs - let mbAttrMarRight = mbpgmar >>= lookupAttrTextBy ((=="right") . qName) . elAttribs + let mbAttrMarLeft = mbpgmar >>= lookupAttrBy ((=="left") . qName) . elAttribs + let mbAttrMarRight = mbpgmar >>= lookupAttrBy ((=="right") . qName) . elAttribs -- Get the available area (converting the size and the margins to int and -- doing the difference @@ -250,24 +245,21 @@ writeDocx opts doc = do -- styles mblang <- toLang $ getLang opts meta + -- TODO FIXME avoid this generic traversal! + -- lang is in w:docDefaults / w:rPr / w:lang let addLang :: Element -> Element - addLang e = case (\l -> XMLC.toTree . go (T.unpack $ renderLang l) $ - XMLC.fromElement e) <$> mblang of - Just (Elem e') -> e' - _ -> e -- return original - where go :: String -> Cursor -> Cursor - go l cursor = case XMLC.findRec (isLangElt . current) cursor of - Nothing -> cursor - Just t -> XMLC.modifyContent (setval l) t - setval :: String -> Content -> Content - setval l (Elem e') = Elem $ e'{ elAttribs = map (setvalattr l) $ - elAttribs e' } - setval _ x = x - setvalattr :: String -> XML.Attr -> XML.Attr - setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l - setvalattr _ x = x - isLangElt (Elem e') = qName (elName e') == "lang" - isLangElt _ = False + addLang = case mblang of + Nothing -> id + Just l -> everywhere (mkT (go (renderLang l))) + where + go :: Text -> Element -> Element + go l e' + | qName (elName e') == "lang" + = e'{ elAttribs = map (setvalattr l) $ elAttribs e' } + | otherwise = e' + + setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l + setvalattr _ x = x let stylepath = "word/styles.xml" styledoc <- addLang <$> parseXml refArchive distArchive stylepath @@ -337,12 +329,13 @@ writeDocx opts doc = do -- [Content_Types].xml let mkOverrideNode (part', contentType') = mknode "Override" - [("PartName",part'),("ContentType",contentType')] () + [("PartName", T.pack part') + ,("ContentType", contentType')] () let mkImageOverride (_, imgpath, mbMimeType, _) = - mkOverrideNode ("/word/" ++ imgpath, - maybe "application/octet-stream" T.unpack mbMimeType) + mkOverrideNode ("/word/" <> imgpath, + fromMaybe "application/octet-stream" mbMimeType) let mkMediaOverride imgpath = - mkOverrideNode ('/':imgpath, T.unpack $ getMimeTypeDef imgpath) + mkOverrideNode ("/" <> imgpath, getMimeTypeDef imgpath) let overrides = map mkOverrideNode ( [("/word/webSettings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml") @@ -369,13 +362,14 @@ writeDocx opts doc = do ,("/word/footnotes.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml") ] ++ - map (\x -> (maybe "" ("/word/" ++) $ extractTarget x, + map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x), "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++ - map (\x -> (maybe "" ("/word/" ++) $ extractTarget x, + map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x), "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml")) footers) ++ map mkImageOverride imgs ++ - [ mkMediaOverride (eRelativePath e) | e <- zEntries refArchive - , "word/media/" `isPrefixOf` eRelativePath e ] + [ mkMediaOverride (eRelativePath e) + | e <- zEntries refArchive + , "word/media/" `isPrefixOf` eRelativePath e ] let defaultnodes = [mknode "Default" [("Extension","xml"),("ContentType","application/xml")] (), @@ -421,7 +415,7 @@ writeDocx opts doc = do let renumHeaders = renumIds (\q -> qName q == "Id") idMap headers let renumFooters = renumIds (\q -> qName q == "Id") idMap footers let baserels = baserels' ++ renumHeaders ++ renumFooters - let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",path)] () + let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",T.pack ident),("Target",T.pack path)] () let imgrels = map toImgRel imgs let toLinkRel (src,ident) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"),("Id",ident),("Target",src),("TargetMode","External") ] () let linkrels = map toLinkRel $ M.toList $ stExternalLinks st @@ -489,10 +483,10 @@ writeDocx opts doc = do numbering <- parseXml refArchive distArchive numpath let newNumElts = mkNumbering (stLists st) let pandocAdded e = - case findAttrTextBy ((== "abstractNumId") . qName) e >>= safeRead of + case findAttrBy ((== "abstractNumId") . qName) e >>= safeRead of Just numid -> numid >= (990 :: Int) Nothing -> - case findAttrTextBy ((== "numId") . qName) e >>= safeRead of + case findAttrBy ((== "numId") . qName) e >>= safeRead of Just numid -> numid >= (1000 :: Int) Nothing -> False let oldElts = filter (not . pandocAdded) $ onlyElems (elContent numbering) @@ -514,7 +508,7 @@ writeDocx opts doc = do let extraCoreProps = ["subject","lang","category","description"] let extraCorePropsMap = M.fromList $ zip extraCoreProps ["dc:subject","dc:language","cp:category","dc:description"] - let lookupMetaString' :: T.Text -> Meta -> T.Text + let lookupMetaString' :: Text -> Meta -> Text lookupMetaString' key' meta' = case key' of "description" -> T.intercalate "_x000d_\n" (map stringify $ lookupMetaBlocks "description" meta') @@ -530,21 +524,21 @@ writeDocx opts doc = do : mktnode "dc:creator" [] (T.intercalate "; " (map stringify $ docAuthors meta)) : [ mktnode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta) | k <- M.keys (unMeta meta), k `elem` extraCoreProps] - ++ mknode "cp:keywords" [] (T.unpack $ T.intercalate ", " keywords) + ++ mknode "cp:keywords" [] (T.intercalate ", " keywords) : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x - ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) + ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime) let docPropsEntry = toEntry docPropsPath epochtime $ renderXml docProps -- docProps/custom.xml - let customProperties :: [(String, String)] - customProperties = [ (T.unpack k, T.unpack $ lookupMetaString k meta) + let customProperties :: [(Text, Text)] + customProperties = [ (k, lookupMetaString k meta) | k <- M.keys (unMeta meta) , k `notElem` (["title", "author", "keywords"] ++ extraCoreProps)] let mkCustomProp (k, v) pid = mknode "property" [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}") - ,("pid", show pid) + ,("pid", tshow pid) ,("name", k)] $ mknode "vt:lpwstr" [] v let customPropsPath = "docProps/custom.xml" let customProps = mknode "Properties" @@ -594,7 +588,8 @@ writeDocx opts doc = do fontTableEntry <- entryFromArchive refArchive "word/fontTable.xml" webSettingsEntry <- entryFromArchive refArchive "word/webSettings.xml" headerFooterEntries <- mapM (entryFromArchive refArchive . ("word/" ++)) $ - mapMaybe extractTarget (headers ++ footers) + mapMaybe (fmap T.unpack . extractTarget) + (headers ++ footers) let miscRelEntries = [ e | e <- zEntries refArchive , "word/_rels/" `isPrefixOf` eRelativePath e , ".xml.rels" `isSuffixOf` eRelativePath e @@ -620,8 +615,8 @@ newParaPropToOpenXml (fromStyleName -> s) = let styleId = T.filter (not . isSpace) s in mknode "w:style" [ ("w:type", "paragraph") , ("w:customStyle", "1") - , ("w:styleId", T.unpack styleId)] - [ mknode "w:name" [("w:val", T.unpack s)] () + , ("w:styleId", styleId)] + [ mknode "w:name" [("w:val", s)] () , mknode "w:basedOn" [("w:val","BodyText")] () , mknode "w:qFormat" [] () ] @@ -631,8 +626,8 @@ newTextPropToOpenXml (fromStyleName -> s) = let styleId = T.filter (not . isSpace) s in mknode "w:style" [ ("w:type", "character") , ("w:customStyle", "1") - , ("w:styleId", T.unpack styleId)] - [ mknode "w:name" [("w:val", T.unpack s)] () + , ("w:styleId", styleId)] + [ mknode "w:name" [("w:val", s)] () , mknode "w:basedOn" [("w:val","BodyTextChar")] () ] @@ -643,13 +638,14 @@ styleToOpenXml sm style = toStyle toktype | hasStyleName (fromString $ show toktype) (smCharStyle sm) = Nothing | otherwise = Just $ mknode "w:style" [("w:type","character"), - ("w:customStyle","1"),("w:styleId",show toktype)] - [ mknode "w:name" [("w:val",show toktype)] () + ("w:customStyle","1"),("w:styleId", tshow toktype)] + [ mknode "w:name" [("w:val", tshow toktype)] () , mknode "w:basedOn" [("w:val","VerbatimChar")] () , mknode "w:rPr" [] $ - [ mknode "w:color" [("w:val",tokCol toktype)] () + [ mknode "w:color" [("w:val", tokCol toktype)] () | tokCol toktype /= "auto" ] ++ - [ mknode "w:shd" [("w:val","clear"),("w:fill",tokBg toktype)] () + [ mknode "w:shd" [("w:val","clear") + ,("w:fill",tokBg toktype)] () | tokBg toktype /= "auto" ] ++ [ mknode "w:b" [] () | tokFeature tokenBold toktype ] ++ [ mknode "w:i" [] () | tokFeature tokenItalic toktype ] ++ @@ -657,10 +653,10 @@ styleToOpenXml sm style = ] tokStyles = tokenStyles style tokFeature f toktype = maybe False f $ M.lookup toktype tokStyles - tokCol toktype = maybe "auto" (drop 1 . fromColor) + tokCol toktype = maybe "auto" (T.pack . drop 1 . fromColor) $ (tokenColor =<< M.lookup toktype tokStyles) `mplus` defaultColor style - tokBg toktype = maybe "auto" (drop 1 . fromColor) + tokBg toktype = maybe "auto" (T.pack . drop 1 . fromColor) $ (tokenBackground =<< M.lookup toktype tokStyles) `mplus` backgroundColor style parStyle | hasStyleName "Source Code" (smParaStyle sm) = Nothing @@ -673,10 +669,11 @@ styleToOpenXml sm style = , mknode "w:pPr" [] $ mknode "w:wordWrap" [("w:val","off")] () : - maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill",drop 1 $ fromColor col)] ()]) (backgroundColor style) + maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill", T.pack $ drop 1 $ fromColor col)] ()]) (backgroundColor style) ] -copyChildren :: (PandocMonad m) => Archive -> Archive -> String -> Integer -> [String] -> m Entry +copyChildren :: (PandocMonad m) + => Archive -> Archive -> String -> Integer -> [Text] -> m Entry copyChildren refArchive distArchive path timestamp elNames = do ref <- parseXml refArchive distArchive path dist <- parseXml distArchive distArchive path @@ -685,7 +682,7 @@ copyChildren refArchive distArchive path timestamp elNames = do } where strName QName{qName=name, qPrefix=prefix} - | Just p <- prefix = p++":"++name + | Just p <- prefix = p <> ":" <> name | otherwise = name shouldCopy = (`elem` elNames) . strName cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}} @@ -706,35 +703,35 @@ maxListLevel = 8 mkNum :: ListMarker -> Int -> Element mkNum marker numid = - mknode "w:num" [("w:numId",show numid)] + mknode "w:num" [("w:numId",tshow numid)] $ mknode "w:abstractNumId" [("w:val",listMarkerToId marker)] () : case marker of NoMarker -> [] BulletMarker -> [] NumberMarker _ _ start -> - map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))] - $ mknode "w:startOverride" [("w:val",show start)] ()) + map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",tshow (lvl :: Int))] + $ mknode "w:startOverride" [("w:val",tshow start)] ()) [0..maxListLevel] mkAbstractNum :: ListMarker -> Integer -> Element mkAbstractNum marker nsid = mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)] - $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] () + $ mknode "w:nsid" [("w:val", T.pack $ printf "%8x" nsid)] () : mknode "w:multiLevelType" [("w:val","multilevel")] () : map (mkLvl marker) [0..maxListLevel] mkLvl :: ListMarker -> Int -> Element mkLvl marker lvl = - mknode "w:lvl" [("w:ilvl",show lvl)] $ + mknode "w:lvl" [("w:ilvl",tshow lvl)] $ [ mknode "w:start" [("w:val",start)] () | marker /= NoMarker && marker /= BulletMarker ] ++ [ mknode "w:numFmt" [("w:val",fmt)] () - , mknode "w:lvlText" [("w:val",lvltxt)] () + , mknode "w:lvlText" [("w:val", lvltxt)] () , mknode "w:lvlJc" [("w:val","left")] () , mknode "w:pPr" [] - [ mknode "w:ind" [ ("w:left",show $ lvl * step + step) - , ("w:hanging",show (hang :: Int)) + [ mknode "w:ind" [ ("w:left",tshow $ lvl * step + step) + , ("w:hanging",tshow (hang :: Int)) ] () ] ] @@ -743,8 +740,8 @@ mkLvl marker lvl = NoMarker -> ("bullet"," ","1") BulletMarker -> ("bullet",bulletFor lvl,"1") NumberMarker st de n -> (styleFor st lvl - ,patternFor de ("%" ++ show (lvl + 1)) - ,show n) + ,patternFor de ("%" <> tshow (lvl + 1)) + ,tshow n) step = 720 hang = 480 bulletFor 0 = "\x2022" -- filled circle @@ -767,9 +764,9 @@ mkLvl marker lvl = styleFor DefaultStyle 5 = "lowerRoman" styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 6) styleFor _ _ = "decimal" - patternFor OneParen s = s ++ ")" - patternFor TwoParens s = "(" ++ s ++ ")" - patternFor _ s = s ++ "." + patternFor OneParen s = s <> ")" + patternFor TwoParens s = "(" <> s <> ")" + patternFor _ s = s <> "." getNumId :: (PandocMonad m) => WS m Int getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists @@ -777,8 +774,8 @@ getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element] makeTOC opts = do - let depth = "1-"++show (writerTOCDepth opts) - let tocCmd = "TOC \\o \""++depth++"\" \\h \\z \\u" + let depth = "1-" <> tshow (writerTOCDepth opts) + let tocCmd = "TOC \\o \"" <> depth <> "\" \\h \\z \\u" tocTitle <- gets stTocTitle title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para tocTitle]) return @@ -831,7 +828,7 @@ writeOpenXML opts (Pandoc meta blocks) = do let toComment (kvs, ils) = do annotation <- inlinesToOpenXML opts ils return $ - mknode "w:comment" [('w':':':T.unpack k,T.unpack v) | (k,v) <- kvs] + mknode "w:comment" [("w:" <> k, v) | (k,v) <- kvs] [ mknode "w:p" [] $ map Elem [ mknode "w:pPr" [] @@ -867,24 +864,24 @@ pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element pStyleM styleName = do pStyleMap <- gets (smParaStyle . stStyleMaps) let sty' = getStyleIdFromName styleName pStyleMap - return $ mknode "w:pStyle" [("w:val", T.unpack $ fromStyleId sty')] () + return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] () rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element rStyleM styleName = do cStyleMap <- gets (smCharStyle . stStyleMaps) let sty' = getStyleIdFromName styleName cStyleMap - return $ mknode "w:rStyle" [("w:val", T.unpack $ fromStyleId sty')] () + return $ mknode "w:rStyle" [("w:val", fromStyleId sty')] () -getUniqueId :: (PandocMonad m) => WS m String +getUniqueId :: (PandocMonad m) => WS m Text -- the + 20 is to ensure that there are no clashes with the rIds -- already in word/document.xml.rel getUniqueId = do n <- gets stCurId modify $ \st -> st{stCurId = n + 1} - return $ show n + return $ tshow n -- | Key for specifying user-defined docx styles. -dynamicStyleKey :: T.Text +dynamicStyleKey :: Text dynamicStyleKey = "custom-style" -- | Convert a Pandoc block element to OpenXML. @@ -979,7 +976,7 @@ blockToOpenXML' opts (Para lst) blockToOpenXML' opts (LineBlock lns) = blockToOpenXML opts $ linesToPara lns blockToOpenXML' _ b@(RawBlock format str) | format == Format "openxml" = return [ - Text (CData CDataRaw (T.unpack str) Nothing) + Text (CData CDataRaw str Nothing) ] | otherwise = do report $ BlockNotRendered b @@ -1036,7 +1033,7 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do let fullrow = 5000 -- 100% specified in pct let rowwidth = fullrow * sum widths let mkgridcol w = mknode "w:gridCol" - [("w:w", show (floor (textwidth * w) :: Integer))] () + [("w:w", tshow (floor (textwidth * w) :: Integer))] () let hasHeader = not $ all null headers modify $ \s -> s { stInTable = False } -- for compatibility with Word <= 2007, we include a val with a bitmask @@ -1054,16 +1051,16 @@ blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do mknode "w:tbl" [] ( mknode "w:tblPr" [] ( mknode "w:tblStyle" [("w:val","Table")] () : - mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () : + mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () : mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") ,("w:lastRow","0") ,("w:firstColumn","0") ,("w:lastColumn","0") ,("w:noHBand","0") ,("w:noVBand","0") - ,("w:val", printf "%04x" tblLookVal) + ,("w:val", T.pack $ printf "%04x" tblLookVal) ] () : - [ mknode "w:tblCaption" [("w:val", T.unpack captionStr)] () + [ mknode "w:tblCaption" [("w:val", captionStr)] () | not (null caption) ] ) : mknode "w:tblGrid" [] (if all (==0) widths @@ -1126,7 +1123,7 @@ listItemToOpenXML opts numid (first:rest) = do modify $ \st -> st{ stInList = oldInList } return $ first'' ++ rest'' -alignmentToString :: Alignment -> [Char] +alignmentToString :: Alignment -> Text alignmentToString alignment = case alignment of AlignLeft -> "left" AlignRight -> "right" @@ -1169,8 +1166,8 @@ getParaProps displayMathPara = do listLevel <- asks envListLevel numid <- asks envListNumId let listPr = [mknode "w:numPr" [] - [ mknode "w:ilvl" [("w:val",show listLevel)] () - , mknode "w:numId" [("w:val",show numid)] () ] | listLevel >= 0 && not displayMathPara] + [ mknode "w:ilvl" [("w:val",tshow listLevel)] () + , mknode "w:numId" [("w:val",tshow numid)] () ] | listLevel >= 0 && not displayMathPara] return $ case listPr ++ squashProps props of [] -> [] ps -> [mknode "w:pPr" [] ps] @@ -1185,7 +1182,7 @@ withParaPropM md p = do d <- md withParaProp d p -formattedString :: PandocMonad m => T.Text -> WS m [Element] +formattedString :: PandocMonad m => Text -> WS m [Element] formattedString str = -- properly handle soft hyphens case splitTextBy (=='\173') str of @@ -1194,7 +1191,7 @@ formattedString str = sh <- formattedRun [mknode "w:softHyphen" [] ()] intercalate sh <$> mapM formattedString' ws -formattedString' :: PandocMonad m => T.Text -> WS m [Element] +formattedString' :: PandocMonad m => Text -> WS m [Element] formattedString' str = do inDel <- asks envInDel formattedRun [ mktnode (if inDel then "w:delText" else "w:t") @@ -1226,7 +1223,7 @@ inlineToOpenXML' opts (Span ("",["csl-right-inline"],[]) ils) = mknode "w:r" [] (mknode "w:t" [("xml:space","preserve")] - ("\t" :: String))] ++) + ("\t" :: Text))] ++) <$> inlinesToOpenXML opts ils inlineToOpenXML' opts (Span ("",["csl-indent"],[]) ils) = inlinesToOpenXML opts ils @@ -1236,17 +1233,17 @@ inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do let ident' = fromMaybe ident (lookup "id" kvs) kvs' = filter (("id" /=) . fst) kvs modify $ \st -> st{ stComments = (("id",ident'):kvs', ils) : stComments st } - return [ Elem $ mknode "w:commentRangeStart" [("w:id", T.unpack ident')] () ] + return [ Elem $ mknode "w:commentRangeStart" [("w:id", ident')] () ] inlineToOpenXML' _ (Span (ident,["comment-end"],kvs) _) = -- prefer the "id" in kvs, since that is the one produced by the docx -- reader. let ident' = fromMaybe ident (lookup "id" kvs) in return . map Elem $ - [ mknode "w:commentRangeEnd" [("w:id", T.unpack ident')] () + [ mknode "w:commentRangeEnd" [("w:id", ident')] () , mknode "w:r" [] [ mknode "w:rPr" [] [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] - , mknode "w:commentReference" [("w:id", T.unpack ident')] () ] + , mknode "w:commentReference" [("w:id", ident')] () ] ] inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do stylemod <- case lookup dynamicStyleKey kvs of @@ -1270,8 +1267,8 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do defaultAuthor <- asks envChangesAuthor let author = fromMaybe defaultAuthor (lookup "author" kvs) let mdate = lookup "date" kvs - return $ ("w:author", T.unpack author) : - maybe [] (\date -> [("w:date", T.unpack date)]) mdate + return $ ("w:author", author) : + maybe [] (\date -> [("w:date", date)]) mdate insmod <- if "insertion" `elem` classes then do changeAuthorDate <- getChangeAuthorDate @@ -1281,7 +1278,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do x <- f return [Elem $ mknode "w:ins" - (("w:id", show insId) : changeAuthorDate) x] + (("w:id", tshow insId) : changeAuthorDate) x] else return id delmod <- if "deletion" `elem` classes then do @@ -1291,7 +1288,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do return $ \f -> local (\env->env{envInDel=True}) $ do x <- f return [Elem $ mknode "w:del" - (("w:id", show delId) : changeAuthorDate) x] + (("w:id", tshow delId) : changeAuthorDate) x] else return id contents <- insmod $ delmod $ dirmod $ stylemod $ pmod $ inlinesToOpenXML opts ils @@ -1322,7 +1319,7 @@ inlineToOpenXML' opts (Strikeout lst) = inlineToOpenXML' _ LineBreak = return [Elem br] inlineToOpenXML' _ il@(RawInline f str) | f == Format "openxml" = return - [Text (CData CDataRaw (T.unpack str) Nothing)] + [Text (CData CDataRaw str Nothing)] | otherwise = do report $ InlineNotRendered il return [] @@ -1335,7 +1332,7 @@ inlineToOpenXML' opts (Math mathType str) = do when (mathType == DisplayMath) setFirstPara res <- (lift . lift) (convertMath writeOMML mathType str) case res of - Right r -> return [Elem r] + Right r -> return [Elem $ fromXLElement r] Left il -> inlineToOpenXML' opts il inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML' opts (Code attrs str) = do @@ -1348,7 +1345,7 @@ inlineToOpenXML' opts (Code attrs str) = do mknode "w:r" [] [ mknode "w:rPr" [] $ maybeToList (lookup toktype tokTypesMap) - , mknode "w:t" [("xml:space","preserve")] (T.unpack tok) ] + , mknode "w:t" [("xml:space","preserve")] tok ] withTextPropM (rStyleM "Verbatim Char") $ if isNothing (writerHighlightStyle opts) then unhighlighted @@ -1365,7 +1362,7 @@ inlineToOpenXML' opts (Note bs) = do let notemarker = mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle , mknode "w:footnoteRef" [] () ] - let notemarkerXml = RawInline (Format "openxml") $ T.pack $ ppElement notemarker + let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : Space : ils) : xs insertNoteRef (Para ils : xs) = Para (notemarkerXml : Space : ils) : xs insertNoteRef xs = Para [notemarkerXml] : xs @@ -1384,17 +1381,17 @@ inlineToOpenXML' opts (Note bs) = do inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),_)) = do contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt return - [ Elem $ mknode "w:hyperlink" [("w:anchor", T.unpack $ toBookmarkName xs)] contents ] + [ Elem $ mknode "w:hyperlink" [("w:anchor", toBookmarkName xs)] contents ] -- external link: inlineToOpenXML' opts (Link _ txt (src,_)) = do contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt extlinks <- gets stExternalLinks - id' <- case M.lookup (T.unpack src) extlinks of + id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do - i <- ("rId"++) `fmap` getUniqueId + i <- ("rId" <>) <$> getUniqueId modify $ \st -> st{ stExternalLinks = - M.insert (T.unpack src) i extlinks } + M.insert src i extlinks } return i return [ Elem $ mknode "w:hyperlink" [("r:id",id')] contents ] inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do @@ -1414,17 +1411,17 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do ,("noChangeAspect","1")] () nvPicPr = mknode "pic:nvPicPr" [] [ mknode "pic:cNvPr" - [("descr",T.unpack src),("id","0"),("name","Picture")] () + [("descr",src),("id","0"),("name","Picture")] () , cNvPicPr ] blipFill = mknode "pic:blipFill" [] - [ mknode "a:blip" [("r:embed",ident)] () + [ mknode "a:blip" [("r:embed",T.pack ident)] () , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] xfrm = mknode "a:xfrm" [] [ mknode "a:off" [("x","0"),("y","0")] () - , mknode "a:ext" [("cx",show xemu) - ,("cy",show yemu)] () ] + , mknode "a:ext" [("cx",tshow xemu) + ,("cy",tshow yemu)] () ] prstGeom = mknode "a:prstGeom" [("prst","rect")] $ mknode "a:avLst" [] () ln = mknode "a:ln" [("w","9525")] @@ -1445,12 +1442,12 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do imgElt = mknode "w:r" [] $ mknode "w:drawing" [] $ mknode "wp:inline" [] - [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () + [ mknode "wp:extent" [("cx",tshow xemu),("cy",tshow yemu)] () , mknode "wp:effectExtent" [("b","0"),("l","0"),("r","0"),("t","0")] () , mknode "wp:docPr" - [ ("descr", T.unpack $ stringify alt) - , ("title", T.unpack title) + [ ("descr", stringify alt) + , ("title", title) , ("id","1") , ("name","Picture") ] () @@ -1463,7 +1460,7 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do Just imgData -> return [Elem $ generateImgElt imgData] Nothing -> ( do --try (img, mt) <- P.fetchItem src - ident <- ("rId"++) `fmap` getUniqueId + ident <- ("rId" <>) <$> getUniqueId let imgext = case mt >>= extensionFromMimeType of @@ -1477,10 +1474,10 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do Just Svg -> ".svg" Just Emf -> ".emf" Nothing -> "" - imgpath = "media/" <> ident <> T.unpack imgext - mbMimeType = mt <|> getMimeType imgpath + imgpath = "media/" <> ident <> imgext + mbMimeType = mt <|> getMimeType (T.unpack imgpath) - imgData = (ident, imgpath, mbMimeType, img) + imgData = (T.unpack ident, T.unpack imgpath, mbMimeType, img) if T.null imgext then -- without an extension there is no rule for content type @@ -1538,20 +1535,20 @@ withDirection x = do , envTextProperties = EnvProps textStyle textProps' } -wrapBookmark :: (PandocMonad m) => T.Text -> [Content] -> WS m [Content] +wrapBookmark :: (PandocMonad m) => Text -> [Content] -> WS m [Content] wrapBookmark "" contents = return contents wrapBookmark ident contents = do id' <- getUniqueId let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id') - ,("w:name", T.unpack $ toBookmarkName ident)] () + ,("w:name", toBookmarkName ident)] () bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] () return $ Elem bookmarkStart : contents ++ [Elem bookmarkEnd] -- Word imposes a 40 character limit on bookmark names and requires -- that they begin with a letter. So we just use a hash of the -- identifier when otherwise we'd have an illegal bookmark name. -toBookmarkName :: T.Text -> T.Text +toBookmarkName :: Text -> Text toBookmarkName s | Just (c, _) <- T.uncons s , isLetter c diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 171ffe582..3f10cb437 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -24,12 +24,13 @@ import Control.Monad.State.Strict (StateT, evalState, evalStateT, get, gets, lift, modify) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 -import Data.Char (isAlphaNum, isAscii, isDigit, toLower) +import Data.Char (isAlphaNum, isAscii, isDigit) import Data.List (isInfixOf, isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust) import qualified Data.Set as Set -import qualified Data.Text as TS +import qualified Data.Text as T +import Data.Text (Text) import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import System.FilePath (takeExtension, takeFileName, makeRelative) @@ -48,16 +49,13 @@ import Text.Pandoc.Options (EPUBVersion (..), HTMLMathMethod (..), ObfuscationMethod (NoObfuscation), WrapOption (..), WriterOptions (..)) import Text.Pandoc.Shared (makeSections, normalizeDate, renderTags', - safeRead, stringify, trim, uniqueIdent, tshow) + stringify, uniqueIdent, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.UUID (getRandomUUID) import Text.Pandoc.Walk (query, walk, walkM) import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) -import Text.XML.Light (Attr (..), Element (..), Node (..), QName (..), - add_attrs, lookupAttr, node, onlyElems, - ppElement, showElement, strContent, unode, unqual) -import Text.Pandoc.XMLParser (parseXMLContents) +import Text.Pandoc.XML.Light import Text.Pandoc.XML (escapeStringForXML) import Text.DocTemplates (FromContext(lookupContext), Context(..), ToContext(toVal), Val(..)) @@ -69,7 +67,7 @@ newtype Chapter = Chapter [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] , stMediaNextId :: Int - , stEpubSubdir :: String + , stEpubSubdir :: FilePath } type E m = StateT EPUBState m @@ -78,62 +76,63 @@ data EPUBMetadata = EPUBMetadata{ epubIdentifier :: [Identifier] , epubTitle :: [Title] , epubDate :: [Date] - , epubLanguage :: String + , epubLanguage :: Text , epubCreator :: [Creator] , epubContributor :: [Creator] - , epubSubject :: [String] - , epubDescription :: Maybe String - , epubType :: Maybe String - , epubFormat :: Maybe String - , epubPublisher :: Maybe String - , epubSource :: Maybe String - , epubRelation :: Maybe String - , epubCoverage :: Maybe String - , epubRights :: Maybe String - , epubBelongsToCollection :: Maybe String - , epubGroupPosition :: Maybe String - , epubCoverImage :: Maybe String + , epubSubject :: [Text] + , epubDescription :: Maybe Text + , epubType :: Maybe Text + , epubFormat :: Maybe Text + , epubPublisher :: Maybe Text + , epubSource :: Maybe Text + , epubRelation :: Maybe Text + , epubCoverage :: Maybe Text + , epubRights :: Maybe Text + , epubBelongsToCollection :: Maybe Text + , epubGroupPosition :: Maybe Text + , epubCoverImage :: Maybe FilePath , epubStylesheets :: [FilePath] , epubPageDirection :: Maybe ProgressionDirection - , epubIbooksFields :: [(String, String)] - , epubCalibreFields :: [(String, String)] + , epubIbooksFields :: [(Text, Text)] + , epubCalibreFields :: [(Text, Text)] } deriving Show data Date = Date{ - dateText :: String - , dateEvent :: Maybe String + dateText :: Text + , dateEvent :: Maybe Text } deriving Show data Creator = Creator{ - creatorText :: String - , creatorRole :: Maybe String - , creatorFileAs :: Maybe String + creatorText :: Text + , creatorRole :: Maybe Text + , creatorFileAs :: Maybe Text } deriving Show data Identifier = Identifier{ - identifierText :: String - , identifierScheme :: Maybe String + identifierText :: Text + , identifierScheme :: Maybe Text } deriving Show data Title = Title{ - titleText :: String - , titleFileAs :: Maybe String - , titleType :: Maybe String + titleText :: Text + , titleFileAs :: Maybe Text + , titleType :: Maybe Text } deriving Show data ProgressionDirection = LTR | RTL deriving Show -dcName :: String -> QName +dcName :: Text -> QName dcName n = QName n Nothing (Just "dc") -dcNode :: Node t => String -> t -> Element +dcNode :: Node t => Text -> t -> Element dcNode = node . dcName -opfName :: String -> QName +opfName :: Text -> QName opfName n = QName n Nothing (Just "opf") -toId :: FilePath -> String -toId = map (\x -> if isAlphaNum x || x == '-' || x == '_' +toId :: FilePath -> Text +toId = T.pack . + map (\x -> if isAlphaNum x || x == '-' || x == '_' then x else '_') . takeFileName @@ -141,8 +140,8 @@ removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x -toVal' :: String -> Val TS.Text -toVal' = toVal . TS.pack +toVal' :: Text -> Val T.Text +toVal' = toVal mkEntry :: PandocMonad m => FilePath -> B.ByteString -> E m Entry mkEntry path content = do @@ -172,21 +171,21 @@ getEPUBMetadata opts meta = do if null (epubIdentifier m) then do randomId <- getRandomUUID - return $ m{ epubIdentifier = [Identifier (show randomId) Nothing] } + return $ m{ epubIdentifier = [Identifier (tshow randomId) Nothing] } else return m let addLanguage m = - if null (epubLanguage m) + if T.null (epubLanguage m) then case lookupContext "lang" (writerVariables opts) of - Just x -> return m{ epubLanguage = TS.unpack x } + Just x -> return m{ epubLanguage = x } Nothing -> do mLang <- lift $ P.lookupEnv "LANG" let localeLang = case mLang of Just lang -> - TS.map (\c -> if c == '_' then '-' else c) $ - TS.takeWhile (/='.') lang + T.map (\c -> if c == '_' then '-' else c) $ + T.takeWhile (/='.') lang Nothing -> "en-US" - return m{ epubLanguage = TS.unpack localeLang } + return m{ epubLanguage = localeLang } else return m let fixDate m = if null (epubDate m) @@ -201,7 +200,7 @@ getEPUBMetadata opts meta = do then return m else do let authors' = map stringify $ docAuthors meta - let toAuthor name = Creator{ creatorText = TS.unpack name + let toAuthor name = Creator{ creatorText = name , creatorRole = Just "aut" , creatorFileAs = Nothing } return $ m{ epubCreator = map toAuthor authors' ++ epubCreator m } @@ -249,31 +248,31 @@ addMetadataFromXML e@(Element (QName name _ (Just "dc")) attrs _ _) md where getAttr n = lookupAttr (opfName n) attrs addMetadataFromXML e@(Element (QName "meta" _ _) attrs _ _) md = case getAttr "property" of - Just s | "ibooks:" `isPrefixOf` s -> - md{ epubIbooksFields = (drop 7 s, strContent e) : + Just s | "ibooks:" `T.isPrefixOf` s -> + md{ epubIbooksFields = (T.drop 7 s, strContent e) : epubIbooksFields md } _ -> case getAttr "name" of - Just s | "calibre:" `isPrefixOf` s -> + Just s | "calibre:" `T.isPrefixOf` s -> md{ epubCalibreFields = - (drop 8 s, fromMaybe "" $ getAttr "content") : + (T.drop 8 s, fromMaybe "" $ getAttr "content") : epubCalibreFields md } _ -> md where getAttr n = lookupAttr (unqual n) attrs addMetadataFromXML _ md = md -metaValueToString :: MetaValue -> String -metaValueToString (MetaString s) = TS.unpack s -metaValueToString (MetaInlines ils) = TS.unpack $ stringify ils -metaValueToString (MetaBlocks bs) = TS.unpack $ stringify bs +metaValueToString :: MetaValue -> Text +metaValueToString (MetaString s) = s +metaValueToString (MetaInlines ils) = stringify ils +metaValueToString (MetaBlocks bs) = stringify bs metaValueToString (MetaBool True) = "true" metaValueToString (MetaBool False) = "false" metaValueToString _ = "" metaValueToPaths :: MetaValue -> [FilePath] -metaValueToPaths (MetaList xs) = map metaValueToString xs -metaValueToPaths x = [metaValueToString x] +metaValueToPaths (MetaList xs) = map (T.unpack . metaValueToString) xs +metaValueToPaths x = [T.unpack $ metaValueToString x] -getList :: TS.Text -> Meta -> (MetaValue -> a) -> [a] +getList :: T.Text -> Meta -> (MetaValue -> a) -> [a] getList s meta handleMetaValue = case lookupMeta s meta of Just (MetaList xs) -> map handleMetaValue xs @@ -297,7 +296,7 @@ getTitle meta = getList "title" meta handleMetaValue , titleType = metaValueToString <$> M.lookup "type" m } handleMetaValue mv = Title (metaValueToString mv) Nothing Nothing -getCreator :: TS.Text -> Meta -> [Creator] +getCreator :: T.Text -> Meta -> [Creator] getCreator s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = Creator{ creatorText = maybe "" metaValueToString $ M.lookup "text" m @@ -305,7 +304,7 @@ getCreator s meta = getList s meta handleMetaValue , creatorRole = metaValueToString <$> M.lookup "role" m } handleMetaValue mv = Creator (metaValueToString mv) Nothing Nothing -getDate :: TS.Text -> Meta -> [Date] +getDate :: T.Text -> Meta -> [Date] getDate s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = Date{ dateText = fromMaybe "" $ @@ -314,7 +313,7 @@ getDate s meta = getList s meta handleMetaValue handleMetaValue mv = Date { dateText = fromMaybe "" $ normalizeDate' $ metaValueToString mv , dateEvent = Nothing } -simpleList :: TS.Text -> Meta -> [String] +simpleList :: T.Text -> Meta -> [Text] simpleList s meta = case lookupMeta s meta of Just (MetaList xs) -> map metaValueToString xs @@ -339,7 +338,7 @@ metadataFromMeta opts meta = EPUBMetadata{ , epubCoverage = coverage , epubRights = rights , epubBelongsToCollection = belongsToCollection - , epubGroupPosition = groupPosition + , epubGroupPosition = groupPosition , epubCoverImage = coverImage , epubStylesheets = stylesheets , epubPageDirection = pageDirection @@ -363,31 +362,30 @@ metadataFromMeta opts meta = EPUBMetadata{ coverage = metaValueToString <$> lookupMeta "coverage" meta rights = metaValueToString <$> lookupMeta "rights" meta belongsToCollection = metaValueToString <$> lookupMeta "belongs-to-collection" meta - groupPosition = metaValueToString <$> lookupMeta "group-position" meta - coverImage = - (TS.unpack <$> lookupContext "epub-cover-image" - (writerVariables opts)) + groupPosition = metaValueToString <$> lookupMeta "group-position" meta + coverImage = T.unpack <$> + lookupContext "epub-cover-image" (writerVariables opts) `mplus` (metaValueToString <$> lookupMeta "cover-image" meta) mCss = lookupMeta "css" meta <|> lookupMeta "stylesheet" meta stylesheets = maybe [] metaValueToPaths mCss ++ case lookupContext "css" (writerVariables opts) of - Just xs -> map TS.unpack xs + Just xs -> map T.unpack xs Nothing -> case lookupContext "css" (writerVariables opts) of - Just x -> [TS.unpack x] + Just x -> [T.unpack x] Nothing -> [] - pageDirection = case map toLower . metaValueToString <$> + pageDirection = case T.toLower . metaValueToString <$> lookupMeta "page-progression-direction" meta of Just "ltr" -> Just LTR Just "rtl" -> Just RTL _ -> Nothing ibooksFields = case lookupMeta "ibooks" meta of Just (MetaMap mp) - -> M.toList $ M.mapKeys TS.unpack $ M.map metaValueToString mp + -> M.toList $ M.map metaValueToString mp _ -> [] calibreFields = case lookupMeta "calibre" meta of Just (MetaMap mp) - -> M.toList $ M.mapKeys TS.unpack $ M.map metaValueToString mp + -> M.toList $ M.map metaValueToString mp _ -> [] -- | Produce an EPUB2 file from a Pandoc document. @@ -413,9 +411,11 @@ writeEPUB :: PandocMonad m writeEPUB epubVersion opts doc = do let epubSubdir = writerEpubSubdirectory opts -- sanity check on epubSubdir - unless (TS.all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ + unless (T.all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ throwError $ PandocEpubSubdirectoryError epubSubdir - let initState = EPUBState { stMediaPaths = [], stMediaNextId = 0, stEpubSubdir = TS.unpack epubSubdir } + let initState = EPUBState { stMediaPaths = [] + , stMediaNextId = 0 + , stEpubSubdir = T.unpack epubSubdir } evalStateT (pandocToEPUB epubVersion opts doc) initState pandocToEPUB :: PandocMonad m @@ -439,7 +439,7 @@ pandocToEPUB version opts doc = do [] -> case epubTitle metadata of [] -> "UNTITLED" (x:_) -> titleText x - x -> TS.unpack $ stringify x + x -> stringify x -- stylesheet stylesheets <- case epubStylesheets metadata of @@ -461,7 +461,8 @@ pandocToEPUB version opts doc = do (ListVal $ map (\e -> toVal' $ (if useprefix then "../" else "") <> - makeRelative epubSubdir (eRelativePath e)) + T.pack + (makeRelative epubSubdir (eRelativePath e))) stylesheetEntries) mempty @@ -490,18 +491,19 @@ pandocToEPUB version opts doc = do case imageSize opts' (B.toStrict imgContent) of Right sz -> return $ sizeInPixels sz Left err' -> (0, 0) <$ report - (CouldNotDetermineImageSize (TS.pack img) err') + (CouldNotDetermineImageSize (T.pack img) err') cpContent <- lift $ writeHtml opts'{ writerVariables = Context (M.fromList [ ("coverpage", toVal' "true"), ("pagetitle", toVal $ - escapeStringForXML $ TS.pack plainTitle), - ("cover-image", toVal' coverImageName), + escapeStringForXML plainTitle), + ("cover-image", + toVal' $ T.pack coverImageName), ("cover-image-width", toVal' $ - show coverImageWidth), + tshow coverImageWidth), ("cover-image-height", toVal' $ - show coverImageHeight)]) <> + tshow coverImageHeight)]) <> cssvars True <> vars } (Pandoc meta []) coverEntry <- mkEntry "text/cover.xhtml" cpContent @@ -517,7 +519,7 @@ pandocToEPUB version opts doc = do ("titlepage", toVal' "true"), ("body-type", toVal' "frontmatter"), ("pagetitle", toVal $ - escapeStringForXML $ TS.pack plainTitle)]) + escapeStringForXML plainTitle)]) <> cssvars True <> vars } (Pandoc meta []) tpEntry <- mkEntry "text/title_page.xhtml" tpContent @@ -526,7 +528,7 @@ pandocToEPUB version opts doc = do let matchingGlob f = do xs <- lift $ P.glob f when (null xs) $ - report $ CouldNotFetchResource (TS.pack f) "glob did not match any font files" + report $ CouldNotFetchResource (T.pack f) "glob did not match any font files" return xs let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<< lift (P.readFileLazy f) @@ -573,13 +575,13 @@ pandocToEPUB version opts doc = do let chapters' = secsToChapters secs - let extractLinkURL' :: Int -> Inline -> [(TS.Text, TS.Text)] + let extractLinkURL' :: Int -> Inline -> [(T.Text, T.Text)] extractLinkURL' num (Span (ident, _, _) _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL' num (Link (ident, _, _) _ _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL' num (Image (ident, _, _) _ _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL' num (RawInline fmt raw) | isHtmlFormat fmt = foldr (\tag -> @@ -587,18 +589,18 @@ pandocToEPUB version opts doc = do TagOpen{} -> case fromAttrib "id" tag of "" -> id - x -> ((x, TS.pack (showChapter num) <> "#" <> x):) + x -> ((x, showChapter num <> "#" <> x):) _ -> id) [] (parseTags raw) extractLinkURL' _ _ = [] - let extractLinkURL :: Int -> Block -> [(TS.Text, TS.Text)] + let extractLinkURL :: Int -> Block -> [(T.Text, T.Text)] extractLinkURL num (Div (ident, _, _) _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL num (Header _ (ident, _, _) _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL num (Table (ident,_,_) _ _ _ _ _) - | not (TS.null ident) = [(ident, TS.pack (showChapter num) <> "#" <> ident)] + | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)] extractLinkURL num (RawBlock fmt raw) | isHtmlFormat fmt = foldr (\tag -> @@ -606,7 +608,7 @@ pandocToEPUB version opts doc = do TagOpen{} -> case fromAttrib "id" tag of "" -> id - x -> ((x, TS.pack (showChapter num) <> "#" <> x):) + x -> ((x, showChapter num <> "#" <> x):) _ -> id) [] (parseTags raw) extractLinkURL num b = query (extractLinkURL' num) b @@ -617,7 +619,7 @@ pandocToEPUB version opts doc = do let fixInternalReferences :: Inline -> Inline fixInternalReferences (Link attr lab (src, tit)) - | Just ('#', xs) <- TS.uncons src = case lookup xs reftable of + | Just ('#', xs) <- T.uncons src = case lookup xs reftable of Just ys -> Link attr lab (ys, tit) Nothing -> Link attr lab (src, tit) fixInternalReferences x = x @@ -630,7 +632,7 @@ pandocToEPUB version opts doc = do chapters' let chapToEntry num (Chapter bs) = - mkEntry ("text/" ++ showChapter num) =<< + mkEntry ("text/" ++ T.unpack (showChapter num)) =<< writeHtml opts'{ writerVariables = Context (M.fromList [("body-type", toVal' bodyType), @@ -677,12 +679,12 @@ pandocToEPUB version opts doc = do let chapterNode ent = unode "item" ! ([("id", toId $ makeRelative epubSubdir $ eRelativePath ent), - ("href", makeRelative epubSubdir + ("href", T.pack $ makeRelative epubSubdir $ eRelativePath ent), ("media-type", "application/xhtml+xml")] ++ case props ent of [] -> [] - xs -> [("properties", unwords xs)]) + xs -> [("properties", T.unwords xs)]) $ () let chapterRefNode ent = unode "itemref" ! @@ -691,17 +693,17 @@ pandocToEPUB version opts doc = do let pictureNode ent = unode "item" ! [("id", toId $ makeRelative epubSubdir $ eRelativePath ent), - ("href", makeRelative epubSubdir + ("href", T.pack $ makeRelative epubSubdir $ eRelativePath ent), ("media-type", - maybe "application/octet-stream" TS.unpack + fromMaybe "application/octet-stream" $ mediaTypeOf $ eRelativePath ent)] $ () let fontNode ent = unode "item" ! [("id", toId $ makeRelative epubSubdir $ eRelativePath ent), - ("href", makeRelative epubSubdir + ("href", T.pack $ makeRelative epubSubdir $ eRelativePath ent), - ("media-type", maybe "" TS.unpack $ + ("media-type", fromMaybe "" $ getMimeType $ eRelativePath ent)] $ () let tocTitle = maybe plainTitle @@ -710,7 +712,7 @@ pandocToEPUB version opts doc = do (x:_) -> return $ identifierText x -- use first identifier as UUID [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen currentTime <- lift P.getTimestamp - let contentsData = UTF8.fromStringLazy $ ppTopElement $ + let contentsData = UTF8.fromTextLazy $ TL.fromStrict $ ppTopElement $ unode "package" ! ([("version", case version of EPUB2 -> "2.0" @@ -728,7 +730,8 @@ pandocToEPUB version opts doc = do ,("media-type","application/xhtml+xml")] ++ [("properties","nav") | epub3 ]) $ () ] ++ - [ unode "item" ! [("id","stylesheet" ++ show n), ("href",fp) + [ unode "item" ! [("id","stylesheet" <> tshow n) + , ("href", T.pack fp) ,("media-type","text/css")] $ () | (n :: Int, fp) <- zip [1..] (map (makeRelative epubSubdir . eRelativePath) @@ -773,7 +776,7 @@ pandocToEPUB version opts doc = do let tocLevel = writerTOCDepth opts let navPointNode :: PandocMonad m - => (Int -> [Inline] -> TS.Text -> [Element] -> Element) + => (Int -> [Inline] -> T.Text -> [Element] -> Element) -> Block -> StateT Int m [Element] navPointNode formatter (Div (ident,_,_) (Header lvl (_,_,kvs) ils : children)) = @@ -783,7 +786,7 @@ pandocToEPUB version opts doc = do n <- get modify (+1) let num = fromMaybe "" $ lookup "number" kvs - let tit = if writerNumberSections opts && not (TS.null num) + let tit = if writerNumberSections opts && not (T.null num) then Span ("", ["section-header-number"], []) [Str num] : Space : ils else ils @@ -797,21 +800,21 @@ pandocToEPUB version opts doc = do concat <$> mapM (navPointNode formatter) bs navPointNode _ _ = return [] - let navMapFormatter :: Int -> [Inline] -> TS.Text -> [Element] -> Element + let navMapFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element navMapFormatter n tit src subs = unode "navPoint" ! - [("id", "navPoint-" ++ show n)] $ - [ unode "navLabel" $ unode "text" $ TS.unpack $ stringify tit - , unode "content" ! [("src", "text/" <> TS.unpack src)] $ () + [("id", "navPoint-" <> tshow n)] $ + [ unode "navLabel" $ unode "text" $ stringify tit + , unode "content" ! [("src", "text/" <> src)] $ () ] ++ subs let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ - [ unode "navLabel" $ unode "text" (TS.unpack $ stringify $ docTitle' meta) + [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) , unode "content" ! [("src", "text/title_page.xhtml")] $ () ] navMap <- lift $ evalStateT (concat <$> mapM (navPointNode navMapFormatter) secs) 1 - let tocData = UTF8.fromStringLazy $ ppTopElement $ + let tocData = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "ncx" ! [("version","2005-1") ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $ [ unode "head" $ @@ -833,11 +836,11 @@ pandocToEPUB version opts doc = do ] tocEntry <- mkEntry "toc.ncx" tocData - let navXhtmlFormatter :: Int -> [Inline] -> TS.Text -> [Element] -> Element + let navXhtmlFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element navXhtmlFormatter n tit src subs = unode "li" ! - [("id", "toc-li-" ++ show n)] $ + [("id", "toc-li-" <> tshow n)] $ (unode "a" ! - [("href", "text/" <> TS.unpack src)] + [("href", "text/" <> src)] $ titElements) : case subs of [] -> [] @@ -850,7 +853,7 @@ pandocToEPUB version opts doc = do , writerVariables = Context (M.fromList [("pagetitle", toVal $ - escapeStringForXML $ TS.pack plainTitle)]) + escapeStringForXML plainTitle)]) <> writerVariables opts} (Pandoc nullMeta [Plain $ walk clean tit])) of @@ -865,7 +868,7 @@ pandocToEPUB version opts doc = do tocBlocks <- lift $ evalStateT (concat <$> mapM (navPointNode navXhtmlFormatter) secs) 1 let navBlocks = [RawBlock (Format "html") - $ TS.pack $ showElement $ -- prettyprinting introduces bad spaces + $ showElement $ -- prettyprinting introduces bad spaces unode navtag ! ([("epub:type","toc") | epub3] ++ [("id","toc")]) $ [ unode "h1" ! [("id","toc-title")] $ tocTitle @@ -875,21 +878,21 @@ pandocToEPUB version opts doc = do [ unode "a" ! [("href", "text/title_page.xhtml") ,("epub:type", "titlepage")] $ - ("Title Page" :: String) ] : + ("Title Page" :: Text) ] : [ unode "li" [ unode "a" ! [("href", "text/cover.xhtml") ,("epub:type", "cover")] $ - ("Cover" :: String)] | + ("Cover" :: Text)] | isJust (epubCoverImage metadata) ] ++ [ unode "li" [ unode "a" ! [("href", "#toc") ,("epub:type", "toc")] $ - ("Table of Contents" :: String) + ("Table of Contents" :: Text) ] | writerTableOfContents opts ] else [] - let landmarks = [RawBlock (Format "html") $ TS.pack $ ppElement $ + let landmarks = [RawBlock (Format "html") $ ppElement $ unode "nav" ! [("epub:type","landmarks") ,("id","landmarks") ,("hidden","hidden")] $ @@ -910,22 +913,22 @@ pandocToEPUB version opts doc = do UTF8.fromStringLazy "application/epub+zip" -- container.xml - let containerData = UTF8.fromStringLazy $ ppTopElement $ + let containerData = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ unode "rootfile" ! [("full-path", (if null epubSubdir then "" - else epubSubdir ++ "/") ++ "content.opf") + else T.pack epubSubdir <> "/") <> "content.opf") ,("media-type","application/oebps-package+xml")] $ () containerEntry <- mkEntry "META-INF/container.xml" containerData -- com.apple.ibooks.display-options.xml - let apple = UTF8.fromStringLazy $ ppTopElement $ + let apple = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "display_options" $ unode "platform" ! [("name","*")] $ - unode "option" ! [("name","specified-fonts")] $ ("true" :: String) + unode "option" ! [("name","specified-fonts")] $ ("true" :: Text) appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple -- construct archive @@ -947,7 +950,8 @@ metadataElement version md currentTime = ++ publisherNodes ++ sourceNodes ++ relationNodes ++ coverageNodes ++ rightsNodes ++ coverImageNodes ++ modifiedNodes ++ belongsToCollectionNodes - withIds base f = concat . zipWith f (map (\x -> base ++ ('-' : show x)) + withIds base f = concat . zipWith f (map (\x -> base <> + T.cons '-' (tshow x)) ([1..] :: [Int])) identifierNodes = withIds "epub-id" toIdentifierNode $ epubIdentifier md @@ -961,9 +965,9 @@ metadataElement version md currentTime = (x:_) -> [dcNode "date" ! [("id","epub-date")] $ dateText x] ibooksNodes = map ibooksNode (epubIbooksFields md) - ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" ++ k)] $ v + ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" <> k)] $ v calibreNodes = map calibreNode (epubCalibreFields md) - calibreNode (k, v) = unode "meta" ! [("name", "calibre:" ++ k), + calibreNode (k, v) = unode "meta" ! [("name", "calibre:" <> k), ("content", v)] $ () languageNodes = [dcTag "language" $ epubLanguage md] creatorNodes = withIds "epub-creator" (toCreatorNode "creator") $ @@ -989,12 +993,12 @@ metadataElement version md currentTime = maybe [] (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-id-1")] $ belongsToCollection ) : - [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: String) ]) + [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: Text) ]) (epubBelongsToCollection md)++ maybe [] (\groupPosition -> [unode "meta" ! [("refines", "#epub-id-1"), ("property", "group-position")] $ groupPosition ]) (epubGroupPosition md) - dcTag n s = unode ("dc:" ++ n) s + dcTag n s = unode ("dc:" <> n) s dcTag' n s = [dcTag n s] toIdentifierNode id' (Identifier txt scheme) | version == EPUB2 = [dcNode "identifier" ! @@ -1002,7 +1006,7 @@ metadataElement version md currentTime = txt] | otherwise = (dcNode "identifier" ! [("id",id')] $ txt) : maybe [] ((\x -> [unode "meta" ! - [ ("refines",'#':id') + [ ("refines","#" <> id') , ("property","identifier-type") , ("scheme","onix:codelist5") ] @@ -1018,10 +1022,10 @@ metadataElement version md currentTime = (creatorRole creator >>= toRelator)) $ creatorText creator] | otherwise = [dcNode s ! [("id",id')] $ creatorText creator] ++ maybe [] (\x -> [unode "meta" ! - [("refines",'#':id'),("property","file-as")] $ x]) + [("refines","#" <> id'),("property","file-as")] $ x]) (creatorFileAs creator) ++ maybe [] (\x -> [unode "meta" ! - [("refines",'#':id'),("property","role"), + [("refines","#" <> id'),("property","role"), ("scheme","marc:relators")] $ x]) (creatorRole creator >>= toRelator) toTitleNode id' title @@ -1033,16 +1037,16 @@ metadataElement version md currentTime = | otherwise = [dcNode "title" ! [("id",id')] $ titleText title] ++ maybe [] (\x -> [unode "meta" ! - [("refines",'#':id'),("property","file-as")] $ x]) + [("refines","#" <> id'),("property","file-as")] $ x]) (titleFileAs title) ++ maybe [] (\x -> [unode "meta" ! - [("refines",'#':id'),("property","title-type")] $ x]) + [("refines","#" <> id'),("property","title-type")] $ x]) (titleType title) toDateNode id' date = [dcNode "date" ! (("id",id') : maybe [] (\x -> [("opf:event",x)]) (dateEvent date)) $ dateText date] - schemeToOnix :: String -> String + schemeToOnix :: Text -> Text schemeToOnix "ISBN-10" = "02" schemeToOnix "GTIN-13" = "03" schemeToOnix "UPC" = "04" @@ -1060,48 +1064,48 @@ metadataElement version md currentTime = schemeToOnix "OLCC" = "28" schemeToOnix _ = "01" -showDateTimeISO8601 :: UTCTime -> String -showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" +showDateTimeISO8601 :: UTCTime -> Text +showDateTimeISO8601 = T.pack . formatTime defaultTimeLocale "%FT%TZ" transformTag :: PandocMonad m - => Tag TS.Text - -> E m (Tag TS.Text) + => Tag T.Text + -> E m (Tag T.Text) transformTag tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && isNothing (lookup "data-external" attr) = do let src = fromAttrib "src" tag let poster = fromAttrib "poster" tag - newsrc <- modifyMediaRef $ TS.unpack src - newposter <- modifyMediaRef $ TS.unpack poster + newsrc <- modifyMediaRef $ T.unpack src + newposter <- modifyMediaRef $ T.unpack poster let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++ - [("src", "../" <> newsrc) | not (TS.null newsrc)] ++ - [("poster", "../" <> newposter) | not (TS.null newposter)] + [("src", "../" <> newsrc) | not (T.null newsrc)] ++ + [("poster", "../" <> newposter) | not (T.null newposter)] return $ TagOpen name attr' transformTag tag = return tag modifyMediaRef :: PandocMonad m => FilePath - -> E m TS.Text + -> E m T.Text modifyMediaRef "" = return "" modifyMediaRef oldsrc = do media <- gets stMediaPaths case lookup oldsrc media of - Just (n,_) -> return $ TS.pack n + Just (n,_) -> return $ T.pack n Nothing -> catchError - (do (img, mbMime) <- P.fetchItem $ TS.pack oldsrc - let ext = maybe (takeExtension (takeWhile (/='?') oldsrc)) TS.unpack + (do (img, mbMime) <- P.fetchItem $ T.pack oldsrc + let ext = maybe (takeExtension (takeWhile (/='?') oldsrc)) T.unpack (("." <>) <$> (mbMime >>= extensionFromMimeType)) newName <- getMediaNextNewName ext let newPath = "media/" ++ newName entry <- mkEntry newPath (B.fromChunks . (:[]) $ img) modify $ \st -> st{ stMediaPaths = (oldsrc, (newPath, Just entry)):media} - return $ TS.pack newPath) + return $ T.pack newPath) (\e -> do - report $ CouldNotFetchResource (TS.pack oldsrc) (tshow e) - return $ TS.pack oldsrc) + report $ CouldNotFetchResource (T.pack oldsrc) (tshow e) + return $ T.pack oldsrc) -getMediaNextNewName :: PandocMonad m => String -> E m String +getMediaNextNewName :: PandocMonad m => FilePath -> E m FilePath getMediaNextNewName ext = do nextId <- gets stMediaNextId modify $ \st -> st { stMediaNextId = nextId + 1 } @@ -1128,11 +1132,11 @@ transformInline :: PandocMonad m -> Inline -> E m Inline transformInline _opts (Image attr lab (src,tit)) = do - newsrc <- modifyMediaRef $ TS.unpack src + newsrc <- modifyMediaRef $ T.unpack src return $ Image attr lab ("../" <> newsrc, tit) transformInline opts x@(Math t m) | WebTeX url <- writerHTMLMathMethod opts = do - newsrc <- modifyMediaRef (TS.unpack url <> urlEncode (TS.unpack m)) + newsrc <- modifyMediaRef (T.unpack url <> urlEncode (T.unpack m)) let mathclass = if t == DisplayMath then "display" else "inline" return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] ("../" <> newsrc, "")] @@ -1143,40 +1147,26 @@ transformInline _opts (RawInline fmt raw) return $ RawInline fmt (renderTags' tags') transformInline _ x = return x -(!) :: (t -> Element) -> [(String, String)] -> t -> Element +(!) :: (t -> Element) -> [(Text, Text)] -> t -> Element (!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n) --- | Version of 'ppTopElement' that specifies UTF-8 encoding. -ppTopElement :: Element -> String -ppTopElement = ("\n" ++) . unEntity . ppElement - -- unEntity removes numeric entities introduced by ppElement - -- (kindlegen seems to choke on these). - where unEntity [] = "" - unEntity ('&':'#':xs) = - let (ds,ys) = break (==';') xs - rest = drop 1 ys - in case safeRead (TS.pack $ "'\\" <> ds <> "'") of - Just x -> x : unEntity rest - Nothing -> '&':'#':unEntity xs - unEntity (x:xs) = x : unEntity xs - mediaTypeOf :: FilePath -> Maybe MimeType mediaTypeOf x = let mediaPrefixes = ["image", "video", "audio"] in case getMimeType x of - Just y | any (`TS.isPrefixOf` y) mediaPrefixes -> Just y + Just y | any (`T.isPrefixOf` y) mediaPrefixes -> Just y _ -> Nothing -- Returns filename for chapter number. -showChapter :: Int -> String -showChapter = printf "ch%03d.xhtml" +showChapter :: Int -> Text +showChapter = T.pack . printf "ch%03d.xhtml" -- Add identifiers to any headers without them. addIdentifiers :: WriterOptions -> [Block] -> [Block] addIdentifiers opts bs = evalState (mapM go bs) Set.empty where go (Header n (ident,classes,kvs) ils) = do ids <- get - let ident' = if TS.null ident + let ident' = if T.null ident then uniqueIdent (writerExtensions opts) ils ids else ident modify $ Set.insert ident' @@ -1184,27 +1174,27 @@ addIdentifiers opts bs = evalState (mapM go bs) Set.empty go x = return x -- Variant of normalizeDate that allows partial dates: YYYY, YYYY-MM -normalizeDate' :: String -> Maybe String -normalizeDate' = fmap TS.unpack . go . trim . TS.pack +normalizeDate' :: Text -> Maybe Text +normalizeDate' = go . T.strip where go xs - | TS.length xs == 4 -- YYY - , TS.all isDigit xs = Just xs - | (y, s) <- TS.splitAt 4 xs -- YYY-MM - , Just ('-', m) <- TS.uncons s - , TS.length m == 2 - , TS.all isDigit y && TS.all isDigit m = Just xs + | T.length xs == 4 -- YYY + , T.all isDigit xs = Just xs + | (y, s) <- T.splitAt 4 xs -- YYY-MM + , Just ('-', m) <- T.uncons s + , T.length m == 2 + , T.all isDigit y && T.all isDigit m = Just xs | otherwise = normalizeDate xs -toRelator :: String -> Maybe String +toRelator :: Text -> Maybe Text toRelator x | x `elem` relators = Just x - | otherwise = lookup (map toLower x) relatorMap + | otherwise = lookup (T.toLower x) relatorMap -relators :: [String] +relators :: [Text] relators = map snd relatorMap -relatorMap :: [(String, String)] +relatorMap :: [(Text, Text)] relatorMap = [("abridger", "abr") ,("actor", "act") diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 9334d6e9a..3b5d04427 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -25,15 +25,12 @@ import Data.ByteString.Base64 (encode) import Data.Char (isAscii, isControl, isSpace) import Data.Either (lefts, rights) import Data.List (intercalate) -import Data.Text (Text, pack) +import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE import Network.HTTP (urlEncode) -import Text.XML.Light -import qualified Text.XML.Light as X -import qualified Text.XML.Light.Cursor as XC -import Text.Pandoc.XMLParser (parseXMLContents) +import Text.Pandoc.XML.Light as X import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import qualified Text.Pandoc.Class.PandocMonad as P @@ -44,6 +41,7 @@ import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) import Text.Pandoc.Shared (capitalize, isURI, orderedListMarkers, makeSections, tshow, stringify) import Text.Pandoc.Writers.Shared (lookupMetaString, toLegacyTable) +import Data.Generics (everywhere, mkT) -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. @@ -88,7 +86,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do (imgs,missing) <- get >>= (lift . fetchImages . imagesToFetch) let body' = replaceImagesWithAlt missing body let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) - return $ pack $ xml_head ++ showContent fb2_xml ++ "\n" + return $ xml_head <> showContent fb2_xml <> "\n" where xml_head = "\n" fb2_attrs = @@ -100,8 +98,8 @@ pandocToFB2 opts (Pandoc meta blocks) = do description :: PandocMonad m => Meta -> FBM m Content description meta' = do let genre = case lookupMetaString "genre" meta' of - "" -> el "genre" ("unrecognised" :: String) - s -> el "genre" (T.unpack s) + "" -> el "genre" ("unrecognised" :: Text) + s -> el "genre" s bt <- booktitle meta' let as = authors meta' dd <- docdate meta' @@ -112,7 +110,7 @@ description meta' = do Just (MetaInlines [Str s]) -> [el "lang" $ iso639 s] Just (MetaString s) -> [el "lang" $ iso639 s] _ -> [] - where iso639 = T.unpack . T.takeWhile (/= '-') -- Convert BCP 47 to ISO 639 + where iso639 = T.takeWhile (/= '-') -- Convert BCP 47 to ISO 639 let coverimage url = do let img = Image nullAttr mempty (url, "") im <- insertImage InlineImage img @@ -124,7 +122,7 @@ description meta' = do return $ el "description" [ el "title-info" (genre : (as ++ bt ++ annotation ++ dd ++ coverpage ++ lang)) - , el "document-info" [el "program-used" ("pandoc" :: String)] + , el "document-info" [el "program-used" ("pandoc" :: Text)] ] booktitle :: PandocMonad m => Meta -> FBM m [Content] @@ -137,15 +135,15 @@ authors meta' = cMap author (docAuthors meta') author :: [Inline] -> [Content] author ss = - let ws = words . cMap plain $ ss - email = el "email" <$> take 1 (filter ('@' `elem`) ws) - ws' = filter ('@' `notElem`) ws + let ws = T.words $ mconcat $ map plain ss + email = el "email" <$> take 1 (filter (T.any (=='@')) ws) + ws' = filter (not . T.any (== '@')) ws names = case ws' of [nickname] -> [ el "nickname" nickname ] [fname, lname] -> [ el "first-name" fname , el "last-name" lname ] (fname:rest) -> [ el "first-name" fname - , el "middle-name" (concat . init $ rest) + , el "middle-name" (T.concat . init $ rest) , el "last-name" (last rest) ] [] -> [] in list $ el "author" (names ++ email) @@ -206,7 +204,7 @@ renderFootnotes = do el "body" ([uattr "name" "notes"], map renderFN (reverse fns)) where renderFN (n, idstr, cs) = - let fn_texts = el "title" (el "p" (show n)) : cs + let fn_texts = el "title" (el "p" (tshow n)) : cs in el "section" ([uattr "id" idstr], fn_texts) -- | Fetch images and encode them for the FictionBook XML. @@ -282,7 +280,7 @@ isMimeType s = where types = ["text","image","audio","video","application","message","multipart"] valid c = isAscii c && not (isControl c) && not (isSpace c) && - c `notElem` ("()<>@,;:\\\"/[]?=" :: String) + c `notElem` ("()<>@,;:\\\"/[]?=" :: [Char]) footnoteID :: Int -> Text footnoteID i = "n" <> tshow i @@ -306,7 +304,7 @@ blockToXml (Para [Image atr alt (src,tgt)]) = insertImage NormalImage (Image atr alt (src,tit)) blockToXml (Para ss) = list . el "p" <$> cMapM toXml ss blockToXml (CodeBlock _ s) = return . spaceBeforeAfter . - map (el "p" . el "code" . T.unpack) . T.lines $ s + map (el "p" . el "code") . T.lines $ s blockToXml (RawBlock f str) = if f == Format "fb2" then @@ -346,11 +344,11 @@ blockToXml (Table _ blkCapt specs thead tbody tfoot) = do c <- el "emphasis" <$> cMapM toXml caption return [el "table" (hd <> bd), el "p" c] where - mkrow :: PandocMonad m => String -> [[Block]] -> [Alignment] -> FBM m Content + mkrow :: PandocMonad m => Text -> [[Block]] -> [Alignment] -> FBM m Content mkrow tag cells aligns' = el "tr" <$> mapM (mkcell tag) (zip cells aligns') -- - mkcell :: PandocMonad m => String -> ([Block], Alignment) -> FBM m Content + mkcell :: PandocMonad m => Text -> ([Block], Alignment) -> FBM m Content mkcell tag (cell, align) = do cblocks <- cMapM blockToXml cell return $ el tag ([align_attr align], cblocks) @@ -424,7 +422,7 @@ toXml (Quoted DoubleQuote ss) = do inner <- cMapM toXml ss return $ [txt "“"] ++ inner ++ [txt "”"] toXml (Cite _ ss) = cMapM toXml ss -- FIXME: support citation styles -toXml (Code _ s) = return [el "code" $ T.unpack s] +toXml (Code _ s) = return [el "code" s] toXml Space = return [txt " "] toXml SoftBreak = return [txt "\n"] toXml LineBreak = return [txt "\n"] @@ -456,7 +454,7 @@ insertMath immode formula = do let imgurl = url <> T.pack (urlEncode $ T.unpack formula) let img = Image nullAttr alt (imgurl, "") insertImage immode img - _ -> return [el "code" $ T.unpack formula] + _ -> return [el "code" formula] insertImage :: PandocMonad m => ImageMode -> Inline -> FBM m [Content] insertImage immode (Image _ alt (url,ttl)) = do @@ -471,31 +469,16 @@ insertImage immode (Image _ alt (url,ttl)) = do el "image" $ [ attr ("l","href") ("#" <> fname) , attr ("l","type") (tshow immode) - , uattr "alt" (T.pack $ cMap plain alt) ] + , uattr "alt" (mconcat $ map plain alt) ] ++ ttlattr insertImage _ _ = error "unexpected inline instead of image" replaceImagesWithAlt :: [Text] -> Content -> Content -replaceImagesWithAlt missingHrefs body = - let cur = XC.fromContent body - cur' = replaceAll cur - in XC.toTree . XC.root $ cur' +replaceImagesWithAlt missingHrefs = everywhere (mkT go) where - -- - replaceAll :: XC.Cursor -> XC.Cursor - replaceAll c = - let n = XC.current c - c' = if isImage n && isMissing n - then XC.modifyContent replaceNode c - else c - in case XC.nextDF c' of - (Just cnext) -> replaceAll cnext - Nothing -> c' -- end of document - -- - isImage :: Content -> Bool - isImage (Elem e) = elName e == uname "image" - isImage _ = False - -- + go c = if isMissing c + then replaceNode c + else c isMissing (Elem img@Element{}) = let imgAttrs = elAttribs img badAttrs = map (attr ("l","href")) missingHrefs @@ -505,18 +488,18 @@ replaceImagesWithAlt missingHrefs body = replaceNode :: Content -> Content replaceNode n@(Elem img@Element{}) = let attrs = elAttribs img - alt = getAttrVal attrs (uname "alt") + alt = getAttrVal attrs (unqual "alt") imtype = getAttrVal attrs (qname "l" "type") in case (alt, imtype) of (Just alt', Just imtype') -> - if imtype' == show NormalImage + if imtype' == tshow NormalImage then el "p" alt' - else txt $ T.pack alt' - (Just alt', Nothing) -> txt $ T.pack alt' -- no type attribute + else txt alt' + (Just alt', Nothing) -> txt alt' -- no type attribute _ -> n -- don't replace if alt text is not found replaceNode n = n -- - getAttrVal :: [X.Attr] -> QName -> Maybe String + getAttrVal :: [X.Attr] -> QName -> Maybe Text getAttrVal attrs name = case filter ((name ==) . attrKey) attrs of (a:_) -> Just (attrVal a) @@ -524,7 +507,7 @@ replaceImagesWithAlt missingHrefs body = -- | Wrap all inlines with an XML tag (given its unqualified name). -wrap :: PandocMonad m => String -> [Inline] -> FBM m Content +wrap :: PandocMonad m => Text -> [Inline] -> FBM m Content wrap tagname inlines = el tagname `liftM` cMapM toXml inlines -- " Create a singleton list. @@ -532,31 +515,31 @@ list :: a -> [a] list = (:[]) -- | Convert an 'Inline' to plaintext. -plain :: Inline -> String -plain (Str s) = T.unpack s -plain (Emph ss) = cMap plain ss -plain (Underline ss) = cMap plain ss -plain (Span _ ss) = cMap plain ss -plain (Strong ss) = cMap plain ss -plain (Strikeout ss) = cMap plain ss -plain (Superscript ss) = cMap plain ss -plain (Subscript ss) = cMap plain ss -plain (SmallCaps ss) = cMap plain ss -plain (Quoted _ ss) = cMap plain ss -plain (Cite _ ss) = cMap plain ss -- FIXME -plain (Code _ s) = T.unpack s +plain :: Inline -> Text +plain (Str s) = s +plain (Emph ss) = mconcat $ map plain ss +plain (Underline ss) = mconcat $ map plain ss +plain (Span _ ss) = mconcat $ map plain ss +plain (Strong ss) = mconcat $ map plain ss +plain (Strikeout ss) = mconcat $ map plain ss +plain (Superscript ss) = mconcat $ map plain ss +plain (Subscript ss) = mconcat $ map plain ss +plain (SmallCaps ss) = mconcat $ map plain ss +plain (Quoted _ ss) = mconcat $ map plain ss +plain (Cite _ ss) = mconcat $ map plain ss -- FIXME +plain (Code _ s) = s plain Space = " " plain SoftBreak = " " plain LineBreak = "\n" -plain (Math _ s) = T.unpack s +plain (Math _ s) = s plain (RawInline _ _) = "" -plain (Link _ text (url,_)) = concat (map plain text ++ [" <", T.unpack url, ">"]) -plain (Image _ alt _) = cMap plain alt +plain (Link _ text (url,_)) = mconcat (map plain text ++ [" <", url, ">"]) +plain (Image _ alt _) = mconcat $ map plain alt plain (Note _) = "" -- FIXME -- | Create an XML element. el :: (Node t) - => String -- ^ unqualified element name + => Text -- ^ unqualified element name -> t -- ^ node contents -> Content -- ^ XML content el name cs = Elem $ unode name cs @@ -569,22 +552,18 @@ spaceBeforeAfter cs = -- | Create a plain-text XML content. txt :: Text -> Content -txt s = Text $ CData CDataText (T.unpack s) Nothing +txt s = Text $ CData CDataText s Nothing -- | Create an XML attribute with an unqualified name. -uattr :: String -> Text -> Text.XML.Light.Attr -uattr name = Attr (uname name) . T.unpack +uattr :: Text -> Text -> X.Attr +uattr name = Attr (unqual name) -- | Create an XML attribute with a qualified name from given namespace. -attr :: (String, String) -> Text -> Text.XML.Light.Attr -attr (ns, name) = Attr (qname ns name) . T.unpack - --- | Unqualified name -uname :: String -> QName -uname name = QName name Nothing Nothing +attr :: (Text, Text) -> Text -> X.Attr +attr (ns, name) = Attr (qname ns name) -- | Qualified name -qname :: String -> String -> QName +qname :: Text -> Text -> QName qname ns name = QName name Nothing (Just ns) -- | Abbreviation for 'concatMap'. diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 06369b4db..101b236aa 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -40,9 +40,9 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.XML -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light import Text.TeXMath -import Text.XML.Light +import qualified Text.XML.Light as XL newtype ODTState = ODTState { stEntries :: [Entry] } @@ -181,18 +181,20 @@ updateStyleWithLang (Just lang) arch = do PandocXMLError "styles.xml" msg Right d -> return $ toEntry "styles.xml" epochtime - ( fromStringLazy + ( fromTextLazy + . TL.fromStrict . ppTopElement . addLang lang $ d ) else return e) (zEntries arch) return arch{ zEntries = entries } +-- TODO FIXME avoid this generic traversal! addLang :: Lang -> Element -> Element addLang lang = everywhere' (mkT updateLangAttr) where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) - = Attr n (T.unpack $ langLanguage lang) + = Attr n (langLanguage lang) updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) - = Attr n (T.unpack $ langRegion lang) + = Attr n (langRegion lang) updateLangAttr x = x -- | transform both Image and Math elements @@ -238,8 +240,8 @@ transformPicMath _ (Math t math) = do case writeMathML dt <$> readTeX math of Left _ -> return $ Math t math Right r -> do - let conf = useShortEmptyTags (const False) defaultConfigPP - let mathml = ppcTopElement conf r + let conf = XL.useShortEmptyTags (const False) XL.defaultConfigPP + let mathml = XL.ppcTopElement conf r epochtime <- floor `fmap` lift P.getPOSIXTime let dirname = "Formula-" ++ show (length entries) ++ "/" let fname = dirname ++ "content.xml" diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs index 8f60e70d5..0533d6c12 100644 --- a/src/Text/Pandoc/Writers/OOXML.hs +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -29,33 +29,32 @@ import Control.Monad.Except (throwError) import Text.Pandoc.Error import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL -import qualified Data.ByteString.Lazy.Char8 as BL8 import Data.Maybe (mapMaybe) import qualified Data.Text as T +import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.UTF8 as UTF8 -import Text.XML.Light as XML -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light -mknode :: Node t => String -> [(String,String)] -> t -> Element +mknode :: Node t => Text -> [(Text,Text)] -> t -> Element mknode s attrs = add_attrs (map (\(k,v) -> Attr (nodename k) v) attrs) . node (nodename s) -mktnode :: String -> [(String,String)] -> T.Text -> Element -mktnode s attrs = mknode s attrs . T.unpack +mktnode :: Text -> [(Text,Text)] -> T.Text -> Element +mktnode s attrs = mknode s attrs -nodename :: String -> QName +nodename :: Text -> QName nodename s = QName{ qName = name, qURI = Nothing, qPrefix = prefix } - where (name, prefix) = case break (==':') s of - (xs,[]) -> (xs, Nothing) - (ys, _:zs) -> (zs, Just ys) + where (name, prefix) = case T.break (==':') s of + (xs,ys) -> case T.uncons ys of + Nothing -> (xs, Nothing) + Just (_,zs) -> (zs, Just xs) toLazy :: B.ByteString -> BL.ByteString toLazy = BL.fromChunks . (:[]) renderXml :: Element -> BL.ByteString -renderXml elt = BL8.pack "\n" <> - UTF8.fromStringLazy (showElement elt) +renderXml elt = BL.fromStrict (UTF8.fromText (showTopElement elt)) parseXml :: PandocMonad m => Archive -> Archive -> String -> m Element parseXml refArchive distArchive relpath = @@ -70,25 +69,25 @@ parseXml refArchive distArchive relpath = -- Copied from Util -attrToNSPair :: XML.Attr -> Maybe (String, String) -attrToNSPair (XML.Attr (QName s _ (Just "xmlns")) val) = Just (s, val) +attrToNSPair :: Attr -> Maybe (Text, Text) +attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val) attrToNSPair _ = Nothing elemToNameSpaces :: Element -> NameSpaces elemToNameSpaces = mapMaybe attrToNSPair . elAttribs -elemName :: NameSpaces -> String -> String -> QName +elemName :: NameSpaces -> Text -> Text -> QName elemName ns prefix name = - QName name (lookup prefix ns) (if null prefix then Nothing else Just prefix) + QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix) -isElem :: NameSpaces -> String -> String -> Element -> Bool +isElem :: NameSpaces -> Text -> Text -> Element -> Bool isElem ns prefix name element = let ns' = ns ++ elemToNameSpaces element in qName (elName element) == name && qURI (elName element) == lookup prefix ns' -type NameSpaces = [(String, String)] +type NameSpaces = [(Text, Text)] -- | Scales the image to fit the page -- sizes are passed in emu diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 0a7060895..5caeb0753 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -20,16 +20,16 @@ import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip -import Data.Char (toUpper) import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf, intersperse) import Data.Default +import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Read import Data.Time (formatTime, defaultTimeLocale) import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) -import Text.XML.Light -import Text.Pandoc.XMLParser (parseXMLElement) +import Text.Pandoc.XML.Light as XML import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class.PandocMonad (PandocMonad) @@ -48,6 +48,7 @@ import Text.DocTemplates (FromContext(lookupContext)) import Text.TeXMath import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation +import Text.Pandoc.Shared (tshow) import Skylighting (fromColor) -- |The 'EMU' type is used to specify sizes in English Metric Units. @@ -84,10 +85,13 @@ getPresentationSize refArchive distArchive = do sldSize <- findChild (elemName ns "p" "sldSz") presElement cxS <- findAttr (QName "cx" Nothing Nothing) sldSize cyS <- findAttr (QName "cy" Nothing Nothing) sldSize - (cx, _) <- listToMaybe $ reads cxS :: Maybe (Integer, String) - (cy, _) <- listToMaybe $ reads cyS :: Maybe (Integer, String) + cx <- readTextAsInteger cxS + cy <- readTextAsInteger cyS return (cx `div` 12700, cy `div` 12700) +readTextAsInteger :: Text -> Maybe Integer +readTextAsInteger = either (const Nothing) (Just . fst) . Data.Text.Read.decimal + data WriterEnv = WriterEnv { envRefArchive :: Archive , envDistArchive :: Archive , envUTCTime :: UTCTime @@ -161,9 +165,6 @@ runP env st p = evalStateT (runReaderT p env) st -------------------------------------------------------------------- -findAttrText :: QName -> Element -> Maybe T.Text -findAttrText n = fmap T.pack . findAttr n - monospaceFont :: Monad m => P m T.Text monospaceFont = do vars <- writerVariables <$> asks envOpts @@ -171,10 +172,9 @@ monospaceFont = do Just s -> return s Nothing -> return "Courier" --- Kept as string for XML.Light -fontSizeAttributes :: Monad m => RunProps -> P m [(String, String)] +fontSizeAttributes :: Monad m => RunProps -> P m [(Text, Text)] fontSizeAttributes RunProps { rPropForceSize = Just sz } = - return [("sz", show $ sz * 100)] + return [("sz", tshow $ sz * 100)] fontSizeAttributes _ = return [] copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive @@ -365,7 +365,7 @@ shapeHasId :: NameSpaces -> T.Text -> Element -> Bool shapeHasId ns ident element | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr - , Just nm <- findAttrText (QName "id" Nothing Nothing) cNvPr = + , Just nm <- findAttr (QName "id" Nothing Nothing) cNvPr = nm == ident | otherwise = False @@ -396,10 +396,10 @@ getShapeDimensions ns element ext <- findChild (elemName ns "a" "ext") xfrm cxS <- findAttr (QName "cx" Nothing Nothing) ext cyS <- findAttr (QName "cy" Nothing Nothing) ext - (x, _) <- listToMaybe $ reads xS - (y, _) <- listToMaybe $ reads yS - (cx, _) <- listToMaybe $ reads cxS - (cy, _) <- listToMaybe $ reads cyS + x <- readTextAsInteger xS + y <- readTextAsInteger yS + cx <- readTextAsInteger cxS + cy <- readTextAsInteger cyS return ((x `div` 12700, y `div` 12700), (cx `div` 12700, cy `div` 12700)) | otherwise = Nothing @@ -430,7 +430,7 @@ getContentShapeSize ns layout master Nothing -> do let mbSz = findChild (elemName ns "p" "nvSpPr") sp >>= findChild (elemName ns "p" "cNvPr") >>= - findAttrText (QName "id" Nothing Nothing) >>= + findAttr (QName "id" Nothing Nothing) >>= flip getMasterShapeDimensionsById master case mbSz of Just sz' -> return sz' @@ -450,8 +450,8 @@ buildSpTree ns spTreeElem newShapes = fn _ = True replaceNamedChildren :: NameSpaces - -> String - -> String + -> Text + -> Text -> [Element] -> Element -> Element @@ -654,10 +654,10 @@ createCaption contentShapeDimensions paraElements = do ] , mknode "p:spPr" [] [ mknode "a:xfrm" [] - [ mknode "a:off" [("x", show $ 12700 * x), - ("y", show $ 12700 * (y + cy - captionHeight))] () - , mknode "a:ext" [("cx", show $ 12700 * cx), - ("cy", show $ 12700 * captionHeight)] () + [ mknode "a:off" [("x", tshow $ 12700 * x), + ("y", tshow $ 12700 * (y + cy - captionHeight))] () + , mknode "a:ext" [("cx", tshow $ 12700 * cx), + ("cy", tshow $ 12700 * captionHeight)] () ] , mknode "a:prstGeom" [("prst", "rect")] [ mknode "a:avLst" [] () @@ -706,11 +706,13 @@ makePicElements layout picProps mInfo alt = do ,("noChangeAspect","1")] () -- cNvPr will contain the link information so we do that separately, -- and register the link if necessary. - let cNvPrAttr = [("descr", mInfoFilePath mInfo), ("id","0"),("name","Picture 1")] + let cNvPrAttr = [("descr", T.pack $ mInfoFilePath mInfo), + ("id","0"), + ("name","Picture 1")] cNvPr <- case picPropLink picProps of Just link -> do idNum <- registerLink link return $ mknode "p:cNvPr" cNvPrAttr $ - mknode "a:hlinkClick" [("r:id", "rId" <> show idNum)] () + mknode "a:hlinkClick" [("r:id", "rId" <> tshow idNum)] () Nothing -> return $ mknode "p:cNvPr" cNvPrAttr () let nvPicPr = mknode "p:nvPicPr" [] [ cNvPr @@ -718,13 +720,13 @@ makePicElements layout picProps mInfo alt = do , mknode "p:nvPr" [] ()] let blipFill = mknode "p:blipFill" [] [ mknode "a:blip" [("r:embed", "rId" <> - show (mInfoLocalId mInfo))] () + tshow (mInfoLocalId mInfo))] () , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x",show xoff'), ("y",show yoff')] () - , mknode "a:ext" [("cx",show dimX') - ,("cy",show dimY')] () ] + [ mknode "a:off" [("x", tshow xoff'), ("y", tshow yoff')] () + , mknode "a:ext" [("cx", tshow dimX') + ,("cy", tshow dimY')] () ] let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ mknode "a:avLst" [] () let ln = mknode "a:ln" [("w","9525")] @@ -763,7 +765,7 @@ paraElemToElements (Run rpr s) = do Just DoubleStrike -> [("strike", "dblStrike")] Nothing -> []) <> (case rBaseline rpr of - Just n -> [("baseline", show n)] + Just n -> [("baseline", tshow n)] Nothing -> []) <> (case rCap rpr of Just NoCapitals -> [("cap", "none")] @@ -780,43 +782,44 @@ paraElemToElements (Run rpr s) = do return $ case link of InternalTarget _ -> let linkAttrs = - [ ("r:id", "rId" <> show idNum) + [ ("r:id", "rId" <> tshow idNum) , ("action", "ppaction://hlinksldjump") ] in [mknode "a:hlinkClick" linkAttrs ()] -- external ExternalTarget _ -> let linkAttrs = - [ ("r:id", "rId" <> show idNum) + [ ("r:id", "rId" <> tshow idNum) ] in [mknode "a:hlinkClick" linkAttrs ()] Nothing -> return [] let colorContents = case rSolidFill rpr of Just color -> case fromColor color of - '#':hx -> [mknode "a:solidFill" [] - [mknode "a:srgbClr" [("val", map toUpper hx)] ()] - ] + '#':hx -> + [mknode "a:solidFill" [] + [mknode "a:srgbClr" + [("val", T.toUpper $ T.pack hx)] ()]] _ -> [] Nothing -> [] codeFont <- monospaceFont let codeContents = - [mknode "a:latin" [("typeface", T.unpack codeFont)] () | rPropCode rpr] + [mknode "a:latin" [("typeface", codeFont)] () | rPropCode rpr] let propContents = linkProps <> colorContents <> codeContents return [Elem $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents - , mknode "a:t" [] $ T.unpack s + , mknode "a:t" [] s ]] paraElemToElements (MathElem mathType texStr) = do isInSpkrNotes <- asks envInSpeakerNotes if isInSpkrNotes then paraElemToElements $ Run def $ unTeXString texStr else do res <- convertMath writeOMML mathType (unTeXString texStr) - case res of + case fromXLElement <$> res of Right r -> return [Elem $ mknode "a14:m" [] $ addMathInfo r] Left (Str s) -> paraElemToElements (Run def s) Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback" paraElemToElements (RawOOXMLParaElem str) = return - [Text (CData CDataRaw (T.unpack str) Nothing)] + [Text (CData CDataRaw str Nothing)] -- This is a bit of a kludge -- really requires adding an option to @@ -824,9 +827,10 @@ paraElemToElements (RawOOXMLParaElem str) = return -- step at a time. addMathInfo :: Element -> Element addMathInfo element = - let mathspace = Attr { attrKey = QName "m" Nothing (Just "xmlns") - , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" - } + let mathspace = + Attr { attrKey = QName "m" Nothing (Just "xmlns") + , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" + } in add_attr mathspace element -- We look through the element to see if it contains an a14:m @@ -849,13 +853,13 @@ surroundWithMathAlternate element = paragraphToElement :: PandocMonad m => Paragraph -> P m Element paragraphToElement par = do let - attrs = [("lvl", show $ pPropLevel $ paraProps par)] <> + attrs = [("lvl", tshow $ pPropLevel $ paraProps par)] <> (case pPropMarginLeft (paraProps par) of - Just px -> [("marL", show $ pixelsToEmu px)] + Just px -> [("marL", tshow $ pixelsToEmu px)] Nothing -> [] ) <> (case pPropIndent (paraProps par) of - Just px -> [("indent", show $ pixelsToEmu px)] + Just px -> [("indent", tshow $ pixelsToEmu px)] Nothing -> [] ) <> (case pPropAlign (paraProps par) of @@ -867,7 +871,7 @@ paragraphToElement par = do props = [] <> (case pPropSpaceBefore $ paraProps par of Just px -> [mknode "a:spcBef" [] [ - mknode "a:spcPts" [("val", show $ 100 * px)] () + mknode "a:spcPts" [("val", tshow $ 100 * px)] () ] ] Nothing -> [] @@ -910,7 +914,7 @@ shapeToElements layout (Pic picProps fp alt) = do shapeToElements layout (GraphicFrame tbls cptn) = map Elem <$> graphicFrameToElements layout tbls cptn shapeToElements _ (RawOOXMLShape str) = return - [Text (CData CDataRaw (T.unpack str) Nothing)] + [Text (CData CDataRaw str Nothing)] shapeToElements layout shp = do element <- shapeToElement layout shp return [Elem element] @@ -942,8 +946,10 @@ graphicFrameToElements layout tbls caption = do [mknode "p:ph" [("idx", "1")] ()] ] , mknode "p:xfrm" [] - [ mknode "a:off" [("x", show $ 12700 * x), ("y", show $ 12700 * y)] () - , mknode "a:ext" [("cx", show $ 12700 * cx), ("cy", show $ 12700 * cy)] () + [ mknode "a:off" [("x", tshow $ 12700 * x), + ("y", tshow $ 12700 * y)] () + , mknode "a:ext" [("cx", tshow $ 12700 * cx), + ("cy", tshow $ 12700 * cy)] () ] ] <> elements @@ -957,7 +963,7 @@ getDefaultTableStyle = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive tblStyleLst <- parseXml refArchive distArchive "ppt/tableStyles.xml" - return $ findAttrText (QName "def" Nothing Nothing) tblStyleLst + return $ findAttr (QName "def" Nothing Nothing) tblStyleLst graphicToElement :: PandocMonad m => Integer -> Graphic -> P m Element graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do @@ -995,7 +1001,7 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells let mkgridcol w = mknode "a:gridCol" - [("w", show ((12700 * w) :: Integer))] () + [("w", tshow ((12700 * w) :: Integer))] () let hasHeader = not (all null hdrCells) mbDefTblStyle <- getDefaultTableStyle @@ -1004,7 +1010,7 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do , ("bandRow", if tblPrBandRow tblPr then "1" else "0") ] (case mbDefTblStyle of Nothing -> [] - Just sty -> [mknode "a:tableStyleId" [] $ T.unpack sty]) + Just sty -> [mknode "a:tableStyleId" [] sty]) return $ mknode "a:graphic" [] [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] @@ -1037,7 +1043,7 @@ findPHType ns spElem phType -- if it's a named PHType, we want to check that the attribute -- value matches. Just phElem | (PHType tp) <- phType -> - case findAttrText (QName "type" Nothing Nothing) phElem of + case findAttr (QName "type" Nothing Nothing) phElem of Just tp' -> tp == tp' Nothing -> False -- if it's an ObjType, we want to check that there is NO @@ -1204,7 +1210,7 @@ getSlideNumberFieldId notesMaster , Just txBody <- findChild (elemName ns "p" "txBody") sp , Just p <- findChild (elemName ns "a" "p") txBody , Just fld <- findChild (elemName ns "a" "fld") p - , Just fldId <- findAttrText (QName "id" Nothing Nothing) fld = + , Just fldId <- findAttr (QName "id" Nothing Nothing) fld = return fldId | otherwise = throwError $ PandocSomeError @@ -1283,11 +1289,11 @@ speakerNotesSlideNumber pgNum fieldId = [ mknode "a:bodyPr" [] () , mknode "a:lstStyle" [] () , mknode "a:p" [] - [ mknode "a:fld" [ ("id", T.unpack fieldId) + [ mknode "a:fld" [ ("id", fieldId) , ("type", "slidenum") ] [ mknode "a:rPr" [("lang", "en-US")] () - , mknode "a:t" [] (show pgNum) + , mknode "a:t" [] (tshow pgNum) ] , mknode "a:endParaRPr" [("lang", "en-US")] () ] @@ -1339,7 +1345,7 @@ getSlideIdNum sldId = do Just n -> return n Nothing -> throwError $ PandocShouldNeverHappenError $ - "Slide Id " <> T.pack (show sldId) <> " not found." + "Slide Id " <> tshow sldId <> " not found." slideNum :: PandocMonad m => Slide -> P m Int slideNum slide = getSlideIdNum $ slideId slide @@ -1356,7 +1362,7 @@ slideToRelId :: PandocMonad m => Slide -> P m T.Text slideToRelId slide = do n <- slideNum slide offset <- asks envSlideIdOffset - return $ "rId" <> T.pack (show $ n + offset) + return $ "rId" <> tshow (n + offset) data Relationship = Relationship { relId :: Int @@ -1368,13 +1374,11 @@ elementToRel :: Element -> Maybe Relationship elementToRel element | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing = do rId <- findAttr (QName "Id" Nothing Nothing) element - numStr <- stripPrefix "rId" rId - num <- case reads numStr :: [(Int, String)] of - (n, _) : _ -> Just n - [] -> Nothing - type' <- findAttrText (QName "Type" Nothing Nothing) element + numStr <- T.stripPrefix "rId" rId + num <- fromIntegral <$> readTextAsInteger numStr + type' <- findAttr (QName "Type" Nothing Nothing) element target <- findAttr (QName "Target" Nothing Nothing) element - return $ Relationship num type' target + return $ Relationship num type' (T.unpack target) | otherwise = Nothing slideToPresRel :: PandocMonad m => Slide -> P m Relationship @@ -1463,10 +1467,9 @@ topLevelRelsEntry :: PandocMonad m => P m Entry topLevelRelsEntry = elemToEntry "_rels/.rels" $ relsToElement topLevelRels relToElement :: Relationship -> Element -relToElement rel = mknode "Relationship" [ ("Id", "rId" <> - show (relId rel)) - , ("Type", T.unpack $ relType rel) - , ("Target", relTarget rel) ] () +relToElement rel = mknode "Relationship" [ ("Id", "rId" <> tshow (relId rel)) + , ("Type", relType rel) + , ("Target", T.pack $ relTarget rel) ] () relsToElement :: [Relationship] -> Element relsToElement rels = mknode "Relationships" @@ -1501,7 +1504,8 @@ slideToSpeakerNotesEntry slide = do Just element | Just notesIdNum <- mbNotesIdNum -> Just <$> elemToEntry - ("ppt/notesSlides/notesSlide" <> show notesIdNum <> ".xml") + ("ppt/notesSlides/notesSlide" <> show notesIdNum <> + ".xml") element _ -> return Nothing @@ -1514,7 +1518,7 @@ slideToSpeakerNotesRelElement slide@Slide{} = do [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] [ mknode "Relationship" [ ("Id", "rId2") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") - , ("Target", "../slides/slide" <> show idNum <> ".xml") + , ("Target", "../slides/slide" <> tshow idNum <> ".xml") ] () , mknode "Relationship" [ ("Id", "rId1") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") @@ -1547,15 +1551,15 @@ linkRelElement :: PandocMonad m => (Int, LinkTarget) -> P m Element linkRelElement (rIdNum, InternalTarget targetId) = do targetIdNum <- getSlideIdNum targetId return $ - mknode "Relationship" [ ("Id", "rId" <> show rIdNum) + mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") - , ("Target", "slide" <> show targetIdNum <> ".xml") + , ("Target", "slide" <> tshow targetIdNum <> ".xml") ] () linkRelElement (rIdNum, ExternalTarget (url, _)) = return $ - mknode "Relationship" [ ("Id", "rId" <> show rIdNum) + mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") - , ("Target", T.unpack url) + , ("Target", url) , ("TargetMode", "External") ] () @@ -1567,10 +1571,10 @@ mediaRelElement mInfo = let ext = fromMaybe "" (mInfoExt mInfo) in mknode "Relationship" [ ("Id", "rId" <> - show (mInfoLocalId mInfo)) + tshow (mInfoLocalId mInfo)) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") , ("Target", "../media/image" <> - show (mInfoGlobalId mInfo) <> T.unpack ext) + tshow (mInfoGlobalId mInfo) <> ext) ] () speakerNotesSlideRelElement :: PandocMonad m => Slide -> P m (Maybe Element) @@ -1580,7 +1584,7 @@ speakerNotesSlideRelElement slide = do return $ case M.lookup idNum mp of Nothing -> Nothing Just n -> - let target = "../notesSlides/notesSlide" <> show n <> ".xml" + let target = "../notesSlides/notesSlide" <> tshow n <> ".xml" in Just $ mknode "Relationship" [ ("Id", "rId2") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") @@ -1619,9 +1623,9 @@ slideToSlideRelElement slide = do slideToSldIdElement :: PandocMonad m => Slide -> P m Element slideToSldIdElement slide = do n <- slideNum slide - let id' = show $ n + 255 + let id' = tshow $ n + 255 rId <- slideToRelId slide - return $ mknode "p:sldId" [("id", id'), ("r:id", T.unpack rId)] () + return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element presentationToSldIdLst (Presentation _ slides) = do @@ -1646,7 +1650,7 @@ presentationToPresentationElement pres@(Presentation _ slds) = do notesMasterElem = mknode "p:notesMasterIdLst" [] [ mknode "p:NotesMasterId" - [("r:id", "rId" <> show notesMasterRId)] + [("r:id", "rId" <> tshow notesMasterRId)] () ] @@ -1702,17 +1706,17 @@ docPropsElement docProps = do ,("xmlns:dcmitype","http://purl.org/dc/dcmitype/") ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")] $ - mknode "dc:title" [] (maybe "" T.unpack $ dcTitle docProps) + mknode "dc:title" [] (fromMaybe "" $ dcTitle docProps) : - mknode "dc:creator" [] (maybe "" T.unpack $ dcCreator docProps) + mknode "dc:creator" [] (fromMaybe "" $ dcCreator docProps) : - mknode "cp:keywords" [] (T.unpack keywords) - : ( [mknode "dc:subject" [] $ maybe "" T.unpack $ dcSubject docProps | isJust (dcSubject docProps)]) - <> ( [mknode "dc:description" [] $ maybe "" T.unpack $ dcDescription docProps | isJust (dcDescription docProps)]) - <> ( [mknode "cp:category" [] $ maybe "" T.unpack $ cpCategory docProps | isJust (cpCategory docProps)]) + mknode "cp:keywords" [] keywords + : ( [mknode "dc:subject" [] $ fromMaybe "" $ dcSubject docProps | isJust (dcSubject docProps)]) + <> ( [mknode "dc:description" [] $ fromMaybe "" $ dcDescription docProps | isJust (dcDescription docProps)]) + <> ( [mknode "cp:category" [] $ fromMaybe "" $ cpCategory docProps | isJust (cpCategory docProps)]) <> (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x - , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x - ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) + , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x + ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime) docPropsToEntry :: PandocMonad m => DocProps -> P m Entry docPropsToEntry docProps = docPropsElement docProps >>= @@ -1723,8 +1727,8 @@ docCustomPropsElement :: PandocMonad m => DocProps -> P m Element docCustomPropsElement docProps = do let mkCustomProp (k, v) pid = mknode "property" [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}") - ,("pid", show pid) - ,("name", T.unpack k)] $ mknode "vt:lpwstr" [] (T.unpack v) + ,("pid", tshow pid) + ,("name", k)] $ mknode "vt:lpwstr" [] v return $ mknode "Properties" [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties") ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes") @@ -1743,7 +1747,7 @@ viewPropsElement = do distArchive <- asks envDistArchive viewPrElement <- parseXml refArchive distArchive "ppt/viewProps.xml" -- remove "lastView" if it exists: - let notLastView :: Text.XML.Light.Attr -> Bool + let notLastView :: XML.Attr -> Bool notLastView attr = qName (attrKey attr) /= "lastView" return $ @@ -1755,15 +1759,15 @@ makeViewPropsEntry = viewPropsElement >>= elemToEntry "ppt/viewProps.xml" defaultContentTypeToElem :: DefaultContentType -> Element defaultContentTypeToElem dct = mknode "Default" - [("Extension", T.unpack $ defContentTypesExt dct), - ("ContentType", T.unpack $ defContentTypesType dct)] + [("Extension", defContentTypesExt dct), + ("ContentType", defContentTypesType dct)] () overrideContentTypeToElem :: OverrideContentType -> Element overrideContentTypeToElem oct = mknode "Override" - [("PartName", overrideContentTypesPart oct), - ("ContentType", T.unpack $ overrideContentTypesType oct)] + [("PartName", T.pack $ overrideContentTypesPart oct), + ("ContentType", overrideContentTypesType oct)] () contentTypesToElement :: ContentTypes -> Element @@ -1821,7 +1825,8 @@ getSpeakerNotesFilePaths :: PandocMonad m => P m [FilePath] getSpeakerNotesFilePaths = do mp <- asks envSpeakerNotesIdMap let notesIdNums = M.elems mp - return $ map (\n -> "ppt/notesSlides/notesSlide" <> show n <> ".xml") notesIdNums + return $ map (\n -> "ppt/notesSlides/notesSlide" <> show n <> ".xml") + notesIdNums presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes presentationToContentTypes p@(Presentation _ slides) = do @@ -1885,11 +1890,11 @@ getContentType fp | otherwise = Nothing -- Kept as String for XML.Light -autoNumAttrs :: ListAttributes -> [(String, String)] +autoNumAttrs :: ListAttributes -> [(Text, Text)] autoNumAttrs (startNum, numStyle, numDelim) = numAttr <> typeAttr where - numAttr = [("startAt", show startNum) | startNum /= 1] + numAttr = [("startAt", tshow startNum) | startNum /= 1] typeAttr = [("type", typeString <> delimString)] typeString = case numStyle of Decimal -> "arabic" diff --git a/src/Text/Pandoc/XML/Light.hs b/src/Text/Pandoc/XML/Light.hs new file mode 100644 index 000000000..38e4df218 --- /dev/null +++ b/src/Text/Pandoc/XML/Light.hs @@ -0,0 +1,586 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.XML.Light + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +xml-light, which we used in pandoc's the XML-based readers, has +some limitations: in particular, it produces nodes with String +instead of Text, and the parser falls over on processing instructions +(see #7091). + +This module exports much of the API of xml-light, but using Text instead +of String. In addition, the xml-light parsers are replaced by xml-conduit's +well-tested parser. (The xml-conduit types are mapped to types +isomorphic to xml-light's, to avoid the need for massive code modifications +elsewhere.) Bridge functions to map xml-light types to this module's +types are also provided (since libraries like texmath still use xml-light). + +Another advantage of the xml-conduit parser is that it gives us +detailed information on xml parse errors. + +In the future we may want to move to using xml-conduit or another +xml library in the code base, but this change gives us +better performance and accuracy without much change in the +code that used xml-light. +-} +module Text.Pandoc.XML.Light + ( -- * Basic types, duplicating those from xml-light but with Text + -- instead of String + Line + , Content(..) + , Element(..) + , Attr(..) + , CData(..) + , CDataKind(..) + , QName(..) + , Node(..) + , unode + , unqual + , add_attr + , add_attrs + -- * Conversion functions from xml-light types + , fromXLQName + , fromXLCData + , fromXLElement + , fromXLAttr + , fromXLContent + -- * Replacement for xml-light's Text.XML.Proc + , strContent + , onlyElems + , elChildren + , onlyText + , findChildren + , filterChildren + , filterChildrenName + , findChild + , filterChild + , filterChildName + , findElement + , filterElement + , filterElementName + , findElements + , filterElements + , filterElementsName + , findAttr + , lookupAttr + , lookupAttrBy + , findAttrBy + -- * Replacement for xml-light's Text.XML.Output + , ppTopElement + , ppElement + , ppContent + , ppcElement + , ppcContent + , showTopElement + , showElement + , showContent + , useShortEmptyTags + , defaultConfigPP + , ConfigPP(..) + -- * Replacement for xml-light's Text.XML.Input + , parseXMLElement + , parseXMLContents + ) where + +import qualified Control.Exception as E +import qualified Text.XML as Conduit +import Text.XML.Unresolved (InvalidEventStream(..)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText) +import qualified Data.Map as M +import Data.Data (Data) +import Data.Typeable (Typeable) +import Data.Maybe (mapMaybe, listToMaybe) +import Data.List(find) +import qualified Text.XML.Light as XL + +-- Drop in replacement for parseXMLDoc in xml-light. +parseXMLElement :: TL.Text -> Either T.Text Element +parseXMLElement t = + elementToElement . Conduit.documentRoot <$> + either (Left . T.pack . E.displayException) Right + (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t) + +parseXMLContents :: TL.Text -> Either T.Text [Content] +parseXMLContents t = + case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t of + Left e -> + case E.fromException e of + Just (ContentAfterRoot _) -> + elContent <$> parseXMLElement ("" <> t <> "") + _ -> Left . T.pack . E.displayException $ e + Right x -> Right [Elem . elementToElement . Conduit.documentRoot $ x] + +elementToElement :: Conduit.Element -> Element +elementToElement (Conduit.Element name attribMap nodes) = + Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing + where + attrs = map (\(n,v) -> Attr (nameToQname n) v) $ + M.toList attribMap + nameToQname (Conduit.Name localName mbns mbpref) = + case mbpref of + Nothing -> + case T.stripPrefix "xmlns:" localName of + Just rest -> QName rest mbns (Just "xmlns") + Nothing -> QName localName mbns mbpref + _ -> QName localName mbns mbpref + +nodeToContent :: Conduit.Node -> Maybe Content +nodeToContent (Conduit.NodeElement el) = + Just (Elem (elementToElement el)) +nodeToContent (Conduit.NodeContent t) = + Just (Text (CData CDataText t Nothing)) +nodeToContent _ = Nothing + +unqual :: Text -> QName +unqual x = QName x Nothing Nothing + +-- | Add an attribute to an element. +add_attr :: Attr -> Element -> Element +add_attr a e = add_attrs [a] e + +-- | Add some attributes to an element. +add_attrs :: [Attr] -> Element -> Element +add_attrs as e = e { elAttribs = as ++ elAttribs e } + +-- +-- type definitions lightly modified from xml-light +-- + +-- | A line is an Integer +type Line = Integer + +-- | XML content +data Content = Elem Element + | Text CData + | CRef Text + deriving (Show, Typeable, Data) + +-- | XML elements +data Element = Element { + elName :: QName, + elAttribs :: [Attr], + elContent :: [Content], + elLine :: Maybe Line + } deriving (Show, Typeable, Data) + +-- | XML attributes +data Attr = Attr { + attrKey :: QName, + attrVal :: Text + } deriving (Eq, Ord, Show, Typeable, Data) + +-- | XML CData +data CData = CData { + cdVerbatim :: CDataKind, + cdData :: Text, + cdLine :: Maybe Line + } deriving (Show, Typeable, Data) + +data CDataKind + = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc. + | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in case (qURI q1, qURI q2) of + (Nothing,Nothing) -> compare (qPrefix q1) (qPrefix q2) + (u1,u2) -> compare u1 u2 + x -> x + +class Node t where + node :: QName -> t -> Element + +instance Node ([Attr],[Content]) where + node n (attrs,cont) = Element { elName = n + , elAttribs = attrs + , elContent = cont + , elLine = Nothing + } + +instance Node [Attr] where node n as = node n (as,[]::[Content]) +instance Node Attr where node n a = node n [a] +instance Node () where node n () = node n ([]::[Attr]) + +instance Node [Content] where node n cs = node n ([]::[Attr],cs) +instance Node Content where node n c = node n [c] +instance Node ([Attr],Content) where node n (as,c) = node n (as,[c]) +instance Node (Attr,Content) where node n (a,c) = node n ([a],[c]) + +instance Node ([Attr],[Element]) where + node n (as,cs) = node n (as,map Elem cs) + +instance Node ([Attr],Element) where node n (as,c) = node n (as,[c]) +instance Node (Attr,Element) where node n (a,c) = node n ([a],c) +instance Node [Element] where node n es = node n ([]::[Attr],es) +instance Node Element where node n e = node n [e] + +instance Node ([Attr],[CData]) where + node n (as,cs) = node n (as,map Text cs) + +instance Node ([Attr],CData) where node n (as,c) = node n (as,[c]) +instance Node (Attr,CData) where node n (a,c) = node n ([a],c) +instance Node [CData] where node n es = node n ([]::[Attr],es) +instance Node CData where node n e = node n [e] + +instance Node ([Attr],Text) where + node n (as,t) = node n (as, CData { cdVerbatim = CDataText + , cdData = t + , cdLine = Nothing }) + +instance Node (Attr,Text ) where node n (a,t) = node n ([a],t) +instance Node Text where node n t = node n ([]::[Attr],t) + +-- | Create node with unqualified name +unode :: Node t => Text -> t -> Element +unode = node . unqual + +-- +-- conversion from xml-light +-- + +fromXLQName :: XL.QName -> QName +fromXLQName qn = QName { qName = T.pack $ XL.qName qn + , qURI = T.pack <$> XL.qURI qn + , qPrefix = T.pack <$> XL.qPrefix qn } + +fromXLCData :: XL.CData -> CData +fromXLCData cd = CData { cdVerbatim = case XL.cdVerbatim cd of + XL.CDataText -> CDataText + XL.CDataVerbatim -> CDataVerbatim + XL.CDataRaw -> CDataRaw + , cdData = T.pack $ XL.cdData cd + , cdLine = XL.cdLine cd } + +fromXLElement :: XL.Element -> Element +fromXLElement el = Element { elName = fromXLQName $ XL.elName el + , elAttribs = map fromXLAttr $ XL.elAttribs el + , elContent = map fromXLContent $ XL.elContent el + , elLine = XL.elLine el } + +fromXLAttr :: XL.Attr -> Attr +fromXLAttr (XL.Attr qn s) = Attr (fromXLQName qn) (T.pack s) + +fromXLContent :: XL.Content -> Content +fromXLContent (XL.Elem el) = Elem $ fromXLElement el +fromXLContent (XL.Text cd) = Text $ fromXLCData cd +fromXLContent (XL.CRef s) = CRef (T.pack s) + +-- +-- copied from xml-light Text.XML.Proc +-- + +-- | Get the text value of an XML element. This function +-- ignores non-text elements, and concatenates all text elements. +strContent :: Element -> Text +strContent = mconcat . map cdData . onlyText . elContent + +-- | Select only the elements from a list of XML content. +onlyElems :: [Content] -> [Element] +onlyElems xs = [ x | Elem x <- xs ] + +-- | Select only the elements from a parent. +elChildren :: Element -> [Element] +elChildren e = [ x | Elem x <- elContent e ] + +-- | Select only the text from a list of XML content. +onlyText :: [Content] -> [CData] +onlyText xs = [ x | Text x <- xs ] + +-- | Find all immediate children with the given name. +findChildren :: QName -> Element -> [Element] +findChildren q e = filterChildren ((q ==) . elName) e + +-- | Filter all immediate children wrt a given predicate. +filterChildren :: (Element -> Bool) -> Element -> [Element] +filterChildren p e = filter p (onlyElems (elContent e)) + + +-- | Filter all immediate children wrt a given predicate over their names. +filterChildrenName :: (QName -> Bool) -> Element -> [Element] +filterChildrenName p e = filter (p.elName) (onlyElems (elContent e)) + + +-- | Find an immediate child with the given name. +findChild :: QName -> Element -> Maybe Element +findChild q e = listToMaybe (findChildren q e) + +-- | Find an immediate child with the given name. +filterChild :: (Element -> Bool) -> Element -> Maybe Element +filterChild p e = listToMaybe (filterChildren p e) + +-- | Find an immediate child with name matching a predicate. +filterChildName :: (QName -> Bool) -> Element -> Maybe Element +filterChildName p e = listToMaybe (filterChildrenName p e) + +-- | Find the left-most occurrence of an element matching given name. +findElement :: QName -> Element -> Maybe Element +findElement q e = listToMaybe (findElements q e) + +-- | Filter the left-most occurrence of an element wrt. given predicate. +filterElement :: (Element -> Bool) -> Element -> Maybe Element +filterElement p e = listToMaybe (filterElements p e) + +-- | Filter the left-most occurrence of an element wrt. given predicate. +filterElementName :: (QName -> Bool) -> Element -> Maybe Element +filterElementName p e = listToMaybe (filterElementsName p e) + +-- | Find all non-nested occurances of an element. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +findElements :: QName -> Element -> [Element] +findElements qn e = filterElementsName (qn==) e + +-- | Find all non-nested occurrences of an element wrt. given predicate. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +filterElements :: (Element -> Bool) -> Element -> [Element] +filterElements p e + | p e = [e] + | otherwise = concatMap (filterElements p) $ onlyElems $ elContent e + +-- | Find all non-nested occurences of an element wrt a predicate over element names. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +filterElementsName :: (QName -> Bool) -> Element -> [Element] +filterElementsName p e = filterElements (p.elName) e + +-- | Lookup the value of an attribute. +findAttr :: QName -> Element -> Maybe Text +findAttr x e = lookupAttr x (elAttribs e) + +-- | Lookup attribute name from list. +lookupAttr :: QName -> [Attr] -> Maybe Text +lookupAttr x = lookupAttrBy (x ==) + +-- | Lookup the first attribute whose name satisfies the given predicate. +lookupAttrBy :: (QName -> Bool) -> [Attr] -> Maybe Text +lookupAttrBy p as = attrVal `fmap` find (p . attrKey) as + +-- | Lookup the value of the first attribute whose name +-- satisfies the given predicate. +findAttrBy :: (QName -> Bool) -> Element -> Maybe Text +findAttrBy p e = lookupAttrBy p (elAttribs e) + + +-- +-- duplicates functinos from Text.XML.Output +-- + +-- | The XML 1.0 header +xmlHeader :: Text +xmlHeader = "" + + +-------------------------------------------------------------------------------- +data ConfigPP = ConfigPP + { shortEmptyTag :: QName -> Bool + , prettify :: Bool + } + +-- | Default pretty orinting configuration. +-- * Always use abbreviate empty tags. +defaultConfigPP :: ConfigPP +defaultConfigPP = ConfigPP { shortEmptyTag = const True + , prettify = False + } + +-- | The predicate specifies for which empty tags we should use XML's +-- abbreviated notation . This is useful if we are working with +-- some XML-ish standards (such as certain versions of HTML) where some +-- empty tags should always be displayed in the form. +useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP +useShortEmptyTags p c = c { shortEmptyTag = p } + + +-- | Specify if we should use extra white-space to make document more readable. +-- WARNING: This adds additional white-space to text elements, +-- and so it may change the meaning of the document. +useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP +useExtraWhiteSpace p c = c { prettify = p } + +-- | A configuration that tries to make things pretty +-- (possibly at the cost of changing the semantics a bit +-- through adding white space.) +prettyConfigPP :: ConfigPP +prettyConfigPP = useExtraWhiteSpace True defaultConfigPP + + +-------------------------------------------------------------------------------- + + +-- | Pretty printing renders XML documents faithfully, +-- with the exception that whitespace may be added\/removed +-- in non-verbatim character data. +ppTopElement :: Element -> Text +ppTopElement = ppcTopElement prettyConfigPP + +-- | Pretty printing elements +ppElement :: Element -> Text +ppElement = ppcElement prettyConfigPP + +-- | Pretty printing content +ppContent :: Content -> Text +ppContent = ppcContent prettyConfigPP + +-- | Pretty printing renders XML documents faithfully, +-- with the exception that whitespace may be added\/removed +-- in non-verbatim character data. +ppcTopElement :: ConfigPP -> Element -> Text +ppcTopElement c e = T.unlines [xmlHeader,ppcElement c e] + +-- | Pretty printing elements +ppcElement :: ConfigPP -> Element -> Text +ppcElement c = TL.toStrict . toLazyText . ppElementS c mempty + +-- | Pretty printing content +ppcContent :: ConfigPP -> Content -> Text +ppcContent c = TL.toStrict . toLazyText . ppContentS c mempty + +ppcCData :: ConfigPP -> CData -> Text +ppcCData c = TL.toStrict . toLazyText . ppCDataS c mempty + +type Indent = Builder + +-- | Pretty printing content using ShowT +ppContentS :: ConfigPP -> Indent -> Content -> Builder +ppContentS c i x = case x of + Elem e -> ppElementS c i e + Text t -> ppCDataS c i t + CRef r -> showCRefS r + +ppElementS :: ConfigPP -> Indent -> Element -> Builder +ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <> + (case elContent e of + [] | "?" `T.isPrefixOf` qName name -> fromText " ?>" + | shortEmptyTag c name -> fromText " />" + [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name + cs -> singleton '>' <> nl <> + mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <> + i <> tagEnd name + where (nl,sp) = if prettify c then ("\n"," ") else ("","") + ) + where name = elName e + +ppCDataS :: ConfigPP -> Indent -> CData -> Builder +ppCDataS c i t = i <> if cdVerbatim t /= CDataText || not (prettify c) + then showCDataS t + else foldr cons mempty (T.unpack (showCData t)) + where cons :: Char -> Builder -> Builder + cons '\n' ys = singleton '\n' <> i <> ys + cons y ys = singleton y <> ys + + + +-------------------------------------------------------------------------------- + +-- | Adds the header. +showTopElement :: Element -> Text +showTopElement c = xmlHeader <> showElement c + +showContent :: Content -> Text +showContent = ppcContent defaultConfigPP + +showElement :: Element -> Text +showElement = ppcElement defaultConfigPP + +showCData :: CData -> Text +showCData = ppcCData defaultConfigPP + +-- Note: crefs should not contain '&', ';', etc. +showCRefS :: Text -> Builder +showCRefS r = singleton '&' <> fromText r <> singleton ';' + +-- | Convert a text element to characters. +showCDataS :: CData -> Builder +showCDataS cd = + case cdVerbatim cd of + CDataText -> escStr (cdData cd) + CDataVerbatim -> fromText " escCData (cdData cd) <> + fromText "]]>" + CDataRaw -> fromText (cdData cd) + +-------------------------------------------------------------------------------- +escCData :: Text -> Builder +escCData t + | "]]>" `T.isPrefixOf` t = + fromText "]]]]>" <> fromText (T.drop 3 t) +escCData t + = case T.uncons t of + Nothing -> mempty + Just (c,t') -> singleton c <> escCData t' + +escChar :: Char -> Builder +escChar c = case c of + '<' -> fromText "<" + '>' -> fromText ">" + '&' -> fromText "&" + '"' -> fromText """ + -- we use ' instead of ' because IE apparently has difficulties + -- rendering ' in xhtml. + -- Reported by Rohan Drape . + '\'' -> fromText "'" + _ -> singleton c + + {- original xml-light version: + -- NOTE: We escape '\r' explicitly because otherwise they get lost + -- when parsed back in because of then end-of-line normalization rules. + _ | isPrint c || c == '\n' -> singleton c + | otherwise -> showText "&#" . showsT oc . singleton ';' + where oc = ord c + -} + +escStr :: Text -> Builder +escStr cs = if T.any needsEscape cs + then mconcat (map escChar (T.unpack cs)) + else fromText cs + where + needsEscape '<' = True + needsEscape '>' = True + needsEscape '&' = True + needsEscape '"' = True + needsEscape '\'' = True + needsEscape _ = False + +tagEnd :: QName -> Builder +tagEnd qn = fromText " showQName qn <> singleton '>' + +tagStart :: QName -> [Attr] -> Builder +tagStart qn as = singleton '<' <> showQName qn <> as_str + where as_str = if null as + then mempty + else mconcat (map showAttr as) + +showAttr :: Attr -> Builder +showAttr (Attr qn v) = singleton ' ' <> showQName qn <> + singleton '=' <> + singleton '"' <> escStr v <> singleton '"' + +showQName :: QName -> Builder +showQName q = + case qPrefix q of + Nothing -> fromText (qName q) + Just p -> fromText p <> singleton ':' <> fromText (qName q) diff --git a/src/Text/Pandoc/XMLParser.hs b/src/Text/Pandoc/XMLParser.hs deleted file mode 100644 index 8ad22a66a..000000000 --- a/src/Text/Pandoc/XMLParser.hs +++ /dev/null @@ -1,66 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{- | - Module : Text.Pandoc.XMLParser - Copyright : Copyright (C) 2021 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - -Bridge to allow using xml-conduit's parser with xml-light's types. --} -module Text.Pandoc.XMLParser - ( parseXMLElement - , parseXMLContents - , module Text.XML.Light.Types - ) where - -import qualified Control.Exception as E -import qualified Text.XML as Conduit -import Text.XML.Unresolved (InvalidEventStream(..)) -import qualified Text.XML.Light as Light -import Text.XML.Light.Types -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL -import qualified Data.Map as M -import Data.Maybe (mapMaybe) - --- Drop in replacement for parseXMLDoc in xml-light. -parseXMLElement :: TL.Text -> Either T.Text Light.Element -parseXMLElement t = - elementToElement . Conduit.documentRoot <$> - either (Left . T.pack . E.displayException) Right - (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t) - -parseXMLContents :: TL.Text -> Either T.Text [Light.Content] -parseXMLContents t = - case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True } t of - Left e -> - case E.fromException e of - Just (ContentAfterRoot _) -> - elContent <$> parseXMLElement ("" <> t <> "") - _ -> Left . T.pack . E.displayException $ e - Right x -> Right [Light.Elem . elementToElement . Conduit.documentRoot $ x] - -elementToElement :: Conduit.Element -> Light.Element -elementToElement (Conduit.Element name attribMap nodes) = - Light.Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing - where - attrs = map (\(n,v) -> Light.Attr (nameToQname n) (T.unpack v)) $ - M.toList attribMap - nameToQname (Conduit.Name localName mbns mbpref) = - case mbpref of - Nothing | "xmlns:" `T.isPrefixOf` localName -> - Light.QName (T.unpack $ T.drop 6 localName) (T.unpack <$> mbns) - (Just "xmlns") - _ -> Light.QName (T.unpack localName) (T.unpack <$> mbns) - (T.unpack <$> mbpref) - -nodeToContent :: Conduit.Node -> Maybe Light.Content -nodeToContent (Conduit.NodeElement el) = - Just (Light.Elem (elementToElement el)) -nodeToContent (Conduit.NodeContent t) = - Just (Light.Text (Light.CData Light.CDataText (T.unpack t) Nothing)) -nodeToContent _ = Nothing - diff --git a/test/Tests/Writers/OOXML.hs b/test/Tests/Writers/OOXML.hs index 376f02c55..c1e47622d 100644 --- a/test/Tests/Writers/OOXML.hs +++ b/test/Tests/Writers/OOXML.hs @@ -43,7 +43,8 @@ compareXMLBool _ _ = False displayDiff :: Content -> Content -> String displayDiff elemA elemB = - showDiff (1,1) $ getDiff (lines $ ppContent elemA) (lines $ ppContent elemB) + showDiff (1,1) + (getDiff (lines $ showContent elemA) (lines $ showContent elemB)) goldenArchive :: FilePath -> IO Archive goldenArchive fp = toArchive . BL.fromStrict <$> BS.readFile fp diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index d3b16d0f2..ed7d1165c 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index 6293ef493..07ae75676 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index 4205a1516..e5f034378 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index adb3f23db..174942135 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 92c8137fe..b5c31a851 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index f53470617..c42ca1b05 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index d6af90a72..1cb4c1fd7 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index e18dbe853..7122456ea 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index 820299043..616ba0f81 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index ae0f41d12..c30dcdee9 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 94cd35dfa..8a704b41e 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 879f2a25b..b1906c8c4 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index 93f86478f..8adf1cf75 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 967d297f2..584117503 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index c5614e2fa..8859fe55c 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index 0f39a831f..b80f3b3ba 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 07046f223..35beed68a 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index 3656618e6..2c29fd674 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 8798253d5..10a948886 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index 0a24d1840..5b90e74a0 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index 52bb7a217..cc81b46d1 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index 182c06c64..1394dc442 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index 7b69a56a3..0d1688694 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index 3d3a35701..be1caef2d 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index 5ae37b406..a1d2323c2 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index c29aa6716..2f3a831a7 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 664493246..af066107c 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index b6d15340e..9cc7a075f 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index f8e1092d2..f8b8dcfde 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index b4cda82f2..1c3baf0bf 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index ee222efa0..28686970d 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index c6f8d9c96..7051cefbd 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index ea8146690..9df631640 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ diff --git a/test/pptx/code-custom.pptx b/test/pptx/code-custom.pptx index 58070eb3f..5e9c2c630 100644 Binary files a/test/pptx/code-custom.pptx and b/test/pptx/code-custom.pptx differ diff --git a/test/pptx/code-custom_templated.pptx b/test/pptx/code-custom_templated.pptx index db9b7e371..15232c32d 100644 Binary files a/test/pptx/code-custom_templated.pptx and b/test/pptx/code-custom_templated.pptx differ diff --git a/test/pptx/code.pptx b/test/pptx/code.pptx index c7b1ed7d5..aab0cc6f5 100644 Binary files a/test/pptx/code.pptx and b/test/pptx/code.pptx differ diff --git a/test/pptx/code_templated.pptx b/test/pptx/code_templated.pptx index 6944d92bf..fe5b675f3 100644 Binary files a/test/pptx/code_templated.pptx and b/test/pptx/code_templated.pptx differ diff --git a/test/pptx/document-properties-short-desc.pptx b/test/pptx/document-properties-short-desc.pptx index ae0d28429..de5e68151 100644 Binary files a/test/pptx/document-properties-short-desc.pptx and b/test/pptx/document-properties-short-desc.pptx differ diff --git a/test/pptx/document-properties-short-desc_templated.pptx b/test/pptx/document-properties-short-desc_templated.pptx index 37c74c69a..89e6fbdf2 100644 Binary files a/test/pptx/document-properties-short-desc_templated.pptx and b/test/pptx/document-properties-short-desc_templated.pptx differ diff --git a/test/pptx/document-properties.pptx b/test/pptx/document-properties.pptx index 324e443a1..6bcbd1b9c 100644 Binary files a/test/pptx/document-properties.pptx and b/test/pptx/document-properties.pptx differ diff --git a/test/pptx/document-properties_templated.pptx b/test/pptx/document-properties_templated.pptx index c81b983e3..79d48560b 100644 Binary files a/test/pptx/document-properties_templated.pptx and b/test/pptx/document-properties_templated.pptx differ diff --git a/test/pptx/endnotes.pptx b/test/pptx/endnotes.pptx index 30ce33db6..9d46036fe 100644 Binary files a/test/pptx/endnotes.pptx and b/test/pptx/endnotes.pptx differ diff --git a/test/pptx/endnotes_templated.pptx b/test/pptx/endnotes_templated.pptx index d6c604968..54ec7f305 100644 Binary files a/test/pptx/endnotes_templated.pptx and b/test/pptx/endnotes_templated.pptx differ diff --git a/test/pptx/endnotes_toc.pptx b/test/pptx/endnotes_toc.pptx index 000e17ecd..a028b346f 100644 Binary files a/test/pptx/endnotes_toc.pptx and b/test/pptx/endnotes_toc.pptx differ diff --git a/test/pptx/endnotes_toc_templated.pptx b/test/pptx/endnotes_toc_templated.pptx index fdcd2e29b..1158c16fc 100644 Binary files a/test/pptx/endnotes_toc_templated.pptx and b/test/pptx/endnotes_toc_templated.pptx differ diff --git a/test/pptx/images.pptx b/test/pptx/images.pptx index e73126376..670a825de 100644 Binary files a/test/pptx/images.pptx and b/test/pptx/images.pptx differ diff --git a/test/pptx/images_templated.pptx b/test/pptx/images_templated.pptx index e3f968e9e..6d297ef11 100644 Binary files a/test/pptx/images_templated.pptx and b/test/pptx/images_templated.pptx differ diff --git a/test/pptx/inline_formatting.pptx b/test/pptx/inline_formatting.pptx index eadb9372e..473b9498d 100644 Binary files a/test/pptx/inline_formatting.pptx and b/test/pptx/inline_formatting.pptx differ diff --git a/test/pptx/inline_formatting_templated.pptx b/test/pptx/inline_formatting_templated.pptx index 8ca6bab2b..2cdf54474 100644 Binary files a/test/pptx/inline_formatting_templated.pptx and b/test/pptx/inline_formatting_templated.pptx differ diff --git a/test/pptx/lists.pptx b/test/pptx/lists.pptx index ae188ee68..ffc2eb9f7 100644 Binary files a/test/pptx/lists.pptx and b/test/pptx/lists.pptx differ diff --git a/test/pptx/lists_templated.pptx b/test/pptx/lists_templated.pptx index 60301fa50..676954cb8 100644 Binary files a/test/pptx/lists_templated.pptx and b/test/pptx/lists_templated.pptx differ diff --git a/test/pptx/raw_ooxml.pptx b/test/pptx/raw_ooxml.pptx index 17124a50d..29164af15 100644 Binary files a/test/pptx/raw_ooxml.pptx and b/test/pptx/raw_ooxml.pptx differ diff --git a/test/pptx/raw_ooxml_templated.pptx b/test/pptx/raw_ooxml_templated.pptx index 19ae7dd4e..1742b3296 100644 Binary files a/test/pptx/raw_ooxml_templated.pptx and b/test/pptx/raw_ooxml_templated.pptx differ diff --git a/test/pptx/remove_empty_slides.pptx b/test/pptx/remove_empty_slides.pptx index b650b7585..c6df8e18e 100644 Binary files a/test/pptx/remove_empty_slides.pptx and b/test/pptx/remove_empty_slides.pptx differ diff --git a/test/pptx/remove_empty_slides_templated.pptx b/test/pptx/remove_empty_slides_templated.pptx index 0ab029614..cf6e52eef 100644 Binary files a/test/pptx/remove_empty_slides_templated.pptx and b/test/pptx/remove_empty_slides_templated.pptx differ diff --git a/test/pptx/slide_breaks.pptx b/test/pptx/slide_breaks.pptx index 2a6e35080..e06d9079d 100644 Binary files a/test/pptx/slide_breaks.pptx and b/test/pptx/slide_breaks.pptx differ diff --git a/test/pptx/slide_breaks_slide_level_1.pptx b/test/pptx/slide_breaks_slide_level_1.pptx index a7bcf6a4b..449339778 100644 Binary files a/test/pptx/slide_breaks_slide_level_1.pptx and b/test/pptx/slide_breaks_slide_level_1.pptx differ diff --git a/test/pptx/slide_breaks_slide_level_1_templated.pptx b/test/pptx/slide_breaks_slide_level_1_templated.pptx index 21b018c25..258098082 100644 Binary files a/test/pptx/slide_breaks_slide_level_1_templated.pptx and b/test/pptx/slide_breaks_slide_level_1_templated.pptx differ diff --git a/test/pptx/slide_breaks_templated.pptx b/test/pptx/slide_breaks_templated.pptx index 4ec4772a4..2f0213919 100644 Binary files a/test/pptx/slide_breaks_templated.pptx and b/test/pptx/slide_breaks_templated.pptx differ diff --git a/test/pptx/slide_breaks_toc.pptx b/test/pptx/slide_breaks_toc.pptx index 5983657b6..9dbfa41a0 100644 Binary files a/test/pptx/slide_breaks_toc.pptx and b/test/pptx/slide_breaks_toc.pptx differ diff --git a/test/pptx/slide_breaks_toc_templated.pptx b/test/pptx/slide_breaks_toc_templated.pptx index dd54c7082..f288dde14 100644 Binary files a/test/pptx/slide_breaks_toc_templated.pptx and b/test/pptx/slide_breaks_toc_templated.pptx differ diff --git a/test/pptx/speaker_notes.pptx b/test/pptx/speaker_notes.pptx index b3e5ed5b9..0ab1302da 100644 Binary files a/test/pptx/speaker_notes.pptx and b/test/pptx/speaker_notes.pptx differ diff --git a/test/pptx/speaker_notes_after_metadata.pptx b/test/pptx/speaker_notes_after_metadata.pptx index 1078854bb..6343bffe4 100644 Binary files a/test/pptx/speaker_notes_after_metadata.pptx and b/test/pptx/speaker_notes_after_metadata.pptx differ diff --git a/test/pptx/speaker_notes_after_metadata_templated.pptx b/test/pptx/speaker_notes_after_metadata_templated.pptx index 5116c6c4e..5d4465f64 100644 Binary files a/test/pptx/speaker_notes_after_metadata_templated.pptx and b/test/pptx/speaker_notes_after_metadata_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterheader.pptx b/test/pptx/speaker_notes_afterheader.pptx index 0c8e49bd9..d581681aa 100644 Binary files a/test/pptx/speaker_notes_afterheader.pptx and b/test/pptx/speaker_notes_afterheader.pptx differ diff --git a/test/pptx/speaker_notes_afterheader_templated.pptx b/test/pptx/speaker_notes_afterheader_templated.pptx index 68695939d..c922df3a8 100644 Binary files a/test/pptx/speaker_notes_afterheader_templated.pptx and b/test/pptx/speaker_notes_afterheader_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterseps.pptx b/test/pptx/speaker_notes_afterseps.pptx index 7ed9b946d..13f564bf0 100644 Binary files a/test/pptx/speaker_notes_afterseps.pptx and b/test/pptx/speaker_notes_afterseps.pptx differ diff --git a/test/pptx/speaker_notes_afterseps_templated.pptx b/test/pptx/speaker_notes_afterseps_templated.pptx index 79fc82345..f0b302738 100644 Binary files a/test/pptx/speaker_notes_afterseps_templated.pptx and b/test/pptx/speaker_notes_afterseps_templated.pptx differ diff --git a/test/pptx/speaker_notes_templated.pptx b/test/pptx/speaker_notes_templated.pptx index 9f943c279..2d8d00242 100644 Binary files a/test/pptx/speaker_notes_templated.pptx and b/test/pptx/speaker_notes_templated.pptx differ diff --git a/test/pptx/start_numbering_at.pptx b/test/pptx/start_numbering_at.pptx index ac72d8ced..4320128b3 100644 Binary files a/test/pptx/start_numbering_at.pptx and b/test/pptx/start_numbering_at.pptx differ diff --git a/test/pptx/start_numbering_at_templated.pptx b/test/pptx/start_numbering_at_templated.pptx index 15c7b5469..0a3f6e56d 100644 Binary files a/test/pptx/start_numbering_at_templated.pptx and b/test/pptx/start_numbering_at_templated.pptx differ diff --git a/test/pptx/tables.pptx b/test/pptx/tables.pptx index 926c5e699..e41219844 100644 Binary files a/test/pptx/tables.pptx and b/test/pptx/tables.pptx differ diff --git a/test/pptx/tables_templated.pptx b/test/pptx/tables_templated.pptx index a37e72d2c..82b8aa13d 100644 Binary files a/test/pptx/tables_templated.pptx and b/test/pptx/tables_templated.pptx differ diff --git a/test/pptx/two_column.pptx b/test/pptx/two_column.pptx index 7f86533fe..270a7eeac 100644 Binary files a/test/pptx/two_column.pptx and b/test/pptx/two_column.pptx differ diff --git a/test/pptx/two_column_templated.pptx b/test/pptx/two_column_templated.pptx index 89e3db0ab..44985d701 100644 Binary files a/test/pptx/two_column_templated.pptx and b/test/pptx/two_column_templated.pptx differ -- cgit v1.2.3 From d7a4996b1e7e201095ce792375a0776984fa8fcd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Feb 2021 18:40:06 -0800 Subject: Split up T.P.XML.Light into submodules. --- pandoc.cabal | 3 + src/Text/Pandoc/XML/Light.hs | 511 +----------------------------------- src/Text/Pandoc/XML/Light/Output.hs | 230 ++++++++++++++++ src/Text/Pandoc/XML/Light/Proc.hs | 138 ++++++++++ src/Text/Pandoc/XML/Light/Types.hs | 190 ++++++++++++++ 5 files changed, 568 insertions(+), 504 deletions(-) create mode 100644 src/Text/Pandoc/XML/Light/Output.hs create mode 100644 src/Text/Pandoc/XML/Light/Proc.hs create mode 100644 src/Text/Pandoc/XML/Light/Types.hs diff --git a/pandoc.cabal b/pandoc.cabal index 22aebd55e..d27520ba0 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -688,6 +688,9 @@ library Text.Pandoc.Lua.Util, Text.Pandoc.Lua.Walk, Text.Pandoc.XML.Light, + Text.Pandoc.XML.Light.Types, + Text.Pandoc.XML.Light.Proc, + Text.Pandoc.XML.Light.Output, Text.Pandoc.CSS, Text.Pandoc.CSV, Text.Pandoc.RoffChar, diff --git a/src/Text/Pandoc/XML/Light.hs b/src/Text/Pandoc/XML/Light.hs index 38e4df218..07113ea92 100644 --- a/src/Text/Pandoc/XML/Light.hs +++ b/src/Text/Pandoc/XML/Light.hs @@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light @@ -31,59 +30,9 @@ better performance and accuracy without much change in the code that used xml-light. -} module Text.Pandoc.XML.Light - ( -- * Basic types, duplicating those from xml-light but with Text - -- instead of String - Line - , Content(..) - , Element(..) - , Attr(..) - , CData(..) - , CDataKind(..) - , QName(..) - , Node(..) - , unode - , unqual - , add_attr - , add_attrs - -- * Conversion functions from xml-light types - , fromXLQName - , fromXLCData - , fromXLElement - , fromXLAttr - , fromXLContent - -- * Replacement for xml-light's Text.XML.Proc - , strContent - , onlyElems - , elChildren - , onlyText - , findChildren - , filterChildren - , filterChildrenName - , findChild - , filterChild - , filterChildName - , findElement - , filterElement - , filterElementName - , findElements - , filterElements - , filterElementsName - , findAttr - , lookupAttr - , lookupAttrBy - , findAttrBy - -- * Replacement for xml-light's Text.XML.Output - , ppTopElement - , ppElement - , ppContent - , ppcElement - , ppcContent - , showTopElement - , showElement - , showContent - , useShortEmptyTags - , defaultConfigPP - , ConfigPP(..) + ( module Text.Pandoc.XML.Light.Types + , module Text.Pandoc.XML.Light.Proc + , module Text.Pandoc.XML.Light.Output -- * Replacement for xml-light's Text.XML.Input , parseXMLElement , parseXMLContents @@ -92,16 +41,13 @@ module Text.Pandoc.XML.Light import qualified Control.Exception as E import qualified Text.XML as Conduit import Text.XML.Unresolved (InvalidEventStream(..)) -import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL -import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText) import qualified Data.Map as M -import Data.Data (Data) -import Data.Typeable (Typeable) -import Data.Maybe (mapMaybe, listToMaybe) -import Data.List(find) -import qualified Text.XML.Light as XL +import Data.Maybe (mapMaybe) +import Text.Pandoc.XML.Light.Types +import Text.Pandoc.XML.Light.Proc +import Text.Pandoc.XML.Light.Output -- Drop in replacement for parseXMLDoc in xml-light. parseXMLElement :: TL.Text -> Either T.Text Element @@ -141,446 +87,3 @@ nodeToContent (Conduit.NodeContent t) = Just (Text (CData CDataText t Nothing)) nodeToContent _ = Nothing -unqual :: Text -> QName -unqual x = QName x Nothing Nothing - --- | Add an attribute to an element. -add_attr :: Attr -> Element -> Element -add_attr a e = add_attrs [a] e - --- | Add some attributes to an element. -add_attrs :: [Attr] -> Element -> Element -add_attrs as e = e { elAttribs = as ++ elAttribs e } - --- --- type definitions lightly modified from xml-light --- - --- | A line is an Integer -type Line = Integer - --- | XML content -data Content = Elem Element - | Text CData - | CRef Text - deriving (Show, Typeable, Data) - --- | XML elements -data Element = Element { - elName :: QName, - elAttribs :: [Attr], - elContent :: [Content], - elLine :: Maybe Line - } deriving (Show, Typeable, Data) - --- | XML attributes -data Attr = Attr { - attrKey :: QName, - attrVal :: Text - } deriving (Eq, Ord, Show, Typeable, Data) - --- | XML CData -data CData = CData { - cdVerbatim :: CDataKind, - cdData :: Text, - cdLine :: Maybe Line - } deriving (Show, Typeable, Data) - -data CDataKind - = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc. - | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in case (qURI q1, qURI q2) of - (Nothing,Nothing) -> compare (qPrefix q1) (qPrefix q2) - (u1,u2) -> compare u1 u2 - x -> x - -class Node t where - node :: QName -> t -> Element - -instance Node ([Attr],[Content]) where - node n (attrs,cont) = Element { elName = n - , elAttribs = attrs - , elContent = cont - , elLine = Nothing - } - -instance Node [Attr] where node n as = node n (as,[]::[Content]) -instance Node Attr where node n a = node n [a] -instance Node () where node n () = node n ([]::[Attr]) - -instance Node [Content] where node n cs = node n ([]::[Attr],cs) -instance Node Content where node n c = node n [c] -instance Node ([Attr],Content) where node n (as,c) = node n (as,[c]) -instance Node (Attr,Content) where node n (a,c) = node n ([a],[c]) - -instance Node ([Attr],[Element]) where - node n (as,cs) = node n (as,map Elem cs) - -instance Node ([Attr],Element) where node n (as,c) = node n (as,[c]) -instance Node (Attr,Element) where node n (a,c) = node n ([a],c) -instance Node [Element] where node n es = node n ([]::[Attr],es) -instance Node Element where node n e = node n [e] - -instance Node ([Attr],[CData]) where - node n (as,cs) = node n (as,map Text cs) - -instance Node ([Attr],CData) where node n (as,c) = node n (as,[c]) -instance Node (Attr,CData) where node n (a,c) = node n ([a],c) -instance Node [CData] where node n es = node n ([]::[Attr],es) -instance Node CData where node n e = node n [e] - -instance Node ([Attr],Text) where - node n (as,t) = node n (as, CData { cdVerbatim = CDataText - , cdData = t - , cdLine = Nothing }) - -instance Node (Attr,Text ) where node n (a,t) = node n ([a],t) -instance Node Text where node n t = node n ([]::[Attr],t) - --- | Create node with unqualified name -unode :: Node t => Text -> t -> Element -unode = node . unqual - --- --- conversion from xml-light --- - -fromXLQName :: XL.QName -> QName -fromXLQName qn = QName { qName = T.pack $ XL.qName qn - , qURI = T.pack <$> XL.qURI qn - , qPrefix = T.pack <$> XL.qPrefix qn } - -fromXLCData :: XL.CData -> CData -fromXLCData cd = CData { cdVerbatim = case XL.cdVerbatim cd of - XL.CDataText -> CDataText - XL.CDataVerbatim -> CDataVerbatim - XL.CDataRaw -> CDataRaw - , cdData = T.pack $ XL.cdData cd - , cdLine = XL.cdLine cd } - -fromXLElement :: XL.Element -> Element -fromXLElement el = Element { elName = fromXLQName $ XL.elName el - , elAttribs = map fromXLAttr $ XL.elAttribs el - , elContent = map fromXLContent $ XL.elContent el - , elLine = XL.elLine el } - -fromXLAttr :: XL.Attr -> Attr -fromXLAttr (XL.Attr qn s) = Attr (fromXLQName qn) (T.pack s) - -fromXLContent :: XL.Content -> Content -fromXLContent (XL.Elem el) = Elem $ fromXLElement el -fromXLContent (XL.Text cd) = Text $ fromXLCData cd -fromXLContent (XL.CRef s) = CRef (T.pack s) - --- --- copied from xml-light Text.XML.Proc --- - --- | Get the text value of an XML element. This function --- ignores non-text elements, and concatenates all text elements. -strContent :: Element -> Text -strContent = mconcat . map cdData . onlyText . elContent - --- | Select only the elements from a list of XML content. -onlyElems :: [Content] -> [Element] -onlyElems xs = [ x | Elem x <- xs ] - --- | Select only the elements from a parent. -elChildren :: Element -> [Element] -elChildren e = [ x | Elem x <- elContent e ] - --- | Select only the text from a list of XML content. -onlyText :: [Content] -> [CData] -onlyText xs = [ x | Text x <- xs ] - --- | Find all immediate children with the given name. -findChildren :: QName -> Element -> [Element] -findChildren q e = filterChildren ((q ==) . elName) e - --- | Filter all immediate children wrt a given predicate. -filterChildren :: (Element -> Bool) -> Element -> [Element] -filterChildren p e = filter p (onlyElems (elContent e)) - - --- | Filter all immediate children wrt a given predicate over their names. -filterChildrenName :: (QName -> Bool) -> Element -> [Element] -filterChildrenName p e = filter (p.elName) (onlyElems (elContent e)) - - --- | Find an immediate child with the given name. -findChild :: QName -> Element -> Maybe Element -findChild q e = listToMaybe (findChildren q e) - --- | Find an immediate child with the given name. -filterChild :: (Element -> Bool) -> Element -> Maybe Element -filterChild p e = listToMaybe (filterChildren p e) - --- | Find an immediate child with name matching a predicate. -filterChildName :: (QName -> Bool) -> Element -> Maybe Element -filterChildName p e = listToMaybe (filterChildrenName p e) - --- | Find the left-most occurrence of an element matching given name. -findElement :: QName -> Element -> Maybe Element -findElement q e = listToMaybe (findElements q e) - --- | Filter the left-most occurrence of an element wrt. given predicate. -filterElement :: (Element -> Bool) -> Element -> Maybe Element -filterElement p e = listToMaybe (filterElements p e) - --- | Filter the left-most occurrence of an element wrt. given predicate. -filterElementName :: (QName -> Bool) -> Element -> Maybe Element -filterElementName p e = listToMaybe (filterElementsName p e) - --- | Find all non-nested occurances of an element. --- (i.e., once we have found an element, we do not search --- for more occurances among the element's children). -findElements :: QName -> Element -> [Element] -findElements qn e = filterElementsName (qn==) e - --- | Find all non-nested occurrences of an element wrt. given predicate. --- (i.e., once we have found an element, we do not search --- for more occurances among the element's children). -filterElements :: (Element -> Bool) -> Element -> [Element] -filterElements p e - | p e = [e] - | otherwise = concatMap (filterElements p) $ onlyElems $ elContent e - --- | Find all non-nested occurences of an element wrt a predicate over element names. --- (i.e., once we have found an element, we do not search --- for more occurances among the element's children). -filterElementsName :: (QName -> Bool) -> Element -> [Element] -filterElementsName p e = filterElements (p.elName) e - --- | Lookup the value of an attribute. -findAttr :: QName -> Element -> Maybe Text -findAttr x e = lookupAttr x (elAttribs e) - --- | Lookup attribute name from list. -lookupAttr :: QName -> [Attr] -> Maybe Text -lookupAttr x = lookupAttrBy (x ==) - --- | Lookup the first attribute whose name satisfies the given predicate. -lookupAttrBy :: (QName -> Bool) -> [Attr] -> Maybe Text -lookupAttrBy p as = attrVal `fmap` find (p . attrKey) as - --- | Lookup the value of the first attribute whose name --- satisfies the given predicate. -findAttrBy :: (QName -> Bool) -> Element -> Maybe Text -findAttrBy p e = lookupAttrBy p (elAttribs e) - - --- --- duplicates functinos from Text.XML.Output --- - --- | The XML 1.0 header -xmlHeader :: Text -xmlHeader = "" - - --------------------------------------------------------------------------------- -data ConfigPP = ConfigPP - { shortEmptyTag :: QName -> Bool - , prettify :: Bool - } - --- | Default pretty orinting configuration. --- * Always use abbreviate empty tags. -defaultConfigPP :: ConfigPP -defaultConfigPP = ConfigPP { shortEmptyTag = const True - , prettify = False - } - --- | The predicate specifies for which empty tags we should use XML's --- abbreviated notation . This is useful if we are working with --- some XML-ish standards (such as certain versions of HTML) where some --- empty tags should always be displayed in the form. -useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP -useShortEmptyTags p c = c { shortEmptyTag = p } - - --- | Specify if we should use extra white-space to make document more readable. --- WARNING: This adds additional white-space to text elements, --- and so it may change the meaning of the document. -useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP -useExtraWhiteSpace p c = c { prettify = p } - --- | A configuration that tries to make things pretty --- (possibly at the cost of changing the semantics a bit --- through adding white space.) -prettyConfigPP :: ConfigPP -prettyConfigPP = useExtraWhiteSpace True defaultConfigPP - - --------------------------------------------------------------------------------- - - --- | Pretty printing renders XML documents faithfully, --- with the exception that whitespace may be added\/removed --- in non-verbatim character data. -ppTopElement :: Element -> Text -ppTopElement = ppcTopElement prettyConfigPP - --- | Pretty printing elements -ppElement :: Element -> Text -ppElement = ppcElement prettyConfigPP - --- | Pretty printing content -ppContent :: Content -> Text -ppContent = ppcContent prettyConfigPP - --- | Pretty printing renders XML documents faithfully, --- with the exception that whitespace may be added\/removed --- in non-verbatim character data. -ppcTopElement :: ConfigPP -> Element -> Text -ppcTopElement c e = T.unlines [xmlHeader,ppcElement c e] - --- | Pretty printing elements -ppcElement :: ConfigPP -> Element -> Text -ppcElement c = TL.toStrict . toLazyText . ppElementS c mempty - --- | Pretty printing content -ppcContent :: ConfigPP -> Content -> Text -ppcContent c = TL.toStrict . toLazyText . ppContentS c mempty - -ppcCData :: ConfigPP -> CData -> Text -ppcCData c = TL.toStrict . toLazyText . ppCDataS c mempty - -type Indent = Builder - --- | Pretty printing content using ShowT -ppContentS :: ConfigPP -> Indent -> Content -> Builder -ppContentS c i x = case x of - Elem e -> ppElementS c i e - Text t -> ppCDataS c i t - CRef r -> showCRefS r - -ppElementS :: ConfigPP -> Indent -> Element -> Builder -ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <> - (case elContent e of - [] | "?" `T.isPrefixOf` qName name -> fromText " ?>" - | shortEmptyTag c name -> fromText " />" - [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name - cs -> singleton '>' <> nl <> - mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <> - i <> tagEnd name - where (nl,sp) = if prettify c then ("\n"," ") else ("","") - ) - where name = elName e - -ppCDataS :: ConfigPP -> Indent -> CData -> Builder -ppCDataS c i t = i <> if cdVerbatim t /= CDataText || not (prettify c) - then showCDataS t - else foldr cons mempty (T.unpack (showCData t)) - where cons :: Char -> Builder -> Builder - cons '\n' ys = singleton '\n' <> i <> ys - cons y ys = singleton y <> ys - - - --------------------------------------------------------------------------------- - --- | Adds the header. -showTopElement :: Element -> Text -showTopElement c = xmlHeader <> showElement c - -showContent :: Content -> Text -showContent = ppcContent defaultConfigPP - -showElement :: Element -> Text -showElement = ppcElement defaultConfigPP - -showCData :: CData -> Text -showCData = ppcCData defaultConfigPP - --- Note: crefs should not contain '&', ';', etc. -showCRefS :: Text -> Builder -showCRefS r = singleton '&' <> fromText r <> singleton ';' - --- | Convert a text element to characters. -showCDataS :: CData -> Builder -showCDataS cd = - case cdVerbatim cd of - CDataText -> escStr (cdData cd) - CDataVerbatim -> fromText " escCData (cdData cd) <> - fromText "]]>" - CDataRaw -> fromText (cdData cd) - --------------------------------------------------------------------------------- -escCData :: Text -> Builder -escCData t - | "]]>" `T.isPrefixOf` t = - fromText "]]]]>" <> fromText (T.drop 3 t) -escCData t - = case T.uncons t of - Nothing -> mempty - Just (c,t') -> singleton c <> escCData t' - -escChar :: Char -> Builder -escChar c = case c of - '<' -> fromText "<" - '>' -> fromText ">" - '&' -> fromText "&" - '"' -> fromText """ - -- we use ' instead of ' because IE apparently has difficulties - -- rendering ' in xhtml. - -- Reported by Rohan Drape . - '\'' -> fromText "'" - _ -> singleton c - - {- original xml-light version: - -- NOTE: We escape '\r' explicitly because otherwise they get lost - -- when parsed back in because of then end-of-line normalization rules. - _ | isPrint c || c == '\n' -> singleton c - | otherwise -> showText "&#" . showsT oc . singleton ';' - where oc = ord c - -} - -escStr :: Text -> Builder -escStr cs = if T.any needsEscape cs - then mconcat (map escChar (T.unpack cs)) - else fromText cs - where - needsEscape '<' = True - needsEscape '>' = True - needsEscape '&' = True - needsEscape '"' = True - needsEscape '\'' = True - needsEscape _ = False - -tagEnd :: QName -> Builder -tagEnd qn = fromText " showQName qn <> singleton '>' - -tagStart :: QName -> [Attr] -> Builder -tagStart qn as = singleton '<' <> showQName qn <> as_str - where as_str = if null as - then mempty - else mconcat (map showAttr as) - -showAttr :: Attr -> Builder -showAttr (Attr qn v) = singleton ' ' <> showQName qn <> - singleton '=' <> - singleton '"' <> escStr v <> singleton '"' - -showQName :: QName -> Builder -showQName q = - case qPrefix q of - Nothing -> fromText (qName q) - Just p -> fromText p <> singleton ':' <> fromText (qName q) diff --git a/src/Text/Pandoc/XML/Light/Output.hs b/src/Text/Pandoc/XML/Light/Output.hs new file mode 100644 index 000000000..dc94ce914 --- /dev/null +++ b/src/Text/Pandoc/XML/Light/Output.hs @@ -0,0 +1,230 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.XML.Light.Output + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable +-} +module Text.Pandoc.XML.Light.Output + ( -- * Replacement for xml-light's Text.XML.Output + ppTopElement + , ppElement + , ppContent + , ppcElement + , ppcContent + , showTopElement + , showElement + , showContent + , useShortEmptyTags + , defaultConfigPP + , ConfigPP(..) + ) where + +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText) +import Text.Pandoc.XML.Light.Types + +-- +-- duplicates functinos from Text.XML.Output +-- + +-- | The XML 1.0 header +xmlHeader :: Text +xmlHeader = "" + + +-------------------------------------------------------------------------------- +data ConfigPP = ConfigPP + { shortEmptyTag :: QName -> Bool + , prettify :: Bool + } + +-- | Default pretty orinting configuration. +-- * Always use abbreviate empty tags. +defaultConfigPP :: ConfigPP +defaultConfigPP = ConfigPP { shortEmptyTag = const True + , prettify = False + } + +-- | The predicate specifies for which empty tags we should use XML's +-- abbreviated notation . This is useful if we are working with +-- some XML-ish standards (such as certain versions of HTML) where some +-- empty tags should always be displayed in the form. +useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP +useShortEmptyTags p c = c { shortEmptyTag = p } + + +-- | Specify if we should use extra white-space to make document more readable. +-- WARNING: This adds additional white-space to text elements, +-- and so it may change the meaning of the document. +useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP +useExtraWhiteSpace p c = c { prettify = p } + +-- | A configuration that tries to make things pretty +-- (possibly at the cost of changing the semantics a bit +-- through adding white space.) +prettyConfigPP :: ConfigPP +prettyConfigPP = useExtraWhiteSpace True defaultConfigPP + + +-------------------------------------------------------------------------------- + + +-- | Pretty printing renders XML documents faithfully, +-- with the exception that whitespace may be added\/removed +-- in non-verbatim character data. +ppTopElement :: Element -> Text +ppTopElement = ppcTopElement prettyConfigPP + +-- | Pretty printing elements +ppElement :: Element -> Text +ppElement = ppcElement prettyConfigPP + +-- | Pretty printing content +ppContent :: Content -> Text +ppContent = ppcContent prettyConfigPP + +-- | Pretty printing renders XML documents faithfully, +-- with the exception that whitespace may be added\/removed +-- in non-verbatim character data. +ppcTopElement :: ConfigPP -> Element -> Text +ppcTopElement c e = T.unlines [xmlHeader,ppcElement c e] + +-- | Pretty printing elements +ppcElement :: ConfigPP -> Element -> Text +ppcElement c = TL.toStrict . toLazyText . ppElementS c mempty + +-- | Pretty printing content +ppcContent :: ConfigPP -> Content -> Text +ppcContent c = TL.toStrict . toLazyText . ppContentS c mempty + +ppcCData :: ConfigPP -> CData -> Text +ppcCData c = TL.toStrict . toLazyText . ppCDataS c mempty + +type Indent = Builder + +-- | Pretty printing content using ShowT +ppContentS :: ConfigPP -> Indent -> Content -> Builder +ppContentS c i x = case x of + Elem e -> ppElementS c i e + Text t -> ppCDataS c i t + CRef r -> showCRefS r + +ppElementS :: ConfigPP -> Indent -> Element -> Builder +ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <> + (case elContent e of + [] | "?" `T.isPrefixOf` qName name -> fromText " ?>" + | shortEmptyTag c name -> fromText " />" + [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name + cs -> singleton '>' <> nl <> + mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <> + i <> tagEnd name + where (nl,sp) = if prettify c then ("\n"," ") else ("","") + ) + where name = elName e + +ppCDataS :: ConfigPP -> Indent -> CData -> Builder +ppCDataS c i t = i <> if cdVerbatim t /= CDataText || not (prettify c) + then showCDataS t + else foldr cons mempty (T.unpack (showCData t)) + where cons :: Char -> Builder -> Builder + cons '\n' ys = singleton '\n' <> i <> ys + cons y ys = singleton y <> ys + + + +-------------------------------------------------------------------------------- + +-- | Adds the header. +showTopElement :: Element -> Text +showTopElement c = xmlHeader <> showElement c + +showContent :: Content -> Text +showContent = ppcContent defaultConfigPP + +showElement :: Element -> Text +showElement = ppcElement defaultConfigPP + +showCData :: CData -> Text +showCData = ppcCData defaultConfigPP + +-- Note: crefs should not contain '&', ';', etc. +showCRefS :: Text -> Builder +showCRefS r = singleton '&' <> fromText r <> singleton ';' + +-- | Convert a text element to characters. +showCDataS :: CData -> Builder +showCDataS cd = + case cdVerbatim cd of + CDataText -> escStr (cdData cd) + CDataVerbatim -> fromText " escCData (cdData cd) <> + fromText "]]>" + CDataRaw -> fromText (cdData cd) + +-------------------------------------------------------------------------------- +escCData :: Text -> Builder +escCData t + | "]]>" `T.isPrefixOf` t = + fromText "]]]]>" <> fromText (T.drop 3 t) +escCData t + = case T.uncons t of + Nothing -> mempty + Just (c,t') -> singleton c <> escCData t' + +escChar :: Char -> Builder +escChar c = case c of + '<' -> fromText "<" + '>' -> fromText ">" + '&' -> fromText "&" + '"' -> fromText """ + -- we use ' instead of ' because IE apparently has difficulties + -- rendering ' in xhtml. + -- Reported by Rohan Drape . + '\'' -> fromText "'" + _ -> singleton c + + {- original xml-light version: + -- NOTE: We escape '\r' explicitly because otherwise they get lost + -- when parsed back in because of then end-of-line normalization rules. + _ | isPrint c || c == '\n' -> singleton c + | otherwise -> showText "&#" . showsT oc . singleton ';' + where oc = ord c + -} + +escStr :: Text -> Builder +escStr cs = if T.any needsEscape cs + then mconcat (map escChar (T.unpack cs)) + else fromText cs + where + needsEscape '<' = True + needsEscape '>' = True + needsEscape '&' = True + needsEscape '"' = True + needsEscape '\'' = True + needsEscape _ = False + +tagEnd :: QName -> Builder +tagEnd qn = fromText " showQName qn <> singleton '>' + +tagStart :: QName -> [Attr] -> Builder +tagStart qn as = singleton '<' <> showQName qn <> as_str + where as_str = if null as + then mempty + else mconcat (map showAttr as) + +showAttr :: Attr -> Builder +showAttr (Attr qn v) = singleton ' ' <> showQName qn <> + singleton '=' <> + singleton '"' <> escStr v <> singleton '"' + +showQName :: QName -> Builder +showQName q = + case qPrefix q of + Nothing -> fromText (qName q) + Just p -> fromText p <> singleton ':' <> fromText (qName q) diff --git a/src/Text/Pandoc/XML/Light/Proc.hs b/src/Text/Pandoc/XML/Light/Proc.hs new file mode 100644 index 000000000..838d5af74 --- /dev/null +++ b/src/Text/Pandoc/XML/Light/Proc.hs @@ -0,0 +1,138 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.XML.Light.Proc + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable +-} +module Text.Pandoc.XML.Light.Proc + ( + -- * Replacement for xml-light's Text.XML.Proc + strContent + , onlyElems + , elChildren + , onlyText + , findChildren + , filterChildren + , filterChildrenName + , findChild + , filterChild + , filterChildName + , findElement + , filterElement + , filterElementName + , findElements + , filterElements + , filterElementsName + , findAttr + , lookupAttr + , lookupAttrBy + , findAttrBy + ) where + +import Data.Text (Text) +import Data.Maybe (listToMaybe) +import Data.List(find) +import Text.Pandoc.XML.Light.Types + +-- +-- copied from xml-light Text.XML.Proc +-- + +-- | Get the text value of an XML element. This function +-- ignores non-text elements, and concatenates all text elements. +strContent :: Element -> Text +strContent = mconcat . map cdData . onlyText . elContent + +-- | Select only the elements from a list of XML content. +onlyElems :: [Content] -> [Element] +onlyElems xs = [ x | Elem x <- xs ] + +-- | Select only the elements from a parent. +elChildren :: Element -> [Element] +elChildren e = [ x | Elem x <- elContent e ] + +-- | Select only the text from a list of XML content. +onlyText :: [Content] -> [CData] +onlyText xs = [ x | Text x <- xs ] + +-- | Find all immediate children with the given name. +findChildren :: QName -> Element -> [Element] +findChildren q e = filterChildren ((q ==) . elName) e + +-- | Filter all immediate children wrt a given predicate. +filterChildren :: (Element -> Bool) -> Element -> [Element] +filterChildren p e = filter p (onlyElems (elContent e)) + + +-- | Filter all immediate children wrt a given predicate over their names. +filterChildrenName :: (QName -> Bool) -> Element -> [Element] +filterChildrenName p e = filter (p.elName) (onlyElems (elContent e)) + + +-- | Find an immediate child with the given name. +findChild :: QName -> Element -> Maybe Element +findChild q e = listToMaybe (findChildren q e) + +-- | Find an immediate child with the given name. +filterChild :: (Element -> Bool) -> Element -> Maybe Element +filterChild p e = listToMaybe (filterChildren p e) + +-- | Find an immediate child with name matching a predicate. +filterChildName :: (QName -> Bool) -> Element -> Maybe Element +filterChildName p e = listToMaybe (filterChildrenName p e) + +-- | Find the left-most occurrence of an element matching given name. +findElement :: QName -> Element -> Maybe Element +findElement q e = listToMaybe (findElements q e) + +-- | Filter the left-most occurrence of an element wrt. given predicate. +filterElement :: (Element -> Bool) -> Element -> Maybe Element +filterElement p e = listToMaybe (filterElements p e) + +-- | Filter the left-most occurrence of an element wrt. given predicate. +filterElementName :: (QName -> Bool) -> Element -> Maybe Element +filterElementName p e = listToMaybe (filterElementsName p e) + +-- | Find all non-nested occurances of an element. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +findElements :: QName -> Element -> [Element] +findElements qn e = filterElementsName (qn==) e + +-- | Find all non-nested occurrences of an element wrt. given predicate. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +filterElements :: (Element -> Bool) -> Element -> [Element] +filterElements p e + | p e = [e] + | otherwise = concatMap (filterElements p) $ onlyElems $ elContent e + +-- | Find all non-nested occurences of an element wrt a predicate over element names. +-- (i.e., once we have found an element, we do not search +-- for more occurances among the element's children). +filterElementsName :: (QName -> Bool) -> Element -> [Element] +filterElementsName p e = filterElements (p.elName) e + +-- | Lookup the value of an attribute. +findAttr :: QName -> Element -> Maybe Text +findAttr x e = lookupAttr x (elAttribs e) + +-- | Lookup attribute name from list. +lookupAttr :: QName -> [Attr] -> Maybe Text +lookupAttr x = lookupAttrBy (x ==) + +-- | Lookup the first attribute whose name satisfies the given predicate. +lookupAttrBy :: (QName -> Bool) -> [Attr] -> Maybe Text +lookupAttrBy p as = attrVal `fmap` find (p . attrKey) as + +-- | Lookup the value of the first attribute whose name +-- satisfies the given predicate. +findAttrBy :: (QName -> Bool) -> Element -> Maybe Text +findAttrBy p e = lookupAttrBy p (elAttribs e) + + diff --git a/src/Text/Pandoc/XML/Light/Types.hs b/src/Text/Pandoc/XML/Light/Types.hs new file mode 100644 index 000000000..f338da6ea --- /dev/null +++ b/src/Text/Pandoc/XML/Light/Types.hs @@ -0,0 +1,190 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE DeriveDataTypeable #-} +{- | + Module : Text.Pandoc.XML.Light.Types + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +-} +module Text.Pandoc.XML.Light.Types + ( -- * Basic types, duplicating those from xml-light but with Text + -- instead of String + Line + , Content(..) + , Element(..) + , Attr(..) + , CData(..) + , CDataKind(..) + , QName(..) + , Node(..) + , unode + , unqual + , add_attr + , add_attrs + -- * Conversion functions from xml-light types + , fromXLQName + , fromXLCData + , fromXLElement + , fromXLAttr + , fromXLContent + ) where + +import Data.Text (Text) +import qualified Data.Text as T +import Data.Data (Data) +import Data.Typeable (Typeable) +import qualified Text.XML.Light as XL + +-- +-- type definitions lightly modified from xml-light +-- + +-- | A line is an Integer +type Line = Integer + +-- | XML content +data Content = Elem Element + | Text CData + | CRef Text + deriving (Show, Typeable, Data) + +-- | XML elements +data Element = Element { + elName :: QName, + elAttribs :: [Attr], + elContent :: [Content], + elLine :: Maybe Line + } deriving (Show, Typeable, Data) + +-- | XML attributes +data Attr = Attr { + attrKey :: QName, + attrVal :: Text + } deriving (Eq, Ord, Show, Typeable, Data) + +-- | XML CData +data CData = CData { + cdVerbatim :: CDataKind, + cdData :: Text, + cdLine :: Maybe Line + } deriving (Show, Typeable, Data) + +data CDataKind + = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc. + | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in case (qURI q1, qURI q2) of + (Nothing,Nothing) -> compare (qPrefix q1) (qPrefix q2) + (u1,u2) -> compare u1 u2 + x -> x + +class Node t where + node :: QName -> t -> Element + +instance Node ([Attr],[Content]) where + node n (attrs,cont) = Element { elName = n + , elAttribs = attrs + , elContent = cont + , elLine = Nothing + } + +instance Node [Attr] where node n as = node n (as,[]::[Content]) +instance Node Attr where node n a = node n [a] +instance Node () where node n () = node n ([]::[Attr]) + +instance Node [Content] where node n cs = node n ([]::[Attr],cs) +instance Node Content where node n c = node n [c] +instance Node ([Attr],Content) where node n (as,c) = node n (as,[c]) +instance Node (Attr,Content) where node n (a,c) = node n ([a],[c]) + +instance Node ([Attr],[Element]) where + node n (as,cs) = node n (as,map Elem cs) + +instance Node ([Attr],Element) where node n (as,c) = node n (as,[c]) +instance Node (Attr,Element) where node n (a,c) = node n ([a],c) +instance Node [Element] where node n es = node n ([]::[Attr],es) +instance Node Element where node n e = node n [e] + +instance Node ([Attr],[CData]) where + node n (as,cs) = node n (as,map Text cs) + +instance Node ([Attr],CData) where node n (as,c) = node n (as,[c]) +instance Node (Attr,CData) where node n (a,c) = node n ([a],c) +instance Node [CData] where node n es = node n ([]::[Attr],es) +instance Node CData where node n e = node n [e] + +instance Node ([Attr],Text) where + node n (as,t) = node n (as, CData { cdVerbatim = CDataText + , cdData = t + , cdLine = Nothing }) + +instance Node (Attr,Text ) where node n (a,t) = node n ([a],t) +instance Node Text where node n t = node n ([]::[Attr],t) + +-- | Create node with unqualified name +unode :: Node t => Text -> t -> Element +unode = node . unqual + +unqual :: Text -> QName +unqual x = QName x Nothing Nothing + +-- | Add an attribute to an element. +add_attr :: Attr -> Element -> Element +add_attr a e = add_attrs [a] e + +-- | Add some attributes to an element. +add_attrs :: [Attr] -> Element -> Element +add_attrs as e = e { elAttribs = as ++ elAttribs e } + +-- +-- conversion from xml-light +-- + +fromXLQName :: XL.QName -> QName +fromXLQName qn = QName { qName = T.pack $ XL.qName qn + , qURI = T.pack <$> XL.qURI qn + , qPrefix = T.pack <$> XL.qPrefix qn } + +fromXLCData :: XL.CData -> CData +fromXLCData cd = CData { cdVerbatim = case XL.cdVerbatim cd of + XL.CDataText -> CDataText + XL.CDataVerbatim -> CDataVerbatim + XL.CDataRaw -> CDataRaw + , cdData = T.pack $ XL.cdData cd + , cdLine = XL.cdLine cd } + +fromXLElement :: XL.Element -> Element +fromXLElement el = Element { elName = fromXLQName $ XL.elName el + , elAttribs = map fromXLAttr $ XL.elAttribs el + , elContent = map fromXLContent $ XL.elContent el + , elLine = XL.elLine el } + +fromXLAttr :: XL.Attr -> Attr +fromXLAttr (XL.Attr qn s) = Attr (fromXLQName qn) (T.pack s) + +fromXLContent :: XL.Content -> Content +fromXLContent (XL.Elem el) = Elem $ fromXLElement el +fromXLContent (XL.Text cd) = Text $ fromXLCData cd +fromXLContent (XL.CRef s) = CRef (T.pack s) + + -- cgit v1.2.3 From 4af378702ae31d4c8a11d0c827a5986f54b5e310 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Feb 2021 18:44:38 -0800 Subject: Add orig copyright/license info for code derived from xml-light. --- src/Text/Pandoc/XML/Light/Output.hs | 6 +++++- src/Text/Pandoc/XML/Light/Proc.hs | 4 +++- src/Text/Pandoc/XML/Light/Types.hs | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/XML/Light/Output.hs b/src/Text/Pandoc/XML/Light/Output.hs index dc94ce914..8182ef2ec 100644 --- a/src/Text/Pandoc/XML/Light/Output.hs +++ b/src/Text/Pandoc/XML/Light/Output.hs @@ -2,12 +2,16 @@ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light.Output - Copyright : Copyright (C) 2021 John MacFarlane + Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane License : GNU GPL, version 2 or above + Maintainer : John MacFarlane Stability : alpha Portability : portable + + This code is based on code from xml-light, released under the BSD3 license. + We use a text Builder instead of ShowS. -} module Text.Pandoc.XML.Light.Output ( -- * Replacement for xml-light's Text.XML.Output diff --git a/src/Text/Pandoc/XML/Light/Proc.hs b/src/Text/Pandoc/XML/Light/Proc.hs index 838d5af74..b53c4b545 100644 --- a/src/Text/Pandoc/XML/Light/Proc.hs +++ b/src/Text/Pandoc/XML/Light/Proc.hs @@ -2,12 +2,14 @@ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light.Proc - Copyright : Copyright (C) 2021 John MacFarlane + Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable + + This code is taken from xml-light, released under the BSD3 license. -} module Text.Pandoc.XML.Light.Proc ( diff --git a/src/Text/Pandoc/XML/Light/Types.hs b/src/Text/Pandoc/XML/Light/Types.hs index f338da6ea..03fdc2e4d 100644 --- a/src/Text/Pandoc/XML/Light/Types.hs +++ b/src/Text/Pandoc/XML/Light/Types.hs @@ -2,13 +2,16 @@ {-# LANGUAGE DeriveDataTypeable #-} {- | Module : Text.Pandoc.XML.Light.Types - Copyright : Copyright (C) 2021 John MacFarlane + Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable + This code is taken from xml-light, released under the BSD3 license. + It has been modified to use Text instead of String, and the fromXL* + functions have been added. -} module Text.Pandoc.XML.Light.Types ( -- * Basic types, duplicating those from xml-light but with Text -- cgit v1.2.3 From d8fc4971868104274881570ce9bc3d9edf0d2506 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Feb 2021 18:51:41 -0800 Subject: Add T.P.XML.Light.Cursor. --- pandoc.cabal | 1 + src/Text/Pandoc/XML/Light/Cursor.hs | 346 ++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 src/Text/Pandoc/XML/Light/Cursor.hs diff --git a/pandoc.cabal b/pandoc.cabal index d27520ba0..61bf0b51d 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -689,6 +689,7 @@ library Text.Pandoc.Lua.Walk, Text.Pandoc.XML.Light, Text.Pandoc.XML.Light.Types, + Text.Pandoc.XML.Light.Cursor, Text.Pandoc.XML.Light.Proc, Text.Pandoc.XML.Light.Output, Text.Pandoc.CSS, diff --git a/src/Text/Pandoc/XML/Light/Cursor.hs b/src/Text/Pandoc/XML/Light/Cursor.hs new file mode 100644 index 000000000..2e6da5346 --- /dev/null +++ b/src/Text/Pandoc/XML/Light/Cursor.hs @@ -0,0 +1,346 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.XML.Light.Cursor + Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + + This code is taken from xml-light, released under the BSD3 license. +-} +module Text.Pandoc.XML.Light.Cursor + ( -- * Replacement for xml-light's Text.XML.Cursor + Tag(..) + , getTag + , setTag + , fromTag + , Cursor(..) + , Path + + -- * Conversions + , fromContent + , fromElement + , fromForest + , toForest + , toTree + + -- * Moving around + , parent + , root + , getChild + , firstChild + , lastChild + , left + , right + , nextDF + + -- ** Searching + , findChild + , findLeft + , findRight + , findRec + + -- * Node classification + , isRoot + , isFirst + , isLast + , isLeaf + , isChild + , hasChildren + , getNodeIndex + + -- * Updates + , setContent + , modifyContent + , modifyContentM + + -- ** Inserting content + , insertLeft + , insertRight + , insertGoLeft + , insertGoRight + + -- ** Removing content + , removeLeft + , removeRight + , removeGoLeft + , removeGoRight + , removeGoUp + + ) where + +import Text.Pandoc.XML.Light.Types +import Data.Maybe(isNothing) +import Control.Monad(mplus) + +data Tag = Tag { tagName :: QName + , tagAttribs :: [Attr] + , tagLine :: Maybe Line + } deriving (Show) + +getTag :: Element -> Tag +getTag e = Tag { tagName = elName e + , tagAttribs = elAttribs e + , tagLine = elLine e + } + +setTag :: Tag -> Element -> Element +setTag t e = fromTag t (elContent e) + +fromTag :: Tag -> [Content] -> Element +fromTag t cs = Element { elName = tagName t + , elAttribs = tagAttribs t + , elLine = tagLine t + , elContent = cs + } + +type Path = [([Content],Tag,[Content])] + +-- | The position of a piece of content in an XML document. +data Cursor = Cur + { current :: Content -- ^ The currently selected content. + , lefts :: [Content] -- ^ Siblings on the left, closest first. + , rights :: [Content] -- ^ Siblings on the right, closest first. + , parents :: Path -- ^ The contexts of the parent elements of this location. + } deriving (Show) + +-- Moving around --------------------------------------------------------------- + +-- | The parent of the given location. +parent :: Cursor -> Maybe Cursor +parent loc = + case parents loc of + (pls,v,prs) : ps -> Just + Cur { current = Elem + (fromTag v + (combChildren (lefts loc) (current loc) (rights loc))) + , lefts = pls, rights = prs, parents = ps + } + [] -> Nothing + + +-- | The top-most parent of the given location. +root :: Cursor -> Cursor +root loc = maybe loc root (parent loc) + +-- | The left sibling of the given location. +left :: Cursor -> Maybe Cursor +left loc = + case lefts loc of + t : ts -> Just loc { current = t, lefts = ts + , rights = current loc : rights loc } + [] -> Nothing + +-- | The right sibling of the given location. +right :: Cursor -> Maybe Cursor +right loc = + case rights loc of + t : ts -> Just loc { current = t, lefts = current loc : lefts loc + , rights = ts } + [] -> Nothing + +-- | The first child of the given location. +firstChild :: Cursor -> Maybe Cursor +firstChild loc = + do (t : ts, ps) <- downParents loc + return Cur { current = t, lefts = [], rights = ts , parents = ps } + +-- | The last child of the given location. +lastChild :: Cursor -> Maybe Cursor +lastChild loc = + do (ts, ps) <- downParents loc + case reverse ts of + l : ls -> return Cur { current = l, lefts = ls, rights = [] + , parents = ps } + [] -> Nothing + +-- | Find the next left sibling that satisfies a predicate. +findLeft :: (Cursor -> Bool) -> Cursor -> Maybe Cursor +findLeft p loc = do loc1 <- left loc + if p loc1 then return loc1 else findLeft p loc1 + +-- | Find the next right sibling that satisfies a predicate. +findRight :: (Cursor -> Bool) -> Cursor -> Maybe Cursor +findRight p loc = do loc1 <- right loc + if p loc1 then return loc1 else findRight p loc1 + +-- | The first child that satisfies a predicate. +findChild :: (Cursor -> Bool) -> Cursor -> Maybe Cursor +findChild p loc = + do loc1 <- firstChild loc + if p loc1 then return loc1 else findRight p loc1 + +-- | The next position in a left-to-right depth-first traversal of a document: +-- either the first child, right sibling, or the right sibling of a parent that +-- has one. +nextDF :: Cursor -> Maybe Cursor +nextDF c = firstChild c `mplus` up c + where up x = right x `mplus` (up =<< parent x) + +-- | Perform a depth first search for a descendant that satisfies the +-- given predicate. +findRec :: (Cursor -> Bool) -> Cursor -> Maybe Cursor +findRec p c = if p c then Just c else findRec p =<< nextDF c + +-- | The child with the given index (starting from 0). +getChild :: Int -> Cursor -> Maybe Cursor +getChild n loc = + do (ts,ps) <- downParents loc + (ls,t,rs) <- splitChildren ts n + return Cur { current = t, lefts = ls, rights = rs, parents = ps } + + +-- | private: computes the parent for "down" operations. +downParents :: Cursor -> Maybe ([Content], Path) +downParents loc = + case current loc of + Elem e -> Just ( elContent e + , (lefts loc, getTag e, rights loc) : parents loc + ) + _ -> Nothing + +-- Conversions ----------------------------------------------------------------- + +-- | A cursor for the given content. +fromContent :: Content -> Cursor +fromContent t = Cur { current = t, lefts = [], rights = [], parents = [] } + +-- | A cursor for the given element. +fromElement :: Element -> Cursor +fromElement e = fromContent (Elem e) + +-- | The location of the first tree in a forest. +fromForest :: [Content] -> Maybe Cursor +fromForest (t:ts) = Just Cur { current = t, lefts = [], rights = ts + , parents = [] } +fromForest [] = Nothing + +-- | Computes the tree containing this location. +toTree :: Cursor -> Content +toTree loc = current (root loc) + +-- | Computes the forest containing this location. +toForest :: Cursor -> [Content] +toForest loc = let r = root loc in combChildren (lefts r) (current r) (rights r) + + +-- Queries --------------------------------------------------------------------- + +-- | Are we at the top of the document? +isRoot :: Cursor -> Bool +isRoot loc = null (parents loc) + +-- | Are we at the left end of the the document? +isFirst :: Cursor -> Bool +isFirst loc = null (lefts loc) + +-- | Are we at the right end of the document? +isLast :: Cursor -> Bool +isLast loc = null (rights loc) + +-- | Are we at the bottom of the document? +isLeaf :: Cursor -> Bool +isLeaf loc = isNothing (downParents loc) + +-- | Do we have a parent? +isChild :: Cursor -> Bool +isChild loc = not (isRoot loc) + +-- | Get the node index inside the sequence of children +getNodeIndex :: Cursor -> Int +getNodeIndex loc = length (lefts loc) + +-- | Do we have children? +hasChildren :: Cursor -> Bool +hasChildren loc = not (isLeaf loc) + + + +-- Updates --------------------------------------------------------------------- + +-- | Change the current content. +setContent :: Content -> Cursor -> Cursor +setContent t loc = loc { current = t } + +-- | Modify the current content. +modifyContent :: (Content -> Content) -> Cursor -> Cursor +modifyContent f loc = setContent (f (current loc)) loc + +-- | Modify the current content, allowing for an effect. +modifyContentM :: Monad m => (Content -> m Content) -> Cursor -> m Cursor +modifyContentM f loc = do x <- f (current loc) + return (setContent x loc) + +-- | Insert content to the left of the current position. +insertLeft :: Content -> Cursor -> Cursor +insertLeft t loc = loc { lefts = t : lefts loc } + +-- | Insert content to the right of the current position. +insertRight :: Content -> Cursor -> Cursor +insertRight t loc = loc { rights = t : rights loc } + +-- | Remove the content on the left of the current position, if any. +removeLeft :: Cursor -> Maybe (Content,Cursor) +removeLeft loc = case lefts loc of + l : ls -> return (l,loc { lefts = ls }) + [] -> Nothing + +-- | Remove the content on the right of the current position, if any. +removeRight :: Cursor -> Maybe (Content,Cursor) +removeRight loc = case rights loc of + l : ls -> return (l,loc { rights = ls }) + [] -> Nothing + + +-- | Insert content to the left of the current position. +-- The new content becomes the current position. +insertGoLeft :: Content -> Cursor -> Cursor +insertGoLeft t loc = loc { current = t, rights = current loc : rights loc } + +-- | Insert content to the right of the current position. +-- The new content becomes the current position. +insertGoRight :: Content -> Cursor -> Cursor +insertGoRight t loc = loc { current = t, lefts = current loc : lefts loc } + +-- | Remove the current element. +-- The new position is the one on the left. +removeGoLeft :: Cursor -> Maybe Cursor +removeGoLeft loc = case lefts loc of + l : ls -> Just loc { current = l, lefts = ls } + [] -> Nothing + +-- | Remove the current element. +-- The new position is the one on the right. +removeGoRight :: Cursor -> Maybe Cursor +removeGoRight loc = case rights loc of + l : ls -> Just loc { current = l, rights = ls } + [] -> Nothing + +-- | Remove the current element. +-- The new position is the parent of the old position. +removeGoUp :: Cursor -> Maybe Cursor +removeGoUp loc = + case parents loc of + (pls,v,prs) : ps -> Just + Cur { current = Elem (fromTag v (reverse (lefts loc) ++ rights loc)) + , lefts = pls, rights = prs, parents = ps + } + [] -> Nothing + + +-- | private: Gets the given element of a list. +-- Also returns the preceding elements (reversed) and the following elements. +splitChildren :: [a] -> Int -> Maybe ([a],a,[a]) +splitChildren _ n | n < 0 = Nothing +splitChildren cs pos = loop [] cs pos + where loop acc (x:xs) 0 = Just (acc,x,xs) + loop acc (x:xs) n = loop (x:acc) xs $! n-1 + loop _ _ _ = Nothing + +-- | private: combChildren ls x ys = reverse ls ++ [x] ++ ys +combChildren :: [a] -> a -> [a] -> [a] +combChildren ls t rs = foldl (flip (:)) (t:rs) ls + -- cgit v1.2.3 From 80a1d5c9b60b676ba7b7e6ed0267197c8f0ec459 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Feb 2021 19:18:01 -0800 Subject: Revert "Add T.P.XML.Light.Cursor." This reverts commit d8fc4971868104274881570ce9bc3d9edf0d2506. --- pandoc.cabal | 1 - src/Text/Pandoc/XML/Light/Cursor.hs | 346 ------------------------------------ 2 files changed, 347 deletions(-) delete mode 100644 src/Text/Pandoc/XML/Light/Cursor.hs diff --git a/pandoc.cabal b/pandoc.cabal index 61bf0b51d..d27520ba0 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -689,7 +689,6 @@ library Text.Pandoc.Lua.Walk, Text.Pandoc.XML.Light, Text.Pandoc.XML.Light.Types, - Text.Pandoc.XML.Light.Cursor, Text.Pandoc.XML.Light.Proc, Text.Pandoc.XML.Light.Output, Text.Pandoc.CSS, diff --git a/src/Text/Pandoc/XML/Light/Cursor.hs b/src/Text/Pandoc/XML/Light/Cursor.hs deleted file mode 100644 index 2e6da5346..000000000 --- a/src/Text/Pandoc/XML/Light/Cursor.hs +++ /dev/null @@ -1,346 +0,0 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{- | - Module : Text.Pandoc.XML.Light.Cursor - Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - - This code is taken from xml-light, released under the BSD3 license. --} -module Text.Pandoc.XML.Light.Cursor - ( -- * Replacement for xml-light's Text.XML.Cursor - Tag(..) - , getTag - , setTag - , fromTag - , Cursor(..) - , Path - - -- * Conversions - , fromContent - , fromElement - , fromForest - , toForest - , toTree - - -- * Moving around - , parent - , root - , getChild - , firstChild - , lastChild - , left - , right - , nextDF - - -- ** Searching - , findChild - , findLeft - , findRight - , findRec - - -- * Node classification - , isRoot - , isFirst - , isLast - , isLeaf - , isChild - , hasChildren - , getNodeIndex - - -- * Updates - , setContent - , modifyContent - , modifyContentM - - -- ** Inserting content - , insertLeft - , insertRight - , insertGoLeft - , insertGoRight - - -- ** Removing content - , removeLeft - , removeRight - , removeGoLeft - , removeGoRight - , removeGoUp - - ) where - -import Text.Pandoc.XML.Light.Types -import Data.Maybe(isNothing) -import Control.Monad(mplus) - -data Tag = Tag { tagName :: QName - , tagAttribs :: [Attr] - , tagLine :: Maybe Line - } deriving (Show) - -getTag :: Element -> Tag -getTag e = Tag { tagName = elName e - , tagAttribs = elAttribs e - , tagLine = elLine e - } - -setTag :: Tag -> Element -> Element -setTag t e = fromTag t (elContent e) - -fromTag :: Tag -> [Content] -> Element -fromTag t cs = Element { elName = tagName t - , elAttribs = tagAttribs t - , elLine = tagLine t - , elContent = cs - } - -type Path = [([Content],Tag,[Content])] - --- | The position of a piece of content in an XML document. -data Cursor = Cur - { current :: Content -- ^ The currently selected content. - , lefts :: [Content] -- ^ Siblings on the left, closest first. - , rights :: [Content] -- ^ Siblings on the right, closest first. - , parents :: Path -- ^ The contexts of the parent elements of this location. - } deriving (Show) - --- Moving around --------------------------------------------------------------- - --- | The parent of the given location. -parent :: Cursor -> Maybe Cursor -parent loc = - case parents loc of - (pls,v,prs) : ps -> Just - Cur { current = Elem - (fromTag v - (combChildren (lefts loc) (current loc) (rights loc))) - , lefts = pls, rights = prs, parents = ps - } - [] -> Nothing - - --- | The top-most parent of the given location. -root :: Cursor -> Cursor -root loc = maybe loc root (parent loc) - --- | The left sibling of the given location. -left :: Cursor -> Maybe Cursor -left loc = - case lefts loc of - t : ts -> Just loc { current = t, lefts = ts - , rights = current loc : rights loc } - [] -> Nothing - --- | The right sibling of the given location. -right :: Cursor -> Maybe Cursor -right loc = - case rights loc of - t : ts -> Just loc { current = t, lefts = current loc : lefts loc - , rights = ts } - [] -> Nothing - --- | The first child of the given location. -firstChild :: Cursor -> Maybe Cursor -firstChild loc = - do (t : ts, ps) <- downParents loc - return Cur { current = t, lefts = [], rights = ts , parents = ps } - --- | The last child of the given location. -lastChild :: Cursor -> Maybe Cursor -lastChild loc = - do (ts, ps) <- downParents loc - case reverse ts of - l : ls -> return Cur { current = l, lefts = ls, rights = [] - , parents = ps } - [] -> Nothing - --- | Find the next left sibling that satisfies a predicate. -findLeft :: (Cursor -> Bool) -> Cursor -> Maybe Cursor -findLeft p loc = do loc1 <- left loc - if p loc1 then return loc1 else findLeft p loc1 - --- | Find the next right sibling that satisfies a predicate. -findRight :: (Cursor -> Bool) -> Cursor -> Maybe Cursor -findRight p loc = do loc1 <- right loc - if p loc1 then return loc1 else findRight p loc1 - --- | The first child that satisfies a predicate. -findChild :: (Cursor -> Bool) -> Cursor -> Maybe Cursor -findChild p loc = - do loc1 <- firstChild loc - if p loc1 then return loc1 else findRight p loc1 - --- | The next position in a left-to-right depth-first traversal of a document: --- either the first child, right sibling, or the right sibling of a parent that --- has one. -nextDF :: Cursor -> Maybe Cursor -nextDF c = firstChild c `mplus` up c - where up x = right x `mplus` (up =<< parent x) - --- | Perform a depth first search for a descendant that satisfies the --- given predicate. -findRec :: (Cursor -> Bool) -> Cursor -> Maybe Cursor -findRec p c = if p c then Just c else findRec p =<< nextDF c - --- | The child with the given index (starting from 0). -getChild :: Int -> Cursor -> Maybe Cursor -getChild n loc = - do (ts,ps) <- downParents loc - (ls,t,rs) <- splitChildren ts n - return Cur { current = t, lefts = ls, rights = rs, parents = ps } - - --- | private: computes the parent for "down" operations. -downParents :: Cursor -> Maybe ([Content], Path) -downParents loc = - case current loc of - Elem e -> Just ( elContent e - , (lefts loc, getTag e, rights loc) : parents loc - ) - _ -> Nothing - --- Conversions ----------------------------------------------------------------- - --- | A cursor for the given content. -fromContent :: Content -> Cursor -fromContent t = Cur { current = t, lefts = [], rights = [], parents = [] } - --- | A cursor for the given element. -fromElement :: Element -> Cursor -fromElement e = fromContent (Elem e) - --- | The location of the first tree in a forest. -fromForest :: [Content] -> Maybe Cursor -fromForest (t:ts) = Just Cur { current = t, lefts = [], rights = ts - , parents = [] } -fromForest [] = Nothing - --- | Computes the tree containing this location. -toTree :: Cursor -> Content -toTree loc = current (root loc) - --- | Computes the forest containing this location. -toForest :: Cursor -> [Content] -toForest loc = let r = root loc in combChildren (lefts r) (current r) (rights r) - - --- Queries --------------------------------------------------------------------- - --- | Are we at the top of the document? -isRoot :: Cursor -> Bool -isRoot loc = null (parents loc) - --- | Are we at the left end of the the document? -isFirst :: Cursor -> Bool -isFirst loc = null (lefts loc) - --- | Are we at the right end of the document? -isLast :: Cursor -> Bool -isLast loc = null (rights loc) - --- | Are we at the bottom of the document? -isLeaf :: Cursor -> Bool -isLeaf loc = isNothing (downParents loc) - --- | Do we have a parent? -isChild :: Cursor -> Bool -isChild loc = not (isRoot loc) - --- | Get the node index inside the sequence of children -getNodeIndex :: Cursor -> Int -getNodeIndex loc = length (lefts loc) - --- | Do we have children? -hasChildren :: Cursor -> Bool -hasChildren loc = not (isLeaf loc) - - - --- Updates --------------------------------------------------------------------- - --- | Change the current content. -setContent :: Content -> Cursor -> Cursor -setContent t loc = loc { current = t } - --- | Modify the current content. -modifyContent :: (Content -> Content) -> Cursor -> Cursor -modifyContent f loc = setContent (f (current loc)) loc - --- | Modify the current content, allowing for an effect. -modifyContentM :: Monad m => (Content -> m Content) -> Cursor -> m Cursor -modifyContentM f loc = do x <- f (current loc) - return (setContent x loc) - --- | Insert content to the left of the current position. -insertLeft :: Content -> Cursor -> Cursor -insertLeft t loc = loc { lefts = t : lefts loc } - --- | Insert content to the right of the current position. -insertRight :: Content -> Cursor -> Cursor -insertRight t loc = loc { rights = t : rights loc } - --- | Remove the content on the left of the current position, if any. -removeLeft :: Cursor -> Maybe (Content,Cursor) -removeLeft loc = case lefts loc of - l : ls -> return (l,loc { lefts = ls }) - [] -> Nothing - --- | Remove the content on the right of the current position, if any. -removeRight :: Cursor -> Maybe (Content,Cursor) -removeRight loc = case rights loc of - l : ls -> return (l,loc { rights = ls }) - [] -> Nothing - - --- | Insert content to the left of the current position. --- The new content becomes the current position. -insertGoLeft :: Content -> Cursor -> Cursor -insertGoLeft t loc = loc { current = t, rights = current loc : rights loc } - --- | Insert content to the right of the current position. --- The new content becomes the current position. -insertGoRight :: Content -> Cursor -> Cursor -insertGoRight t loc = loc { current = t, lefts = current loc : lefts loc } - --- | Remove the current element. --- The new position is the one on the left. -removeGoLeft :: Cursor -> Maybe Cursor -removeGoLeft loc = case lefts loc of - l : ls -> Just loc { current = l, lefts = ls } - [] -> Nothing - --- | Remove the current element. --- The new position is the one on the right. -removeGoRight :: Cursor -> Maybe Cursor -removeGoRight loc = case rights loc of - l : ls -> Just loc { current = l, rights = ls } - [] -> Nothing - --- | Remove the current element. --- The new position is the parent of the old position. -removeGoUp :: Cursor -> Maybe Cursor -removeGoUp loc = - case parents loc of - (pls,v,prs) : ps -> Just - Cur { current = Elem (fromTag v (reverse (lefts loc) ++ rights loc)) - , lefts = pls, rights = prs, parents = ps - } - [] -> Nothing - - --- | private: Gets the given element of a list. --- Also returns the preceding elements (reversed) and the following elements. -splitChildren :: [a] -> Int -> Maybe ([a],a,[a]) -splitChildren _ n | n < 0 = Nothing -splitChildren cs pos = loop [] cs pos - where loop acc (x:xs) 0 = Just (acc,x,xs) - loop acc (x:xs) n = loop (x:acc) xs $! n-1 - loop _ _ _ = Nothing - --- | private: combChildren ls x ys = reverse ls ++ [x] ++ ys -combChildren :: [a] -> a -> [a] -> [a] -combChildren ls t rs = foldl (flip (:)) (t:rs) ls - -- cgit v1.2.3 From 73add0578989e1da6e9cd1de68e2e4142f789188 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Feb 2021 09:54:39 -0800 Subject: Docx reader: use Map instead of list for Namespaces. This gives a speedup of about 5-10%. The reader is now approximately twice as fast as in the last release. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 14 +++++++------- src/Text/Pandoc/Readers/Docx/Util.hs | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index c76f3c171..f8ed248d7 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -396,9 +396,9 @@ archiveToNotes zf = >>= parseXMLFromEntry enElem = findEntryByPath "word/endnotes.xml" zf >>= parseXMLFromEntry - fn_namespaces = maybe [] elemToNameSpaces fnElem - en_namespaces = maybe [] elemToNameSpaces enElem - ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces + fn_namespaces = maybe mempty elemToNameSpaces fnElem + en_namespaces = maybe mempty elemToNameSpaces enElem + ns = M.union fn_namespaces en_namespaces fn = fnElem >>= elemToNotes ns "footnote" . walkDocument ns en = enElem >>= elemToNotes ns "endnote" . walkDocument ns in @@ -408,7 +408,7 @@ archiveToComments :: Archive -> Comments archiveToComments zf = let cmtsElem = findEntryByPath "word/comments.xml" zf >>= parseXMLFromEntry - cmts_namespaces = maybe [] elemToNameSpaces cmtsElem + cmts_namespaces = maybe mempty elemToNameSpaces cmtsElem cmts = elemToComments cmts_namespaces . walkDocument cmts_namespaces <$> cmtsElem in @@ -518,7 +518,7 @@ levelElemToLevel _ _ = Nothing archiveToNumbering' :: Archive -> Maybe Numbering archiveToNumbering' zf = case findEntryByPath "word/numbering.xml" zf of - Nothing -> Just $ Numbering [] [] [] + Nothing -> Just $ Numbering mempty [] [] Just entry -> do numberingElem <- parseXMLFromEntry entry let namespaces = elemToNameSpaces numberingElem @@ -530,7 +530,7 @@ archiveToNumbering' zf = archiveToNumbering :: Archive -> Numbering archiveToNumbering archive = - fromMaybe (Numbering [] [] []) (archiveToNumbering' archive) + fromMaybe (Numbering mempty [] []) (archiveToNumbering' archive) elemToNotes :: NameSpaces -> Text -> Element -> Maybe (M.Map T.Text Element) elemToNotes ns notetype element @@ -875,7 +875,7 @@ childElemToRun ns element = let (title, alt) = getTitleAndAlt ns element a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main" drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem - >>= findAttr (QName "embed" (lookup "r" ns) (Just "r")) + >>= findAttr (QName "embed" (M.lookup "r" ns) (Just "r")) in case drawing of Just s -> expandDrawingId s >>= diff --git a/src/Text/Pandoc/Readers/Docx/Util.hs b/src/Text/Pandoc/Readers/Docx/Util.hs index 21df03d9e..ac331cba6 100644 --- a/src/Text/Pandoc/Readers/Docx/Util.hs +++ b/src/Text/Pandoc/Readers/Docx/Util.hs @@ -22,42 +22,42 @@ module Text.Pandoc.Readers.Docx.Util ( , findAttrByName ) where -import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.XML.Light +import qualified Data.Map as M -type NameSpaces = [(Text, Text)] +type NameSpaces = M.Map Text Text elemToNameSpaces :: Element -> NameSpaces -elemToNameSpaces = mapMaybe attrToNSPair . elAttribs - -attrToNSPair :: Attr -> Maybe (Text, Text) -attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val) -attrToNSPair _ = Nothing +elemToNameSpaces = foldr (\(Attr qn val) -> + case qn of + QName s _ (Just "xmlns") -> M.insert s val + _ -> id) mempty . elAttribs elemName :: NameSpaces -> Text -> Text -> QName elemName ns prefix name = - QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix) + QName name (M.lookup prefix ns) + (if T.null prefix then Nothing else Just prefix) isElem :: NameSpaces -> Text -> Text -> Element -> Bool isElem ns prefix name element = - let ns' = ns ++ elemToNameSpaces element + let ns' = ns <> elemToNameSpaces element in qName (elName element) == name && - qURI (elName element) == lookup prefix ns' + qURI (elName element) == M.lookup prefix ns' findChildByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element findChildByName ns pref name el = - let ns' = ns ++ elemToNameSpaces el + let ns' = ns <> elemToNameSpaces el in findChild (elemName ns' pref name) el findChildrenByName :: NameSpaces -> Text -> Text -> Element -> [Element] findChildrenByName ns pref name el = - let ns' = ns ++ elemToNameSpaces el + let ns' = ns <> elemToNameSpaces el in findChildren (elemName ns' pref name) el findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text findAttrByName ns pref name el = - let ns' = ns ++ elemToNameSpaces el + let ns' = ns <> elemToNameSpaces el in findAttr (elemName ns' pref name) el -- cgit v1.2.3 From ef741f384232d70cc617b9d06d0163b23e482258 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovanikov Date: Thu, 18 Feb 2021 15:28:20 +0000 Subject: Allow base64-bytestring-1.2.* --- pandoc.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index d27520ba0..ae4e8b341 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -440,7 +440,7 @@ library aeson >= 0.7 && < 1.6, aeson-pretty >= 0.8.5 && < 0.9, attoparsec >= 0.12 && < 0.14, - base64-bytestring >= 0.1 && < 1.2, + base64-bytestring >= 0.1 && < 1.3, binary >= 0.7 && < 0.11, blaze-html >= 0.9 && < 0.10, blaze-markup >= 0.8 && < 0.9, @@ -742,7 +742,7 @@ test-suite test-pandoc Diff >= 0.2 && < 0.5, Glob >= 0.7 && < 0.11, QuickCheck >= 2.4 && < 2.15, - base64-bytestring >= 0.1 && < 1.2, + base64-bytestring >= 0.1 && < 1.3, bytestring >= 0.9 && < 0.12, containers >= 0.4.2.1 && < 0.7, directory >= 1.2.3 && < 1.4, -- cgit v1.2.3 From 743f7216de98556a316301ac72b8606bafc2deee Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 18 Feb 2021 21:53:19 +0100 Subject: Org reader: fix bug in org-ref citation parsing. The org-ref syntax allows to list multiple citations separated by comma. This fixes a bug that accepted commas as part of the citation id, so all citation lists were parsed as one single citation. Fixes: #7101 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 2 +- test/Tests/Readers/Org/Inline/Citation.hs | 40 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 68c2ba5e0..519a6ce04 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -322,7 +322,7 @@ linkLikeOrgRefCite = try $ do -- from the `org-ref-cite-re` variable in `org-ref.el`. orgRefCiteKey :: PandocMonad m => OrgParser m Text orgRefCiteKey = - let citeKeySpecialChars = "-_:\\./," :: String + let citeKeySpecialChars = "-_:\\./" :: String isCiteKeySpecialChar c = c `elem` citeKeySpecialChars isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c endOfCitation = try $ do diff --git a/test/Tests/Readers/Org/Inline/Citation.hs b/test/Tests/Readers/Org/Inline/Citation.hs index 5538f1ec8..a11804983 100644 --- a/test/Tests/Readers/Org/Inline/Citation.hs +++ b/test/Tests/Readers/Org/Inline/Citation.hs @@ -116,6 +116,46 @@ tests = } in (para $ cite [citation] "citep:pandoc") + , "multiple simple citations" =: + "citep:picard,riker" =?> + let picard = Citation + { citationId = "picard" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + riker = Citation + { citationId = "riker" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [picard,riker] "citep:picard,riker") + + , "multiple simple citations succeeded by comma" =: + "citep:picard,riker," =?> + let picard = Citation + { citationId = "picard" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + riker = Citation + { citationId = "riker" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [picard,riker] "citep:picard,riker" <> str ",") + , "extended citation" =: "[[citep:Dominik201408][See page 20::, for example]]" =?> let citation = Citation -- cgit v1.2.3 From 9e728b40f36d48c687372ad447670186ed415337 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Feb 2021 17:21:22 -0800 Subject: T.P.Shared: cleanup. Cleanup up some functions and added deprecation pragmas to funtions no longer used in the code base. --- src/Text/Pandoc/Shared.hs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 6d5d4c97d..a579681b1 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -151,21 +151,22 @@ splitBy :: (a -> Bool) -> [a] -> [[a]] splitBy _ [] = [] splitBy isSep lst = let (first, rest) = break isSep lst - rest' = dropWhile isSep rest - in first:splitBy isSep rest' + in first:splitBy isSep (dropWhile isSep rest) +-- | Split text by groups of one or more separator. splitTextBy :: (Char -> Bool) -> T.Text -> [T.Text] splitTextBy isSep t | T.null t = [] | otherwise = let (first, rest) = T.break isSep t - rest' = T.dropWhile isSep rest - in first : splitTextBy isSep rest' + in first : splitTextBy isSep (T.dropWhile isSep rest) +{-# DEPRECATED splitByIndices "This function is slated for removal" #-} splitByIndices :: [Int] -> [a] -> [[a]] splitByIndices [] lst = [lst] splitByIndices (x:xs) lst = first:splitByIndices (map (\y -> y - x) xs) rest where (first, rest) = splitAt x lst +{-# DEPRECATED splitStringByIndices "This function is slated for removal" #-} -- | Split string into chunks divided at specified indices. splitStringByIndices :: [Int] -> [Char] -> [[Char]] splitStringByIndices [] lst = [lst] @@ -173,15 +174,22 @@ splitStringByIndices (x:xs) lst = let (first, rest) = splitAt' x lst in first : splitStringByIndices (map (\y -> y - x) xs) rest -splitTextByIndices :: [Int] -> T.Text -> [T.Text] -splitTextByIndices ns = fmap T.pack . splitStringByIndices ns . T.unpack - +-- DEPRECATED: can be removed when splitStringByIndices is splitAt' :: Int -> [Char] -> ([Char],[Char]) splitAt' _ [] = ([],[]) splitAt' n xs | n <= 0 = ([],xs) splitAt' n (x:xs) = (x:ys,zs) where (ys,zs) = splitAt' (n - charWidth x) xs +splitTextByIndices :: [Int] -> T.Text -> [T.Text] +splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) + where + splitTextByRelIndices [] t = [t] + splitTextByRelIndices (x:xs) t = + let (first, rest) = T.splitAt x t + in first : splitTextByRelIndices xs rest + +{-# DEPRECATED substitute "This function is slated for removal" #-} -- | Replace each occurrence of one sublist in a list with another. substitute :: (Eq a) => [a] -> [a] -> [a] -> [a] substitute _ _ [] = [] @@ -254,17 +262,24 @@ notElemText c = T.all (/= c) stripTrailingNewlines :: T.Text -> T.Text stripTrailingNewlines = T.dropWhileEnd (== '\n') +isWS :: Char -> Bool +isWS ' ' = True +isWS '\r' = True +isWS '\n' = True +isWS '\t' = True +isWS _ = False + -- | Remove leading and trailing space (including newlines) from string. trim :: T.Text -> T.Text -trim = T.dropAround (`elemText` " \r\n\t") +trim = T.dropAround isWS -- | Remove leading space (including newlines) from string. triml :: T.Text -> T.Text -triml = T.dropWhile (`elemText` " \r\n\t") +triml = T.dropWhile isWS -- | Remove trailing space (including newlines) from string. trimr :: T.Text -> T.Text -trimr = T.dropWhileEnd (`elemText` " \r\n\t") +trimr = T.dropWhileEnd isWS -- | Trim leading space and trailing space unless after \. trimMath :: T.Text -> T.Text @@ -275,7 +290,7 @@ trimMath = triml . T.reverse . stripBeginSpace . T.reverse -- no Text.spanEnd | Just ('\\', _) <- T.uncons suff = T.cons (T.last pref) suff | otherwise = suff where - (pref, suff) = T.span (`elemText` " \t\n\r") t + (pref, suff) = T.span isWS t -- | Strip leading and trailing characters from string stripFirstAndLast :: T.Text -> T.Text -- cgit v1.2.3 From 53cf8295a4825297f78e20b6ce73d2da8fd7ef84 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 14:33:56 -0800 Subject: LaTeX writer: adjust hypertargets to beginnings of paragraphs. Use `\vadjust pre` so that the hypertarget takes you to the beginning of the paragraph rather than one line down. Closes #7078. This makes a particular difference for links to citations using `--citeproc` and `link-citations: true`. --- src/Text/Pandoc/Writers/LaTeX.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 93603a26e..e8a187599 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -500,12 +500,13 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do else blockListToLaTeX bs modify $ \st -> st{ stIncremental = oldIncremental } linkAnchor' <- hypertarget True identifier empty - -- see #2704 for the motivation for adding \leavevmode: + -- see #2704 for the motivation for adding \leavevmode + -- and #7078 for \vadjust pre let linkAnchor = case bs of Para _ : _ | not (isEmpty linkAnchor') - -> "\\leavevmode" <> linkAnchor' <> "%" + -> "\\leavevmode\\vadjust pre{" <> linkAnchor' <> "}%" _ -> linkAnchor' wrapNotes txt = if beamer && "notes" `elem` classes then "\\note" <> braces txt -- speaker notes -- cgit v1.2.3 From 24d7cd539ba70aa94480976a7957420c020cb19a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 15:48:40 -0800 Subject: LaTeX template: disable `` ?` `` and `` !` `` ligatures. These are often triggered by accident in languagegs that use ` `` ` for end quote (e.g. German). See jgm/citeproc#54. --- data/templates/default.latex | 1 + test/lhs-test.latex | 1 + test/lhs-test.latex+lhs | 1 + test/writer.latex | 1 + test/writers-lang-and-dir.latex | 1 + 5 files changed, 5 insertions(+) diff --git a/data/templates/default.latex b/data/templates/default.latex index c98f53bb6..efc905741 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -181,6 +181,7 @@ $endif$ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts + \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} $if(indent)$ $else$ diff --git a/test/lhs-test.latex b/test/lhs-test.latex index fdc344335..8c91c88a2 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -21,6 +21,7 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts + \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index f9149ba02..0062a8f2d 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -21,6 +21,7 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts + \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/writer.latex b/test/writer.latex index abf772df4..a1cefc333 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -21,6 +21,7 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts + \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index d91f77325..2c6bc0001 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -22,6 +22,7 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts + \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class -- cgit v1.2.3 From 005344fb18c9e3bc3c2601ab3b6a7d567c46f398 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 17:03:11 -0800 Subject: Revert "LaTeX template: disable `` ?` `` and `` !` `` ligatures." This reverts commit 24d7cd539ba70aa94480976a7957420c020cb19a. --- data/templates/default.latex | 1 - test/lhs-test.latex | 1 - test/lhs-test.latex+lhs | 1 - test/writer.latex | 1 - test/writers-lang-and-dir.latex | 1 - 5 files changed, 5 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index efc905741..c98f53bb6 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -181,7 +181,6 @@ $endif$ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts - \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} $if(indent)$ $else$ diff --git a/test/lhs-test.latex b/test/lhs-test.latex index 8c91c88a2..fdc344335 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -21,7 +21,6 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts - \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index 0062a8f2d..f9149ba02 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -21,7 +21,6 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts - \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/writer.latex b/test/writer.latex index a1cefc333..abf772df4 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -21,7 +21,6 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts - \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index 2c6bc0001..d91f77325 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -22,7 +22,6 @@ \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts - \DisableLigatures[?,!]{encoding=T1} % disable ?` and !` ligatures }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class -- cgit v1.2.3 From 0f5c56dfb171e27745f4fe8530325223ecefe52a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 17:11:53 -0800 Subject: T.P.PDF: disable `smart` when building PDF via LaTeX. This is to prevent accidental creation of ligatures like `` ?` `` and `` !` `` (especially in languages with quotations like German), and similar ligature issues. See jgm/citeproc#54. --- src/Text/Pandoc/PDF.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 7c0082c29..3f9dd8dad 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -43,6 +43,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (PandocPDFProgramNotFoundError)) import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) +import Text.Pandoc.Extensions (disableExtension, Extension(Ext_smart)) import Text.Pandoc.Process (pipeProcess) import System.Process (readProcessWithExitCode) import Text.Pandoc.Shared (inDirectory, stringify, tshow) @@ -114,7 +115,10 @@ makePDF program pdfargs writer opts doc = runIOorExplode $ do putCommonState commonState doc' <- handleImages opts tmpdir doc - source <- writer opts doc' + source <- writer opts{ writerExtensions = -- disable use of quote + -- ligatures to avoid bad ligatures like ?` + disableExtension Ext_smart + (writerExtensions opts) } doc' res <- case baseProg of "context" -> context2pdf verbosity program pdfargs tmpdir source "tectonic" -> tectonic2pdf verbosity program pdfargs tmpdir source -- cgit v1.2.3 From ef642e2bbc1f46056fc27560ceba791f27f2daa6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 18:11:27 -0800 Subject: T.P.XML Improve fromEntities. --- src/Text/Pandoc/XML.hs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index c4e3ed1e7..6dbbce1d2 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -1,6 +1,5 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.XML Copyright : Copyright (C) 2006-2021 John MacFarlane @@ -123,23 +122,20 @@ html5EntityMap = foldr go mempty htmlEntities -- Unescapes XML entities fromEntities :: Text -> Text -fromEntities = T.pack . fromEntities' +fromEntities t + = let (x, y) = T.break (== '&') t + in if T.null y + then t + else x <> + let (ent, rest) = T.break (\c -> isSpace c || c == ';') y + rest' = case T.uncons rest of + Just (';',ys) -> ys + _ -> rest + ent' = T.drop 1 ent <> ";" + in case T.pack <$> lookupEntity (T.unpack ent') of + Just c -> c <> fromEntities rest' + Nothing -> ent <> fromEntities rest -fromEntities' :: Text -> String -fromEntities' (T.uncons -> Just ('&', xs)) = - case lookupEntity $ T.unpack ent' of - Just c -> c <> fromEntities' rest - Nothing -> "&" <> fromEntities' xs - where (ent, rest) = case T.break (\c -> isSpace c || c == ';') xs of - (zs,T.uncons -> Just (';',ys)) -> (zs,ys) - (zs, ys) -> (zs,ys) - ent' - | Just ys <- T.stripPrefix "#X" ent = "#x" <> ys -- workaround tagsoup bug - | Just ('#', _) <- T.uncons ent = ent - | otherwise = ent <> ";" -fromEntities' t = case T.uncons t of - Just (x, xs) -> x : fromEntities' xs - Nothing -> "" html5Attributes :: Set.Set Text html5Attributes = Set.fromList -- cgit v1.2.3 From 98d26c234579a06446a5bef1992ed77bac48a4ac Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 21:24:31 -0800 Subject: DocBook, JATS, OPML readers: performance optimization. With the new XML parser, we can avoid the expensive tree normalization step we used to do. This gives a significant speed boost in docbook and JATS parsing (e.g. 9.7 to 6 ms). --- src/Text/Pandoc/Readers/DocBook.hs | 26 ++++---------------------- src/Text/Pandoc/Readers/JATS.hs | 24 +++--------------------- src/Text/Pandoc/Readers/OPML.hs | 22 +--------------------- 3 files changed, 8 insertions(+), 64 deletions(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index e201b54fe..d38b07864 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -12,7 +12,7 @@ Conversion of DocBook XML to 'Pandoc' document. -} module Text.Pandoc.Readers.DocBook ( readDocBook ) where import Control.Monad.State.Strict -import Data.Char (isSpace, toUpper, isLetter) +import Data.Char (isSpace, isLetter) import Data.Default import Data.Either (rights) import Data.Foldable (asum) @@ -540,8 +540,9 @@ instance Default DBState where readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do - tree <- either (throwError . PandocXMLError "") (return . normalizeTree) $ - parseXMLContents (TL.fromStrict . handleInstructions $ crFilter inp) + tree <- either (throwError . PandocXMLError "") return $ + parseXMLContents + (TL.fromStrict . handleInstructions $ crFilter inp) (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree return $ Pandoc (dbMeta st') (toList . mconcat $ bs) @@ -571,25 +572,6 @@ getFigure e = do modify $ \st -> st{ dbFigureTitle = mempty, dbFigureId = mempty } return res --- normalize input, consolidating adjacent Text and CRef elements -normalizeTree :: [Content] -> [Content] -normalizeTree = everywhere (mkT go) - where go :: [Content] -> [Content] - go (Text (CData CDataRaw _ _):xs) = xs - go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 <> s2) z):xs - go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 <> convertEntity r) z):xs - go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r <> s1) z):xs - go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 <> - convertEntity r2) Nothing):xs - go xs = xs - -convertEntity :: Text -> Text -convertEntity e = maybe (T.map toUpper e) T.pack (lookupEntity $ T.unpack e) - -- convenience function to get an attribute value, defaulting to "" attrValue :: Text -> Element -> Text attrValue attr elt = diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 5353f2001..602f3b4f2 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -54,30 +54,11 @@ instance Default JATSState where readJATS :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readJATS _ inp = do - tree <- either (throwError . PandocXMLError "") - (return . normalizeTree) $ + tree <- either (throwError . PandocXMLError "") return $ parseXMLContents (TL.fromStrict $ crFilter inp) (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree return $ Pandoc (jatsMeta st') (toList . mconcat $ bs) --- normalize input, consolidating adjacent Text and CRef elements -normalizeTree :: [Content] -> [Content] -normalizeTree = everywhere (mkT go) - where go :: [Content] -> [Content] - go (Text (CData CDataRaw _ _):xs) = xs - go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 <> s2) z):xs - go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 <> convertEntity r) z):xs - go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r <> s1) z):xs - go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs - go xs = xs - -convertEntity :: Text -> Text -convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity $ T.unpack e) - -- convenience function to get an attribute value, defaulting to "" attrValue :: Text -> Element -> Text attrValue attr = @@ -454,7 +435,8 @@ elementToStr x = x parseInline :: PandocMonad m => Content -> JATS m Inlines parseInline (Text (CData _ s _)) = return $ text s -parseInline (CRef ref) = return . text . convertEntity $ ref +parseInline (CRef ref) = return $ maybe (text $ T.toUpper ref) (text . T.pack) + $ lookupEntity (T.unpack ref) parseInline (Elem e) = case qName (elName e) of "italic" -> innerInlines emph diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 184d5a63f..5f2ddb876 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -14,12 +14,10 @@ Conversion of OPML to 'Pandoc' document. module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State.Strict import Data.Default -import Data.Generics import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL -import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options @@ -53,7 +51,7 @@ readOPML opts inp = do (bs, st') <- runStateT (case parseXMLContents (TL.fromStrict (crFilter inp)) of Left msg -> throwError $ PandocXMLError "" msg - Right ns -> mapM parseBlock $ normalizeTree ns) + Right ns -> mapM parseBlock ns) def{ opmlOptions = opts } return $ setTitle (opmlDocTitle st') $ @@ -61,24 +59,6 @@ readOPML opts inp = do setDate (opmlDocDate st') $ doc $ mconcat bs --- normalize input, consolidating adjacent Text and CRef elements -normalizeTree :: [Content] -> [Content] -normalizeTree = everywhere (mkT go) - where go :: [Content] -> [Content] - go (Text (CData CDataRaw _ _):xs) = xs - go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = - Text (CData CDataText (s1 <> s2) z):xs - go (Text (CData CDataText s1 z):CRef r:xs) = - Text (CData CDataText (s1 <> convertEntity r) z):xs - go (CRef r:Text (CData CDataText s1 z):xs) = - Text (CData CDataText (convertEntity r <> s1) z):xs - go (CRef r1:CRef r2:xs) = - Text (CData CDataText (convertEntity r1 <> convertEntity r2) Nothing):xs - go xs = xs - -convertEntity :: Text -> Text -convertEntity e = maybe (T.toUpper e) T.pack (lookupEntity (T.unpack e)) - -- convenience function to get an attribute value, defaulting to "" attrValue :: Text -> Element -> Text attrValue attr elt = -- cgit v1.2.3 From b2b32d9bb2bb27b4f207db6d95938390b6ac8042 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Feb 2021 23:22:18 -0800 Subject: 'make bench': Create csv files for comparison. --- Makefile | 3 ++- pandoc.cabal | 2 +- stack.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index bcf09172a..4f72ac9d4 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ version?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}') pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1) SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$") BRANCH?=master +COMMIT=$(shell git rev-parse --short HEAD) GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 @@ -38,7 +39,7 @@ ghcid: ghcid -c "stack repl --flag 'pandoc:embed_data_files'" bench: - stack bench --benchmark-arguments='$(BENCHARGS)' --ghc-options '$(GHCOPTS)' + stack bench --benchmark-arguments='$(BENCHARGS) --csv bench-$(COMMIT).csv' --ghc-options '$(GHCOPTS)' weigh: stack build --ghc-options '$(GHCOPTS)' pandoc:weigh-pandoc && stack exec weigh-pandoc diff --git a/pandoc.cabal b/pandoc.cabal index ae4e8b341..30226f445 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -827,6 +827,6 @@ benchmark benchmark-pandoc build-depends: bytestring, containers, tasty, - tasty-bench >= 0.1 && <= 0.2, + tasty-bench >= 0.2 && <= 0.3, mtl >= 2.2 && < 2.3, time diff --git a/stack.yaml b/stack.yaml index dcb1c9742..4af76f19e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -11,7 +11,7 @@ extra-deps: - commonmark-0.1.1.4 - commonmark-extensions-0.2.0.4 - commonmark-pandoc-0.2.0.1 -- tasty-bench-0.1 +- tasty-bench-0.2.1 - citeproc-0.3.0.7 #- citeproc: # git: https://github.com/jgm/citeproc.git -- cgit v1.2.3 From 5eedbb6e8e7ec034be38dd9196eb8f660a28b8a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Feb 2021 09:22:13 -0800 Subject: Clarify `tex_math_dollars` extension. Note that no blank lines are allowed between the delimiters in display math. --- MANUAL.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index 486cee0b1..9403f43a8 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4360,7 +4360,8 @@ you need to enclose text in literal `$` characters, backslash-escape them and they won't be treated as math delimiters. For display math, use `$$` delimiters. (In this case, the delimiters -may be separated from the formula by whitespace.) +may be separated from the formula by whitespace. However, there can be +no blank lines betwen the opening and closing `$$` delimiters.) TeX math will be printed in all output formats. How it is rendered depends on the output format: -- cgit v1.2.3 From d68bbae5520f0ad023b0d83b727e5e248ce26d67 Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Fri, 19 Feb 2021 15:41:58 +0100 Subject: Update default ODT style As of now, the default style for ODT documents has a "First paragraph" style that inherits from "Standard" style and has no top or bottom margin. All subsequent paragraphs have "Text_20_body" style that inherits from "Standard" and add "0.0598in" margins on top and bottom. This makes the final document a bit ugly since the first paragraph has a small gap ("0.0598in") towards the second one, and all subsequent have double that. The proposed fix makes "First paragraph" inherit from "Text_20_body" instead so that it also has a consistent margin. Another approach would be to inherit "Text_20_body" and add a 0 margin on top. --- data/odt/styles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/odt/styles.xml b/data/odt/styles.xml index d066b300b..54459de04 100644 --- a/data/odt/styles.xml +++ b/data/odt/styles.xml @@ -350,7 +350,7 @@ xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2"> -- cgit v1.2.3 From b745bf393801aedb3ab336b9974c88377dc5dd23 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Feb 2021 10:12:29 -0800 Subject: make bench: compare against a baseline, use datestamps for bench results. --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4f72ac9d4..134aedcb4 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,13 @@ pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1) SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$") BRANCH?=master COMMIT=$(shell git rev-parse --short HEAD) +TIMESTAMP=$(shell date "+%Y%m%d_%H%M") +LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv)) +ifeq ($(LATESTBENCH),) +BASELINE= +else +BASELINE=--baseline $(LATESTBENCH) +endif GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 @@ -39,7 +46,7 @@ ghcid: ghcid -c "stack repl --flag 'pandoc:embed_data_files'" bench: - stack bench --benchmark-arguments='$(BENCHARGS) --csv bench-$(COMMIT).csv' --ghc-options '$(GHCOPTS)' + stack bench --benchmark-arguments='$(BENCHARGS) $(BASELINE) --csv bench_$(TIMESTAMP).csv' --ghc-options '$(GHCOPTS)' weigh: stack build --ghc-options '$(GHCOPTS)' pandoc:weigh-pandoc && stack exec weigh-pandoc -- cgit v1.2.3 From fc335801ef94ab5bfcb8da75b3da3760c7f1f968 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Feb 2021 10:22:07 -0800 Subject: MANUAL: block-level formatting is not allowed in line blocks. Closes #7107. --- MANUAL.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MANUAL.txt b/MANUAL.txt index 9403f43a8..51898f224 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3481,6 +3481,9 @@ line must begin with a space. | 200 Main St. | Berkeley, CA 94718 +Inline formatting (such as emphasis) is allowed in the content, +but not block-level formatting (such as block quotes or lists). + This syntax is borrowed from [reStructuredText]. ## Lists -- cgit v1.2.3 From 13847267e969b634e9c16c15170e7f217d432e8a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 00:07:38 -0800 Subject: HTML reader: efficiency improvements. Do a lookahead to find the right parser to use. Benchmarks from 34ms to 23ms, with less allocation. Also speeds up the epub reader. --- src/Text/Pandoc/Readers/HTML.hs | 210 ++++++++++++++++++++++++---------------- 1 file changed, 129 insertions(+), 81 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index cc60b5501..47856d2f7 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -159,29 +159,65 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAny) block :: PandocMonad m => TagParser m Blocks block = do - res <- choice - [ eSection - , eSwitch B.para block - , mempty <$ eFootnote - , mempty <$ eTOC - , mempty <$ eTitlePage - , pPara - , pHeader - , pBlockQuote - , pCodeBlock - , pList - , pHrule - , pTable block - , pHtml - , pHead - , pBody - , pLineBlock - , pDiv - , pPlain - , pFigure - , pIframe - , pRawHtmlBlock - ] + exts <- getOption readerExtensions + tag <- lookAhead pAny + res <- + (case tag of + TagOpen name attr -> + let type' = fromMaybe "" $ + lookup "type" attr <|> lookup "epub:type" attr + epubExts = extensionEnabled Ext_epub_html_exts exts + in + case name of + _ | name `elem` sectioningContent + , epubExts + , "chapter" `T.isInfixOf` type' + -> eSection + _ | epubExts + , type' `elem` ["footnote", "rearnote"] + -> mempty <$ eFootnote + _ | epubExts + , type' == "toc" + -> mempty <$ eTOC + _ | "titlepage" `T.isInfixOf` type' + , name `elem` ("section" : groupingContent) + -> mempty <$ eTitlePage + "p" -> pPara + "h1" -> pHeader + "h2" -> pHeader + "h3" -> pHeader + "h4" -> pHeader + "h5" -> pHeader + "h6" -> pHeader + "blockquote" -> pBlockQuote + "pre" -> pCodeBlock + "ul" -> pBulletList + "ol" -> pOrderedList + "dl" -> pDefinitionList + "table" -> pTable block + "hr" -> pHrule + "html" -> pHtml + "head" -> pHead + "body" -> pBody + "div" + | extensionEnabled Ext_line_blocks exts + , Just "line-block" <- lookup "class" attr + -> pLineBlock + | otherwise + -> pDiv + "section" -> pDiv + "main" -> pDiv + "figure" -> pFigure + "iframe" -> pIframe + "style" -> pRawHtmlBlock + "textarea" -> pRawHtmlBlock + "switch" + | epubExts + -> eSwitch B.para block + _ -> mzero + _ -> mzero) + <|> pPlain + <|> pRawHtmlBlock trace (T.take 60 $ tshow $ B.toList res) return res @@ -256,9 +292,6 @@ eTOC = try $ do guard $ (lookup "type" attr <|> lookup "epub:type" attr) == Just "toc" void (pInTags tag block) -pList :: PandocMonad m => TagParser m Blocks -pList = pBulletList <|> pOrderedList <|> pDefinitionList - pBulletList :: PandocMonad m => TagParser m Blocks pBulletList = try $ do pSatisfy (matchTagOpen "ul" []) @@ -369,13 +402,15 @@ pLineBlock = try $ do B.toList ils return $ B.lineBlock lns +isDivLike :: Text -> Bool +isDivLike "div" = True +isDivLike "section" = True +isDivLike "main" = True +isDivLike _ = False + pDiv :: PandocMonad m => TagParser m Blocks pDiv = try $ do guardEnabled Ext_native_divs - let isDivLike "div" = True - isDivLike "section" = True - isDivLike "main" = True - isDivLike _ = False TagOpen tag attr' <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) let (ident, classes, kvs) = toAttr attr' contents <- pInTags tag block @@ -544,31 +579,47 @@ tagToText (TagOpen "br" _) = "\n" tagToText _ = "" inline :: PandocMonad m => TagParser m Inlines -inline = choice - [ eNoteref - , eSwitch id inline - , pTagText - , pQ - , pEmph - , pStrong - , pSuperscript - , pSubscript - , pSpanLike - , pSmall - , pStrikeout - , pUnderline - , pLineBreak - , pLink - , pImage - , pSvg - , pBdo - , pCode - , pCodeWithClass [("samp","sample"),("var","variable")] - , pSpan - , pMath False - , pScriptMath - , pRawHtmlInline - ] +inline = do + exts <- getOption readerExtensions + tag <- lookAhead pAny + case tag of + TagOpen name attr -> + case name of + "a" | extensionEnabled Ext_epub_html_exts exts + , Just "noteref" <- lookup "type" attr <|> lookup "epub:type" attr + , Just ('#',_) <- lookup "href" attr >>= T.uncons + -> eNoteref + | otherwise -> pLink + "switch" -> eSwitch id inline + "q" -> pQ + "em" -> pEmph + "i" -> pEmph + "strong" -> pStrong + "b" -> pStrong + "sup" -> pSuperscript + "sub" -> pSubscript + "small" -> pSmall + "s" -> pStrikeout + "strike" -> pStrikeout + "del" -> pStrikeout + "u" -> pUnderline + "ins" -> pUnderline + "br" -> pLineBreak + "img" -> pImage + "svg" -> pSvg + "bdo" -> pBdo + "code" -> pCode + "samp" -> pCodeWithClass "samp" "sample" + "var" -> pCodeWithClass "var" "variable" + "span" -> pSpan + "math" -> pMath False + "script" + | Just x <- lookup "type" attr + , "math/tex" `T.isPrefixOf` x -> pScriptMath + _ | name `elem` htmlSpanLikeElements -> pSpanLike + _ -> pRawHtmlInline + TagText _ -> pTagText + _ -> pRawHtmlInline pSelfClosing :: PandocMonad m => (Text -> Bool) -> ([Attribute Text] -> Bool) @@ -579,27 +630,25 @@ pSelfClosing f g = do return open pQ :: PandocMonad m => TagParser m Inlines -pQ = choice $ map try [citedQuote, normalQuote] - where citedQuote = do - tag <- pSatisfy $ tagOpenLit "q" (any ((=="cite") . fst)) - - url <- canonicalizeUrl $ fromAttrib "cite" tag - let uid = fromMaybe (fromAttrib "name" tag) $ - maybeFromAttrib "id" tag - let cls = T.words $ fromAttrib "class" tag - - makeQuote $ B.spanWith (uid, cls, [("cite", escapeURI url)]) - normalQuote = do - pSatisfy $ tagOpenLit "q" (const True) - makeQuote id - makeQuote wrapper = do - ctx <- asks quoteContext - let (constructor, innerContext) = case ctx of - InDoubleQuote -> (B.singleQuoted, InSingleQuote) - _ -> (B.doubleQuoted, InDoubleQuote) - - content <- withQuoteContext innerContext (mconcat <$> manyTill inline (pCloses "q")) - return $ extractSpaces (constructor . wrapper) content +pQ = do + TagOpen _ attrs <- pSatisfy $ tagOpenLit "q" (const True) + case lookup "cite" attrs of + Just url -> do + let uid = fromMaybe mempty $ + lookup "name" attrs <> lookup "id" attrs + let cls = maybe [] T.words $ lookup "class" attrs + url' <- canonicalizeUrl url + makeQuote $ B.spanWith (uid, cls, [("cite", escapeURI url')]) + Nothing -> makeQuote id + where + makeQuote wrapper = do + ctx <- asks quoteContext + let (constructor, innerContext) = case ctx of + InDoubleQuote -> (B.singleQuoted, InSingleQuote) + _ -> (B.doubleQuoted, InDoubleQuote) + content <- withQuoteContext innerContext + (mconcat <$> manyTill inline (pCloses "q")) + return $ extractSpaces (constructor . wrapper) content pEmph :: PandocMonad m => TagParser m Inlines pEmph = pInlinesInTags "em" B.emph <|> pInlinesInTags "i" B.emph @@ -690,13 +739,12 @@ pSvg = do UTF8.toText (encode $ UTF8.fromText rawText) return $ B.imageWith (ident,cls,[]) svgData mempty mempty -pCodeWithClass :: PandocMonad m => [(T.Text,Text)] -> TagParser m Inlines -pCodeWithClass elemToClass = try $ do - let tagTest = flip elem . fmap fst $ elemToClass - TagOpen open attr' <- pSatisfy $ tagOpen tagTest (const True) +pCodeWithClass :: PandocMonad m => Text -> Text -> TagParser m Inlines +pCodeWithClass name class' = try $ do + TagOpen open attr' <- pSatisfy $ tagOpen (== name) (const True) result <- manyTill pAny (pCloses open) let (ids,cs,kvs) = toAttr attr' - cs' = maybe cs (:cs) . lookup open $ elemToClass + cs' = class' : cs return . B.codeWith (ids,cs',kvs) . T.unwords . T.lines . innerText $ result -- cgit v1.2.3 From 36e745c678bcd330545b4e490984ce8e04ed342d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 00:28:10 -0800 Subject: Benchmark improvements. + Run writer benchmarks for binary formats too. + Alphabetize benchmarks. + Don't run benchmarks for bibliography formats (yet; we need a special input for them). --- benchmark/benchmark-pandoc.hs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 96810a477..1315166b9 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} import Text.Pandoc import Text.Pandoc.MIME +import Control.Monad (when) import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B @@ -25,12 +26,15 @@ import qualified Data.Text as T import Test.Tasty.Bench import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe) +import Data.List (sortOn) readerBench :: Pandoc -> T.Text -> Maybe Benchmark readerBench doc name = either (const Nothing) Just $ runPure $ do + when (name `elem` ["bibtex", "biblatex", "csljson"]) $ + throwError $ PandocSomeError $ name <> " not supported for benchmark" (rdr, rexts) <- getReader name (wtr, wexts) <- getWriter name case (rdr, wtr) of @@ -63,6 +67,8 @@ writerBench :: [(FilePath, MimeType, BL.ByteString)] -> Maybe Benchmark writerBench imgs doc name = either (const Nothing) Just $ runPure $ do + when (name `elem` ["bibtex", "biblatex", "csljson"]) $ + throwError $ PandocSomeError $ name <> " not supported for benchmark" (wtr, wexts) <- getWriter name case wtr of TextWriter writerFun -> @@ -72,8 +78,13 @@ writerBench imgs doc name = either (const Nothing) Just $ mapM_ (\(fp,mt,bs) -> insertMedia fp (Just mt) bs) imgs writerFun def{ writerExtensions = wexts} d) doc - _ -> throwError $ PandocSomeError - $ "could not get text writer for " <> name + ByteStringWriter writerFun -> + return $ bench (T.unpack name) + $ nf (\d -> either (error . show) id $ + runPure $ do + mapM_ (\(fp,mt,bs) -> insertMedia fp (Just mt) bs) imgs + writerFun def{ writerExtensions = wexts} d) + doc main :: IO () main = do @@ -83,7 +94,9 @@ main = do imgs <- getImages defaultMain [ bgroup "writers" $ mapMaybe (writerBench imgs doc . fst) - (writers :: [(T.Text, Writer PandocPure)]) + (sortOn fst + writers :: [(T.Text, Writer PandocPure)]) , bgroup "readers" $ mapMaybe (readerBench doc . fst) - (readers :: [(T.Text, Reader PandocPure)]) + (sortOn fst + readers :: [(T.Text, Reader PandocPure)]) ] -- cgit v1.2.3 From 6b7d61488820eca1605b2e1ead43644d9be942dc Mon Sep 17 00:00:00 2001 From: maurerle Date: Sat, 20 Feb 2021 19:17:31 +0100 Subject: revealjs writer: add 'center' option for vertical slide centering. Closes #7104. --- data/templates/default.revealjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs index ff88a4482..e611e5b0b 100644 --- a/data/templates/default.revealjs +++ b/data/templates/default.revealjs @@ -82,6 +82,10 @@ $endif$ // Full list of configuration options available at: // https://revealjs.com/config/ Reveal.initialize({ +$if(center)$ + // Determines whether slide content should be vertically centered + center: $center$, +$endif$ $if(controls)$ // Display controls in the bottom right corner controls: $controls$, -- cgit v1.2.3 From 0f955b10b455e9b3d326262d03261f17538a6943 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 18:57:21 -0800 Subject: T.P.Readers.LaTeX.Parsing: improve braced'. Remove the parameter, have it parse the opening brace, and make it more efficient. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 313aa6c51..dab4d334e 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -680,28 +680,25 @@ grouped parser = try $ do -- {{a,b}} should be parsed the same as {a,b} try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup) -braced' :: PandocMonad m => LP m Tok -> Int -> LP m [Tok] -braced' getTok n = - handleEgroup <|> handleBgroup <|> handleOther - where handleEgroup = do - t <- symbol '}' - if n == 1 - then return [] - else (t:) <$> braced' getTok (n - 1) - handleBgroup = do - t <- symbol '{' - (t:) <$> braced' getTok (n + 1) - handleOther = do - t <- getTok - (t:) <$> braced' getTok n +braced' :: PandocMonad m => LP m Tok -> LP m [Tok] +braced' getTok = symbol '{' *> go (1 :: Int) + where + go n = do + t <- getTok + case t of + Tok _ Symbol "}" + | n > 1 -> (t:) <$> go (n - 1) + | otherwise -> return [] + Tok _ Symbol "{" -> (t:) <$> go (n + 1) + _ -> (t:) <$> go n braced :: PandocMonad m => LP m [Tok] -braced = symbol '{' *> braced' anyTok 1 +braced = braced' anyTok -- URLs require special handling, because they can contain % -- characters. So we retonenize comments as we go... bracedUrl :: PandocMonad m => LP m [Tok] -bracedUrl = bgroup *> braced' (retokenizeComment >> anyTok) 1 +bracedUrl = braced' (retokenizeComment >> anyTok) -- For handling URLs, which allow literal % characters... retokenizeComment :: PandocMonad m => LP m () -- cgit v1.2.3 From 31b8f60ea82d96b370cf4a765c46c18004ff6fa8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 22:03:29 -0800 Subject: LaTeX reader: avoid macro resolution code if no macros defined. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 35 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index dab4d334e..fc8542894 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -453,20 +453,24 @@ doMacros = do updateState $ \st -> st{ sExpanded = True } doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] -doMacros' n inp = - case inp of - Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : - Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros n spos name ts - Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : - Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros n spos ("end" <> name) ts - Tok _ (CtrlSeq "expandafter") _ : t : ts - -> combineTok t <$> doMacros' n ts - Tok spos (CtrlSeq name) _ : ts - -> handleMacros n spos name ts - _ -> return inp - <|> return inp +doMacros' n inp = do + macros <- sMacros <$> getState + if M.null macros + then return inp + else + case inp of + Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros macros n spos name ts + Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros macros n spos ("end" <> name) ts + Tok _ (CtrlSeq "expandafter") _ : t : ts + -> combineTok t <$> doMacros' n ts + Tok spos (CtrlSeq name) _ : ts + -> handleMacros macros n spos name ts + _ -> return inp + <|> return inp where combineTok (Tok spos (CtrlSeq name) x) (Tok _ Word w : ts) @@ -507,10 +511,9 @@ doMacros' n inp = Tok spos (CtrlSeq x) (txt <> " ") : acc addTok _ _ spos t acc = setpos spos t : acc - handleMacros n' spos name ts = do + handleMacros macros n' spos name ts = do when (n' > 20) -- detect macro expansion loops $ throwError $ PandocMacroLoop name - macros <- sMacros <$> getState case M.lookup name macros of Nothing -> mzero Just (Macro expansionPoint argspecs optarg newtoks) -> do -- cgit v1.2.3 From cec541e54cd947c8032f9148db18104cd1a48783 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 22:14:31 -0800 Subject: LaTeX reader: Another small improvement to macro handling. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index fc8542894..953747d2f 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -461,16 +461,15 @@ doMacros' n inp = do case inp of Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros macros n spos name ts + -> handleMacros macros n spos name ts <|> return inp Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros macros n spos ("end" <> name) ts + -> handleMacros macros n spos ("end" <> name) ts <|> return inp Tok _ (CtrlSeq "expandafter") _ : t : ts -> combineTok t <$> doMacros' n ts Tok spos (CtrlSeq name) _ : ts - -> handleMacros macros n spos name ts + -> handleMacros macros n spos name ts <|> return inp _ -> return inp - <|> return inp where combineTok (Tok spos (CtrlSeq name) x) (Tok _ Word w : ts) -- cgit v1.2.3 From 321343b2cf8a4a75abe1b6713aa40e278ca57997 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 22:49:20 -0800 Subject: HTML reader: small efficiency improvements. Also, remove exported class NamedTag(..) [API change]. This was just intended to smooth over the transition from String to Text and is no longer needed. The functions isInlineTag and isBlockTag are no longer polymorphic. --- src/Text/Pandoc/Readers/HTML.hs | 43 +++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 47856d2f7..50201fe77 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -19,7 +19,6 @@ module Text.Pandoc.Readers.HTML ( readHtml , htmlInBalanced , isInlineTag , isBlockTag - , NamedTag(..) , isTextTag , isCommentTag ) where @@ -579,9 +578,9 @@ tagToText (TagOpen "br" _) = "\n" tagToText _ = "" inline :: PandocMonad m => TagParser m Inlines -inline = do +inline = pTagText <|> do + tag <- lookAhead (pSatisfy isInlineTag) exts <- getOption readerExtensions - tag <- lookAhead pAny case tag of TagOpen name attr -> case name of @@ -935,27 +934,21 @@ pSpace = many1 (satisfy isSpace) >>= \xs -> then return B.softbreak else return B.space -class NamedTag a where - getTagName :: a -> Maybe Text - -instance NamedTag (Tag Text) where - getTagName (TagOpen t _) = Just t - getTagName (TagClose t) = Just t - getTagName _ = Nothing +getTagName :: Tag Text -> Maybe Text +getTagName (TagOpen t _) = Just t +getTagName (TagClose t) = Just t +getTagName _ = Nothing -instance NamedTag (Tag String) where - getTagName (TagOpen t _) = Just (T.pack t) - getTagName (TagClose t) = Just (T.pack t) - getTagName _ = Nothing - -isInlineTag :: NamedTag (Tag a) => Tag a -> Bool +isInlineTag :: Tag Text -> Bool isInlineTag t = - isCommentTag t || case getTagName t of - Nothing -> False - Just x -> x `Set.notMember` blockTags || - T.take 1 x == "?" -- processing instr. - -isBlockTag :: NamedTag (Tag a) => Tag a -> Bool + isCommentTag t || + case getTagName t of + Nothing -> False + Just "script" -> "math/tex" `T.isPrefixOf` fromAttrib "type" t + Just x -> x `Set.notMember` blockTags || + T.take 1 x == "?" -- processing instr. + +isBlockTag :: Tag Text -> Bool isBlockTag t = isBlockTagName || isTagComment t where isBlockTagName = case getTagName t of @@ -966,10 +959,10 @@ isBlockTag t = isBlockTagName || isTagComment t || x `Set.member` eitherBlockOrInline Nothing -> False -isTextTag :: Tag a -> Bool +isTextTag :: Tag Text -> Bool isTextTag = tagText (const True) -isCommentTag :: Tag a -> Bool +isCommentTag :: Tag Text -> Bool isCommentTag = tagComment (const True) --- parsers for use in markdown, textile readers @@ -1018,7 +1011,7 @@ htmlInBalanced' tagname ts = fromMaybe [] $ go 0 ts go n (t:ts') = (t :) <$> go n ts' go _ [] = mzero -hasTagWarning :: [Tag a] -> Bool +hasTagWarning :: [Tag Text] -> Bool hasTagWarning (TagWarning _:_) = True hasTagWarning _ = False -- cgit v1.2.3 From d8ef383692a167c97c67114107878a60d0aee6e1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 23:01:07 -0800 Subject: T.P.Shared: remove some obsolete functions [API change]. Removed: - `splitByIndices` - `splitStringByIndicies` - `substitute` - `underlineSpan` None of these are used elsewhere in the code base. --- src/Text/Pandoc/Shared.hs | 44 +------------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index a579681b1..922df7922 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -21,10 +21,7 @@ module Text.Pandoc.Shared ( -- * List processing splitBy, splitTextBy, - splitByIndices, - splitStringByIndices, splitTextByIndices, - substitute, ordNub, findM, -- * Text processing @@ -74,7 +71,6 @@ module Text.Pandoc.Shared ( addMetaField, makeMeta, eastAsianLineBreakFilter, - underlineSpan, htmlSpanLikeElements, splitSentences, filterIpynbOutput, @@ -113,7 +109,7 @@ import qualified Data.Bifunctor as Bifunctor import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum, generalCategory, GeneralCategory(NonSpacingMark, SpacingCombiningMark, EnclosingMark, ConnectorPunctuation)) -import Data.List (find, intercalate, intersperse, stripPrefix, sortOn) +import Data.List (find, intercalate, intersperse, sortOn) import qualified Data.Map as M import Data.Maybe (mapMaybe, fromMaybe) import Data.Monoid (Any (..)) @@ -160,27 +156,6 @@ splitTextBy isSep t | otherwise = let (first, rest) = T.break isSep t in first : splitTextBy isSep (T.dropWhile isSep rest) -{-# DEPRECATED splitByIndices "This function is slated for removal" #-} -splitByIndices :: [Int] -> [a] -> [[a]] -splitByIndices [] lst = [lst] -splitByIndices (x:xs) lst = first:splitByIndices (map (\y -> y - x) xs) rest - where (first, rest) = splitAt x lst - -{-# DEPRECATED splitStringByIndices "This function is slated for removal" #-} --- | Split string into chunks divided at specified indices. -splitStringByIndices :: [Int] -> [Char] -> [[Char]] -splitStringByIndices [] lst = [lst] -splitStringByIndices (x:xs) lst = - let (first, rest) = splitAt' x lst in - first : splitStringByIndices (map (\y -> y - x) xs) rest - --- DEPRECATED: can be removed when splitStringByIndices is -splitAt' :: Int -> [Char] -> ([Char],[Char]) -splitAt' _ [] = ([],[]) -splitAt' n xs | n <= 0 = ([],xs) -splitAt' n (x:xs) = (x:ys,zs) - where (ys,zs) = splitAt' (n - charWidth x) xs - splitTextByIndices :: [Int] -> T.Text -> [T.Text] splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) where @@ -189,16 +164,6 @@ splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) let (first, rest) = T.splitAt x t in first : splitTextByRelIndices xs rest -{-# DEPRECATED substitute "This function is slated for removal" #-} --- | Replace each occurrence of one sublist in a list with another. -substitute :: (Eq a) => [a] -> [a] -> [a] -> [a] -substitute _ _ [] = [] -substitute [] _ xs = xs -substitute target replacement lst@(x:xs) = - case stripPrefix target lst of - Just lst' -> replacement ++ substitute target replacement lst' - Nothing -> x : substitute target replacement xs - ordNub :: (Ord a) => [a] -> [a] ordNub l = go Set.empty l where @@ -765,13 +730,6 @@ eastAsianLineBreakFilter = bottomUp go go xs = xs -{-# DEPRECATED underlineSpan "Use Text.Pandoc.Builder.underline instead" #-} --- | Builder for underline (deprecated). --- This probably belongs in Builder.hs in pandoc-types. --- Will be replaced once Underline is an element. -underlineSpan :: Inlines -> Inlines -underlineSpan = B.underline - -- | Set of HTML elements that are represented as Span with a class equal as -- the element tag itself. htmlSpanLikeElements :: Set.Set T.Text -- cgit v1.2.3 From c0c8865eaac7089b0304c9b21d981f82ea4c2ebd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Feb 2021 23:40:02 -0800 Subject: HTML reader: small performance tweak. --- src/Text/Pandoc/Readers/HTML.hs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 50201fe77..b73c138ab 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -157,11 +157,10 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAny) return mempty block :: PandocMonad m => TagParser m Blocks -block = do +block = ((do + tag <- lookAhead (pSatisfy isBlockTag) exts <- getOption readerExtensions - tag <- lookAhead pAny - res <- - (case tag of + case tag of TagOpen name attr -> let type' = fromMaybe "" $ lookup "type" attr <|> lookup "epub:type" attr @@ -214,11 +213,8 @@ block = do | epubExts -> eSwitch B.para block _ -> mzero - _ -> mzero) - <|> pPlain - <|> pRawHtmlBlock - trace (T.take 60 $ tshow $ B.toList res) - return res + _ -> mzero) <|> pPlain <|> pRawHtmlBlock) >>= \res -> + res <$ trace (T.take 60 $ tshow $ B.toList res) namespaces :: PandocMonad m => [(Text, TagParser m Inlines)] namespaces = [(mathMLNamespace, pMath True)] -- cgit v1.2.3 From f43cb5ddcf56ab9387b24ad55c2c30eceb606fad Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Feb 2021 10:26:48 -0800 Subject: LaTeX reader: further performance optimization. Avoid unnecessary 'doMacros'. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 42 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 953747d2f..20311651b 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -446,30 +446,25 @@ satisfyTok f = do doMacros :: PandocMonad m => LP m () doMacros = do - expanded <- sExpanded <$> getState - verbatimMode <- sVerbatimMode <$> getState - unless (expanded || verbatimMode) $ do - getInput >>= doMacros' 1 >>= setInput - updateState $ \st -> st{ sExpanded = True } + st <- getState + unless (sExpanded st || sVerbatimMode st || M.null (sMacros st)) $ do + getInput >>= doMacros' 1 >>= setInput + updateState $ \s -> s{ sExpanded = True } doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] -doMacros' n inp = do - macros <- sMacros <$> getState - if M.null macros - then return inp - else - case inp of - Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : - Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros macros n spos name ts <|> return inp - Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : - Tok _ Word name : Tok _ Symbol "}" : ts - -> handleMacros macros n spos ("end" <> name) ts <|> return inp - Tok _ (CtrlSeq "expandafter") _ : t : ts - -> combineTok t <$> doMacros' n ts - Tok spos (CtrlSeq name) _ : ts - -> handleMacros macros n spos name ts <|> return inp - _ -> return inp +doMacros' n inp = + case inp of + Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros n spos name ts <|> return inp + Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros n spos ("end" <> name) ts <|> return inp + Tok _ (CtrlSeq "expandafter") _ : t : ts + -> combineTok t <$> doMacros' n ts + Tok spos (CtrlSeq name) _ : ts + -> handleMacros n spos name ts <|> return inp + _ -> return inp where combineTok (Tok spos (CtrlSeq name) x) (Tok _ Word w : ts) @@ -510,9 +505,10 @@ doMacros' n inp = do Tok spos (CtrlSeq x) (txt <> " ") : acc addTok _ _ spos t acc = setpos spos t : acc - handleMacros macros n' spos name ts = do + handleMacros n' spos name ts = do when (n' > 20) -- detect macro expansion loops $ throwError $ PandocMacroLoop name + macros <- sMacros <$> getState case M.lookup name macros of Nothing -> mzero Just (Macro expansionPoint argspecs optarg newtoks) -> do -- cgit v1.2.3 From db4f8823152578d199691f8084fddcc0f04f679b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Feb 2021 11:23:04 -0800 Subject: LaTeX reader: removed sExpanded in state. This isn't actually needed and checking it doesn't change anything. Also remove an unnecessary `doMacros` before `satisfyTok`, which does it anyway. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 20311651b..0e098d9d9 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -155,7 +155,6 @@ data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sLabels :: M.Map Text [Inline] , sHasChapters :: Bool , sToggles :: M.Map Text Bool - , sExpanded :: Bool , sFileContents :: M.Map Text Text , sEnableWithRaw :: Bool , sRawTokens :: IntMap.IntMap [Tok] @@ -183,7 +182,6 @@ defaultLaTeXState = LaTeXState{ sOptions = def , sLabels = M.empty , sHasChapters = False , sToggles = M.empty - , sExpanded = False , sFileContents = M.empty , sEnableWithRaw = True , sRawTokens = IntMap.empty @@ -256,7 +254,6 @@ rawLaTeXParser toks retokenize parser valParser = do Right toks' -> do res <- lift $ runParserT (do when retokenize $ do -- retokenize, applying macros - doMacros ts <- many (satisfyTok (const True)) setInput ts rawparser) @@ -432,8 +429,7 @@ satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok satisfyTok f = do doMacros -- apply macros on remaining input stream res <- tokenPrim (T.unpack . untoken) updatePos matcher - updateState $ \st -> st{ sExpanded = False - , sRawTokens = + updateState $ \st -> st{ sRawTokens = if sEnableWithRaw st then IntMap.map (res:) $ sRawTokens st else sRawTokens st } @@ -447,9 +443,8 @@ satisfyTok f = do doMacros :: PandocMonad m => LP m () doMacros = do st <- getState - unless (sExpanded st || sVerbatimMode st || M.null (sMacros st)) $ do + unless (sVerbatimMode st || M.null (sMacros st)) $ do getInput >>= doMacros' 1 >>= setInput - updateState $ \s -> s{ sExpanded = True } doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] doMacros' n inp = -- cgit v1.2.3 From 2b37ed9f212b711a6381dc89ff7d3431d7c5d916 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Feb 2021 11:29:38 -0800 Subject: LaTeX reader: further optimizations in satisfyTok. Benchmarks show 2/3 of the run time and 2/3 of the allocation of the Feb. 10 benchmarks. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 0e098d9d9..c2e10570d 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -429,11 +429,11 @@ satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok satisfyTok f = do doMacros -- apply macros on remaining input stream res <- tokenPrim (T.unpack . untoken) updatePos matcher - updateState $ \st -> st{ sRawTokens = - if sEnableWithRaw st - then IntMap.map (res:) $ sRawTokens st - else sRawTokens st } - return res + updateState $ \st -> + if sEnableWithRaw st + then st{ sRawTokens = IntMap.map (res:) $ sRawTokens st } + else st + return $! res where matcher t | f t = Just t | otherwise = Nothing updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos -- cgit v1.2.3 From aae1e617a6cff84d737a7195e4e9bc3cb730b978 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Feb 2021 19:02:28 -0800 Subject: Fix changelog-helper.sh --- tools/changelog-helper.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/changelog-helper.sh b/tools/changelog-helper.sh index 3608e96f5..d5c950bf8 100755 --- a/tools/changelog-helper.sh +++ b/tools/changelog-helper.sh @@ -2,11 +2,11 @@ # generate preliminary list of changes since changelog # was last modified -lastmod=`git log -n2 --format=oneline changelog | awk '{print $1;}'` -#git log --format=oneline $starthash..HEAD -files=`git ls-tree -r master --name-only` +lastmod=`git log -n1 --format=oneline changelog.md | awk '{print $1;}'` +files=`git ls-tree -r master --name-only | grep --invert-match '^test\/\|^data\/docx\/\|^data\/odt\/\|^data\/pptx\/\|^citeproc\/\|^data\/translations\/'` for x in $files do + echo $x 1>&2 commits=`git log -n1 $lastmod..HEAD $x` if [ ! -z "$commits" ] then @@ -17,7 +17,14 @@ do file=$x fi echo " * $file" - GIT_PAGER=cat git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' -- "$lastmod..HEAD" "$x" + git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" "$x" fi done +echo "test/" 1>&2 +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" test/ +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/docx/ +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/odt/ +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/pptx/ +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/translations/ +git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" citeproc/ -- cgit v1.2.3 From 607c014e9d7a7cee0b5f82efb6b6cb80ab75ec18 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Feb 2021 22:05:50 -0800 Subject: Update changelog. --- changelog.md | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/changelog.md b/changelog.md index 34a3002ce..b13bf1fd4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,360 @@ # Revision history for pandoc +## pandoc 2.12 (UNRELEASED -- PROVISIONAL) + + * Add new unexported module Text.Pandoc.XML.Light, as well + as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc, + Text.Pandoc.XML.Light.Output. (Closes #6001, #6565, #7091). + + This module exports definitions of `Element` and `Content` + that are isomorphic to xml-light's, but with Text + instead of String. This allows us to keep most of the code in existing + readers that use xml-light, but avoid lots of unnecessary allocation. + + We also add versions of the functions from xml-light's + Text.XML.Light.Output and Text.XML.Light.Proc that operate on our + modified XML types, and functions that convert xml-light types to our + types (since some of our dependencies, like texmath, use xml-light). + + We export functions that use xml-conduit's parser to produce an + `Element` or `[Content]`. This allows existing pandoc code to use + a better parser without much modification. + + The new parser is used in all places where xml-light's parser was + previously used. Benchmarks show a significant performance improvement + in parsing XML-based formats (with docbook, opml, jats, and docx + almost twice as fast, odt and fb2 more than twice as fast). + + In addition, the new parser gives us better error reporting than + xml-light. We report XML errors, when possible, using the new + `PandocXMLError` constructor in `PandocError`. + + These changes revealed the need for some changes in the tests. The + docbook-reader.docbook test lacked definitions for the entities it used; + these have been added. And the docx golden tests have been updated, + because the new parser does not preserve the order of attributes. + + * Text.Pandoc.App + + + Add `parseOptionsFromArgs` [API change, new exported function]. + + * Text.Pandoc.Citeproc.BibTeX + + + `Text.Pandoc.Citeproc.writeBibTeXString` now returns + `Doc Text` instead of `Text` (#7068). + + Correctly handle `pages` (= `page` in CSL) (#7067). + + Correctly handle BibLaTeX `langid` (= `language` in CSL, #7067). + + In BibTeX output, protect foreign titles since there's no language + field (#7067). + + Clean up BibTeX parsing (#7049). Previously there was a messy code + path that gave strange results in some cases, not passing through raw + tex but trying to extract a string content. This was an artefact of + trying to handle some special bibtex-specific commands in the BibTeX + reader. Now we just handle these in the LaTeX reader and simplify + parsing in the BibTeX reader. This does mean that more raw tex will + be passed through (and currently this is not sensitive to the + `raw_tex` extension; this should be fixed). + + * Text.Pandoc.Citeproc.MetaValue + + + Correctly parse "raw" date value in markdown references metadata. + (See jgm/citeproc#53.) + + * Text.Pandoc.Class + + + Add `getTimestamp` [API change]. This attempts to read the + `SOURCE_DATE_EPOCH` environment variable and parse a UTC time + from it (treating it as a unix date stamp, see + https://reproducible-builds.org/specs/source-date-epoch/). If the + variable is not set or can't be parsed as a unix date stamp, then the + function returns the current date. + + * Text.Pandoc.Error + + + Remove unused variables (Albert Krewinkel) + + Export `renderError` [API change]. + + Refactor `handleError` to use `renderError`. This allows us render + error messages without exiting. + + * Text.Pandoc.Extensions + + + `Ext_task_lists` is now supported by org (and turned + on by default) (Albert Krewinkel, #6336). + + Remove `Ext_fenced_code_attributes` from allowed commonmark attributes + (#7097). This attribute was listed as allowed, but it didn't actually + do anything. Use `attributes` for code attributes and more. + + * Lua subsystem: + + + Always load built-in Lua scripts from default data-dir (Albert + Krewinkel). The Lua modules `pandoc` and `pandoc.List` are now always + loaded from the system's default data directory. Loading from a + different directory by overriding the default path, e.g. via + `--data-dir`, is no longer supported to avoid unexpected behavior + and to address security concerns. + + Add module "pandoc.path" (Albert Krewinkel, #6001, #6565). + The module allows to work with file paths in a convenient and + platform-independent manner. + + * Text.Pandoc.PDF + + + Disable `smart` extension when building PDF via LaTeX. + This is to prevent accidental creation of ligatures like + `` ?` `` and `` !` `` (especially in languages with quotations like + German), and similar ligature issues. (See jgm/citeproc#54.) + + * DocBook reader: + + + Avoid expensive tree normalization step, as it is not necessary + with the new XML parser. + + Support `informalfigure` (#7079) (Nils Carlson). + + * Docx reader: + + + Use Map instead of list for Namespaces. This gives a speedup of + about 5-10%. With this and the XML parsing changes, the docx reader + is now about twice as fast as in the previous release. + + * HTML reader: + + + Small performance tweaks. + + Also, remove exported class `NamedTag(..)` [API change]. This was just + intended to smooth over the transition from String to Text and is no + longer needed. + + As a result, the functions `isInlineTag` and `isBlockTag` + are no longer polymorphic; they apply to a `Tag Text` [API change]. + + Do a lookahead to find the right parser to use. This takes + benchmarks from 34ms to 23ms, with less allocation. + + Fix bad handling of empty `src` attribute in `iframe` (#7099). + If `src` is empty, we simply skip the `iframe`. + If `src` is invalid or cannot be fetched, we issue a warning + nd skip instead of failing with an error. + + * JATS reader: + + + Avoid tree normalization, which is no longer necessary given the + new XML parser. + + * LaTeX reader: + + + Code cleanup, removing some unnecessary things. + + Rewrite `withRaw` so it doesn't rely on fragile assumptions + about token positions (which break when macros are expanded) + (#7092). This requires the addition of `sEnableWithRaw` and + `sRawTokens` in `LaTeXState`, and a new combinator `disablingWithRaw` + to disable collecting of raw tokens in certain contexts. + Add `parseFromToks` to Text.Pandoc.Readers.LaTeX.Parsing. + Fix parsing of single character tokens so it doesn't mess + up the new raw token collecting. These changes slightly increase + allocations and have a small performance impact. + + Handle some bibtex/biblatex-specific commands that used to be + dealt with in pandoc-citeproc (#7049). + + Optimize `satisfyTok`, avoiding unnecessary macro expansion steps. + Benchmarks after this change show 2/3 of the run time and 2/3 of the + allocation of the Feb. 10 benchmarks. + + Removed `sExpanded` in state. This isn't actually needed and checking + it doesn't change anything. + + Improve `braced'`. Remove the parameter, have it parse the + opening brace, and make it more efficient. + + * Markdown reader: + + + Improved handling of mmd link attributes in references (#7080). + Previously they only worked for links that had titles. + + * OPML reader: + + + Avoid tree normalization, which is no longer necessary with the + new XML parser. + + * ODT reader: + + + Finer-grained errors on parse failure (#7091). + + Give more information if the zip container can't be unpacked. + + * Org reader: + + + Support `task_lists` extension (Albert Krewinkel, #6336). + + Fix bug in org-ref citation parsing (Albert Krewinkel, #7101). + The org-ref syntax allows to list multiple citations separated by + comma. Previously commas were accepted as part of the citation id, + so all citation lists were parsed as one single citation. + + * RST reader: + + + Use `getTimestamp` instead of `getCurrentTime` to fetch timestamp. + Setting `SOURCE_DATE_EPOCH` will allow reproducible builds. + + RST reader: fix handling of header in CSV tables (#7064). + The interpretation of this line is not affected by the delim option. + + * Jira reader: + + + Modified the Doc parser to skip leading blank lines. This fixes + parsing of documents which start with multiple blank lines (#7095). + + Prevent URLs within link aliases to be treated as autolinks + (#6944). + + * Text.Pandoc.Shared + + + Remove formerly exported functions that are no longer used in the + code base: `splitByIndices`, `splitStringByIndicies`, `substitute`, + and `underlineSpan` (which had been deprecated in April 2020) + [API change]. + + Export `handleTaskListItem` (Albert Krewinkel) [API change]. + + * BibTeX writer: + + + BibTeX writer: use doclayout and doctemplate. This change allows + bibtex/biblatex output to wrap as other formats do, + depending on the settings of `--wrap` and `--columns` (#7068). + + * CSL JSON writer: + + + Output `[]` if no references in input, instead of raising a + PandocAppError as before. + + * Docx writer: + + + Use `getTimestamp` instead of `getCurrentTime` for timestamp. + Setting `SOURCE_DATE_EPOCH` will allow reproducible builds. + + * Text.Pandoc.Writers.EPUB + + + Use `getTimestamp` instead of `getCurrentTime` for timestamp. + Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093). + This does not suffice to fully enable reproducible in EPUB, since + a unique id is still being generated for each build. + + Support `belongs-to-collection` metadata (#7063) (Nick Berendsen). + + * JATS writer: + + + Escape special chars in reference elements (Albert Krewinkel). + Prevents the generation of invalid markup if a citation element + contains an ampersand or another character with a special meaning + in XML. + + * LaTeX writer: + + + Adjust hypertargets to beginnings of paragraphs (#7078). + Use `\vadjust pre` so that the hypertarget takes you to the beginning + of the paragraph rather than one line down. + This makes a particular difference for links to citations using + `--citeproc` and `link-citations: true`. + + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047). + + * Markdown writer: + + + Handle math right before digit. We insert an HTML comment to + avoid a `$` right before a digit, which pandoc will not recognize + as a math delimiter. + + * ODT writer: + + + Use `getTimestamp` instead of `getCurrentTime` for timestamp. + Setting `SOURCE_DATE_EPOCH` will allow reproducible builds. + + Update default ODT style (Lorenzo). Previously, the "First paragraph" + style inherited from "Standard" but not from "Text body." Now + it is adjusted to inherit from "Text body", to avoid some ugly + spacing issues. It may be necessary to update a custom `reference.odt` + in light of this change. + + * Org writer: + + + Support `task_lists` extension (Albert Krewinkel, #6336). + + * Pptx writer: + + + Use `getTimestamp` instead of `getCurrentTime` for timestamp. + Setting `SOURCE_DATE_EPOCH` will allow reproducible builds. + + * JATS templates: tag `author.name` as `string-name` (Albert Krewinkel). + The partitioning the components of a name into surname, given names, + etc. is not always possible or not available. Using `author.name` + allows to give the full name as a fallback to be used when + `author.surname` is not available. + + * Add default templates for bibtex and biblatex, so that + the variables `header-include`, `include-before`, `include-after` + (or alternatively the command line options + `--include-in-header`, `--include-before-body`, `--include-after-body`) + may be used. + + * LaTeX template: Update to iftex package (#7073) (Andrew Dunning) + + * revealjs template: Add 'center' option for vertical slide centering. + (maurerle, #7104). + + * Text.Pandoc.XML: Improve efficiency of `fromEntities`. + + * Test suite: a more robust way of testing the executable. + Many of our tests require running the pandoc executable. This is + problematic for a few different reasons. First, cabal-install will + sometimes run the test suite after building the library but before + building the executable, which means the executable isn't in place for + the tests. One can work around that by first building, then building and + running the tests, but that's fragile. Second, we have to find the + executable. So far, we've done that using a function `findPandoc` that + attempts to locate it relative to the test executable (which can be + located using findExecutablePath). But the logic here is delicate and + work with every combination of options. To solve both problems, we add + an `--emulate` option to the `test-pandoc` executable. When `--emulate` + occurs as the first argument passed to `test-pandoc`, the program simply + emulates the regular pandoc executable, using the rest of the arguments + (after `--emulate`). Thus, `test-pandoc --emulate -f markdown -t latex` + is just like `pandoc -f markdown -t latex`. + Since all the work is done by library functions, implementing this + emulation just takes a couple lines of code and should be entirely + reliable. With this change, we can test the pandoc executable by running + the test program itself (locatable using `findExecutablePath`) with the + `--emulate` option. This removes the need for the fragile `findPandoc` + step, and it means we can run our integration tests even when we're just + building the library, not the executable. [Note: part of this change + involved simplifying some complex handling to set environment variables + for dynamic library paths. I have tested a build with + `--enable-dynamic-executable`, and it works, but further testing may be + needed.] + + * MANUAL.txt + + + MANUAL: block-level formatting is not allowed in line blocks (#7107). + + Clarify `tex_math_dollars` extension. Note that no blank lines + are allowed between the delimiters in display math. + + Add MANUAL section on reproducible builds. + + Document no template fallback for absolute path (#7077, Nixon + Enraght-Moony.) + + Improve docs for cite-method. + + Update README and man page. + + * Makefile: in `make bench`, create CSV files for comparison and compare + against previous benchmark run. Add timestamp to CSV filenames. + + * doc/lua-filters.md: improve documentation for + `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`, + `directory`, `normalize` (Albert Krewinkel). + + * Allow base64-bytestring-1.2.* (Dmitrii Kovanikov) + + * Require jira-wiki-markup 1.3.3 (Albert Krewinkel) + + * Require citeproc 0.3.0.7, which correctly titlecases when titles + contain non-ASCII characters. + + * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert + Krewinkel) + + * Benchmarks + + + Use the lighter-weight tasty-bench instead of criterion. + + Run writer benchmarks for binary formats too. + + Alphabetize benchmarks. + + Don't run benchmarks for bibliography formats + (yet; we need a special input for them). + + Show allocation data + + Clean up benchmark code. + + Allow specifying patterns using `-p blah'. + + + ## pandoc 2.11.4 (2021-01-22) * Add `biblatex`, `bibtex` as output formats (closes #7040). -- cgit v1.2.3 From 80fde18fb1d983b938476ed5b3771ed5d6158d44 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 11:30:07 -0800 Subject: Text.Pandoc.UTF8: change IO functions to return Text, not String. [API change] This affects `readFile`, `getContents`, `writeFileWith`, `writeFile`, `putStrWith`, `putStr`, `putStrLnWith`, `putStrLn`. `hPutStrWith`, `hPutStr`, `hPutStrLnWith`, `hPutStrLn`, `hGetContents`. This avoids the need to uselessly create a linked list of characters when emiting output. --- src/Text/Pandoc/App.hs | 10 ++++---- src/Text/Pandoc/App/CommandLineOptions.hs | 38 +++++++++++++++------------- src/Text/Pandoc/App/OutputSettings.hs | 4 +-- src/Text/Pandoc/Class/IO.hs | 6 ++--- src/Text/Pandoc/Error.hs | 2 +- src/Text/Pandoc/PDF.hs | 25 +++++++++--------- src/Text/Pandoc/Parsing.hs | 2 +- src/Text/Pandoc/UTF8.hs | 42 +++++++++++++++---------------- test/Tests/Command.hs | 2 +- test/Tests/Old.hs | 5 ++-- test/Tests/Readers/LaTeX.hs | 2 +- 11 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6a071ad5a..63996828e 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -73,8 +73,9 @@ convertWithOpts opts = do let verbosity = optVerbosity opts when (optDumpArgs opts) $ - do UTF8.hPutStrLn stdout outputFile - mapM_ (UTF8.hPutStrLn stdout) (fromMaybe ["-"] $ optInputFiles opts) + do UTF8.hPutStrLn stdout (T.pack outputFile) + mapM_ (UTF8.hPutStrLn stdout . T.pack) + (fromMaybe ["-"] $ optInputFiles opts) exitSuccess let sources = case optInputFiles opts of @@ -354,6 +355,5 @@ writeFnBinary "-" = liftIO . BL.putStr writeFnBinary f = liftIO . BL.writeFile (UTF8.encodePath f) writerFn :: MonadIO m => IO.Newline -> FilePath -> Text -> m () --- TODO this implementation isn't maximally efficient: -writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack -writerFn eol f = liftIO . UTF8.writeFileWith eol f . T.unpack +writerFn eol "-" = liftIO . UTF8.putStrWith eol +writerFn eol f = liftIO . UTF8.writeFileWith eol f diff --git a/src/Text/Pandoc/App/CommandLineOptions.hs b/src/Text/Pandoc/App/CommandLineOptions.hs index a4c510d97..0a8193f6c 100644 --- a/src/Text/Pandoc/App/CommandLineOptions.hs +++ b/src/Text/Pandoc/App/CommandLineOptions.hs @@ -812,10 +812,10 @@ options = map (\c -> ['-',c]) shorts ++ map ("--" ++) longs let allopts = unwords (concatMap optnames options) - UTF8.hPutStrLn stdout $ printf tpl allopts - (unwords readersNames) - (unwords writersNames) - (unwords $ map (T.unpack . fst) highlightingStyles) + UTF8.hPutStrLn stdout $ T.pack $ printf tpl allopts + (T.unpack $ T.unwords readersNames) + (T.unpack $ T.unwords writersNames) + (T.unpack $ T.unwords $ map fst highlightingStyles) (unwords datafiles) exitSuccess )) "" -- "Print bash completion script" @@ -854,7 +854,7 @@ options = else if extensionEnabled x allExts then '-' else ' ') : drop 4 (show x) - mapM_ (UTF8.hPutStrLn stdout . showExt) + mapM_ (UTF8.hPutStrLn stdout . T.pack . showExt) [ex | ex <- extList, extensionEnabled ex allExts] exitSuccess ) "FORMAT") @@ -868,14 +868,14 @@ options = , sShortname s `notElem` [T.pack "Alert", T.pack "Alert_indent"] ] - mapM_ (UTF8.hPutStrLn stdout) (sort langs) + mapM_ (UTF8.hPutStrLn stdout . T.pack) (sort langs) exitSuccess )) "" , Option "" ["list-highlight-styles"] (NoArg (\_ -> do - mapM_ (UTF8.hPutStrLn stdout . T.unpack . fst) highlightingStyles + mapM_ (UTF8.hPutStrLn stdout . fst) highlightingStyles exitSuccess )) "" @@ -893,7 +893,7 @@ options = | T.null t -> -- e.g. for docx, odt, json: E.throwIO $ PandocCouldNotFindDataFileError $ T.pack ("templates/default." ++ arg) - | otherwise -> write . T.unpack $ t + | otherwise -> write t Left e -> E.throwIO e exitSuccess) "FORMAT") @@ -940,11 +940,13 @@ options = (\_ -> do prg <- getProgName defaultDatadirs <- defaultUserDataDirs - UTF8.hPutStrLn stdout (prg ++ " " ++ T.unpack pandocVersion ++ - compileInfo ++ - "\nUser data directory: " ++ - intercalate " or " defaultDatadirs ++ - ('\n':copyrightMessage)) + UTF8.hPutStrLn stdout + $ T.pack + $ prg ++ " " ++ T.unpack pandocVersion ++ + compileInfo ++ + "\nUser data directory: " ++ + intercalate " or " defaultDatadirs ++ + ('\n':copyrightMessage) exitSuccess )) "" -- "Print version" @@ -952,7 +954,7 @@ options = (NoArg (\_ -> do prg <- getProgName - UTF8.hPutStr stdout (usageMessage prg options) + UTF8.hPutStr stdout (T.pack $ usageMessage prg options) exitSuccess )) "" -- "Show help" ] @@ -1013,12 +1015,12 @@ handleUnrecognizedOption "-R" = handleUnrecognizedOption "--parse-raw" handleUnrecognizedOption x = (("Unknown option " ++ x ++ ".") :) -readersNames :: [String] -readersNames = sort (map (T.unpack . fst) (readers :: [(Text, Reader PandocIO)])) +readersNames :: [Text] +readersNames = sort (map fst (readers :: [(Text, Reader PandocIO)])) -writersNames :: [String] +writersNames :: [Text] writersNames = sort - ("pdf" : map (T.unpack . fst) (writers :: [(Text, Writer PandocIO)])) + ("pdf" : map fst (writers :: [(Text, Writer PandocIO)])) splitField :: String -> (String, String) splitField = second (tailDef "true") . break (`elemText` ":=") diff --git a/src/Text/Pandoc/App/OutputSettings.hs b/src/Text/Pandoc/App/OutputSettings.hs index 53c7d82ef..3864ab188 100644 --- a/src/Text/Pandoc/App/OutputSettings.hs +++ b/src/Text/Pandoc/App/OutputSettings.hs @@ -59,8 +59,8 @@ optToOutputSettings opts = do let outputFile = fromMaybe "-" (optOutputFile opts) when (optDumpArgs opts) . liftIO $ do - UTF8.hPutStrLn stdout outputFile - mapM_ (UTF8.hPutStrLn stdout) (fromMaybe [] $ optInputFiles opts) + UTF8.hPutStrLn stdout (T.pack outputFile) + mapM_ (UTF8.hPutStrLn stdout . T.pack) (fromMaybe [] $ optInputFiles opts) exitSuccess epubMetadata <- traverse readUtf8File $ optEpubMetadata opts diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs index eecda5711..bb4e2b732 100644 --- a/src/Text/Pandoc/Class/IO.hs +++ b/src/Text/Pandoc/Class/IO.hs @@ -183,7 +183,7 @@ getModificationTime = liftIOError System.Directory.getModificationTime logOutput :: (PandocMonad m, MonadIO m) => LogMessage -> m () logOutput msg = liftIO $ do UTF8.hPutStr stderr $ - "[" ++ show (messageVerbosity msg) ++ "] " + "[" <> T.pack (show (messageVerbosity msg)) <> "] " alertIndent $ T.lines $ showLogMessage msg -- | Prints the list of lines to @stderr@, indenting every but the first @@ -191,10 +191,10 @@ logOutput msg = liftIO $ do alertIndent :: [Text] -> IO () alertIndent [] = return () alertIndent (l:ls) = do - UTF8.hPutStrLn stderr $ unpack l + UTF8.hPutStrLn stderr l mapM_ go ls where go l' = do UTF8.hPutStr stderr " " - UTF8.hPutStrLn stderr $ unpack l' + UTF8.hPutStrLn stderr l' -- | Extract media from the mediabag into a directory. extractMedia :: (PandocMonad m, MonadIO m) => FilePath -> Pandoc -> m Pandoc diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 94c013cdb..0fdb7bfe5 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -191,6 +191,6 @@ handleError (Left e) = err :: Int -> Text -> IO a err exitCode msg = do - UTF8.hPutStrLn stderr (T.unpack msg) + UTF8.hPutStrLn stderr msg exitWith $ ExitFailure exitCode return undefined diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 3f9dd8dad..6f462aad5 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -270,7 +270,7 @@ missingCharacterWarnings verbosity log' = do | isAscii c = T.singleton c | otherwise = T.pack $ c : " (U+" ++ printf "%04X" (ord c) ++ ")" let addCodePoint = T.concatMap toCodePoint - let warnings = [ addCodePoint (T.pack $ utf8ToString (BC.drop 19 l)) + let warnings = [ addCodePoint (utf8ToText (BC.drop 19 l)) | l <- ls , isMissingCharacterWarning l ] @@ -314,7 +314,7 @@ runTectonic verbosity program args' tmpDir' source = do env <- liftIO getEnvironment when (verbosity >= INFO) $ liftIO $ showVerboseInfo (Just tmpDir) program programArgs env - (utf8ToString sourceBL) + (utf8ToText sourceBL) (exit, out) <- liftIO $ E.catch (pipeProcess (Just env) program programArgs sourceBL) (handlePDFProgramNotFound program) @@ -385,7 +385,7 @@ runTeXProgram verbosity program args numRuns tmpDir' source = do (pipeProcess (Just env'') program programArgs BL.empty) (handlePDFProgramNotFound program) when (verbosity >= INFO) $ liftIO $ do - UTF8.hPutStrLn stderr $ "[makePDF] Run #" ++ show runNumber + UTF8.hPutStrLn stderr $ "[makePDF] Run #" <> tshow runNumber BL.hPutStr stderr out UTF8.hPutStr stderr "\n" if runNumber < numRuns @@ -405,7 +405,7 @@ generic2pdf :: Verbosity generic2pdf verbosity program args source = do env' <- getEnvironment when (verbosity >= INFO) $ - showVerboseInfo Nothing program args env' (T.unpack source) + showVerboseInfo Nothing program args env' source (exit, out) <- E.catch (pipeProcess (Just env') program args (BL.fromStrict $ UTF8.fromText source)) @@ -494,19 +494,20 @@ showVerboseInfo :: Maybe FilePath -> String -> [String] -> [(String, String)] - -> String + -> Text -> IO () showVerboseInfo mbTmpDir program programArgs env source = do case mbTmpDir of Just tmpDir -> do UTF8.hPutStrLn stderr "[makePDF] temp dir:" - UTF8.hPutStrLn stderr tmpDir + UTF8.hPutStrLn stderr (T.pack tmpDir) Nothing -> return () UTF8.hPutStrLn stderr "[makePDF] Command line:" - UTF8.hPutStrLn stderr $ program ++ " " ++ unwords (map show programArgs) + UTF8.hPutStrLn stderr $ + T.pack program <> " " <> T.pack (unwords (map show programArgs)) UTF8.hPutStr stderr "\n" UTF8.hPutStrLn stderr "[makePDF] Environment:" - mapM_ (UTF8.hPutStrLn stderr . show) env + mapM_ (UTF8.hPutStrLn stderr . tshow) env UTF8.hPutStr stderr "\n" UTF8.hPutStrLn stderr "[makePDF] Source:" UTF8.hPutStrLn stderr source @@ -517,8 +518,8 @@ handlePDFProgramNotFound program e E.throwIO $ PandocPDFProgramNotFoundError $ T.pack program | otherwise = E.throwIO e -utf8ToString :: ByteString -> String -utf8ToString lbs = +utf8ToText :: ByteString -> Text +utf8ToText lbs = case decodeUtf8' lbs of - Left _ -> BC.unpack lbs -- if decoding fails, treat as latin1 - Right t -> TL.unpack t + Left _ -> T.pack $ BC.unpack lbs -- if decoding fails, treat as latin1 + Right t -> TL.toStrict t diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 4c4dd531d..8d3799c3e 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1148,7 +1148,7 @@ testStringWith :: Show a => ParserT Text ParserState Identity a -> Text -> IO () -testStringWith parser str = UTF8.putStrLn $ show $ +testStringWith parser str = UTF8.putStrLn $ tshow $ readWith parser defaultParserState str -- | Parsing options. diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index 567f5abe5..4d5921faf 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -39,67 +39,65 @@ where import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL +import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.IO as TIO import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TL import Prelude hiding (getContents, putStr, putStrLn, readFile, writeFile) import System.IO hiding (getContents, hGetContents, hPutStr, hPutStrLn, putStr, putStrLn, readFile, writeFile) -import qualified System.IO as IO -readFile :: FilePath -> IO String +readFile :: FilePath -> IO Text readFile f = do h <- openFile (encodePath f) ReadMode hGetContents h -getContents :: IO String +getContents :: IO Text getContents = hGetContents stdin -writeFileWith :: Newline -> FilePath -> String -> IO () +writeFileWith :: Newline -> FilePath -> Text -> IO () writeFileWith eol f s = withFile (encodePath f) WriteMode $ \h -> hPutStrWith eol h s -writeFile :: FilePath -> String -> IO () +writeFile :: FilePath -> Text -> IO () writeFile = writeFileWith nativeNewline -putStrWith :: Newline -> String -> IO () +putStrWith :: Newline -> Text -> IO () putStrWith eol s = hPutStrWith eol stdout s -putStr :: String -> IO () +putStr :: Text -> IO () putStr = putStrWith nativeNewline -putStrLnWith :: Newline -> String -> IO () +putStrLnWith :: Newline -> Text -> IO () putStrLnWith eol s = hPutStrLnWith eol stdout s -putStrLn :: String -> IO () +putStrLn :: Text -> IO () putStrLn = putStrLnWith nativeNewline -hPutStrWith :: Newline -> Handle -> String -> IO () +hPutStrWith :: Newline -> Handle -> Text -> IO () hPutStrWith eol h s = hSetNewlineMode h (NewlineMode eol eol) >> - hSetEncoding h utf8 >> IO.hPutStr h s + hSetEncoding h utf8 >> TIO.hPutStr h s -hPutStr :: Handle -> String -> IO () +hPutStr :: Handle -> Text -> IO () hPutStr = hPutStrWith nativeNewline -hPutStrLnWith :: Newline -> Handle -> String -> IO () +hPutStrLnWith :: Newline -> Handle -> Text -> IO () hPutStrLnWith eol h s = hSetNewlineMode h (NewlineMode eol eol) >> - hSetEncoding h utf8 >> IO.hPutStrLn h s + hSetEncoding h utf8 >> TIO.hPutStrLn h s -hPutStrLn :: Handle -> String -> IO () +hPutStrLn :: Handle -> Text -> IO () hPutStrLn = hPutStrLnWith nativeNewline -hGetContents :: Handle -> IO String -hGetContents = fmap toString . B.hGetContents --- hGetContents h = hSetEncoding h utf8_bom --- >> hSetNewlineMode h universalNewlineMode --- >> IO.hGetContents h +hGetContents :: Handle -> IO Text +hGetContents = fmap toText . B.hGetContents -- | Convert UTF8-encoded ByteString to Text, also -- removing '\r' characters. -toText :: B.ByteString -> T.Text +toText :: B.ByteString -> Text toText = T.decodeUtf8 . filterCRs . dropBOM where dropBOM bs = if "\xEF\xBB\xBF" `B.isPrefixOf` bs @@ -127,7 +125,7 @@ toTextLazy = TL.decodeUtf8 . filterCRs . dropBOM toStringLazy :: BL.ByteString -> String toStringLazy = TL.unpack . toTextLazy -fromText :: T.Text -> B.ByteString +fromText :: Text -> B.ByteString fromText = T.encodeUtf8 fromTextLazy :: TL.Text -> BL.ByteString diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index 07d825f73..59b04eac1 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -130,7 +130,7 @@ runCommandTest testExePath fp num code = let cmdline = "% " <> cmd let x = cmdline <> "\n" <> input <> "^D\n" <> norm let y = cmdline <> "\n" <> input <> "^D\n" <> newnorm - let updated = T.unpack $ T.replace (T.pack x) (T.pack y) (T.pack raw) + let updated = T.replace (T.pack x) (T.pack y) raw UTF8.writeFile fp' updated extractCommandTest :: FilePath -> FilePath -> TestTree diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index 17ece49fd..160086be2 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -22,6 +22,7 @@ import Test.Tasty (TestTree, testGroup) import Test.Tasty.Golden.Advanced (goldenTest) import Tests.Helpers hiding (test) import qualified Text.Pandoc.UTF8 as UTF8 +import qualified Data.Text as T tests :: FilePath -> [TestTree] tests pandocPath = @@ -231,7 +232,7 @@ tests pandocPath = -- makes sure file is fully closed after reading readFile' :: FilePath -> IO String readFile' f = do s <- UTF8.readFile f - return $! (length s `seq` s) + return $! (T.length s `seq` T.unpack s) lhsWriterTests :: FilePath -> String -> [TestTree] lhsWriterTests pandocPath format @@ -333,7 +334,7 @@ testWithNormalize normalizer pandocPath testname opts inp norm = $ UTF8.toStringLazy out -- filter \r so the tests will work on Windows machines else fail $ "Pandoc failed with error code " ++ show ec - updateGolden = UTF8.writeFile norm + updateGolden = UTF8.writeFile norm . T.pack options = ["--data-dir=../data","--quiet"] ++ [inp] ++ opts compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String) diff --git a/test/Tests/Readers/LaTeX.hs b/test/Tests/Readers/LaTeX.hs index 77104c853..8385b751e 100644 --- a/test/Tests/Readers/LaTeX.hs +++ b/test/Tests/Readers/LaTeX.hs @@ -54,7 +54,7 @@ tokUntokRt s = untokenize (tokenize "random" t) == t tests :: [TestTree] tests = [ testGroup "tokenization" [ testCase "tokenizer round trip on test case" $ do - orig <- T.pack <$> UTF8.readFile "../test/latex-reader.latex" + orig <- UTF8.readFile "../test/latex-reader.latex" let new = untokenize $ tokenize "../test/latex-reader.latex" orig assertEqual "untokenize . tokenize is identity" orig new -- cgit v1.2.3 From 4617f229ea051fea50bce6307fe8221b246a23fe Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 13:28:47 -0800 Subject: Text.Pandoc.MIME: add exported function getCharset. [API change] --- src/Text/Pandoc/MIME.hs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index 53c5cd018..3d06e1579 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -10,8 +10,13 @@ Mime type lookup. -} -module Text.Pandoc.MIME ( MimeType, getMimeType, getMimeTypeDef, - extensionFromMimeType, mediaCategory ) where +module Text.Pandoc.MIME ( + MimeType, + getMimeType, + getMimeTypeDef, + getCharset, + extensionFromMimeType, + mediaCategory ) where import Data.List (isPrefixOf, isSuffixOf) import qualified Data.Map as M import qualified Data.Text as T @@ -54,6 +59,14 @@ reverseMimeTypes = M.fromList $ map swap mimeTypesList mimeTypes :: M.Map T.Text MimeType mimeTypes = M.fromList mimeTypesList +-- | Get the charset from a mime type, if one is present. +getCharset :: MimeType -> Maybe T.Text +getCharset mt = + let (_,y) = T.breakOn "charset=" mt + in if T.null y + then Nothing + else Just $ T.toUpper $ T.takeWhile (/= ';') $ T.drop 8 y + -- | Collection of common mime types. -- Except for first entry, list borrowed from -- -- cgit v1.2.3 From bafccd5aa2dc977e5e49b67c587e1507dd73417c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 13:59:38 -0800 Subject: T.P.Error: Add PandocUnsupportedCharsetError constructor... ...for PandocError. [API change] --- MANUAL.txt | 1 + src/Text/Pandoc/Error.hs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/MANUAL.txt b/MANUAL.txt index 51898f224..ba031bcb2 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1477,6 +1477,7 @@ Nonzero exit codes have the following meanings: 91 PandocMacroLoop 92 PandocUTF8DecodingError 93 PandocIpynbDecodingError + 94 PandocUnsupportedCharsetError 97 PandocCouldNotFindDataFileError 99 PandocResourceNotFound ----- ------------------------------------ diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 0fdb7bfe5..8102f04cc 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -60,6 +60,7 @@ data PandocError = PandocIOError Text IOError | PandocMacroLoop Text | PandocUTF8DecodingError Text Int Word8 | PandocIpynbDecodingError Text + | PandocUnsupportedCharsetError Text | PandocUnknownReaderError Text | PandocUnknownWriterError Text | PandocUnsupportedExtensionError Text Text @@ -124,6 +125,8 @@ renderError e = "The input must be a UTF-8 encoded text." PandocIpynbDecodingError w -> "ipynb decoding error: " <> w + PandocUnsupportedCharsetError charset -> + "Unsupported charset " <> charset PandocUnknownReaderError r -> "Unknown input format " <> r <> case r of @@ -183,6 +186,7 @@ handleError (Left e) = PandocMacroLoop{} -> 91 PandocUTF8DecodingError{} -> 92 PandocIpynbDecodingError{} -> 93 + PandocUnsupportedCharsetError{} -> 94 PandocUnknownReaderError{} -> 21 PandocUnknownWriterError{} -> 22 PandocUnsupportedExtensionError{} -> 23 -- cgit v1.2.3 From 5a73c5d3f8136c7fba7429c3ae3a8ae31c58030b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 14:01:10 -0800 Subject: When downloading content from URL arguments, be sensitive to... the character encoding. We can properly handle UTF-8 and latin1 (ISO-8859-1); for others we raise an error. See #5600. --- src/Text/Pandoc/App.hs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 63996828e..59af029b5 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -28,6 +28,7 @@ import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) import Control.Monad.Except (throwError) import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy as BL import Data.Char (toLower) import Data.Maybe (fromMaybe, isJust, isNothing) @@ -45,6 +46,7 @@ import System.FilePath ( takeBaseName, takeExtension ) import System.IO (nativeNewline, stdout) import qualified System.IO as IO (Newline (..)) import Text.Pandoc +import Text.Pandoc.MIME (getCharset) import Text.Pandoc.App.FormatHeuristics (formatFromFilePaths) import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts, IpynbOutput (..) ) @@ -344,7 +346,13 @@ readSource src = case parseURI src of _ -> PandocAppError (tshow e)) readURI :: FilePath -> PandocIO Text -readURI src = UTF8.toText . fst <$> openURL (T.pack src) +readURI src = do + (bs, mt) <- openURL (T.pack src) + case mt >>= getCharset of + Just "UTF-8" -> return $ UTF8.toText bs + Just "ISO-8859-1" -> return $ T.pack $ B8.unpack bs + Just charset -> throwError $ PandocUnsupportedCharsetError charset + Nothing -> return $ UTF8.toText bs readFile' :: MonadIO m => FilePath -> m BL.ByteString readFile' "-" = liftIO BL.getContents -- cgit v1.2.3 From d30791a38166538be60a134196f1d2675275017d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 14:17:22 -0800 Subject: Fall back to latin1 if UTF-8 decoding fails... ...when handling URL argument served with no charset in the mime type. The assumption is that most pages that don't specify a charset in the mime type are either UTF-8 or latin1. I think that's a good assumption, though I'm not sure. --- src/Text/Pandoc/App.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 59af029b5..40fb34834 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -352,7 +353,12 @@ readURI src = do Just "UTF-8" -> return $ UTF8.toText bs Just "ISO-8859-1" -> return $ T.pack $ B8.unpack bs Just charset -> throwError $ PandocUnsupportedCharsetError charset - Nothing -> return $ UTF8.toText bs + Nothing -> liftIO $ -- try first as UTF-8, then as latin1 + E.catch (return $! UTF8.toText bs) + (\case + TSE.DecodeError{} -> + return $ T.pack $ B8.unpack bs + e -> E.throwIO e) readFile' :: MonadIO m => FilePath -> m BL.ByteString readFile' "-" = liftIO BL.getContents -- cgit v1.2.3 From 00e4bb51e47487fa146caee3bcbbacef51292acc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 22 Feb 2021 23:56:04 +0100 Subject: tests: print accurate location if a test fails Ensures that tasty-hunit reports the location of the failing test instead of the location of the helper `test` function. --- test/Tests/Helpers.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index 31e727a66..64c2785ed 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -35,7 +35,7 @@ import Text.Pandoc.Shared (trimr) import Text.Pandoc.Writers.Native (writeNative) import Text.Printf -test :: (ToString a, ToString b, ToString c) +test :: (ToString a, ToString b, ToString c, HasCallStack) => (a -> b) -- ^ function to test -> String -- ^ name of test case -> (a, c) -- ^ (input, expected value) -- cgit v1.2.3 From f0a991a22be9b82e192d63bf80bbe39679bb2e07 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 21:17:22 -0800 Subject: T.P.CSV: fix parsing of unquoted values. Previously we didn't allow unescaped quotes in unquoted values, but they are allowed. Closes #7112. --- src/Text/Pandoc/CSV.hs | 3 +-- test/command/7112.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/command/7112.md diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index ec212fa9a..2bd21bcfb 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -68,8 +68,7 @@ escaped opts = try $ pCSVUnquotedCell :: CSVOptions -> Parser Text pCSVUnquotedCell opts = T.pack <$> - many (satisfy (\c -> c /= csvDelim opts && c /= '\r' && c /= '\n' - && c /= csvQuote opts)) + many (satisfy (\c -> c /= csvDelim opts && c /= '\r' && c /= '\n')) pCSVDelim :: CSVOptions -> Parser () pCSVDelim opts = do diff --git a/test/command/7112.md b/test/command/7112.md new file mode 100644 index 000000000..a75b9250a --- /dev/null +++ b/test/command/7112.md @@ -0,0 +1,15 @@ +``` +% pandoc -f rst +.. csv-table:: + + setting, ``echo PATH="path"`` +^D + + + + + + + +
settingecho PATH="path"
+``` -- cgit v1.2.3 From 56a6478a408d8199ec13425ed7a3adc78613c6bb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 21:36:19 -0800 Subject: Add linux benchmark to CI. --- .github/workflows/ci.yml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a0a025a..10ff8dcf8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,3 +192,59 @@ jobs: # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 cabal v2-test --disable-optimization + + linux/benchmark: + + runs-on: ubuntu-18.04 + strategy: + fail-fast: true + matrix: + versions: + - ghc: '8.8.4' + cabal: '3.2' + - ghc: '8.10.2' + cabal: '3.2' + steps: + - uses: actions/checkout@v2 + + # need to install older cabal/ghc versions from ppa repository + + - name: Install recent cabal/ghc + uses: haskell/actions/setup@v1 + with: + ghc-version: ${{ matrix.versions.ghc }} + cabal-version: ${{ matrix.versions.cabal }} + + # declare/restore cached things + # caching doesn't work for scheduled runs yet + # https://github.com/actions/cache/issues/63 + + - name: Cache cabal global package db + id: cabal-global + uses: actions/cache@v2 + with: + path: | + ~/.cabal + key: ${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-global-${{ hashFiles('cabal.project') }} + + - name: Cache cabal work + id: cabal-local + uses: actions/cache@v2 + with: + path: | + dist-newstyle + key: ${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-local + + - name: Install dependencies + run: | + v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') + cabal $v2-update + cabal $v2-build --dependencies-only --enable-optimization=1 --enable-benchmarks + + - name: Build and test + run: | + v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') + cabal $v2-build --enable-optimization=1 --enable-benchmarks 2>&1 | tee build.log + # fail if warnings in local build + ! grep -q ": *[Ww]arning:" build.log || exit 1 + cabal $v2-bench --enable-optimization=1 -- cgit v1.2.3 From 15bf01cd63d63e655e8fa90bc986f3dd29a38bff Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 21:38:58 -0800 Subject: Rename benchmark in CI. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10ff8dcf8..b1a5e3b0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -193,7 +193,7 @@ jobs: ! grep -q ": *[Ww]arning:" build.log || exit 1 cabal v2-test --disable-optimization - linux/benchmark: + benchmark: runs-on: ubuntu-18.04 strategy: -- cgit v1.2.3 From 21d0b4745b46f8622b31647c81a9d9178b1a579d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 22:09:32 -0800 Subject: benchmark CI improvements. - don't build tests, just benchmarks. - get allocation info. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1a5e3b0f..5337527ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -239,12 +239,12 @@ jobs: run: | v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') cabal $v2-update - cabal $v2-build --dependencies-only --enable-optimization=1 --enable-benchmarks + cabal $v2-build --dependencies-only --enable-optimization=1 --enable-benchmarks --disable-tests - name: Build and test run: | v2=$([ "${{ matrix.versions.cabal }}" = "2.2" ] && printf 'new' || printf 'v2') - cabal $v2-build --enable-optimization=1 --enable-benchmarks 2>&1 | tee build.log + cabal $v2-build --enable-optimization=1 --enable-benchmarks --disable-tests 2>&1 | tee build.log # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 - cabal $v2-bench --enable-optimization=1 + cabal $v2-bench --enable-optimization=1 --benchmark-options='--timeout=6 +RTS -T -RTS' -- cgit v1.2.3 From d7cfa0ef4c926d6bb91d0a870edeea8911d81c3a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 22:10:20 -0800 Subject: Remove weigh-pandoc. It's not really useful any more, now that our regular benchmarks include data on allocation. --- benchmark/weigh-pandoc.hs | 48 ----------------------------------------------- pandoc.cabal | 8 -------- 2 files changed, 56 deletions(-) delete mode 100644 benchmark/weigh-pandoc.hs diff --git a/benchmark/weigh-pandoc.hs b/benchmark/weigh-pandoc.hs deleted file mode 100644 index b77fa9ee9..000000000 --- a/benchmark/weigh-pandoc.hs +++ /dev/null @@ -1,48 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{- | - Module : Main - Copyright : © 2016-2021 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - -Benchmarks to determine resource use of readers and writers. --} -import Weigh -import Text.Pandoc -import Data.Text (Text, unpack) - -main :: IO () -main = do - doc <- read <$> readFile "test/testsuite.native" - mainWith $ do - func "Pandoc document" id doc - mapM_ - (\(n,r) -> weighReader doc n (either (error . show) id . runPure . r def{readerExtensions = pandocExtensions})) - [("markdown", readMarkdown) - ,("html", readHtml) - ,("docbook", readDocBook) - ,("latex", readLaTeX) - ,("commonmark", readCommonMark) - ] - mapM_ - (\(n,w) -> weighWriter doc n (either (error . show) id . runPure . w def)) - [("markdown", writeMarkdown) - ,("html", writeHtml5String) - ,("docbook", writeDocbook5) - ,("latex", writeLaTeX) - ,("commonmark", writeCommonMark) - ] - -weighWriter :: Pandoc -> String -> (Pandoc -> Text) -> Weigh () -weighWriter doc name writer = func (name ++ " writer") writer doc - -weighReader :: Pandoc -> Text -> (Text -> Pandoc) -> Weigh () -weighReader doc name reader = - case lookup name writers of - Just (TextWriter writer) -> - let inp = either (error . show) id $ runPure $ writer def{ writerWrapText = WrapAuto} doc - in func (unpack $ name <> " reader") reader inp - _ -> return () -- no writer for reader diff --git a/pandoc.cabal b/pandoc.cabal index 30226f445..e4001abb8 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -725,14 +725,6 @@ executable trypandoc else buildable: False -benchmark weigh-pandoc - import: common-executable - type: exitcode-stdio-1.0 - main-is: weigh-pandoc.hs - hs-source-dirs: benchmark - build-depends: mtl >= 2.2 && < 2.3, - weigh >= 0.0 && < 0.1, - test-suite test-pandoc import: common-executable type: exitcode-stdio-1.0 -- cgit v1.2.3 From 9767386676d3fae04e3cd7f2a3622ff1f60cc1f3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 22:25:10 -0800 Subject: Use latest skylighting. --- pandoc.cabal | 4 ++-- stack.yaml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index e4001abb8..0f5e8312f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -480,8 +480,8 @@ library random >= 1 && < 1.2, safe >= 0.3 && < 0.4, scientific >= 0.3 && < 0.4, - skylighting >= 0.10.2 && < 0.11, - skylighting-core >= 0.10.2 && < 0.11, + skylighting >= 0.10.3 && < 0.11, + skylighting-core >= 0.10.3 && < 0.11, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, diff --git a/stack.yaml b/stack.yaml index 4af76f19e..88109f26c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,6 +13,8 @@ extra-deps: - commonmark-pandoc-0.2.0.1 - tasty-bench-0.2.1 - citeproc-0.3.0.7 +- skylighting-0.10.3 +- skylighting-core-0.10.3 #- citeproc: # git: https://github.com/jgm/citeproc.git # commit: feb3b7580c6738eec3b23921f7c1739cfba611aa -- cgit v1.2.3 From 938f402dec7ea4447ec8347506ff6d688bc28446 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Feb 2021 23:24:13 -0800 Subject: CI: Add 'benchmark' to benchmark cache key. For benchmark we build with optimizations, so the regular cache is useless. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5337527ed..561de40d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -225,7 +225,7 @@ jobs: with: path: | ~/.cabal - key: ${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-global-${{ hashFiles('cabal.project') }} + key: benchmark-${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-global-${{ hashFiles('cabal.project') }} - name: Cache cabal work id: cabal-local @@ -233,7 +233,7 @@ jobs: with: path: | dist-newstyle - key: ${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-local + key: benchmark-${{ runner.os }}-${{ matrix.versions.ghc }}-${{ matrix.versions.cabal }}-cabal-local - name: Install dependencies run: | -- cgit v1.2.3 From 8123571e8efa4fc02004e9c3812b5177f9d0c8c4 Mon Sep 17 00:00:00 2001 From: Loïc Grobol Date: Fri, 26 Feb 2021 18:05:30 +0000 Subject: Wrap url colours in braces in the default LaTeX template (#7121) --- data/templates/default.latex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index c98f53bb6..50fad2a49 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -219,10 +219,10 @@ $if(keywords)$ $endif$ $if(colorlinks)$ colorlinks=true, - linkcolor=$if(linkcolor)$$linkcolor$$else$Maroon$endif$, - filecolor=$if(filecolor)$$filecolor$$else$Maroon$endif$, - citecolor=$if(citecolor)$$citecolor$$else$Blue$endif$, - urlcolor=$if(urlcolor)$$urlcolor$$else$Blue$endif$, + linkcolor={$if(linkcolor)$$linkcolor$$else$Maroon$endif$}, + filecolor={$if(filecolor)$$filecolor$$else$Maroon$endif$}, + citecolor={$if(citecolor)$$citecolor$$else$Blue$endif$}, + urlcolor={$if(urlcolor)$$urlcolor$$else$Blue$endif$}, $else$ hidelinks, $endif$ -- cgit v1.2.3 From fae6a204f1a072d5efe4dff35fd08fbad2071198 Mon Sep 17 00:00:00 2001 From: Salim B Date: Sat, 27 Feb 2021 01:56:04 +0000 Subject: Fix/update URLs and use HTTP**S** where possible (#7122) --- CONTRIBUTING.md | 16 ++++++++-------- COPYING.md | 2 +- COPYRIGHT | 6 +++--- INSTALL.md | 29 ++++++++++++++--------------- MANUAL.txt | 2 +- README.template | 10 +++++----- doc/lua-filters.md | 2 +- man/pandoc.1 | 2 +- man/pandoc.1.after | 2 +- src/Text/Pandoc/Citeproc.hs | 2 +- src/Text/Pandoc/Citeproc/BibTeX.hs | 8 ++++---- src/Text/Pandoc/Readers/Textile.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 2 +- test/txt2tags.t2t | 2 +- trypandoc/index.html | 2 +- windows/Makefile | 2 +- windows/pandoc.wxs | 4 ++-- 17 files changed, 47 insertions(+), 48 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e87c96694..595fb94ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -372,7 +372,7 @@ The code -------- Pandoc has a publicly accessible git repository on -GitHub: . To get a local copy of the source: +GitHub: . To get a local copy of the source: git clone https://github.com/jgm/pandoc.git @@ -382,7 +382,7 @@ the pandoc library is in `src/`, the source for the tests is in The modules `Text.Pandoc.Definition`, `Text.Pandoc.Builder`, and `Text.Pandoc.Generic` are in a separate library `pandoc-types`. The code can -be found in . +be found in . To build pandoc, you will need a working installation of the [Haskell platform]. @@ -445,14 +445,14 @@ you may want to consider submitting a pull request to the [closed issues]: https://github.com/jgm/pandoc/issues?q=is%3Aissue+is%3Aclosed [latest released version]: https://github.com/jgm/pandoc/releases/latest [Nightly builds]: https://github.com/jgm/pandoc/actions?query=workflow%3ANightly -[pandoc-discuss]: http://groups.google.com/group/pandoc-discuss +[pandoc-discuss]: https://groups.google.com/group/pandoc-discuss [issue tracker]: https://github.com/jgm/pandoc/issues -[User's Guide]: http://pandoc.org/MANUAL.html -[FAQs]: http://pandoc.org/faqs.html -[EditorConfig]: http://editorconfig.org/ -[Haskell platform]: http://www.haskell.org/platform/ +[User's Guide]: https://pandoc.org/MANUAL.html +[FAQs]: https://pandoc.org/faqs.html +[EditorConfig]: https://editorconfig.org/ +[Haskell platform]: https://www.haskell.org/platform/ [hlint]: https://hackage.haskell.org/package/hlint -[hsb2hs]: http://hackage.haskell.org/package/hsb2hs +[hsb2hs]: https://hackage.haskell.org/package/hsb2hs [pre-commit hook]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks [GitHub labels]: https://github.com/jgm/pandoc/labels [good first issue]:https://github.com/jgm/pandoc/labels/good%20first%20issue diff --git a/COPYING.md b/COPYING.md index af5153d8f..90ae12017 100644 --- a/COPYING.md +++ b/COPYING.md @@ -357,5 +357,5 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public -License](http://www.gnu.org/licenses/lgpl.html) instead of this +License](https://www.gnu.org/licenses/lgpl.html) instead of this License. \ No newline at end of file diff --git a/COPYRIGHT b/COPYRIGHT index a6e3a897c..9752c555d 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -22,11 +22,11 @@ The GNU General Public License is available in the file COPYING.md in the source distribution. On Debian systems, the complete text of the GPL can be found in `/usr/share/common-licenses/GPL`. -[GPL]: http://www.gnu.org/copyleft/gpl.html +[GPL]: https://www.gnu.org/copyleft/gpl.html Pandoc's complete source code is available from the [Pandoc home page]. -[Pandoc home page]: http://pandoc.org +[Pandoc home page]: https://pandoc.org Pandoc includes some code with different copyrights, or subject to different licenses. The copyright and license statements for these sources are included @@ -176,7 +176,7 @@ Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- The dzslides template contains JavaScript and CSS from Paul Rouget's dzslides template. -http://github.com/paulrouget/dzslides +https://github.com/paulrouget/dzslides Released under the Do What the Fuck You Want To Public License. diff --git a/INSTALL.md b/INSTALL.md index 721dc5d66..d23c349c1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -45,7 +45,7 @@ by downloading [this script][uninstaller] and running it with `perl uninstall-pandoc.pl`. Alternatively, you can install pandoc using -[Homebrew](http://brew.sh): +[Homebrew](https://brew.sh): brew install pandoc @@ -116,7 +116,7 @@ package repositories. For example, on Debian/Ubuntu, you can install it with `apt-get install haskell-platform`. For PDF output, you'll need LaTeX. We recommend installing -[TeX Live](http://www.tug.org/texlive/) via your package +[TeX Live](https://www.tug.org/texlive/) via your package manager. (On Debian/Ubuntu, `apt-get install texlive`.) ## Chrome OS @@ -238,7 +238,7 @@ The easiest way to build pandoc from source is to use [stack][stack]: pandoc --help - [Not sure where `$CABALDIR` is?](http://www.haskell.org/haskellwiki/Cabal-Install#The_cabal-install_configuration_file) + [Not sure where `$CABALDIR` is?](https://wiki.haskell.org/Cabal-Install#The_cabal-install_configuration_file) 5. By default `pandoc` uses the "i;unicode-casemap" method to sort bibliography entries (RFC 5051). If you would like to @@ -383,30 +383,29 @@ To run just the markdown benchmarks: [Arch]: https://www.archlinux.org/packages/community/x86_64/pandoc/ -[Cabal User's Guide]: http://www.haskell.org/cabal/release/latest/doc/users-guide/builders.html#setup-configure-paths +[Cabal User's Guide]: https://cabal.readthedocs.io/ [Debian]: https://packages.debian.org/pandoc [Fedora]: https://apps.fedoraproject.org/packages/pandoc -[FreeBSD ports]: http://www.freshports.org/textproc/hs-pandoc/ -[GHC]: http://www.haskell.org/ghc/ -[GPL]: http://www.gnu.org/copyleft/gpl.html -[Haskell platform]: http://hackage.haskell.org/platform/ -[MacPorts]: http://trac.macports.org/browser/trunk/dports/textproc/pandoc/Portfile +[FreeBSD ports]: https://www.freshports.org/textproc/hs-pandoc/ +[GHC]: https://www.haskell.org/ghc/ +[Haskell platform]: https://hackage.haskell.org/platform/ +[MacPorts]: https://trac.macports.org/browser/trunk/dports/textproc/pandoc/Portfile [MacTeX]: https://tug.org/mactex/ -[BasicTeX]: http://www.tug.org/mactex/morepackages.html +[BasicTeX]: https://www.tug.org/mactex/morepackages.html [LaTeX]: https://www.latex-project.org -[MiKTeX]: http://miktex.org/ +[MiKTeX]: https://miktex.org/ [librsvg]: https://wiki.gnome.org/Projects/LibRsvg [Python]: https://www.python.org -[NetBSD]: http://pkgsrc.se/wip/pandoc +[NetBSD]: https://pkgsrc.se/wip/pandoc [NixOS]: https://nixos.org/nixos/packages.html [Slackware]: https://www.slackbuilds.org/result/?search=pandoc&sv= [Ubuntu]: https://packages.ubuntu.com/pandoc [download page]: https://github.com/jgm/pandoc/releases/latest -[gentoo]: http://packages.gentoo.org/package/app-text/pandoc +[gentoo]: https://packages.gentoo.org/package/app-text/pandoc [haskell repository]: https://wiki.archlinux.org/index.php/Haskell_Package_Guidelines#.5Bhaskell.5D [openSUSE]: https://software.opensuse.org/package/pandoc -[source tarball]: http://hackage.haskell.org/package/pandoc +[source tarball]: https://hackage.haskell.org/package/pandoc [stack]: https://docs.haskellstack.org/en/stable/install_and_upgrade.html -[cabal-install]: http://hackage.haskell.org/package/cabal-install +[cabal-install]: https://hackage.haskell.org/package/cabal-install [Void]: https://voidlinux.org/ [uninstaller]: https://raw.githubusercontent.com/jgm/pandoc/master/macos/uninstall-pandoc.pl diff --git a/MANUAL.txt b/MANUAL.txt index ba031bcb2..a9465b20c 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -486,7 +486,7 @@ header when requesting a document from a URL: [Emacs Org mode]: https://orgmode.org [AsciiDoc]: https://www.methods.co.nz/asciidoc/ [AsciiDoctor]: https://asciidoctor.org/ -[DZSlides]: http://paulrouget.com/dzslides/ +[DZSlides]: https://paulrouget.com/dzslides/ [Word docx]: https://en.wikipedia.org/wiki/Office_Open_XML [PDF]: https://www.adobe.com/pdf/ [reveal.js]: https://revealjs.com/ diff --git a/README.template b/README.template index 53d14b584..4b469f22b 100644 --- a/README.template +++ b/README.template @@ -7,9 +7,9 @@ Pandoc ====== [![github release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases) -[![hackage release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](http://hackage.haskell.org/package/pandoc) -[![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](http://brewformulas.org/Pandoc) -[![stackage LTS package](http://stackage.org/package/pandoc/badge/lts)](http://stackage.org/lts/package/pandoc) +[![hackage release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](https://hackage.haskell.org/package/pandoc) +[![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](https://formulae.brew.sh/formula/pandoc) +[![stackage LTS package](https://stackage.org/package/pandoc/badge/lts)](https://www.stackage.org/lts/package/pandoc-types) [![CI tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions) [![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html) [![pandoc-discuss on google groups](https://img.shields.io/badge/pandoc-discuss-red.svg?style=social)](https://groups.google.com/forum/#!forum/pandoc-discuss) @@ -85,5 +85,5 @@ License [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) -[GPL]: http://www.gnu.org/copyleft/gpl.html "GNU General Public License" -[Haskell]: http://haskell.org +[GPL]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License" +[Haskell]: https://haskell.org diff --git a/doc/lua-filters.md b/doc/lua-filters.md index a99dc9008..df55dc44e 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3478,7 +3478,7 @@ Contract a filename, based on a relative path. Note that the resulting path will usually not introduce `..` paths, as the presence of symlinks means `../b` may not reach `a/b` if it starts from `a/c`. For a worked example see [this blog -post](http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html). +post](https://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html). Set `unsafe` to a truthy value to a allow `..` in paths. diff --git a/man/pandoc.1 b/man/pandoc.1 index 491e24278..bcda05489 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -7495,4 +7495,4 @@ This software carries no warranty of any kind. of contributors, see the file AUTHORS.md in the pandoc source code. .PP The Pandoc source code and all documentation may be downloaded -from . +from . diff --git a/man/pandoc.1.after b/man/pandoc.1.after index e5eabb670..7cd7a93f0 100644 --- a/man/pandoc.1.after +++ b/man/pandoc.1.after @@ -1,3 +1,3 @@ .PP The Pandoc source code and all documentation may be downloaded -from . +from . diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index 6658c8c0c..38e992ba1 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -544,7 +544,7 @@ linkifyVariables ref = fixShortDOI x = let x' = extractText x in if "10/" `T.isPrefixOf` x' then TextVal $ T.drop 3 x' - -- see http://shortdoi.org + -- see https://shortdoi.org else TextVal x' tolink pref x = let x' = extractText x x'' = if "://" `T.isInfixOf` x' diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 416fe439e..c0752dadc 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -577,10 +577,10 @@ itemToReference locale variant item = do eprint <- getRawField "eprint" let baseUrl = case T.toLower etype of - "arxiv" -> "http://arxiv.org/abs/" - "jstor" -> "http://www.jstor.org/stable/" - "pubmed" -> "http://www.ncbi.nlm.nih.gov/pubmed/" - "googlebooks" -> "http://books.google.com?id=" + "arxiv" -> "https://arxiv.org/abs/" + "jstor" -> "https://www.jstor.org/stable/" + "pubmed" -> "https://www.ncbi.nlm.nih.gov/pubmed/" + "googlebooks" -> "https://books.google.com?id=" _ -> "" if T.null baseUrl then mzero diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 4991c6308..860da2dc3 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -11,7 +11,7 @@ Portability : portable Conversion from Textile to 'Pandoc' document, based on the spec -available at http://redcloth.org/textile. +available at https://www.promptworks.com/textile/. Implemented and parsed: - Paragraphs diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 898905603..d33246a63 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -14,7 +14,7 @@ Conversion of 'Pandoc' documents to markdown-formatted plain text. -Markdown: +Markdown: -} module Text.Pandoc.Writers.Markdown ( writeMarkdown, diff --git a/test/txt2tags.t2t b/test/txt2tags.t2t index f736cfa93..b636c1511 100644 --- a/test/txt2tags.t2t +++ b/test/txt2tags.t2t @@ -270,7 +270,7 @@ FTP.DOMAIN.COM [img.png] %%% Syntax: Image pointing to a link: [[img] link] -[[img.png] http://txt2tags.org] +[[img.png] https://txt2tags.org] %%% Align: Image position is preserved when inside paragraph [img.png] Image at the line beginning. diff --git a/trypandoc/index.html b/trypandoc/index.html index 792f522eb..5761153c9 100644 --- a/trypandoc/index.html +++ b/trypandoc/index.html @@ -14,7 +14,7 @@ - // reveal.js plugins + -- cgit v1.2.3 From d0af5795f1de5a75028d48b2d48105d435cbbac3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 10:02:25 -0700 Subject: make quick: use -j4 to speed up build --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 04b57939d..93811d3a6 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ REVISION?=1 BENCHARGS?=--timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: - stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j +RTS -A64m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' + stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j4 +RTS -A256m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' quick-cabal: cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests -- cgit v1.2.3 From 2770f88d53c1b4699621e39a4fa254c0cd13f19b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:01:38 -0700 Subject: Fix benchmark CI to use gauge options. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9c5225f3..9a8759f4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -257,4 +257,4 @@ jobs: cabal $v2-build --enable-optimization=1 --enable-benchmarks --disable-tests 2>&1 | tee build.log # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 - cabal $v2-bench --enable-optimization=1 --benchmark-options='--timeout=6 +RTS -T -RTS' + cabal $v2-bench --enable-optimization=1 --benchmark-options='--small' -- cgit v1.2.3 From 87538966a029358d42bc71ab0cd4c1ed9e125520 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:05:29 -0700 Subject: Removed unused LANGUAGE pragmas. --- src/Text/Pandoc/Writers/LaTeX/Caption.hs | 1 - src/Text/Pandoc/XML/Light/Proc.hs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX/Caption.hs b/src/Text/Pandoc/Writers/LaTeX/Caption.hs index 7b9ce186f..ab4d365cc 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Caption.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Caption.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.LaTeX.Caption Copyright : Copyright (C) 2006-2021 John MacFarlane diff --git a/src/Text/Pandoc/XML/Light/Proc.hs b/src/Text/Pandoc/XML/Light/Proc.hs index b53c4b545..a1fb200ff 100644 --- a/src/Text/Pandoc/XML/Light/Proc.hs +++ b/src/Text/Pandoc/XML/Light/Proc.hs @@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light.Proc Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane -- cgit v1.2.3 From 5ae8b8e5865f7734a486f72cfe7fce74cbbec070 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:05:56 -0700 Subject: Update .hlint.yaml to avoid false positives on redundant brackets. These brackets are not redundant, it's a bug in hlint: $(deriveJSON ... ) --- .hlint.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hlint.yaml b/.hlint.yaml index e482b2b37..039e43562 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -46,6 +46,8 @@ - Text.Pandoc.Citeproc - Text.Pandoc.Citeproc.BibTeX - Text.Pandoc.Citeproc.MetaValue + - Text.Pandoc.App.Opt + - Text.Pandoc.Extensions - ignore: name: "Use <$>" -- cgit v1.2.3 From a366e2b5744f0dee38fb3eb69b0b0c4bd8070fd1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:06:38 -0700 Subject: cabal.project: remove -j, which means -j1! --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index e09c88feb..d36a8c3d1 100644 --- a/cabal.project +++ b/cabal.project @@ -4,7 +4,7 @@ tests: True package pandoc flags: +embed_data_files - ghc-options: -j +RTS -A256m -RTS + ghc-options: +RTS -A256m -RTS -- source-repository-package -- type: git -- cgit v1.2.3 From b6b40de7fc5c0906bc8d95addaac621fd8a8fb34 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:07:37 -0700 Subject: Use BENCHARGS in 'make bench'. --- Makefile | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 93811d3a6..274c6f9f2 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,9 @@ endif GHCOPTS=-fdiagnostics-color=always WEBSITE=../../web/pandoc.org REVISION?=1 -BENCHARGS?=--timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) +BENCHARGS?=--small --time-limit=2 --match=pattern $(PATTERN) +# For tasty-bench: +# BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j4 +RTS -A256m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -53,16 +55,11 @@ ghcid-test: bench: stack bench \ --ghc-options '-Rghc-timing $(GHCOPTS)' \ - --benchmark-arguments='--small --time-limit=2 \ - --match=pattern $(PATTERN)' 2>&1 | \ + --benchmark-arguments='$(BENCHARGS)' 2>&1 | \ tee "bench_latest.txt" perl -pe 's/\x1b\[[0-9;]*[mGK]//g;s/\r//;' bench_latest.txt > \ "bench_$(TIMESTAMP).txt" - -# With tasty-bench, we used -# --benchmark-arguments='$(BENCHARGS) $(BASELINE) --csv bench_$(TIMESTAMP).csv' --ghc-options '-Rghc-timing $(GHCOPTS)' - reformat: for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done -- cgit v1.2.3 From 769891833054d9722325a9df1cf9fe6dc792c0c9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:08:01 -0700 Subject: make quick: re-use GHCOPTS for tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 274c6f9f2..196ab4740 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ BENCHARGS?=--small --time-limit=2 --match=pattern $(PATTERN) # BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: - stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j4 +RTS -A256m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)' + stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' quick-cabal: cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests -- cgit v1.2.3 From fdaa56cbcd4f4cb905511f2a131018f2e123dd8a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:08:32 -0700 Subject: Makefile: set -j4 in default GHCOPTS --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 196ab4740..ac1850c2d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ BASELINE= else BASELINE=--baseline $(LATESTBENCH) endif -GHCOPTS=-fdiagnostics-color=always +GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A256m -RTS WEBSITE=../../web/pandoc.org REVISION?=1 BENCHARGS?=--small --time-limit=2 --match=pattern $(PATTERN) -- cgit v1.2.3 From 2e6561fe911dde9f62f71beea6d1b79a7e69a13d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:12:40 -0700 Subject: .hlint.yaml - fix more false positives for redundant brackets. --- .hlint.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.hlint.yaml b/.hlint.yaml index 039e43562..213238458 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -46,6 +46,7 @@ - Text.Pandoc.Citeproc - Text.Pandoc.Citeproc.BibTeX - Text.Pandoc.Citeproc.MetaValue + - Text.Pandoc.Options - Text.Pandoc.App.Opt - Text.Pandoc.Extensions -- cgit v1.2.3 From 94c917c13feed49c2d5ad278849456a488673347 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 13:16:19 -0700 Subject: Use -j4 for linux release build. --- linux/make_artifacts.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/make_artifacts.sh b/linux/make_artifacts.sh index 043b3f9f2..cb7bf4c8c 100644 --- a/linux/make_artifacts.sh +++ b/linux/make_artifacts.sh @@ -26,9 +26,9 @@ ghc --version cabal v2-update cabal v2-clean -cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-split-sections -optc-Os -optl=-pthread' pandoc +cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc cabal v2-build -cabal v2-test -j1 +cabal v2-test -j4 for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f /artifacts/; done # make deb -- cgit v1.2.3 From 1ef35343288ae7cb1d074c6a25aa5e47fdc55da9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 15:59:50 -0700 Subject: Increase heap space in runtime for benchmarks. Otherwise we're essentially benchmarking garbage collecting, which can give very inconsistent results. --- Makefile | 9 +++++---- benchmark/benchmark-pandoc.hs | 1 + pandoc.cabal | 1 + stack.yaml | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ac1850c2d..60aaeb43f 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,10 @@ endif GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A256m -RTS WEBSITE=../../web/pandoc.org REVISION?=1 -BENCHARGS?=--small --time-limit=2 --match=pattern $(PATTERN) +# For gauge: +BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) +RTS -T -A256m -I0 -RTS # For tasty-bench: -# BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) +# BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -A256m -I0 -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -57,8 +58,8 @@ bench: --ghc-options '-Rghc-timing $(GHCOPTS)' \ --benchmark-arguments='$(BENCHARGS)' 2>&1 | \ tee "bench_latest.txt" - perl -pe 's/\x1b\[[0-9;]*[mGK]//g;s/\r//;' bench_latest.txt > \ - "bench_$(TIMESTAMP).txt" + perl -ne 'if (/\r/) { s/\x1b\[[0-9;]*[mGK]//g;s/^.*\r//;print; }' \ + bench_latest.txt > "bench_$(TIMESTAMP).txt" reformat: for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index f9a872011..111e63274 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -23,6 +23,7 @@ import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T +-- import Test.Tasty.Bench import Gauge import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe) diff --git a/pandoc.cabal b/pandoc.cabal index 647505211..2f49f0ac3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -827,6 +827,7 @@ benchmark benchmark-pandoc build-depends: bytestring, containers, gauge >= 0.2 && < 0.3, + -- tasty-bench >= 0.2 && <= 0.3, mtl >= 2.2 && < 2.3, text >= 1.1.1.0 && < 1.3, time, diff --git a/stack.yaml b/stack.yaml index 0f21bbb6c..7a6462d51 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,6 +13,7 @@ extra-deps: - doclayout-0.3.0.2 - citeproc-0.3.0.9 - texmath-0.12.2 +- tasty-bench-0.2.2 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From 956355f8218c4cc0fd236688ab4f26de5dbb1b9c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 16:01:30 -0700 Subject: Disable redundant brackets check in .hlint.yaml. --- .hlint.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index 213238458..350794803 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -39,16 +39,10 @@ name: "Redundant return" within: Text.Pandoc.Citeproc.BibTeX -# TODO: check +# With recent hlint this rule has given false positives on TH - ignore: name: "Redundant bracket" - within: - - Text.Pandoc.Citeproc - - Text.Pandoc.Citeproc.BibTeX - - Text.Pandoc.Citeproc.MetaValue - - Text.Pandoc.Options - - Text.Pandoc.App.Opt - - Text.Pandoc.Extensions + - ignore: name: "Use <$>" -- cgit v1.2.3 From 6075b17ae11e5f1670d5d6e715431c4242dfd943 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 16 Mar 2021 18:04:39 -0700 Subject: Makefile: add --static to quick target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 60aaeb43f..a3547e7f2 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) +RTS -T -A256m -I0 -RTS # BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -A256m -I0 -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: - stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' + stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --static --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' quick-cabal: cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests -- cgit v1.2.3 From f54e218bf86c7de832d5002af5727d7923de272c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 08:57:25 -0700 Subject: Revert "Makefile: add --static to quick target" This reverts commit 6075b17ae11e5f1670d5d6e715431c4242dfd943. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a3547e7f2..60aaeb43f 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) +RTS -T -A256m -I0 -RTS # BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -A256m -I0 -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: - stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --static --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' + stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' quick-cabal: cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests -- cgit v1.2.3 From 0b1869fd428835e01e91fc2005aa3907c0deeba8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 09:01:43 -0700 Subject: Remove unnecessary tasty-bench from extra-deps --- stack.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 7a6462d51..0f21bbb6c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,7 +13,6 @@ extra-deps: - doclayout-0.3.0.2 - citeproc-0.3.0.9 - texmath-0.12.2 -- tasty-bench-0.2.2 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From e66bf891ec21cc09611c5be4e4847fccd39e0084 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 09:02:20 -0700 Subject: Add test for #7155. --- test/command/7155.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test/command/7155.md diff --git a/test/command/7155.md b/test/command/7155.md new file mode 100644 index 000000000..49358bf97 --- /dev/null +++ b/test/command/7155.md @@ -0,0 +1,15 @@ +``` +% pandoc -f markdown+tex_math_single_backslash -t native +\(x\) \[x\] +\\(x\\) \\[x\\] +^D +[Para [Math InlineMath "x",Space,Math DisplayMath "x",SoftBreak,Str "\\(x\\)",Space,Str "\\[x\\]"]] +``` + +``` +% pandoc -f markdown+tex_math_double_backslash -t native +\(x\) \[x\] +\\(x\\) \\[x\\] +^D +[Para [Str "(x)",Space,Str "[x]",SoftBreak,Math InlineMath "x",Space,Math DisplayMath "x"]] +``` -- cgit v1.2.3 From 7bf4be04b0c04ccfd02a2a907071b8747380904f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 09:10:44 -0700 Subject: Fix regression with `tex_math_backslash` in Markdown reader. Added regression test. Closes #7155. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index dc94fc2d6..c836a896b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1503,7 +1503,7 @@ inline = do '$' -> math '~' -> strikeout <|> subscript '<' -> autoLink <|> spanHtml <|> rawHtmlInline <|> ltSign - '\\' -> escapedNewline <|> escapedChar <|> rawLaTeXInline' + '\\' -> math <|> escapedNewline <|> escapedChar <|> rawLaTeXInline' '@' -> cite <|> exampleRef '"' -> smart '\'' -> smart -- cgit v1.2.3 From 84836719aabe055b12d8444f8a6e70c13336f614 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 09:25:39 -0700 Subject: Makefile: Add comment documenting benchmark settings. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 60aaeb43f..0ef0406f8 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ endif GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A256m -RTS WEBSITE=../../web/pandoc.org REVISION?=1 +# Note: for benchmarks we use +RTS -A256m -I0 -RTS ; otherwise the benchmarks +# are measuring garbage collecting, and this can vary depending on which +# other benchmarks are run. # For gauge: BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) +RTS -T -A256m -I0 -RTS # For tasty-bench: -- cgit v1.2.3 From c6e5cf2e7472ab872a537327a03ef9eb9fcef2a1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 13:34:17 -0700 Subject: Benchmark improvements. * Build `+RTS -A256m -RTS` into default ghc-options for benchmark, so we don't have to specify this separately on the command line. This is necessary to get accurate benchmark results; otherwise we are largely measuring garbage collecting, some not related to the current benchmark. * Switch back from gauge to tasty-bench. * Allow specifying BASELINE file in 'make bench' for comparison (otherwise the latest is chosen by default). * Remove obsolete reference to weigh-pandoc from CONTRIBUTING.md. * Remove `-Rghc-timing` from 'make bench'. --- CONTRIBUTING.md | 3 --- Makefile | 18 +++++++----------- benchmark/benchmark-pandoc.hs | 4 ++-- pandoc.cabal | 6 ++++-- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 090cc0a4f..cf8d3aa03 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -318,9 +318,6 @@ With stack: stack bench -You can also build `weigh-pandoc` (`stack build pandoc:weigh-pandoc`) -to get some statistics on memory usage. (Eventually this should -be incorporated into the benchmark suite.) Using the REPL -------------- diff --git a/Makefile b/Makefile index 0ef0406f8..5e5bc7c51 100644 --- a/Makefile +++ b/Makefile @@ -7,21 +7,19 @@ DOCKERIMAGE=registry.gitlab.b-data.ch/ghc/ghc4pandoc:8.10.4 COMMIT=$(shell git rev-parse --short HEAD) TIMESTAMP=$(shell date "+%Y%m%d_%H%M") LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv 2>/dev/null)) -ifeq ($(LATESTBENCH),) -BASELINE= +BASELINE?=$(LATESTBENCH) +ifeq ($(BASELINE),) +BASELINECMD= else -BASELINE=--baseline $(LATESTBENCH) +BASELINECMD=--baseline $(BASELINE) endif GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A256m -RTS WEBSITE=../../web/pandoc.org REVISION?=1 -# Note: for benchmarks we use +RTS -A256m -I0 -RTS ; otherwise the benchmarks -# are measuring garbage collecting, and this can vary depending on which -# other benchmarks are run. # For gauge: -BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) +RTS -T -A256m -I0 -RTS +# BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN) # For tasty-bench: -# BENCHARGS?=--csv bench_$(TIMESTAMP).csv --timeout=6 +RTS -T -A256m -I0 -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) +BENCHARGS?=--csv bench_$(TIMESTAMP).csv $(BASELINECMD) --timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) quick: stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -58,11 +56,9 @@ ghcid-test: bench: stack bench \ - --ghc-options '-Rghc-timing $(GHCOPTS)' \ + --ghc-options '$(GHCOPTS)' \ --benchmark-arguments='$(BENCHARGS)' 2>&1 | \ tee "bench_latest.txt" - perl -ne 'if (/\r/) { s/\x1b\[[0-9;]*[mGK]//g;s/^.*\r//;print; }' \ - bench_latest.txt > "bench_$(TIMESTAMP).txt" reformat: for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 111e63274..1890a998f 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -23,8 +23,8 @@ import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T --- import Test.Tasty.Bench -import Gauge +import Test.Tasty.Bench +-- import Gauge import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe) import Data.List (sortOn) diff --git a/pandoc.cabal b/pandoc.cabal index 2f49f0ac3..3a7436370 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -826,9 +826,11 @@ benchmark benchmark-pandoc hs-source-dirs: benchmark build-depends: bytestring, containers, - gauge >= 0.2 && < 0.3, - -- tasty-bench >= 0.2 && <= 0.3, + -- gauge >= 0.2 && < 0.3, + tasty-bench >= 0.2 && <= 0.3, mtl >= 2.2 && < 2.3, text >= 1.1.1.0 && < 1.3, time, deepseq + -- we increase heap size to avoid benchmarking garbage collection: + ghc-options: -rtsopts -with-rtsopts=-A256m -threaded -- cgit v1.2.3 From 0075a456cc9d133e679a909452ae7aa12764240d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 17:08:42 -0700 Subject: Fix benchmark in ci. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a8759f4a..b9c5225f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -257,4 +257,4 @@ jobs: cabal $v2-build --enable-optimization=1 --enable-benchmarks --disable-tests 2>&1 | tee build.log # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 - cabal $v2-bench --enable-optimization=1 --benchmark-options='--small' + cabal $v2-bench --enable-optimization=1 --benchmark-options='--timeout=6 +RTS -T -RTS' -- cgit v1.2.3 From 613c070cbd10b791bd097acd1b6b693a1e46eba1 Mon Sep 17 00:00:00 2001 From: Alexey Kuleshevich Date: Thu, 18 Mar 2021 04:57:32 +0300 Subject: Update bounds for random (#7156) --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 3a7436370..7e9d81951 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -476,7 +476,7 @@ library pandoc-types >= 1.22 && < 1.23, parsec >= 3.1 && < 3.2, process >= 1.2.3 && < 1.7, - random >= 1 && < 1.2, + random >= 1 && < 1.3, safe >= 0.3 && < 0.4, scientific >= 0.3 && < 0.4, skylighting >= 0.10.4.1 && < 0.11, -- cgit v1.2.3 From c3f9e8c12256d19ed6c89d15470945855ee16a94 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 22:31:20 -0700 Subject: Docx writer: make nsid in abstractNum deterministic. Previously we assigned a random number (though in a deterministic way). But changes in the random package mean we get different results now on different architectures, even with the same random seed. We don't need random values; so now we just assign a value based on the list number id, which is guaranteed to be unique to the list marker. --- src/Text/Pandoc/Writers/Docx.hs | 10 ++++------ test/docx/golden/block_quotes.docx | Bin 10071 -> 10067 bytes test/docx/golden/codeblock.docx | Bin 9920 -> 9916 bytes test/docx/golden/comments.docx | Bin 10258 -> 10254 bytes test/docx/golden/custom_style_no_reference.docx | Bin 10021 -> 10017 bytes test/docx/golden/custom_style_preserve.docx | Bin 10650 -> 10646 bytes test/docx/golden/custom_style_reference.docx | Bin 12403 -> 12400 bytes test/docx/golden/definition_list.docx | Bin 9920 -> 9916 bytes .../docx/golden/document-properties-short-desc.docx | Bin 9925 -> 9922 bytes test/docx/golden/document-properties.docx | Bin 10404 -> 10399 bytes test/docx/golden/headers.docx | Bin 10059 -> 10055 bytes test/docx/golden/image.docx | Bin 26736 -> 26733 bytes test/docx/golden/inline_code.docx | Bin 9859 -> 9855 bytes test/docx/golden/inline_formatting.docx | Bin 10038 -> 10035 bytes test/docx/golden/inline_images.docx | Bin 26793 -> 26789 bytes test/docx/golden/link_in_notes.docx | Bin 10081 -> 10077 bytes test/docx/golden/links.docx | Bin 10251 -> 10248 bytes test/docx/golden/lists.docx | Bin 10332 -> 10314 bytes test/docx/golden/lists_continuing.docx | Bin 10123 -> 10110 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10210 -> 10192 bytes test/docx/golden/lists_restarting.docx | Bin 10122 -> 10108 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10216 -> 10212 bytes test/docx/golden/notes.docx | Bin 10028 -> 10024 bytes test/docx/golden/raw-blocks.docx | Bin 9960 -> 9956 bytes test/docx/golden/raw-bookmarks.docx | Bin 10094 -> 10090 bytes test/docx/golden/table_one_row.docx | Bin 9906 -> 9903 bytes test/docx/golden/table_with_list_cell.docx | Bin 10225 -> 10212 bytes test/docx/golden/tables.docx | Bin 10241 -> 10238 bytes test/docx/golden/track_changes_deletion.docx | Bin 9903 -> 9899 bytes test/docx/golden/track_changes_insertion.docx | Bin 9886 -> 9882 bytes test/docx/golden/track_changes_move.docx | Bin 9920 -> 9916 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 10032 -> 10028 bytes test/docx/golden/unicode.docx | Bin 9845 -> 9841 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9892 -> 9888 bytes 34 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index c47bfb2ea..20bcd0324 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -36,7 +36,6 @@ import qualified Data.Text.Lazy as TL import Data.Time.Clock.POSIX import Data.Digest.Pure.SHA (sha1, showDigest) import Skylighting -import System.Random (randomRs, mkStdGen) import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P @@ -695,8 +694,7 @@ baseListId = 1000 mkNumbering :: [ListMarker] -> [Element] mkNumbering lists = elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] - where elts = zipWith mkAbstractNum (ordNub lists) $ - randomRs (0x10000000, 0xFFFFFFFF) $ mkStdGen 1848 + where elts = map mkAbstractNum (ordNub lists) maxListLevel :: Int maxListLevel = 8 @@ -713,10 +711,10 @@ mkNum marker numid = $ mknode "w:startOverride" [("w:val",tshow start)] ()) [0..maxListLevel] -mkAbstractNum :: ListMarker -> Integer -> Element -mkAbstractNum marker nsid = +mkAbstractNum :: ListMarker -> Element +mkAbstractNum marker = mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)] - $ mknode "w:nsid" [("w:val", T.pack $ printf "%8x" nsid)] () + $ mknode "w:nsid" [("w:val", "A" <> listMarkerToId marker)] () : mknode "w:multiLevelType" [("w:val","multilevel")] () : map (mkLvl marker) [0..maxListLevel] diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index ed7d1165c..af1b33ca2 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index 07ae75676..f748f1f01 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index e5f034378..ac9d56680 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index 174942135..f27727edd 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index b5c31a851..1da499d6a 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index c42ca1b05..4d2fe245d 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index 1cb4c1fd7..f386fcea3 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index 7122456ea..debe9a3f6 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index 616ba0f81..cd17400bf 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index c30dcdee9..d3af8a3dd 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 8a704b41e..1c4e738c0 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index b1906c8c4..35f43f19f 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index 8adf1cf75..8de3f70f6 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 584117503..e76558be9 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index 8859fe55c..88bae8142 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index b80f3b3ba..455adcfc7 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 35beed68a..081d9ddba 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index 2c29fd674..fc9213fc5 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 10a948886..b636fd3f8 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index 5b90e74a0..252623215 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index cc81b46d1..a8c3f5478 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index 1394dc442..43e650ebd 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index 0d1688694..fe4f7845b 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index be1caef2d..45e90608f 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index 1178f7c6e..6eaea2ac2 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 84be2720f..45a97ccaa 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 140366d8b..115a16a48 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index 9cc7a075f..247725aaa 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index f8b8dcfde..3863afef2 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index 1c3baf0bf..5c848b63a 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index 28686970d..e0c843713 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index 7051cefbd..78a773bdd 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index 9df631640..c66a45b74 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ -- cgit v1.2.3 From 27d7652329a5576f928cc5209324e618560b610f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 17 Mar 2021 22:49:50 -0700 Subject: Use random 1.2.0 in stack.yaml. --- stack.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stack.yaml b/stack.yaml index 0f21bbb6c..e37ccd644 100644 --- a/stack.yaml +++ b/stack.yaml @@ -2,6 +2,8 @@ flags: pandoc: trypandoc: false embed_data_files: true + QuickCheck: + old-random: false packages: - '.' extra-deps: @@ -13,6 +15,7 @@ extra-deps: - doclayout-0.3.0.2 - citeproc-0.3.0.9 - texmath-0.12.2 +- random-1.2.0 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From 89a89a8cf33e1558e8a477f98a2c73bc224ccf60 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 09:14:12 -0700 Subject: make_artifacts.sh: try using -j for cabal rather than -j4 ghc-options. --- Makefile | 4 ++-- linux/make_artifacts.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 5e5bc7c51..ced5baf9f 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ quick: quick-cabal: cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests - cabal new-build . --disable-optimization + cabal new-build -j4 . --disable-optimization cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS) full-cabal: @@ -35,7 +35,7 @@ full-cabal: cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS) full: - stack install --flag 'pandoc:embed_data_files' --flag 'pandoc:trypandoc' --bench --no-run-benchmarks --test --test-arguments='-j4 --hide-successes' --ghc-options '-Wall -Werror -fno-warn-unused-do-bind -O0 -j4 $(GHCOPTS)' + stack install --flag 'pandoc:embed_data_files' --flag 'pandoc:trypandoc' --bench --no-run-benchmarks --test --test-arguments='-j4 --hide-successes' --ghc-options '-Wall -Werror -fno-warn-unused-do-bind -O0 $(GHCOPTS)' ghci: stack ghci --flag 'pandoc:embed_data_files' diff --git a/linux/make_artifacts.sh b/linux/make_artifacts.sh index cb7bf4c8c..b5625be1f 100644 --- a/linux/make_artifacts.sh +++ b/linux/make_artifacts.sh @@ -26,9 +26,9 @@ ghc --version cabal v2-update cabal v2-clean -cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc -cabal v2-build -cabal v2-test -j4 +cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '+RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc +cabal v2-build -j +cabal v2-test -j for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f /artifacts/; done # make deb -- cgit v1.2.3 From 0012750c421b74313ad950f95730d6ee21c08408 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 10:22:49 -0700 Subject: build-arm.sh : remove strict checking on first ssh access --- tools/build-arm.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/build-arm.sh b/tools/build-arm.sh index dc13e1435..0a14c7302 100644 --- a/tools/build-arm.sh +++ b/tools/build-arm.sh @@ -49,6 +49,8 @@ done # At this point you can connect via SSH, or run this script: # $ ssh -i ~/.ssh/debian-arm-us-east-2.pem admin@$IPADDR +ssh -o "StrictHostKeyChecking=no" -i "~/.ssh/$KEY_NAME.pem" admin@$IPADDR uname -a + SSH="ssh -i ~/.ssh/$KEY_NAME.pem admin@$IPADDR" echo "Provisioning..." -- cgit v1.2.3 From c7230182a058609263e351ae576705d7a51b0c9e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 11:02:46 -0700 Subject: pandoc.cabal: bake in -A8m to rtsopts. This reserves a larger allocation area and reduces GC, speeding up execution. --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 7e9d81951..7e617c8ee 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -426,7 +426,7 @@ common common-options common common-executable import: common-options build-depends: pandoc - ghc-options: -rtsopts -with-rtsopts=-K16m -threaded + ghc-options: -rtsopts -with-rtsopts=-K16m -with-rtsopts=-A8m -threaded library -- cgit v1.2.3 From 4c51fa1d55c31784311159447454b64e77283242 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 13:07:54 -0700 Subject: Change -A256m to -A8m in GHCOPTS --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ced5baf9f..6ae6ab491 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ BASELINECMD= else BASELINECMD=--baseline $(BASELINE) endif -GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A256m -RTS +GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A8m -RTS WEBSITE=../../web/pandoc.org REVISION?=1 # For gauge: -- cgit v1.2.3 From 87eb8dfda4914dd8738c9ae0d9b591e65d8bf7e2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 14:59:50 -0700 Subject: Revert the -j related changes in make_artifacts.sh. This led to very slow build times. --- linux/make_artifacts.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux/make_artifacts.sh b/linux/make_artifacts.sh index b5625be1f..4395b3c6b 100644 --- a/linux/make_artifacts.sh +++ b/linux/make_artifacts.sh @@ -26,9 +26,9 @@ ghc --version cabal v2-update cabal v2-clean -cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '+RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc -cabal v2-build -j -cabal v2-test -j +cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc +cabal v2-build -j4 +cabal v2-test -j4 for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f /artifacts/; done # make deb -- cgit v1.2.3 From 8235daf780313be09c4bbce578e46b40bcc21215 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 15:46:20 -0700 Subject: Use -A8m for default rtsopts for benchmark --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 7e617c8ee..d0347e22f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -833,4 +833,4 @@ benchmark benchmark-pandoc time, deepseq -- we increase heap size to avoid benchmarking garbage collection: - ghc-options: -rtsopts -with-rtsopts=-A256m -threaded + ghc-options: -rtsopts -with-rtsopts=-A8m -threaded -- cgit v1.2.3 From 2e00ed3fde82aa1bcf58fc883a20093ce3935bb0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 15:50:13 -0700 Subject: Bump to 2.12.1 and update changelog. --- changelog.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pandoc.cabal | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f06283ea2..4570d85ac 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,98 @@ # Revision history for pandoc +## pandoc 2.12.1 (PROVISIONAL) + + * Handle `nocite` better with `--biblatex` and `--natbib` (#4585). + Previously the nocite metadata field was ignored with + these formats. Now it populates a `nocite-ids` template + variable and causes a `\nocite` command to be issued. + + * Text.Pandoc.Citeproc: apply `fixLinks` correctly (#7130). This is code + that incorporates a prefix like `https://doi.org/` into a following link + when appropriate. + + * Markdown reader: Fix regression with `tex_math_backslash` in + Markdown reader (#7155). + + * MediaWiki reader: Allow block-level content in notes (ref) (#7145). + + * Jira reader (Albert Krewinkel): + + + Fixed parsing of autolinks (i.e., of bare URLs in the text). + Previously an autolink would take up the rest of a line, as spaces + were allowed characters in these items. + + Emoji character sequences no longer cause parsing failures. This was + due to missing backtracking when emoji parsing fails. + + Mark divs created from panels with class "panel". + (tarleb/jira-wiki-markup#2). + + * RST reader: fix logic for ending comments (#7134). Previously comments + sometimes got extended too far. + + * Docx writer: + + + Make `nsid` in `abstractNum` deterministic. Previously we assigned + a random number, but we don't need random values, so now we just + assign a value based on the list marker. + + Use integral values for `w:tblW` (#7141). + + * Jira writer (Albert Krewinkel): + + + Block quotes are only rendered as `bq.` if they do not contain a + linebreak. + + Jira writer: improve div/panel handling. Include div attributes in + panels, always render divs with class `panel` as panels, and + avoid nesting of panels. + + * HTML writer: Add warnings on duplicate attribute values. + This prevents emitting invalid HTML. Ultimately it would be good to + prevent this in the types themselves, but this is better for now. + + * Org writer: Prevent unintended creation of ordered list items (#7132, + Albert Krewinkel). Adjust line wrapping if default wrapping would cause + a line to be read as an ordered list item. + + * JATS templates: support 'equal-contrib' attrib for authors (Albert + Krewinkel). Authors who contributed equally to a paper may be marked + with `equal-contrib`. + + * reveal.js template: replace JS comment with HTML (#7154, Florian Kohrt). + + * Text.Pandoc.Shared: Simplify `compactDL`. + + * Text.Pandoc.Logging: Add `DuplicateAttribute` constructor to `LogMessage`. + [API change] + + * Use `-j4` for linux release build. This speeds up the build + dramatically on arm. + + * cabal.project: remove `-j`. + + * Require latest texmath, skylighting, citeproc, jira-wiki-markup. + + * Use doclayout 0.3.0.2. This significantly reduces the time and memory + needed to compile pandoc. + + * Use `foldl'` instead of `foldl` everywhere. + + * Update bounds for random (#7156, Alexey Kuleshevich). + + * Benchmark improvements. + + + Build `+RTS -A8m -RTS` into default ghc-options for benchmark. + This is necessary to get accurate benchmark results; otherwise + we are largely measuring garbage collecting, some not related + to the current benchmark. + + Allow specifying BASELINE file in 'make bench' for comparison + (otherwise the latest benchmark is chosen by default). + + Force `readFile` in benchmarks early (Bodigrim). + + * CONTRIBUTING: suggest using a `cabal.project.local` file (#7153, + Albert Krewinkel). + + * Add ghcid-test to Makefile. This loads the test suite in ghcid. + + ## pandoc 2.12 (2021-03-08) * `--resource-path` now accumulates if specified multiple diff --git a/pandoc.cabal b/pandoc.cabal index d0347e22f..5e988c43e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.12 +version: 2.12.1 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md -- cgit v1.2.3 From a71732fd013b1acfd16988e020466a63e54fcbb2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 17:00:44 -0700 Subject: Minor revisions to provisional changelog. --- changelog.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 4570d85ac..a9af2acfb 100644 --- a/changelog.md +++ b/changelog.md @@ -3,16 +3,15 @@ ## pandoc 2.12.1 (PROVISIONAL) * Handle `nocite` better with `--biblatex` and `--natbib` (#4585). - Previously the nocite metadata field was ignored with - these formats. Now it populates a `nocite-ids` template - variable and causes a `\nocite` command to be issued. + Previously the nocite metadata field was ignored with these formats. + Now it populates a `nocite-ids` template variable and causes a + `\nocite` command to be issued. * Text.Pandoc.Citeproc: apply `fixLinks` correctly (#7130). This is code that incorporates a prefix like `https://doi.org/` into a following link when appropriate. - * Markdown reader: Fix regression with `tex_math_backslash` in - Markdown reader (#7155). + * Markdown reader: Fix regression with `tex_math_backslash` (#7155). * MediaWiki reader: Allow block-level content in notes (ref) (#7145). @@ -24,7 +23,6 @@ + Emoji character sequences no longer cause parsing failures. This was due to missing backtracking when emoji parsing fails. + Mark divs created from panels with class "panel". - (tarleb/jira-wiki-markup#2). * RST reader: fix logic for ending comments (#7134). Previously comments sometimes got extended too far. -- cgit v1.2.3 From 2d10f46de5e348251a649f1854453015f8918954 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 17:18:31 -0700 Subject: Don't bake in extra stack size to the executables. I don't think this is necessary; stack overflows generally indicate a code problem and should be fixed (and have been when reported). --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index 5e988c43e..5936d113f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -426,7 +426,7 @@ common common-options common common-executable import: common-options build-depends: pandoc - ghc-options: -rtsopts -with-rtsopts=-K16m -with-rtsopts=-A8m -threaded + ghc-options: -rtsopts -with-rtsopts=-A8m -threaded library -- cgit v1.2.3 From fd76e605cde2639d6bb1b6891dd4b097a6eded01 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 21:02:05 -0700 Subject: T.P.Readers.Odt.StyleReader: rewrite foldr1 use as foldr. This avoids a partial function. --- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index b722aa07d..ca791ad1e 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -563,12 +563,13 @@ readListLevelStyle levelType = readAttr NsText "level" -- chooseMostSpecificListLevelStyle :: S.Set ListLevelStyle -> Maybe ListLevelStyle -chooseMostSpecificListLevelStyle ls | ls == mempty = Nothing - | otherwise = Just ( F.foldr1 select ls ) +chooseMostSpecificListLevelStyle ls = F.foldr select Nothing ls where - select ( ListLevelStyle t1 p1 s1 f1 b1 ) - ( ListLevelStyle t2 p2 s2 f2 _ ) - = ListLevelStyle (select' t1 t2) (p1 <|> p2) (s1 <|> s2) (selectLinf f1 f2) b1 + select l Nothing = Just l + select ( ListLevelStyle t1 p1 s1 f1 b1 ) + ( Just ( ListLevelStyle t2 p2 s2 f2 _ )) + = Just $ ListLevelStyle (select' t1 t2) (p1 <|> p2) (s1 <|> s2) + (selectLinf f1 f2) b1 select' LltNumbered _ = LltNumbered select' _ LltNumbered = LltNumbered select' _ _ = LltBullet -- cgit v1.2.3 From 67e173bda116ba9793dd625f782a12ab8e35839b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 21:10:22 -0700 Subject: Remove another foldr1 partial function use. --- src/Text/Pandoc/Readers/Txt2Tags.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 08083b177..0ce8e286f 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -261,7 +261,7 @@ table = try $ do rows <- many1 (many commentLine *> tableRow) let columns = transpose rows let ncolumns = length columns - let aligns = map (foldr1 findAlign . map fst) columns + let aligns = map (fromMaybe AlignDefault . foldr findAlign Nothing) columns let rows' = map (map snd) rows let size = maximum (map length rows') let rowsPadded = map (pad size) rows' @@ -278,10 +278,11 @@ pad :: (Monoid a) => Int -> [a] -> [a] pad n xs = xs ++ replicate (n - length xs) mempty -findAlign :: Alignment -> Alignment -> Alignment -findAlign x y - | x == y = x - | otherwise = AlignDefault +findAlign :: (Alignment, a) -> Maybe Alignment -> Maybe Alignment +findAlign (x,_) (Just y) + | x == y = Just x + | otherwise = Just AlignDefault +findAlign (x,_) Nothing = Just x headerRow :: T2T [(Alignment, Blocks)] headerRow = genericRow (string "||") -- cgit v1.2.3 From 1da62083156cf493771274164be67a2bead196dc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 21:30:59 -0700 Subject: Rewrite a foldl1 as a foldl'. --- src/Text/Pandoc/Readers/HTML.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index b73c138ab..c3e68afd8 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -32,6 +32,7 @@ import Data.Char (isAlphaNum, isLetter) import Data.Default (Default (..), def) import Data.Foldable (for_) import Data.List.Split (splitWhen) +import Data.List (foldl') import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid (First (..)) @@ -360,7 +361,10 @@ pDefListItem = try $ do terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline) defs <- many1 (try $ skipMany nonItem >> pInTags "dd" block) skipMany nonItem - let term = foldl1 (\x y -> x <> B.linebreak <> y) $ map trimInlines terms + let term = foldl' (\x y -> if null x + then trimInlines y + else x <> B.linebreak <> trimInlines y) + mempty terms return (term, map (fixPlains True) defs) fixPlains :: Bool -> Blocks -> Blocks -- cgit v1.2.3 From f0e4b9cc3cde01d64e25d331c5b4f3d62d2129b5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 21:37:56 -0700 Subject: Require safe >= 0.3.18 and remove cpp. --- pandoc.cabal | 2 +- src/Text/Pandoc/Readers/LaTeX.hs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 5936d113f..e46518e96 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -477,7 +477,7 @@ library parsec >= 3.1 && < 3.2, process >= 1.2.3 && < 1.7, random >= 1 && < 1.3, - safe >= 0.3 && < 0.4, + safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, skylighting >= 0.10.4.1 && < 0.11, skylighting-core >= 0.10.4.1 && < 0.11, diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index ceac261d2..21a563bed 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -97,11 +96,7 @@ parseLaTeX = do let doc' = doc bs let headerLevel (Header n _ _) = [n] headerLevel _ = [] -#if MIN_VERSION_safe(0,3,18) let bottomLevel = minimumBound 1 $ query headerLevel doc' -#else - let bottomLevel = minimumDef 1 $ query headerLevel doc' -#endif let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils adjustHeaders _ x = x let (Pandoc _ bs') = -- cgit v1.2.3 From 3428248debb14066e7fb1ef216927d3d62e3a43a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 18 Mar 2021 23:01:12 -0700 Subject: Use minimumDef instead of minimum (partial function). --- src/Text/Pandoc/Readers/LaTeX.hs | 2 +- src/Text/Pandoc/Readers/Org/Blocks.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 4 +++- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 7 ++----- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 21a563bed..32f7fc5af 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -96,7 +96,7 @@ parseLaTeX = do let doc' = doc bs let headerLevel (Header n _ _) = [n] headerLevel _ = [] - let bottomLevel = minimumBound 1 $ query headerLevel doc' + let bottomLevel = minimumDef 1 $ query headerLevel doc' let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils adjustHeaders _ x = x let (Pandoc _ bs') = diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index d1aff701e..c6f27118b 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -39,6 +39,7 @@ import Data.Functor (($>)) import Data.List (foldl', intersperse) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) +import Safe (minimumDef) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B @@ -542,8 +543,7 @@ include = try $ do in case (minlvl >>= safeRead :: Maybe Int) of Nothing -> blks Just lvl -> let levels = Walk.query headerLevel blks - -- CAVE: partial function in else - curMin = if null levels then 0 else minimum levels + curMin = minimumDef 0 levels in Walk.walk (shiftHeader (curMin - lvl)) blks headerLevel :: Block -> [Int] diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 99238c7f0..50ffd36ec 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -53,6 +53,7 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (crFilter, trim, tshow) +import Safe (minimumDef) -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m @@ -376,7 +377,8 @@ table = try $ do (toprow, rest) _ -> (mempty, rawrows) let nbOfCols = maximum $ map length (headers:rows) - let aligns = map minimum $ transpose $ map (map (snd . fst)) (headers:rows) + let aligns = map (minimumDef AlignDefault) $ + transpose $ map (map (snd . fst)) (headers:rows) let toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] return $ B.table (B.simpleCaption $ B.plain caption) diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 5caeb0753..b26a7ff3e 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -50,6 +50,7 @@ import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation import Text.Pandoc.Shared (tshow) import Skylighting (fromColor) +import Safe (minimumDef) -- |The 'EMU' type is used to specify sizes in English Metric Units. type EMU = Integer @@ -1427,11 +1428,7 @@ presentationToRels pres@(Presentation _ slides) = do -- all relWithoutSlide rels (unless they're 1) -- 3. If we have a notesmaster slide, we make space for that as well. - let minRelNotOne = case filter (1<) $ map relId relsWeKeep of - [] -> 0 -- doesn't matter in this case, since - -- there will be nothing to map the - -- function over - l -> minimum l + let minRelNotOne = minimumDef 0 $ filter (1 <) $ map relId relsWeKeep modifyRelNum :: Int -> Int modifyRelNum 1 = 1 -- cgit v1.2.3 From a31731b8e2825c5bc8d7fcc9a61ce92b9d28d040 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 10:11:08 -0700 Subject: Docx reader: Don't reimplement NonEmpty. --- src/Text/Pandoc/Readers/Docx.hs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 00de6a0cd..22dd54193 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -85,6 +85,7 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Error import Text.Pandoc.Logging +import Data.List.NonEmpty (nonEmpty) readDocx :: PandocMonad m => ReaderOptions @@ -648,11 +649,6 @@ bodyPartToBlocks (Tbl cap _ look parts@(r:rs)) = do cells <- mapM rowToBlocksList rows let width = maybe 0 maximum $ nonEmpty $ map rowLength parts - -- Data.List.NonEmpty is not available with ghc 7.10 so we roll out - -- our own, see - -- https://github.com/jgm/pandoc/pull/4361#issuecomment-365416155 - nonEmpty [] = Nothing - nonEmpty l = Just l rowLength :: Docx.Row -> Int rowLength (Docx.Row c) = length c -- cgit v1.2.3 From 8d5116381b20442bb3fa58dac1ef7d44db618823 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 10:30:32 -0700 Subject: Use NonEmpty instead of minimumDef. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Blocks.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 4 ++-- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 32f7fc5af..851756065 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -68,7 +68,7 @@ import Text.Pandoc.Readers.LaTeX.Inline (acronymCommands, refCommands, listingsLanguage) import Text.Pandoc.Shared import Text.Pandoc.Walk -import Safe +import Data.List.NonEmpty (nonEmpty) -- for debugging: -- import Text.Pandoc.Extensions (getDefaultExtensions) @@ -96,7 +96,7 @@ parseLaTeX = do let doc' = doc bs let headerLevel (Header n _ _) = [n] headerLevel _ = [] - let bottomLevel = minimumDef 1 $ query headerLevel doc' + let bottomLevel = maybe 1 minimum $ nonEmpty $ query headerLevel doc' let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils adjustHeaders _ x = x let (Pandoc _ bs') = diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index c6f27118b..883434cdc 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -39,7 +39,7 @@ import Data.Functor (($>)) import Data.List (foldl', intersperse) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) -import Safe (minimumDef) +import Data.List.NonEmpty (nonEmpty) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B @@ -543,7 +543,7 @@ include = try $ do in case (minlvl >>= safeRead :: Maybe Int) of Nothing -> blks Just lvl -> let levels = Walk.query headerLevel blks - curMin = minimumDef 0 levels + curMin = maybe 0 minimum $ nonEmpty levels in Walk.walk (shiftHeader (curMin - lvl)) blks headerLevel :: Block -> [Int] diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 50ffd36ec..e26b902f1 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -53,7 +53,7 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (crFilter, trim, tshow) -import Safe (minimumDef) +import Data.List.NonEmpty (nonEmpty) -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m @@ -377,7 +377,7 @@ table = try $ do (toprow, rest) _ -> (mempty, rawrows) let nbOfCols = maximum $ map length (headers:rows) - let aligns = map (minimumDef AlignDefault) $ + let aligns = map (maybe AlignDefault minimum . nonEmpty) $ transpose $ map (map (snd . fst)) (headers:rows) let toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index b26a7ff3e..4dbf32c4e 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -50,7 +50,7 @@ import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation import Text.Pandoc.Shared (tshow) import Skylighting (fromColor) -import Safe (minimumDef) +import Data.List.NonEmpty (nonEmpty) -- |The 'EMU' type is used to specify sizes in English Metric Units. type EMU = Integer @@ -1428,7 +1428,8 @@ presentationToRels pres@(Presentation _ slides) = do -- all relWithoutSlide rels (unless they're 1) -- 3. If we have a notesmaster slide, we make space for that as well. - let minRelNotOne = minimumDef 0 $ filter (1 <) $ map relId relsWeKeep + let minRelNotOne = maybe 0 minimum $ nonEmpty + $ filter (1 <) $ map relId relsWeKeep modifyRelNum :: Int -> Int modifyRelNum 1 = 1 -- cgit v1.2.3 From 4002c35a9184ecc1c9a6553e9ee28e283cb1fd0a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 11:55:59 -0700 Subject: Protect partial uses of maximum with NonEmpty. --- src/Text/Pandoc/Readers/DocBook.hs | 6 ++-- src/Text/Pandoc/Readers/HTML/Table.hs | 3 +- src/Text/Pandoc/Readers/Haddock.hs | 3 +- src/Text/Pandoc/Readers/Markdown.hs | 3 +- src/Text/Pandoc/Readers/Textile.hs | 4 +-- src/Text/Pandoc/Readers/Txt2Tags.hs | 22 +++++++----- src/Text/Pandoc/Writers/AsciiDoc.hs | 3 +- src/Text/Pandoc/Writers/ConTeXt.hs | 6 ++-- src/Text/Pandoc/Writers/DokuWiki.hs | 4 ++- src/Text/Pandoc/Writers/Man.hs | 4 +-- src/Text/Pandoc/Writers/Markdown.hs | 37 ++++++++++--------- src/Text/Pandoc/Writers/Markdown/Inline.hs | 9 ++--- src/Text/Pandoc/Writers/Ms.hs | 4 +-- src/Text/Pandoc/Writers/Muse.hs | 9 +++-- src/Text/Pandoc/Writers/Org.hs | 5 +-- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 42 +++++++++++----------- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 5 ++- src/Text/Pandoc/Writers/RST.hs | 6 ++-- src/Text/Pandoc/Writers/Shared.hs | 6 ++-- src/Text/Pandoc/Writers/Texinfo.hs | 9 +++-- src/Text/Pandoc/Writers/ZimWiki.hs | 4 ++- 21 files changed, 108 insertions(+), 86 deletions(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index d38b07864..6f5bb0ad4 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -18,6 +18,7 @@ import Data.Either (rights) import Data.Foldable (asum) import Data.Generics import Data.List (intersperse,elemIndex) +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe,mapMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -949,9 +950,8 @@ parseBlock (Elem e) = (x >= '0' && x <= '9') || x == '.') w if n > 0 then Just n else Nothing - let numrows = case bodyrows of - [] -> 0 - xs -> maximum $ map length xs + let numrows = maybe 0 maximum $ nonEmpty + $ map length bodyrows let aligns = case colspecs of [] -> replicate numrows AlignDefault cs -> map toAlignment cs diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index 6179ea8e7..ad0b51253 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -17,6 +17,7 @@ module Text.Pandoc.Readers.HTML.Table (pTable) where import Control.Applicative ((<|>)) import Data.Maybe (fromMaybe) +import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks) @@ -216,7 +217,7 @@ normalize widths head' bodies foot = do let rows = headRows head' <> concatMap bodyRows bodies <> footRows foot let cellWidth (Cell _ _ _ (ColSpan cs) _) = cs let rowLength = foldr (\cell acc -> cellWidth cell + acc) 0 . rowCells - let ncols = maximum (map rowLength rows) + let ncols = maybe 0 maximum $ nonEmpty $ map rowLength rows let tblType = tableType (map rowCells rows) -- fail on empty table if null rows diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 25d69f040..48454e353 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -17,6 +17,7 @@ module Text.Pandoc.Readers.Haddock import Control.Monad.Except (throwError) import Data.List (intersperse) +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (Text, unpack) import qualified Data.Text as T @@ -92,7 +93,7 @@ docHToBlocks d' = then ([], map toCells bodyRows) else (toCells (head headerRows), map toCells (tail headerRows ++ bodyRows)) - colspecs = replicate (maximum (map length body)) + colspecs = replicate (maybe 0 maximum (nonEmpty (map length body))) (AlignDefault, ColWidthDefault) in B.table B.emptyCaption colspecs diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index c836a896b..a86286b3a 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -22,6 +22,7 @@ import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isAlphaNum, isPunctuation, isSpace) import Data.List (transpose, elemIndex, sortOn, foldl') +import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.Map as M import Data.Maybe import qualified Data.Set as Set @@ -1364,7 +1365,7 @@ pipeTable = try $ do lines' <- many pipeTableRow let lines'' = map (take (length aligns) <$>) lines' let maxlength = maximum $ - map (\x -> T.length . stringify $ runF x def) (heads' : lines'') + fmap (\x -> T.length . stringify $ runF x def) (heads' :| lines'') numColumns <- getOption readerColumns let widths = if maxlength > numColumns then map (\len -> diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index e26b902f1..8d7900de4 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -39,6 +39,7 @@ import Control.Monad (guard, liftM) import Control.Monad.Except (throwError) import Data.Char (digitToInt, isUpper) import Data.List (intersperse, transpose, foldl') +import Data.List.NonEmpty (NonEmpty(..), nonEmpty) import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup (Tag (..), fromAttrib) @@ -53,7 +54,6 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (crFilter, trim, tshow) -import Data.List.NonEmpty (nonEmpty) -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m @@ -376,7 +376,7 @@ table = try $ do (toprow:rest) | any (fst . fst) toprow -> (toprow, rest) _ -> (mempty, rawrows) - let nbOfCols = maximum $ map length (headers:rows) + let nbOfCols = maximum $ fmap length (headers :| rows) let aligns = map (maybe AlignDefault minimum . nonEmpty) $ transpose $ map (map (snd . fst)) (headers:rows) let toRow = Row nullAttr . map B.simpleCell diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 0ce8e286f..f27a3fc2c 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE LambdaCase #-} {- | Module : Text.Pandoc.Readers.Txt2Tags Copyright : Copyright (C) 2014 Matthew Pickering @@ -19,6 +20,7 @@ import Control.Monad.Except (catchError, throwError) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default import Data.List (intercalate, transpose) +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -53,14 +55,16 @@ getT2TMeta = do inps <- P.getInputFiles outp <- fromMaybe "" <$> P.getOutputFile curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime - let getModTime = fmap (formatTime defaultTimeLocale "%T") . - P.getModificationTime - curMtime <- case inps of - [] -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime - _ -> catchError - (maximum <$> mapM getModTime inps) - (const (return "")) - return $ T2TMeta (T.pack curDate) (T.pack curMtime) (intercalate ", " inps) outp + curMtime <- catchError + ((nonEmpty <$> mapM P.getModificationTime inps) >>= + \case + Nothing -> + formatTime defaultTimeLocale "%T" <$> P.getZonedTime + Just ts -> return $ + formatTime defaultTimeLocale "%T" $ maximum ts) + (const (return "")) + return $ T2TMeta (T.pack curDate) (T.pack curMtime) + (intercalate ", " inps) outp -- | Read Txt2Tags from an input string returning a Pandoc document readTxt2Tags :: PandocMonad m @@ -263,7 +267,7 @@ table = try $ do let ncolumns = length columns let aligns = map (fromMaybe AlignDefault . foldr findAlign Nothing) columns let rows' = map (map snd) rows - let size = maximum (map length rows') + let size = maybe 0 maximum $ nonEmpty $ map length rows' let rowsPadded = map (pad size) rows' let headerPadded = if null tableHeader then mempty else pad size tableHeader let toRow = Row nullAttr . map B.simpleCell diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index b4ef7c8b9..69e608ef9 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -22,6 +22,7 @@ module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc, writeAsciiDoctor) where import Control.Monad.State.Strict import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse) +import Data.List.NonEmpty (NonEmpty(..)) import Data.Maybe (fromMaybe, isJust) import qualified Data.Set as Set import qualified Data.Text as T @@ -274,7 +275,7 @@ blockToAsciiDoc opts block@(Table _ blkCapt specs thead tbody tfoot) = do let colwidth = if writerWrapText opts == WrapAuto then writerColumns opts else 100000 - let maxwidth = maximum $ map offset (head':rows') + let maxwidth = maximum $ fmap offset (head' :| rows') let body = if maxwidth > colwidth then vsep rows' else vcat rows' let border = separator <> text "===" return $ diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 4d44842e2..1c56388ed 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -16,6 +16,7 @@ module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Control.Monad.State.Strict import Data.Char (ord, isDigit) import Data.List (intersperse) +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (mapMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -228,8 +229,9 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do Period -> "stopper=." OneParen -> "stopper=)" TwoParens -> "left=(,stopper=)" - let width = maximum $ map T.length $ take (length contents) - (orderedListMarkers (start, style', delim)) + let width = maybe 0 maximum $ nonEmpty $ map T.length $ + take (length contents) + (orderedListMarkers (start, style', delim)) let width' = (toEnum width + 1) / 2 let width'' = if width' > (1.5 :: Double) then "width=" <> tshow width' <> "em" diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 7df47c912..602c70ebe 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -27,6 +27,7 @@ import Control.Monad.Reader (ReaderT, asks, local, runReaderT) import Control.Monad.State.Strict (StateT, evalStateT) import Data.Default (Default (..)) import Data.List (transpose) +import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) @@ -172,7 +173,8 @@ blockToDokuWiki opts (Table _ blkCapt specs thead tbody tfoot) = do then return [] else zipWithM (tableItemToDokuWiki opts) aligns headers rows' <- mapM (zipWithM (tableItemToDokuWiki opts) aligns) rows - let widths = map (maximum . map T.length) $ transpose (headers':rows') + let widths = map (maybe 0 maximum . nonEmpty . map T.length) + $ transpose (headers':rows') let padTo (width, al) s = case width - T.length s of x | x > 0 -> diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index edb70f53e..87b2d8d21 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -16,6 +16,7 @@ Conversion of 'Pandoc' documents to roff man page format. module Text.Pandoc.Writers.Man ( writeMan ) where import Control.Monad.State.Strict import Data.List (intersperse) +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -175,8 +176,7 @@ blockToMan opts (BulletList items) = do return (vcat contents) blockToMan opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs - let indent = 1 + - maximum (map T.length markers) + let indent = 1 + maybe 0 maximum (nonEmpty (map T.length markers)) contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $ zip markers items return (vcat contents) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 533bcc071..4d9f3d5b0 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -24,6 +24,7 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Data.Default import Data.List (intersperse, sortOn, transpose) +import Data.List.NonEmpty (nonEmpty, NonEmpty(..)) import qualified Data.Map as M import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Set as Set @@ -492,19 +493,20 @@ blockToMarkdown' opts (CodeBlock attribs str) = do | isEnabled Ext_fenced_code_blocks opts -> tildes <> attrs <> cr <> literal str <> cr <> tildes <> blankline _ -> nest (writerTabStop opts) (literal str) <> blankline - where endline c = literal $ case [T.length ln - | ln <- map trim (T.lines str) - , T.pack [c,c,c] `T.isPrefixOf` ln - , T.all (== c) ln] of - [] -> T.replicate 3 $ T.singleton c - xs -> T.replicate (maximum xs + 1) $ T.singleton c - backticks = endline '`' - tildes = endline '~' - attrs = if isEnabled Ext_fenced_code_attributes opts - then nowrap $ " " <> attrsToMarkdown attribs - else case attribs of - (_,cls:_,_) -> " " <> literal cls - _ -> empty + where + endlineLen c = maybe 3 ((+1) . maximum) $ nonEmpty $ + [T.length ln + | ln <- map trim (T.lines str) + , T.pack [c,c,c] `T.isPrefixOf` ln + , T.all (== c) ln] + endline c = literal $ T.replicate (endlineLen c) $ T.singleton c + backticks = endline '`' + tildes = endline '~' + attrs = if isEnabled Ext_fenced_code_attributes opts + then nowrap $ " " <> attrsToMarkdown attribs + else case attribs of + (_,cls:_,_) -> " " <> literal cls + _ -> empty blockToMarkdown' opts (BlockQuote blocks) = do variant <- asks envVariant -- if we're writing literate haskell, put a space before the bird tracks @@ -517,7 +519,7 @@ blockToMarkdown' opts (BlockQuote blocks) = do return $ prefixed leader contents <> blankline blockToMarkdown' opts t@(Table _ blkCapt specs thead tbody tfoot) = do let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot - let numcols = maximum (length aligns : length widths : + let numcols = maximum (length aligns :| length widths : map length (headers:rows)) caption' <- inlineListToMarkdown opts caption let caption'' @@ -619,7 +621,8 @@ pipeTable headless aligns rawHeaders rawRows = do blockFor AlignCenter x y = cblock (x + 2) (sp <> y <> sp) <> lblock 0 empty blockFor AlignRight x y = rblock (x + 2) (y <> sp) <> lblock 0 empty blockFor _ x y = lblock (x + 2) (sp <> y) <> lblock 0 empty - let widths = map (max 3 . maximum . map offset) $ transpose (rawHeaders : rawRows) + let widths = map (max 3 . maybe 3 maximum . nonEmpty . map offset) $ + transpose (rawHeaders : rawRows) let torow cs = nowrap $ literal "|" <> hcat (intersperse (literal "|") $ zipWith3 blockFor aligns widths (map chomp cs)) @@ -653,11 +656,11 @@ pandocTable opts multiline headless aligns widths rawHeaders rawRows = do -- Number of characters per column necessary to output every cell -- without requiring a line break. -- The @+2@ is needed for specifying the alignment. - let numChars = (+ 2) . maximum . map offset + let numChars = (+ 2) . maybe 0 maximum . nonEmpty . map offset -- Number of characters per column necessary to output every cell -- without requiring a line break *inside a word*. -- The @+2@ is needed for specifying the alignment. - let minNumChars = (+ 2) . maximum . map minOffset + let minNumChars = (+ 2) . maybe 0 maximum . nonEmpty . map minOffset let columns = transpose (rawHeaders : rawRows) -- minimal column width without wrapping a single word let relWidth w col = diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index 19157701e..e35e1a0b9 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -17,6 +17,7 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Data.Char (isAlphaNum, isDigit) import Data.List (find, intersperse) +import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) @@ -383,9 +384,7 @@ inlineToMarkdown opts (Quoted DoubleQuote lst) = do else "“" <> contents <> "”" inlineToMarkdown opts (Code attr str) = do let tickGroups = filter (T.any (== '`')) $ T.group str - let longest = if null tickGroups - then 0 - else maximum $ map T.length tickGroups + let longest = maybe 0 maximum $ nonEmpty $ map T.length tickGroups let marker = T.replicate (longest + 1) "`" let spacer = if longest == 0 then "" else " " let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr @@ -438,9 +437,7 @@ inlineToMarkdown opts (Math DisplayMath str) = (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts) inlineToMarkdown opts il@(RawInline f str) = do let tickGroups = filter (T.any (== '`')) $ T.group str - let numticks = if null tickGroups - then 1 - else 1 + maximum (map T.length tickGroups) + let numticks = 1 + maybe 0 maximum (nonEmpty (map T.length tickGroups)) variant <- asks envVariant let Format fmt = f let rawAttribInline = return $ diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 48395c420..0ed7a8a64 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -23,6 +23,7 @@ module Text.Pandoc.Writers.Ms ( writeMs ) where import Control.Monad.State.Strict import Data.Char (isLower, isUpper, ord) import Data.List (intercalate, intersperse) +import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as Map import Data.Maybe (catMaybes) import Data.Text (Text) @@ -274,8 +275,7 @@ blockToMs opts (BulletList items) = do return (vcat contents) blockToMs opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs - let indent = 2 + - maximum (map T.length markers) + let indent = 2 + maybe 0 maximum (nonEmpty (map T.length markers)) contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ zip markers items setFirstPara diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index bf3265107..d5100f43f 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -31,6 +31,7 @@ import Control.Monad.State.Strict import Data.Char (isAlphaNum, isAsciiLower, isAsciiUpper, isDigit, isSpace) import Data.Default import Data.List (intersperse, transpose) +import Data.List.NonEmpty (nonEmpty, NonEmpty(..)) import qualified Data.Set as Set import qualified Data.Text as T import Data.Text (Text) @@ -158,7 +159,8 @@ simpleTable caption headers rows = do caption' <- inlineListToMuse caption headers' <- mapM blockListToMuse headers rows' <- mapM (mapM blockListToMuse) rows - let widthsInChars = maximum . map offset <$> transpose (headers' : rows') + let widthsInChars = maybe 0 maximum . nonEmpty . map offset <$> + transpose (headers' : rows') let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks where sep' = lblock (T.length sep) $ literal sep let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars @@ -238,7 +240,7 @@ blockToMuse (DefinitionList items) = do label' <- local (\env -> env { envOneLine = True, envAfterSpace = True }) $ inlineListToMuse' label let ind = offset' label' -- using Text.DocLayout.offset results in round trip failures hang ind (nowrap label') . vcat <$> mapM descriptionToMuse defs - where offset' d = maximum (0: map T.length + where offset' d = maximum (0 :| map T.length (T.lines $ render Nothing d)) descriptionToMuse :: PandocMonad m => [Block] @@ -269,7 +271,8 @@ blockToMuse (Table _ blkCapt specs thead tbody tfoot) = (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot blocksToDoc opts blocks = local (\env -> env { envOptions = opts }) $ blockListToMuse blocks - numcols = maximum (length aligns : length widths : map length (headers:rows)) + numcols = maximum + (length aligns :| length widths : map length (headers:rows)) isSimple = onlySimpleTableCells (headers : rows) && all (== 0) widths blockToMuse (Div _ bs) = flatBlockListToMuse bs blockToMuse Null = return empty diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 29d58a161..bb645eaf9 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -19,6 +19,7 @@ module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad.State.Strict import Data.Char (isAlphaNum, isDigit) import Data.List (intersect, intersperse, partition, transpose) +import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) @@ -163,7 +164,7 @@ blockToOrg (Table _ blkCapt specs thead tbody tfoot) = do else "#+caption: " <> caption'' headers' <- mapM blockListToOrg headers rawRows <- mapM (mapM blockListToOrg) rows - let numChars = maximum . map offset + let numChars = maybe 0 maximum . nonEmpty . map offset -- FIXME: width is not being used. let widthsInChars = map numChars $ transpose (headers' : rawRows) @@ -198,7 +199,7 @@ blockToOrg (OrderedList (start, _, delim) items) = do x -> x let markers = take (length items) $ orderedListMarkers (start, Decimal, delim') - let maxMarkerLength = maximum $ map T.length markers + let maxMarkerLength = maybe 0 maximum . nonEmpty $ map T.length markers let markers' = map (\m -> let s = maxMarkerLength - T.length m in m <> T.replicate s " ") markers contents <- zipWithM orderedListItemToOrg markers' items diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 4dbf32c4e..0e515b3c2 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -475,15 +475,16 @@ registerLink link = do linkReg <- gets stLinkIds mediaReg <- gets stMediaIds hasSpeakerNotes <- curSlideHasSpeakerNotes - let maxLinkId = case M.lookup curSlideId linkReg of - Just mp -> case M.keys mp of - [] -> if hasSpeakerNotes then 2 else 1 - ks -> maximum ks - Nothing -> if hasSpeakerNotes then 2 else 1 - maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> if hasSpeakerNotes then 2 else 1 - Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> if hasSpeakerNotes then 2 else 1 + let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of + Just xs -> maximum xs + Nothing + | hasSpeakerNotes -> 2 + | otherwise -> 1 + maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of + Just mInfos -> maximum $ fmap mInfoLocalId mInfos + Nothing + | hasSpeakerNotes -> 2 + | otherwise -> 1 maxId = max maxLinkId maxMediaId slideLinks = case M.lookup curSlideId linkReg of Just mp -> M.insert (maxId + 1) link mp @@ -498,20 +499,19 @@ registerMedia fp caption = do mediaReg <- gets stMediaIds globalIds <- gets stMediaGlobalIds hasSpeakerNotes <- curSlideHasSpeakerNotes - let maxLinkId = case M.lookup curSlideId linkReg of - Just mp -> case M.keys mp of - [] -> if hasSpeakerNotes then 2 else 1 - ks -> maximum ks - Nothing -> if hasSpeakerNotes then 2 else 1 - maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> if hasSpeakerNotes then 2 else 1 - Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> if hasSpeakerNotes then 2 else 1 + let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of + Just ks -> maximum ks + Nothing + | hasSpeakerNotes -> 2 + | otherwise -> 1 + maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of + Just mInfos -> maximum $ fmap mInfoLocalId mInfos + Nothing + | hasSpeakerNotes -> 2 + | otherwise -> 1 maxLocalId = max maxLinkId maxMediaId - maxGlobalId = case M.elems globalIds of - [] -> 0 - ids -> maximum ids + maxGlobalId = maybe 0 maximum $ nonEmpty $ M.elems globalIds (imgBytes, mbMt) <- P.fetchItem $ T.pack fp let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ "." <> x)) diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index affec38aa..9246a93e9 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -45,6 +45,7 @@ module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation import Control.Monad.Reader import Control.Monad.State import Data.List (intercalate) +import Data.List.NonEmpty (nonEmpty) import Data.Default import Text.Pandoc.Definition import Text.Pandoc.ImageSize @@ -363,9 +364,7 @@ inlineToParElems (Note blks) = do then return [] else do notes <- gets stNoteIds - let maxNoteId = case M.keys notes of - [] -> 0 - lst -> maximum lst + let maxNoteId = maybe 0 maximum $ nonEmpty $ M.keys notes curNoteId = maxNoteId + 1 modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $ diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 54d042332..0b9fc8331 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -17,6 +17,7 @@ module Text.Pandoc.Writers.RST ( writeRST, flatten ) where import Control.Monad.State.Strict import Data.Char (isSpace) import Data.List (transpose, intersperse, foldl') +import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import qualified Data.Text as T import Data.Text (Text) @@ -335,7 +336,7 @@ blockToRST (OrderedList (start, style', delim) items) = do then replicate (length items) "#." else take (length items) $ orderedListMarkers (start, style', delim) - let maxMarkerLength = maximum $ map T.length markers + let maxMarkerLength = maybe 0 maximum $ nonEmpty $ map T.length markers let markers' = map (\m -> let s = maxMarkerLength - T.length m in m <> T.replicate s " ") markers contents <- zipWithM orderedListItemToRST markers' items @@ -761,8 +762,7 @@ simpleTable opts blocksToDoc headers rows = do then return [] else fixEmpties <$> mapM (blocksToDoc opts) headers rowDocs <- mapM (fmap fixEmpties . mapM (blocksToDoc opts)) rows - let numChars [] = 0 - numChars xs = maximum . map offset $ xs + let numChars = maybe 0 maximum . nonEmpty . map offset let colWidths = map numChars $ transpose (headerDocs : rowDocs) let toRow = mconcat . intersperse (lblock 1 " ") . zipWith lblock colWidths let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths) diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index fc3f8ff3a..91ecb310b 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -44,6 +44,7 @@ import Control.Monad (zipWithM) import Data.Aeson (ToJSON (..), encode) import Data.Char (chr, ord, isSpace) import Data.List (groupBy, intersperse, transpose, foldl') +import Data.List.NonEmpty (NonEmpty(..), nonEmpty) import Data.Text.Conversions (FromText(..)) import qualified Data.Map as M import qualified Data.Text as T @@ -224,7 +225,7 @@ gridTable :: (Monad m, HasChars a) -> m (Doc a) gridTable opts blocksToDoc headless aligns widths headers rows = do -- the number of columns will be used in case of even widths - let numcols = maximum (length aligns : length widths : + let numcols = maximum (length aligns :| length widths : map length (headers:rows)) let officialWidthsInChars widths' = map ( (\x -> if x < 1 then 1 else x) . @@ -253,8 +254,7 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do let handleFullWidths widths' = do rawHeaders' <- mapM (blocksToDoc opts) headers rawRows' <- mapM (mapM (blocksToDoc opts)) rows - let numChars [] = 0 - numChars xs = maximum . map offset $ xs + let numChars = maybe 0 maximum . nonEmpty . map offset let minWidthsInChars = map numChars $ transpose (rawHeaders' : rawRows') let widthsInChars' = zipWith max diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 9d695563f..0146fdfd8 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -16,6 +16,7 @@ import Control.Monad.Except (throwError) import Control.Monad.State.Strict import Data.Char (chr, ord) import Data.List (maximumBy, transpose, foldl') +import Data.List.NonEmpty (nonEmpty) import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) @@ -238,9 +239,13 @@ blockToTexinfo (Table _ blkCapt specs thead tbody tfoot) = do colDescriptors <- if all (== 0) widths then do -- use longest entry instead of column widths - cols <- mapM (mapM (liftM (T.unpack . render Nothing . hcat) . mapM blockToTexinfo)) $ + cols <- mapM (mapM (fmap (T.unpack . render Nothing . hcat) . + mapM blockToTexinfo)) $ transpose $ heads : rows - return $ concatMap ((\x -> "{"++x++"} ") . maximumBy (comparing length)) cols + return $ concatMap + ((\x -> "{"++x++"} ") . + maybe "" (maximumBy (comparing length)) . nonEmpty) + cols else return $ "@columnfractions " ++ concatMap (printf "%.2f ") widths let tableBody = text ("@multitable " ++ colDescriptors) $$ headers $$ diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 9e45f0417..fcf9e000d 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -20,6 +20,7 @@ import Control.Monad (zipWithM) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (transpose) +import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as Map import Text.DocLayout (render, literal) import Data.Maybe (fromMaybe) @@ -143,7 +144,8 @@ blockToZimWiki opts (Table _ blkCapt specs thead tbody tfoot) = do then zipWithM (tableItemToZimWiki opts) aligns (head rows) else mapM (inlineListToZimWiki opts . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows - let widths = map (maximum . map T.length) $ transpose (headers':rows') + let widths = map (maybe 0 maximum . nonEmpty . map T.length) $ + transpose (headers':rows') let padTo (width, al) s = case width - T.length s of x | x > 0 -> -- cgit v1.2.3 From 425c2e47b5245ec0eec3eb294a83250252eb3efa Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 11:58:56 -0700 Subject: Use skylighting 0.10.5. This fixes a bad regression in Haskell syntax highlighting. --- pandoc.cabal | 4 ++-- stack.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index e46518e96..bb12b8cca 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -479,8 +479,8 @@ library random >= 1 && < 1.3, safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, - skylighting >= 0.10.4.1 && < 0.11, - skylighting-core >= 0.10.4.1 && < 0.11, + skylighting >= 0.10.5 && < 0.11, + skylighting-core >= 0.10.5 && < 0.11, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, diff --git a/stack.yaml b/stack.yaml index e37ccd644..7264793cd 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,8 +10,8 @@ extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 - jira-wiki-markup-1.3.4 -- skylighting-core-0.10.4.1 -- skylighting-0.10.4.1 +- skylighting-core-0.10.5 +- skylighting-0.10.5 - doclayout-0.3.0.2 - citeproc-0.3.0.9 - texmath-0.12.2 -- cgit v1.2.3 From 005f0fbcd558636f0d5db1203427a4d7b341f36e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 12:34:20 -0700 Subject: T.P.Shared: Remove ToString, ToText typeclasses [API change]. T.P.Parsing: revise type of readWithM so that it takes a Text rather than a polymorphic ToText value. These typeclasses were there to ease the transition from String to Text. They are no longer needed, and they may clash with more useful versions under the same name. This will require a bump to 2.13. --- src/Text/Pandoc/Parsing.hs | 8 ++++---- src/Text/Pandoc/Shared.hs | 20 -------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 8d3799c3e..10a08d410 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1128,13 +1128,13 @@ gridTableFooter = optional blanklines --- -- | Removes the ParsecT layer from the monad transformer stack -readWithM :: (Stream s m Char, ToText s) - => ParserT s st m a -- ^ parser +readWithM :: Monad m + => ParserT Text st m a -- ^ parser -> st -- ^ initial state - -> s -- ^ input + -> Text -- ^ input -> m (Either PandocError a) readWithM parser state input = - mapLeft (PandocParsecError $ toText input) `liftM` runParserT parser state "source" input + mapLeft (PandocParsecError input) <$> runParserT parser state "source" input -- | Parse a string with a given parser and state readWith :: Parser Text st a diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 0ce9396b3..46aea9c03 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -25,8 +25,6 @@ module Text.Pandoc.Shared ( ordNub, findM, -- * Text processing - ToString (..), - ToText (..), tshow, backslashEscapes, escapeStringUsing, @@ -183,24 +181,6 @@ findM p = foldr go (pure Nothing) -- Text processing -- -class ToString a where - toString :: a -> String - -instance ToString String where - toString = id - -instance ToString T.Text where - toString = T.unpack - -class ToText a where - toText :: a -> T.Text - -instance ToText String where - toText = T.pack - -instance ToText T.Text where - toText = id - tshow :: Show a => a -> T.Text tshow = T.pack . show -- cgit v1.2.3 From 029de661f4fbd7320bd06dc658ec32b4119fb313 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 14:11:11 -0700 Subject: Narrow version bounds for skylighting, citeproc, and texmath. This reduces the chance that tests will fail due to behavior changes in one of these dependencies. (See e.g. #7163) --- pandoc.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index bb12b8cca..6338a4fdc 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -445,7 +445,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.9 && < 0.4, + citeproc >= 0.3.0.9 && < 0.3.1, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, @@ -479,13 +479,13 @@ library random >= 1 && < 1.3, safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, - skylighting >= 0.10.5 && < 0.11, - skylighting-core >= 0.10.5 && < 0.11, + skylighting >= 0.10.5 && < 0.10.6, + skylighting-core >= 0.10.5 && < 0.10.6, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, temporary >= 1.1 && < 1.4, - texmath >= 0.12.2 && < 0.13, + texmath >= 0.12.2 && < 0.12.3, text >= 1.1.1.0 && < 1.3, text-conversions >= 0.3 && < 0.4, time >= 1.5 && < 1.10, -- cgit v1.2.3 From b0c8ba88a773e18581f088c5473351c7ba30af7e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 14:34:13 -0700 Subject: CI: test with different flags. --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9c5225f3..64dda5302 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,26 +46,32 @@ jobs: - ghc: '8.0.2' cabal: '2.2' prefix: 'new-' + cabalopts: '' testopts: '' - ghc: '8.2.2' cabal: '2.4' prefix: 'v2-' + cabalopts: '' testopts: '' - ghc: '8.4.4' cabal: '2.4' prefix: 'v2-' + cabalopts: '-f-embed_data_files' testopts: '' - ghc: '8.6.5' cabal: '3.2' prefix: '' + cabalopts: '' testopts: '--test-option=--hide-successes' - ghc: '8.8.4' cabal: '3.2' prefix: '' + cabalopts: '' testopts: '--test-option=--hide-successes' - ghc: '8.10.2' cabal: '3.2' prefix: '' + cabalopts: '-ftrypandoc' testopts: '--test-option=--hide-successes' steps: - uses: actions/checkout@v2 @@ -101,14 +107,14 @@ jobs: - name: Install dependencies run: | cabal ${{ matrix.versions.prefix }}update - cabal ${{ matrix.versions.prefix }}build --dependencies-only --enable-tests --disable-optimization + cabal ${{ matrix.versions.prefix }}build ${{ matrix.versions.cabalopts }} --dependencies-only --enable-tests --disable-optimization - name: Build and test run: | - cabal ${{ matrix.versions.prefix }}build --enable-tests --disable-optimization 2>&1 | tee build.log + cabal ${{ matrix.versions.prefix }}build ${{ matrix.versions.cabalopts }} --enable-tests --disable-optimization 2>&1 | tee build.log # fail if warnings in local build ! grep -q ": *[Ww]arning:" build.log || exit 1 - cabal ${{ matrix.versions.prefix }}test --disable-optimization ${{ matrix.versions.testopts }} + cabal ${{ matrix.versions.prefix }}test ${{ matrix.versions.cabalopts }} --disable-optimization ${{ matrix.versions.testopts }} windows: -- cgit v1.2.3 From 7678c48122bb9bb7041b75ddc33061b93cbcdbb2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 14:43:42 -0700 Subject: Hlint suggestion. --- src/Text/Pandoc/Readers/Txt2Tags.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index f27a3fc2c..d355a4b55 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -56,12 +56,13 @@ getT2TMeta = do outp <- fromMaybe "" <$> P.getOutputFile curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime curMtime <- catchError - ((nonEmpty <$> mapM P.getModificationTime inps) >>= - \case + (mapM P.getModificationTime inps >>= + (\case Nothing -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime Just ts -> return $ formatTime defaultTimeLocale "%T" $ maximum ts) + . nonEmpty) (const (return "")) return $ T2TMeta (T.pack curDate) (T.pack curMtime) (intercalate ", " inps) outp -- cgit v1.2.3 From c3a9322b930c4b7e9bbf880d5df5e972efcaf0ff Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 14:49:42 -0700 Subject: cabal.project: move flags: to top level (not under pandoc:). If it's under the pandoc stanza, it can't be overriden on the command line, it seems. Also we remove ghc-options. --- cabal.project | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cabal.project b/cabal.project index d36a8c3d1..a0406da3c 100644 --- a/cabal.project +++ b/cabal.project @@ -1,10 +1,6 @@ packages: pandoc.cabal - tests: True - -package pandoc - flags: +embed_data_files - ghc-options: +RTS -A256m -RTS +flags: +embed_data_files -- source-repository-package -- type: git -- cgit v1.2.3 From 2ca1b20a85303dc8656f782f5804afdec215948b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 18:54:49 -0700 Subject: Fix finding of data files from test programs. Apparently Cabal sets a `pandoc_datadir` environment variable so that the data files will be sought in the source directory rather than in the final destination (where they aren't yet installed). So we no longer need to set `--data-dir` in the tests. We just need to make sure `pandoc_datadir` is set in the environment when we call the program in the test suite. This will fix the issue with loading of pandoc.lua when pandoc is built with `-embed_data_files`, reported in #7163. Closes #7163. --- test/Tests/Command.hs | 3 ++- test/Tests/Old.hs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index 59b04eac1..dc0e25dbe 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -40,11 +40,12 @@ execTest :: String -- ^ Path to test executable execTest testExePath cmd inp = do mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" + mpdd <- Env.lookupEnv "pandoc_datadir" let env' = ("PATH",takeDirectory testExePath) : ("TMP",".") : ("LANG","en_US.UTF-8") : ("HOME", "./") : - ("pandoc_datadir", "..") : + maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath let pr = (shell (pandocToEmulate True cmd)){ env = Just env' } diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index 160086be2..e890bad0b 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -321,9 +321,11 @@ testWithNormalize normalizer pandocPath testname opts inp norm = getActual = do mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" + mpdd <- Env.lookupEnv "pandoc_datadir" let env = ("TMP",".") : ("LANG","en_US.UTF-8") : ("HOME", "./") : + maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath @@ -335,7 +337,7 @@ testWithNormalize normalizer pandocPath testname opts inp norm = -- filter \r so the tests will work on Windows machines else fail $ "Pandoc failed with error code " ++ show ec updateGolden = UTF8.writeFile norm . T.pack - options = ["--data-dir=../data","--quiet"] ++ [inp] ++ opts + options = ["--quiet"] ++ [inp] ++ opts compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String) compareValues norm options expected actual = do -- cgit v1.2.3 From dc94601eb5d90d315d8eae3bca57a652da3fa598 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 21:17:13 -0700 Subject: Tests: factor out setupEnvironment in Test.Helpers. This avoids code duplication between Command and Old. --- test/Tests/Command.hs | 15 ++------------- test/Tests/Helpers.hs | 23 +++++++++++++++++++++++ test/Tests/Old.hs | 13 +------------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index dc0e25dbe..e8863b545 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TupleSections #-} {- | Module : Tests.Command Copyright : © 2006-2021 John MacFarlane @@ -19,9 +18,8 @@ import qualified Data.ByteString as BS import qualified Data.Text as T import Data.List (isSuffixOf) import System.Directory -import qualified System.Environment as Env import System.Exit -import System.FilePath (takeDirectory, ()) +import System.FilePath (()) import System.IO (hPutStr, stderr) import System.IO.Unsafe (unsafePerformIO) import System.Process @@ -38,16 +36,7 @@ execTest :: String -- ^ Path to test executable -> String -- ^ Input text -> IO (ExitCode, String) -- ^ Exit code and actual output execTest testExePath cmd inp = do - mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" - mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" - mpdd <- Env.lookupEnv "pandoc_datadir" - let env' = ("PATH",takeDirectory testExePath) : - ("TMP",".") : - ("LANG","en_US.UTF-8") : - ("HOME", "./") : - maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ - maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ - maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath + env' <- setupEnvironment testExePath let pr = (shell (pandocToEmulate True cmd)){ env = Just env' } (ec, out', err') <- readCreateProcessWithExitCode pr inp -- filter \r so the tests will work on Windows machines diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index 64c2785ed..a48a5894e 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleInstances #-} {- | Module : Tests.Helpers @@ -13,6 +14,7 @@ Utility functions for the test suite. -} module Tests.Helpers ( test , TestResult(..) + , setupEnvironment , showDiff , (=?>) , purely @@ -25,6 +27,8 @@ import Data.Algorithm.Diff import qualified Data.Map as M import Data.Text (Text, unpack) import System.Exit +import System.FilePath (takeDirectory) +import qualified System.Environment as Env import Test.Tasty import Test.Tasty.HUnit import Text.Pandoc.Builder (Blocks, Inlines, doc, plain) @@ -57,6 +61,25 @@ test fn name (input, expected) = dashes "" = replicate 72 '-' dashes x = replicate (72 - length x - 5) '-' ++ " " ++ x ++ " ---" +-- | Set up environment for pandoc command tests. +setupEnvironment :: FilePath -> IO [(String, String)] +setupEnvironment testExePath = do + mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" + mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" + mpdd <- Env.lookupEnv "pandoc_datadir" + -- Note that Cabal sets the pandoc_datadir environment variable + -- to point to the source directory, since otherwise getDataFilename + -- will look in the data directory into which pandoc will be installed + -- (but has not yet been). So when we spawn a new process with + -- pandoc, we need to make sure this environment variable is set. + return $ ("PATH",takeDirectory testExePath) : + ("TMP",".") : + ("LANG","en_US.UTF-8") : + ("HOME", "./") : + maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ + maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ + maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath + data TestResult = TestPassed | TestError ExitCode | TestFailed String FilePath [Diff String] diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index e890bad0b..c7b164f3c 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TupleSections #-} {- | Module : Tests.Old Copyright : © 2006-2021 John MacFarlane @@ -15,7 +14,6 @@ module Tests.Old (tests) where import Data.Algorithm.Diff import System.Exit import System.FilePath ((<.>), ()) -import qualified System.Environment as Env import System.Environment.Executable (getExecutablePath) import Text.Pandoc.Process (pipeProcess) import Test.Tasty (TestTree, testGroup) @@ -319,16 +317,7 @@ testWithNormalize normalizer pandocPath testname opts inp norm = (compareValues norm options) updateGolden where getExpected = normalizer <$> readFile' norm getActual = do - mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" - mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" - mpdd <- Env.lookupEnv "pandoc_datadir" - let env = ("TMP",".") : - ("LANG","en_US.UTF-8") : - ("HOME", "./") : - maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ - maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ - maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath - + env <- setupEnvironment pandocPath (ec, out) <- pipeProcess (Just env) pandocPath ("--emulate":options) mempty if ec == ExitSuccess -- cgit v1.2.3 From eacead3eb316dd8e2d75737589dc017de36326fb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 22:57:48 -0700 Subject: Fix fallback to default partials on templates. If the directory containing a template does not contain the partial, it should be sought in the default data files. Closes #7164. --- src/Text/Pandoc/Templates.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index e83f26329..7fd896641 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -34,6 +34,7 @@ import Control.Monad.Except (catchError, throwError) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Error +import System.IO.Error (isDoesNotExistError) -- | Wrap a Monad in this if you want partials to -- be taken only from the default data files. @@ -70,6 +71,9 @@ getTemplate tp = UTF8.toText <$> PandocResourceNotFound _ -> -- see #5987 on reason for takeFileName readDataFile ("templates" takeFileName tp) + PandocIOError _ ioe | isDoesNotExistError ioe -> + -- see #5987 on reason for takeFileName + readDataFile ("templates" takeFileName tp) _ -> throwError e)) -- | Get default template for the specified writer. -- cgit v1.2.3 From ceadf33246bcc42747b42c10c108bfc7d8663ab7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Mar 2021 23:35:47 -0700 Subject: Tests: Use getExecutablePath from base... avoiding the need to depend on the executable-path package. --- pandoc.cabal | 1 - test/Tests/Command.hs | 2 +- test/Tests/Old.hs | 2 +- test/test-pandoc.hs | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 6338a4fdc..b70c98e58 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -748,7 +748,6 @@ test-suite test-pandoc directory >= 1.2.3 && < 1.4, doctemplates >= 0.9 && < 0.10, exceptions >= 0.8 && < 0.11, - executable-path >= 0.0 && < 0.1, filepath >= 1.1 && < 1.5, hslua >= 1.1 && < 1.4, mtl >= 2.2 && < 2.3, diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index e8863b545..3ec6d7230 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -13,7 +13,7 @@ module Tests.Command (runTest, tests) where import Data.Algorithm.Diff -import System.Environment.Executable (getExecutablePath) +import System.Environment (getExecutablePath) import qualified Data.ByteString as BS import qualified Data.Text as T import Data.List (isSuffixOf) diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index c7b164f3c..ad9f249c4 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -14,7 +14,7 @@ module Tests.Old (tests) where import Data.Algorithm.Diff import System.Exit import System.FilePath ((<.>), ()) -import System.Environment.Executable (getExecutablePath) +import System.Environment (getExecutablePath) import Text.Pandoc.Process (pipeProcess) import Test.Tasty (TestTree, testGroup) import Test.Tasty.Golden.Advanced (goldenTest) diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs index 4d9da525b..abb2f105f 100644 --- a/test/test-pandoc.hs +++ b/test/test-pandoc.hs @@ -2,12 +2,11 @@ module Main where -import System.Environment (getArgs) +import System.Environment (getArgs, getExecutablePath) import qualified Control.Exception as E import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptionsFromArgs) import Text.Pandoc.Error (handleError) -import System.Environment.Executable (getExecutablePath) import GHC.IO.Encoding import Test.Tasty import qualified Tests.Command -- cgit v1.2.3 From a1a57bce4e32cc26b968bcc2847a8e8da30f725b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 00:02:24 -0700 Subject: T.P.Shared: remove `backslashEscapes`, `escapeStringUsing`. [API change] These are inefficient association list lookups. Replace with more efficient functions in the writers that used them (with 10-25% performance improvements in haddock, org, rtf, texinfo writers). --- src/Text/Pandoc/Shared.hs | 13 ------------- src/Text/Pandoc/Writers/AsciiDoc.hs | 11 ++++++++--- src/Text/Pandoc/Writers/ConTeXt.hs | 6 +++++- src/Text/Pandoc/Writers/Haddock.hs | 15 +++++++++++++-- src/Text/Pandoc/Writers/LaTeX.hs | 14 +++++++++++++- src/Text/Pandoc/Writers/Org.hs | 15 +++++++++------ src/Text/Pandoc/Writers/RTF.hs | 26 ++++++++++++++++---------- src/Text/Pandoc/Writers/Texinfo.hs | 24 +++++++++++++----------- 8 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 46aea9c03..23adff909 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -26,8 +26,6 @@ module Text.Pandoc.Shared ( findM, -- * Text processing tshow, - backslashEscapes, - escapeStringUsing, elemText, notElemText, stripTrailingNewlines, @@ -184,17 +182,6 @@ findM p = foldr go (pure Nothing) tshow :: Show a => a -> T.Text tshow = T.pack . show --- | Returns an association list of backslash escapes for the --- designated characters. -backslashEscapes :: [Char] -- ^ list of special characters to escape - -> [(Char, T.Text)] -backslashEscapes = map (\ch -> (ch, T.pack ['\\',ch])) - --- | Escape a string of characters, using an association list of --- characters and strings. -escapeStringUsing :: [(Char, T.Text)] -> T.Text -> T.Text -escapeStringUsing tbl = T.concatMap $ \c -> fromMaybe (T.singleton c) $ lookup c tbl - -- | @True@ exactly when the @Char@ appears in the @Text@. elemText :: Char -> T.Text -> Bool elemText c = T.any (== c) diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 69e608ef9..ab7e5f1a9 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -105,8 +105,11 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do -- | Escape special characters for AsciiDoc. escapeString :: Text -> Text -escapeString = escapeStringUsing escs - where escs = backslashEscapes "{" +escapeString t + | T.any (== '{') t = T.concatMap escChar t + | otherwise = t + where escChar '{' = "\\{" + escChar c = T.singleton c -- | Ordered list start parser for use in Para below. olMarker :: Parser Text ParserState Char @@ -496,7 +499,9 @@ inlineToAsciiDoc opts (Quoted qt lst) = do | otherwise -> [Str "``"] ++ lst ++ [Str "''"] inlineToAsciiDoc _ (Code _ str) = do isAsciidoctor <- gets asciidoctorVariant - let contents = literal (escapeStringUsing (backslashEscapes "`") str) + let escChar '`' = "\\'" + escChar c = T.singleton c + let contents = literal (T.concatMap escChar str) return $ if isAsciidoctor then text "`+" <> contents <> "+`" diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 1c56388ed..3c9975be8 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -434,9 +434,13 @@ inlineToConTeXt (Link _ txt (src, _)) = do put $ st {stNextRef = next + 1} let ref = "url" <> tshow next contents <- inlineListToConTeXt txt + let escChar '#' = "\\#" + escChar '%' = "\\%" + escChar c = T.singleton c + let escContextURL = T.concatMap escChar return $ "\\useURL" <> brackets (literal ref) - <> brackets (literal $ escapeStringUsing [('#',"\\#"),('%',"\\%")] src) + <> brackets (literal $ escContextURL src) <> (if isAutolink then empty else brackets empty <> brackets contents) diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index aaa19ed07..75e14714b 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -15,6 +15,7 @@ Haddock: -} module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad.State.Strict +import Data.Char (isAlphaNum) import Data.Default import Data.Text (Text) import qualified Data.Text as T @@ -71,8 +72,18 @@ notesToHaddock opts notes = -- | Escape special characters for Haddock. escapeString :: Text -> Text -escapeString = escapeStringUsing haddockEscapes - where haddockEscapes = backslashEscapes "\\/'`\"@<" +escapeString t + | T.all isAlphaNum t = t + | otherwise = T.concatMap escChar t + where + escChar '\\' = "\\\\" + escChar '/' = "\\/" + escChar '\'' = "\\'" + escChar '`' = "\\`" + escChar '"' = "\\\"" + escChar '@' = "\\@" + escChar '<' = "\\<" + escChar c = T.singleton c -- | Convert Pandoc block element to haddock. blockToHaddock :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 6a205a798..1c970e6ad 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -825,7 +825,19 @@ inlineToLaTeX (Code (_,classes,kvs) str) = do let chr = case "!\"'()*,-./:;?@" \\ T.unpack str of (c:_) -> c [] -> '!' - let str' = escapeStringUsing (backslashEscapes "\\{}%~_&#^") str + let isEscapable '\\' = True + isEscapable '{' = True + isEscapable '}' = True + isEscapable '%' = True + isEscapable '~' = True + isEscapable '_' = True + isEscapable '&' = True + isEscapable '#' = True + isEscapable '^' = True + isEscapable _ = False + let escChar c | isEscapable c = T.pack ['\\',c] + | otherwise = T.singleton c + let str' = T.concatMap escChar str -- we always put lstinline in a dummy 'passthrough' command -- (defined in the default template) so that we don't have -- to change the way we escape characters depending on whether diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index bb645eaf9..88a2b8314 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -84,12 +84,15 @@ noteToOrg num note = do -- | Escape special characters for Org. escapeString :: Text -> Text -escapeString = escapeStringUsing - [ ('\x2014',"---") - , ('\x2013',"--") - , ('\x2019',"'") - , ('\x2026',"...") - ] +escapeString t + | T.all (\c -> c < '\x2013' || c > '\x2026') t = t + | otherwise = T.concatMap escChar t + where + escChar '\x2013' = "--" + escChar '\x2014' = "---" + escChar '\x2019' = "'" + escChar '\x2026' = "..." + escChar c = T.singleton c isRawFormat :: Format -> Bool isRawFormat f = diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index cf27011c2..3527949b4 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -16,7 +16,7 @@ module Text.Pandoc.Writers.RTF ( writeRTF import Control.Monad.Except (catchError, throwError) import Control.Monad import qualified Data.ByteString as B -import Data.Char (chr, isDigit, ord) +import Data.Char (chr, isDigit, ord, isAlphaNum) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T @@ -137,15 +137,21 @@ handleUnicode = T.concatMap $ \c -> -- | Escape special characters. escapeSpecial :: Text -> Text -escapeSpecial = escapeStringUsing $ - [ ('\t',"\\tab ") - , ('\8216',"\\u8216'") - , ('\8217',"\\u8217'") - , ('\8220',"\\u8220\"") - , ('\8221',"\\u8221\"") - , ('\8211',"\\u8211-") - , ('\8212',"\\u8212-") - ] <> backslashEscapes "{\\}" +escapeSpecial t + | T.all isAlphaNum t = t + | otherwise = T.concatMap escChar t + where + escChar '\t' = "\\tab " + escChar '\8216' = "\\u8216'" + escChar '\8217' = "\\u8217'" + escChar '\8220' = "\\u8220\"" + escChar '\8221' = "\\u8221\"" + escChar '\8211' = "\\u8211-" + escChar '\8212' = "\\u8212-" + escChar '{' = "\\{" + escChar '}' = "\\}" + escChar '\\' = "\\\\" + escChar c = T.singleton c -- | Escape strings as needed for rich text format. stringToRTF :: Text -> Text diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 0146fdfd8..6a33b4283 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -14,7 +14,7 @@ Conversion of 'Pandoc' format into Texinfo. module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where import Control.Monad.Except (throwError) import Control.Monad.State.Strict -import Data.Char (chr, ord) +import Data.Char (chr, ord, isAlphaNum) import Data.List (maximumBy, transpose, foldl') import Data.List.NonEmpty (nonEmpty) import Data.Ord (comparing) @@ -85,16 +85,18 @@ pandocToTexinfo options (Pandoc meta blocks) = do -- | Escape things as needed for Texinfo. stringToTexinfo :: Text -> Text -stringToTexinfo = escapeStringUsing texinfoEscapes - where texinfoEscapes = [ ('{', "@{") - , ('}', "@}") - , ('@', "@@") - , ('\160', "@ ") - , ('\x2014', "---") - , ('\x2013', "--") - , ('\x2026', "@dots{}") - , ('\x2019', "'") - ] +stringToTexinfo t + | T.all isAlphaNum t = t + | otherwise = T.concatMap escChar t + where escChar '{' = "@{" + escChar '}' = "@}" + escChar '@' = "@@" + escChar '\160' = "@ " + escChar '\x2014' = "---" + escChar '\x2013' = "--" + escChar '\x2026' = "@dots{}" + escChar '\x2019' = "'" + escChar c = T.singleton c escapeCommas :: PandocMonad m => TI m (Doc Text) -> TI m (Doc Text) escapeCommas parser = do -- cgit v1.2.3 From 99c15f8e851236d2429871d21aac08a137a48cda Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 00:39:38 -0700 Subject: Bump to 2.13, update changelog --- changelog.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- pandoc.cabal | 2 +- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index a9af2acfb..d3559c189 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ # Revision history for pandoc -## pandoc 2.12.1 (PROVISIONAL) +## pandoc 2.13 (PROVISIONAL) + + * Fix fallback to default partials when custom templates are + used. If the directory containing a template does not + contain the partial, it should be sought in the default + templates, but this was not working properly (#7164). * Handle `nocite` better with `--biblatex` and `--natbib` (#4585). Previously the nocite metadata field was ignored with these formats. @@ -11,6 +16,24 @@ that incorporates a prefix like `https://doi.org/` into a following link when appropriate. + * Text.Pandoc.Shared: + + + Remove `backslashEscapes`, `escapeStringUsing` [API + change]. Replace these inefficient association list + lookups with more efficient escaping functions in the + writers that used them (for a 10-25% performance boost + in org, haddock, rtf, texinfo writers). + + Remove `ToString`, `ToText` typeclasses [API change]. + These were needed for the transition from String to Text, + but they are no longer used and may clash with other + things. + + Simplify `compactDL`. + + * Text.Pandoc.Parsing: Change type of `readWithM` so that it + is no longer polymorphic. The `ToText` class has been + removed, and now that we've completed the transition to Text + we no longer need this to operate on Strings. + * Markdown reader: Fix regression with `tex_math_backslash` (#7155). * MediaWiki reader: Allow block-level content in notes (ref) (#7145). @@ -56,17 +79,20 @@ * reveal.js template: replace JS comment with HTML (#7154, Florian Kohrt). - * Text.Pandoc.Shared: Simplify `compactDL`. - * Text.Pandoc.Logging: Add `DuplicateAttribute` constructor to `LogMessage`. [API change] * Use `-j4` for linux release build. This speeds up the build dramatically on arm. - * cabal.project: remove `-j`. + * cabal.project: remove ghcoptions. Move flags to top level, so + they can be set differently on the command line. * Require latest texmath, skylighting, citeproc, jira-wiki-markup. + (The latest skylighting fixes a bad bug with Haskell syntax + highlighting.) Narrow version bounds for texmath, + skylighting, and citeproc, since the test output depend on + them. * Use doclayout 0.3.0.2. This significantly reduces the time and memory needed to compile pandoc. @@ -75,7 +101,23 @@ * Update bounds for random (#7156, Alexey Kuleshevich). - * Benchmark improvements. + * Remove uses of some partial functions. + + * Don't bake in a larger stack size for the executable. + + * Test improvements: + + + Use `getExecutablePath` from base, avoiding the dependency + on `executable-path`. + + Factor out `setupEnvironment` in Helpers, to avoid code + duplication. + + Fix finding of data files by setting teh `pandoc_datadir` + environment variable when we shell out to pandoc. This + avoids the need to use `--data-dir` for the tests, which + caused problems finding `pandoc.lua` when compiling + without the `embed_data_files` flag (#7163). + + * Benchmark improvements: + Build `+RTS -A8m -RTS` into default ghc-options for benchmark. This is necessary to get accurate benchmark results; otherwise diff --git a/pandoc.cabal b/pandoc.cabal index b70c98e58..d5ebb2d6e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.12.1 +version: 2.13 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md -- cgit v1.2.3 From 80ae92992224ec443044bf628bf550ecd66d5ea6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 00:40:04 -0700 Subject: Update man page and manual date. --- MANUAL.txt | 2 +- man/pandoc.1 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 9dfb2909b..bc9e226e9 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: March 8, 2021 +date: March 20, 2021 --- # Synopsis diff --git a/man/pandoc.1 b/man/pandoc.1 index 3ed6fcddb..bfe9872a4 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t -.\" Automatically generated by Pandoc 2.12 +.\" Automatically generated by Pandoc 2.12.1 .\" -.TH "Pandoc User\[cq]s Guide" "" "March 8, 2021" "pandoc 2.12" "" +.TH "Pandoc User\[cq]s Guide" "" "March 20, 2021" "pandoc 2.13" "" .hy .SH NAME pandoc - general markup converter -- cgit v1.2.3 From 38618098153c29ea2a39fa6102e9e681136e6fd4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 00:40:40 -0700 Subject: Add date on changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index d3559c189..9b3485f5b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Revision history for pandoc -## pandoc 2.13 (PROVISIONAL) +## pandoc 2.13 (2021-03-21) * Fix fallback to default partials when custom templates are used. If the directory containing a template does not -- cgit v1.2.3 From 82e8c29cb0a89d7129f459bef6696254ec56e0c6 Mon Sep 17 00:00:00 2001 From: Erik Rask Date: Fri, 19 Feb 2021 13:05:35 +0100 Subject: Include Header.Attr.attributes as XML attributes on section Add key-value pairs found in the attributes list of Header.Attr as XML attributes on the corresponding section element. Any key name not allowed as an XML attribute name is dropped, as are keys with invalid values where they are defined as enums in DocBook, and xml:id (for DocBook 5)/id (for DocBook 4) to not intervene with computed identifiers. --- src/Text/Pandoc/Writers/Docbook.hs | 47 ++++++++++++++++++++++++++++++++++++-- test/Tests/Writers/Docbook.hs | 37 ++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index a6776608d..1f10c9d04 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -168,7 +168,7 @@ blockToDocbook :: PandocMonad m => WriterOptions -> Block -> DB m (Doc Text) blockToDocbook _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: -blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do +blockToDocbook opts (Div (id',"section":_,_) (Header lvl (_,_,attrs) ils : xs)) = do version <- ask -- Docbook doesn't allow sections with no content, so insert some if needed let bs = if null xs @@ -188,7 +188,10 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do -- standalone documents will include them in the template. then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] else [] - attribs = nsAttr <> idAttr + + -- Populate miscAttr with Header.Attr.attributes, filtering out non-valid DocBook section attributes, id, and xml:id + miscAttr = filter (isSectionAttr version) attrs + attribs = nsAttr <> idAttr <> miscAttr title' <- inlinesToDocbook opts ils contents <- blocksToDocbook opts bs return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents @@ -451,3 +454,43 @@ idAndRole (id',cls,_) = ident <> role where ident = [("id", id') | not (T.null id')] role = [("role", T.unwords cls) | not (null cls)] + +isSectionAttr :: DocBookVersion -> (Text, Text) -> Bool +isSectionAttr _ ("label",_) = True +isSectionAttr _ ("status",_) = True +isSectionAttr DocBook5 ("annotations",_) = True +isSectionAttr DocBook5 ("dir","ltr") = True +isSectionAttr DocBook5 ("dir","rtl") = True +isSectionAttr DocBook5 ("dir","lro") = True +isSectionAttr DocBook5 ("dir","rlo") = True +isSectionAttr _ ("remap",_) = True +isSectionAttr _ ("revisionflag","changed") = True +isSectionAttr _ ("revisionflag","added") = True +isSectionAttr _ ("revisionflag","deleted") = True +isSectionAttr _ ("revisionflag","off") = True +isSectionAttr _ ("role",_) = True +isSectionAttr DocBook5 ("version",_) = True +isSectionAttr DocBook5 ("xml:base",_) = True +isSectionAttr DocBook5 ("xml:lang",_) = True +isSectionAttr _ ("xreflabel",_) = True +isSectionAttr DocBook5 ("linkend",_) = True +isSectionAttr DocBook5 ("linkends",_) = True +isSectionAttr DocBook5 ("xlink:actuate",_) = True +isSectionAttr DocBook5 ("xlink:arcrole",_) = True +isSectionAttr DocBook5 ("xlink:from",_) = True +isSectionAttr DocBook5 ("xlink:href",_) = True +isSectionAttr DocBook5 ("xlink:label",_) = True +isSectionAttr DocBook5 ("xlink:role",_) = True +isSectionAttr DocBook5 ("xlink:show",_) = True +isSectionAttr DocBook5 ("xlink:title",_) = True +isSectionAttr DocBook5 ("xlink:to",_) = True +isSectionAttr DocBook5 ("xlink:type",_) = True +isSectionAttr DocBook4 ("arch",_) = True +isSectionAttr DocBook4 ("condition",_) = True +isSectionAttr DocBook4 ("conformance",_) = True +isSectionAttr DocBook4 ("lang",_) = True +isSectionAttr DocBook4 ("os",_) = True +isSectionAttr DocBook4 ("revision",_) = True +isSectionAttr DocBook4 ("security",_) = True +isSectionAttr DocBook4 ("vendor",_) = True +isSectionAttr _ (_,_) = False \ No newline at end of file diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index 842aed7ae..46203eeae 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -11,9 +11,14 @@ import Text.Pandoc.Builder docbook :: (ToPandoc a) => a -> String docbook = docbookWithOpts def{ writerWrapText = WrapNone } +docbook5 :: (ToPandoc a) => a -> String +docbook5 = docbook5WithOpts def{ writerWrapText = WrapNone } + docbookWithOpts :: ToPandoc a => WriterOptions -> a -> String docbookWithOpts opts = unpack . purely (writeDocbook4 opts) . toPandoc +docbook5WithOpts :: ToPandoc a => WriterOptions -> a -> String +docbook5WithOpts opts = unpack . purely (writeDocbook5 opts) . toPandoc {- "my test" =: X =?> Y @@ -366,4 +371,36 @@ tests = [ testGroup "line blocks" ] ] ] + , testGroup "section attributes" $ + let + headers = headerWith ("myid1",[],[("role","internal"),("xml:id","anotherid"),("dir","rtl")]) 1 "header1" + <> headerWith ("myid2",[],[("invalidname","value"),("arch","linux"),("dir","invaliddir")]) 1 "header2" + in + [ test docbook5 "sections with attributes (db5)" $ + headers =?> + unlines [ "
" + , " header1" + , " " + , " " + , "
" + , "
" + , " header2" + , " " + , " " + , "
" + ] + , test docbook "sections with attributes (db4)" $ + headers =?> + unlines [ "" + , " header1" + , " " + , " " + , "" + , "" + , " header2" + , " " + , " " + , "" + ] + ] ] -- cgit v1.2.3 From 84d8f3efd8a45194752209b7d55b18a2291ed1ae Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 10:07:51 -0700 Subject: RST writer: use NonEmpty for init, last. --- src/Text/Pandoc/Writers/RST.hs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 0b9fc8331..983ef412a 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -17,7 +17,7 @@ module Text.Pandoc.Writers.RST ( writeRST, flatten ) where import Control.Monad.State.Strict import Data.Char (isSpace) import Data.List (transpose, intersperse, foldl') -import Data.List.NonEmpty (nonEmpty) +import qualified Data.List.NonEmpty as NE import Data.Maybe (fromMaybe) import qualified Data.Text as T import Data.Text (Text) @@ -336,7 +336,7 @@ blockToRST (OrderedList (start, style', delim) items) = do then replicate (length items) "#." else take (length items) $ orderedListMarkers (start, style', delim) - let maxMarkerLength = maybe 0 maximum $ nonEmpty $ map T.length markers + let maxMarkerLength = maybe 0 maximum $ NE.nonEmpty $ map T.length markers let markers' = map (\m -> let s = maxMarkerLength - T.length m in m <> T.replicate s " ") markers contents <- zipWithM orderedListItemToRST markers' items @@ -538,11 +538,15 @@ flatten outer collapse f i = appendToLast f $ dropInlineParent i appendToLast :: [Inline] -> [Inline] -> [Inline] - appendToLast [] toAppend = [setInlineChildren outer toAppend] - appendToLast flattened toAppend - | isOuter lastFlat = init flattened <> [appendTo lastFlat toAppend] - | otherwise = flattened <> [setInlineChildren outer toAppend] - where lastFlat = last flattened + appendToLast flattened toAppend = + case NE.nonEmpty flattened of + Nothing -> [setInlineChildren outer toAppend] + Just xs -> + if isOuter lastFlat + then NE.init xs <> [appendTo lastFlat toAppend] + else flattened <> [setInlineChildren outer toAppend] + where + lastFlat = NE.last xs appendTo o i = mapNested (<> i) o isOuter i = emptyParent i == emptyParent outer emptyParent i = setInlineChildren i [] @@ -762,7 +766,7 @@ simpleTable opts blocksToDoc headers rows = do then return [] else fixEmpties <$> mapM (blocksToDoc opts) headers rowDocs <- mapM (fmap fixEmpties . mapM (blocksToDoc opts)) rows - let numChars = maybe 0 maximum . nonEmpty . map offset + let numChars = maybe 0 maximum . NE.nonEmpty . map offset let colWidths = map numChars $ transpose (headerDocs : rowDocs) let toRow = mconcat . intersperse (lblock 1 " ") . zipWith lblock colWidths let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths) -- cgit v1.2.3 From 4d041953f56b85d4db241cea11c764856ccbeebe Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 10:41:12 -0700 Subject: T.P.Readers.Metadata: made `yamlBsToMeta`, `yamlBsToRefs` polymorphic... on the parser state, instead of requiring ParserState. [API change] --- src/Text/Pandoc/Readers/Metadata.hs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Text/Pandoc/Readers/Metadata.hs b/src/Text/Pandoc/Readers/Metadata.hs index 927291776..f4a27496f 100644 --- a/src/Text/Pandoc/Readers/Metadata.hs +++ b/src/Text/Pandoc/Readers/Metadata.hs @@ -31,10 +31,10 @@ import Text.Pandoc.Error import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Shared -yamlBsToMeta :: PandocMonad m - => ParserT Text ParserState m (F MetaValue) +yamlBsToMeta :: (PandocMonad m, HasLastStrPosition st) + => ParserT Text st m (Future st MetaValue) -> BL.ByteString - -> ParserT Text ParserState m (F Meta) + -> ParserT Text st m (Future st Meta) yamlBsToMeta pMetaValue bstr = do case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of Right (YAML.Doc (YAML.Mapping _ _ o):_) @@ -63,11 +63,11 @@ lookupYAML t (YAML.Mapping _ _ m) = lookupYAML _ _ = Nothing -- Returns filtered list of references. -yamlBsToRefs :: PandocMonad m - => ParserT Text ParserState m (F MetaValue) +yamlBsToRefs :: (PandocMonad m, HasLastStrPosition st) + => ParserT Text st m (Future st MetaValue) -> (Text -> Bool) -- ^ Filter for id -> BL.ByteString - -> ParserT Text ParserState m (F [MetaValue]) + -> ParserT Text st m (Future st [MetaValue]) yamlBsToRefs pMetaValue idpred bstr = case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of Right (YAML.Doc o@YAML.Mapping{}:_) @@ -104,10 +104,10 @@ nodeToKey (YAML.Scalar _ (YAML.SStr t)) = Just t nodeToKey (YAML.Scalar _ (YAML.SUnknown _ t)) = Just t nodeToKey _ = Nothing -normalizeMetaValue :: PandocMonad m - => ParserT Text ParserState m (F MetaValue) +normalizeMetaValue :: (PandocMonad m, HasLastStrPosition st) + => ParserT Text st m (Future st MetaValue) -> Text - -> ParserT Text ParserState m (F MetaValue) + -> ParserT Text st m (Future st MetaValue) normalizeMetaValue pMetaValue x = -- Note: a standard quoted or unquoted YAML value will -- not end in a newline, but a "block" set off with @@ -129,10 +129,10 @@ checkBoolean t | t == T.pack "false" || t == T.pack "False" || t == T.pack "FALSE" = Just False | otherwise = Nothing -yamlToMetaValue :: PandocMonad m - => ParserT Text ParserState m (F MetaValue) +yamlToMetaValue :: (PandocMonad m, HasLastStrPosition st) + => ParserT Text st m (Future st MetaValue) -> YAML.Node YE.Pos - -> ParserT Text ParserState m (F MetaValue) + -> ParserT Text st m (Future st MetaValue) yamlToMetaValue pMetaValue (YAML.Scalar _ x) = case x of YAML.SStr t -> normalizeMetaValue pMetaValue t @@ -152,10 +152,10 @@ yamlToMetaValue pMetaValue (YAML.Mapping _ _ o) = fmap MetaMap <$> yamlMap pMetaValue o yamlToMetaValue _ _ = return $ return $ MetaString "" -yamlMap :: PandocMonad m - => ParserT Text ParserState m (F MetaValue) +yamlMap :: (PandocMonad m, HasLastStrPosition st) + => ParserT Text st m (Future st MetaValue) -> M.Map (YAML.Node YE.Pos) (YAML.Node YE.Pos) - -> ParserT Text ParserState m (F (M.Map Text MetaValue)) + -> ParserT Text st m (Future st (M.Map Text MetaValue)) yamlMap pMetaValue o = do kvs <- forM (M.toList o) $ \(key, v) -> do k <- maybe (throwError $ PandocParseError -- cgit v1.2.3 From ce418667ae8a3e6e5bbf2523eef43edf4f803bcf Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 10:41:55 -0700 Subject: Text.Pandoc.Parsing: remove F type synonym. Muse and Org were defining their own F anyway, with their own state. We therefore move this definition to the Markdown reader. --- src/Text/Pandoc/App/Opt.hs | 3 ++- src/Text/Pandoc/Parsing.hs | 7 ++----- src/Text/Pandoc/Readers/Markdown.hs | 2 ++ src/Text/Pandoc/Readers/Muse.hs | 2 +- src/Text/Pandoc/Readers/Org/Parsing.hs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Text/Pandoc/App/Opt.hs b/src/Text/Pandoc/App/Opt.hs index e5aaec9c5..c72f63464 100644 --- a/src/Text/Pandoc/App/Opt.hs +++ b/src/Text/Pandoc/App/Opt.hs @@ -686,7 +686,8 @@ yamlToMeta (Mapping _ _ m) = either (fail . show) return $ runEverything (yamlMap pMetaString m) where pMetaString = pure . MetaString <$> P.manyChar P.anyChar - runEverything p = runPure (P.readWithM p def "") + runEverything p = + runPure (P.readWithM p (def :: P.ParserState) "") >>= fmap (Meta . flip P.runF def) yamlToMeta _ = return mempty diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 10a08d410..44e6af59e 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -112,7 +112,6 @@ module Text.Pandoc.Parsing ( take1WhileP, citeKey, Parser, ParserT, - F, Future(..), runF, askF, @@ -229,8 +228,6 @@ type ParserT = ParsecT newtype Future s a = Future { runDelayed :: Reader s a } deriving (Monad, Applicative, Functor) -type F = Future ParserState - runF :: Future s a -> s -> a runF = runReader . runDelayed @@ -1169,7 +1166,7 @@ data ParserState = ParserState stateInNote :: Bool, -- ^ True if parsing note contents stateNoteNumber :: Int, -- ^ Last note number for citations stateMeta :: Meta, -- ^ Document metadata - stateMeta' :: F Meta, -- ^ Document metadata + stateMeta' :: Future ParserState Meta, -- ^ Document metadata stateCitations :: M.Map Text Text, -- ^ RST-style citations stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used stateIdentifiers :: Set.Set Text, -- ^ Header identifiers used @@ -1348,7 +1345,7 @@ data QuoteContext type NoteTable = [(Text, Text)] -type NoteTable' = M.Map Text (SourcePos, F Blocks) +type NoteTable' = M.Map Text (SourcePos, Future ParserState Blocks) -- used in markdown reader newtype Key = Key Text deriving (Show, Read, Eq, Ord) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index a86286b3a..7c557b5a7 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -52,6 +52,8 @@ import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs) type MarkdownParser m = ParserT Text ParserState m +type F = Future ParserState + -- | Read markdown from an input string and return a Pandoc document. readMarkdown :: PandocMonad m => ReaderOptions -- ^ Reader options diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index b4eea9d3a..698bfd3d7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -36,7 +36,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (PandocParsecError)) import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (F) +import Text.Pandoc.Parsing import Text.Pandoc.Shared (crFilter, trimr, tshow) -- | Read Muse from an input string and return a Pandoc document. diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index d33920d47..6ed24a602 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -106,8 +106,8 @@ module Text.Pandoc.Readers.Org.Parsing import Data.Text (Text) import Text.Pandoc.Readers.Org.ParserState -import Text.Pandoc.Parsing hiding (F, anyLine, blanklines, newline, - parseFromString) +import Text.Pandoc.Parsing hiding (anyLine, blanklines, newline, + parseFromString) import qualified Text.Pandoc.Parsing as P import Control.Monad (guard) -- cgit v1.2.3 From bea86f394e6bc77d7441bb98f10a8b8ccfee04c9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 11:23:41 -0700 Subject: Markdown reader: export `yamlMetaBlock`. [API change] This will allow us to parse YAML metadata blocks in other readers, potentially. --- src/Text/Pandoc/Readers/Markdown.hs | 40 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 7c557b5a7..8d68510c5 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -15,6 +15,7 @@ Conversion of markdown-formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Markdown ( readMarkdown, + yamlMetaBlock, yamlToMeta, yamlToRefs ) where @@ -274,24 +275,29 @@ pandocTitleBlock = do $ nullMeta updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' } -yamlMetaBlock :: PandocMonad m => MarkdownParser m (F Blocks) -yamlMetaBlock = do +yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m) + => ParserT Text st m (Future st Blocks) + -> ParserT Text st m (Future st Meta) +yamlMetaBlock parser = try $ do + string "---" + blankline + notFollowedBy blankline -- if --- is followed by a blank it's an HRULE + rawYamlLines <- manyTill anyLine stopLine + -- by including --- and ..., we allow yaml blocks with just comments: + let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."])) + optional blanklines + yamlBsToMeta (fmap B.toMetaValue <$> parser) + $ UTF8.fromTextLazy $ TL.fromStrict rawYaml + +yamlMetaBlock' :: PandocMonad m => MarkdownParser m (F Blocks) +yamlMetaBlock' = do guardEnabled Ext_yaml_metadata_block - try $ do - string "---" - blankline - notFollowedBy blankline -- if --- is followed by a blank it's an HRULE - rawYamlLines <- manyTill anyLine stopLine - -- by including --- and ..., we allow yaml blocks with just comments: - let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."])) - optional blanklines - newMetaF <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks) - $ UTF8.fromTextLazy $ TL.fromStrict rawYaml - -- Since `<>` is left-biased, existing values are not touched: - updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF } - return mempty + newMetaF <- yamlMetaBlock parseBlocks + -- Since `<>` is left-biased, existing values are not touched: + updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF } + return mempty -stopLine :: PandocMonad m => MarkdownParser m () +stopLine :: PandocMonad m => ParserT Text st m () stopLine = try $ (string "---" <|> string "...") >> blankline >> return () mmdTitleBlock :: PandocMonad m => MarkdownParser m () @@ -456,7 +462,7 @@ block :: PandocMonad m => MarkdownParser m (F Blocks) block = do res <- choice [ mempty <$ blanklines , codeBlockFenced - , yamlMetaBlock + , yamlMetaBlock' -- note: bulletList needs to be before header because of -- the possibility of empty list items: - , bulletList -- cgit v1.2.3 From 2274eb88a4dddf622d86bee94bb6f20db6e148b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 11:37:09 -0700 Subject: Move yamlMetaBlock from Markdown reader to T.P.Readers.Metadata. --- src/Text/Pandoc/Readers/Markdown.hs | 24 ++---------------------- src/Text/Pandoc/Readers/Metadata.hs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 8d68510c5..6c3947a81 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -15,7 +15,6 @@ Conversion of markdown-formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Markdown ( readMarkdown, - yamlMetaBlock, yamlToMeta, yamlToRefs ) where @@ -29,7 +28,6 @@ import Data.Maybe import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T -import qualified Data.Text.Lazy as TL import qualified Data.ByteString.Lazy as BL import System.FilePath (addExtension, takeExtension) import Text.HTML.TagSoup hiding (Row) @@ -47,9 +45,8 @@ import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared -import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (fromEntities) -import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs) +import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock) type MarkdownParser m = ParserT Text ParserState m @@ -275,31 +272,14 @@ pandocTitleBlock = do $ nullMeta updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' } -yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m) - => ParserT Text st m (Future st Blocks) - -> ParserT Text st m (Future st Meta) -yamlMetaBlock parser = try $ do - string "---" - blankline - notFollowedBy blankline -- if --- is followed by a blank it's an HRULE - rawYamlLines <- manyTill anyLine stopLine - -- by including --- and ..., we allow yaml blocks with just comments: - let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."])) - optional blanklines - yamlBsToMeta (fmap B.toMetaValue <$> parser) - $ UTF8.fromTextLazy $ TL.fromStrict rawYaml - yamlMetaBlock' :: PandocMonad m => MarkdownParser m (F Blocks) yamlMetaBlock' = do guardEnabled Ext_yaml_metadata_block - newMetaF <- yamlMetaBlock parseBlocks + newMetaF <- yamlMetaBlock (fmap B.toMetaValue <$> parseBlocks) -- Since `<>` is left-biased, existing values are not touched: updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF } return mempty -stopLine :: PandocMonad m => ParserT Text st m () -stopLine = try $ (string "---" <|> string "...") >> blankline >> return () - mmdTitleBlock :: PandocMonad m => MarkdownParser m () mmdTitleBlock = do guardEnabled Ext_mmd_title_block diff --git a/src/Text/Pandoc/Readers/Metadata.hs b/src/Text/Pandoc/Readers/Metadata.hs index f4a27496f..cb141cba5 100644 --- a/src/Text/Pandoc/Readers/Metadata.hs +++ b/src/Text/Pandoc/Readers/Metadata.hs @@ -14,6 +14,7 @@ Parse YAML/JSON metadata to 'Pandoc' 'Meta'. module Text.Pandoc.Readers.Metadata ( yamlBsToMeta, yamlBsToRefs, + yamlMetaBlock, yamlMap ) where import Control.Monad @@ -30,6 +31,8 @@ import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Shared +import qualified Data.Text.Lazy as TL +import qualified Text.Pandoc.UTF8 as UTF8 yamlBsToMeta :: (PandocMonad m, HasLastStrPosition st) => ParserT Text st m (Future st MetaValue) @@ -171,3 +174,20 @@ yamlMap pMetaValue o = do return $ do v' <- fv return (k, v') + +-- | Parse a YAML metadata block using the supplied 'MetaValue' parser. +yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m) + => ParserT Text st m (Future st MetaValue) + -> ParserT Text st m (Future st Meta) +yamlMetaBlock parser = try $ do + string "---" + blankline + notFollowedBy blankline -- if --- is followed by a blank it's an HRULE + rawYamlLines <- manyTill anyLine stopLine + -- by including --- and ..., we allow yaml blocks with just comments: + let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."])) + optional blanklines + yamlBsToMeta parser $ UTF8.fromTextLazy $ TL.fromStrict rawYaml + +stopLine :: Monad m => ParserT Text st m () +stopLine = try $ (string "---" <|> string "...") >> blankline >> return () -- cgit v1.2.3 From c389211e2f678d9327cd2c008d54a1e438f07a07 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 15:45:09 -0700 Subject: Support `yaml_metadata_block` extension form commonmark, gfm. This is a bit more limited than with markdown, as documented in the manual: - The YAML block must be the first thing in the input. - The leaf notes are parsed in isolation from the rest of the document. So, for example, you can't use reference links if the references are defined later in the document. Closes #6537. --- MANUAL.txt | 16 ++++++++++++++++ src/Text/Pandoc/Extensions.hs | 3 +++ src/Text/Pandoc/Parsing.hs | 2 +- src/Text/Pandoc/Readers/CommonMark.hs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index bc9e226e9..845d1dbba 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4240,6 +4240,22 @@ will be interpreted as markdown. For example: \renewcommand{\section}[1]{\clearpage\oldsection{#1}} ``` +Note: the `yaml_metadata_block` extension works with +`commonmark` as well as `markdown` (and it is enabled by default +in `gfm` and `commonmark_x`). However, in these formats the +following restrictions apply: + +- The YAML metadata block must occur at the beginning of the + document (and there can be only one). If multiple files are + given as arguments to pandoc, only the first can be a YAML + metadata block. + +- The leaf nodes of the YAML structure are parsed in isolation from + each other and from the rest of the document. So, for + example, you can't use a reference link in these contexts + if the link definition is somewhere else in the document. + + ## Backslash escapes #### Extension: `all_symbols_escapable` #### diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 266a09e3c..6423d5f56 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -354,6 +354,7 @@ getDefaultExtensions "gfm" = extensionsFromList , Ext_strikeout , Ext_task_lists , Ext_emoji + , Ext_yaml_metadata_block ] getDefaultExtensions "commonmark" = extensionsFromList [Ext_raw_html] @@ -379,6 +380,7 @@ getDefaultExtensions "commonmark_x" = extensionsFromList , Ext_raw_attribute , Ext_implicit_header_references , Ext_attributes + , Ext_yaml_metadata_block ] getDefaultExtensions "org" = extensionsFromList [Ext_citations, @@ -511,6 +513,7 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_implicit_header_references , Ext_attributes , Ext_sourcepos + , Ext_yaml_metadata_block ] getAll "commonmark_x" = getAll "commonmark" getAll "org" = autoIdExtensions <> diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 44e6af59e..0c2078721 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -517,7 +517,7 @@ parseFromString :: (Stream s m Char, IsString s) -> ParserT s st m r parseFromString parser str = do oldPos <- getPosition - setPosition $ initialPos "chunk" + setPosition $ initialPos " chunk" oldInput <- getInput setInput $ fromString $ T.unpack str result <- parser diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 150a837e4..244f77940 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -26,13 +26,43 @@ import Text.Pandoc.Definition import Text.Pandoc.Builder as B import Text.Pandoc.Options import Text.Pandoc.Error +import Text.Pandoc.Readers.Metadata (yamlMetaBlock) import Control.Monad.Except import Data.Functor.Identity (runIdentity) import Data.Typeable +import Text.Pandoc.Parsing (runParserT, getPosition, sourceLine, + runF, defaultParserState, take1WhileP, option) +import qualified Data.Text as T -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s + | isEnabled Ext_yaml_metadata_block opts + , "---" `T.isPrefixOf` s = do + let metaValueParser = do + inp <- option "" $ take1WhileP (const True) + case runIdentity + (commonmarkWith (specFor opts) "metadata value" inp) of + Left _ -> mzero + Right (Cm bls :: Cm () Blocks) + -> return $ return $ B.toMetaValue bls + res <- runParserT (do meta <- yamlMetaBlock metaValueParser + pos <- getPosition + return (meta, pos)) + defaultParserState "YAML metadata" s + case res of + Left _ -> readCommonMarkBody opts s + Right (meta, pos) -> do + let dropLines 0 = id + dropLines n = dropLines (n - 1) . T.drop 1 . T.dropWhile (/='\n') + let metaLines = sourceLine pos - 1 + let body = T.replicate metaLines "\n" <> dropLines metaLines s + Pandoc _ bs <- readCommonMarkBody opts body + return $ Pandoc (runF meta defaultParserState) bs + | otherwise = readCommonMarkBody opts s + +readCommonMarkBody :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readCommonMarkBody opts s | isEnabled Ext_sourcepos opts = case runIdentity (commonmarkWith (specFor opts) "" s) of Left err -> throwError $ PandocParsecError s err -- cgit v1.2.3 From 13021313e87a3074a113c1b81bcd8e5d28a279fb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 20 Mar 2021 18:40:18 -0700 Subject: Update changelog, AUTHORS. --- AUTHORS.md | 3 ++ changelog.md | 89 ++++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index b57bc2e7b..c196d7399 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -35,6 +35,7 @@ - Benoit Schweblin - Bjorn Buckwalter - Blake Eryx +- Bodigrim - Bradley Kuhn - Brent Yorgey - Brian Leung @@ -77,6 +78,7 @@ - Emerson Harkin - Emily Eisenberg - Eric Kow +- Erik Rask - Eric Seidel - Eric Schrijver - Ethan Riley @@ -86,6 +88,7 @@ - Florian Beeres - Florian Eitel - Florian Klink +- Florian Kohrt - Francesco Occhipinti - François Gannaz - Frederik Elwert diff --git a/changelog.md b/changelog.md index 9b3485f5b..254a62315 100644 --- a/changelog.md +++ b/changelog.md @@ -2,10 +2,17 @@ ## pandoc 2.13 (2021-03-21) - * Fix fallback to default partials when custom templates are - used. If the directory containing a template does not - contain the partial, it should be sought in the default - templates, but this was not working properly (#7164). + * Support `yaml_metadata_block` extension for `commonmark`, `gfm` (#6537). + This supported is a bit more limited than with pandoc's + `markdown`. The YAML block must be the first thing in the input, + and the leaf notes are parsed in isolation from the rest of + the document. So, for example, you can't use reference + links if the references are defined later in the document. + + * Fix fallback to default partials when custom templates are used. + If the directory containing a template does not contain the partial, + it should be sought in the default templates, but this was not + working properly (#7164). * Handle `nocite` better with `--biblatex` and `--natbib` (#4585). Previously the nocite metadata field was ignored with these formats. @@ -18,21 +25,29 @@ * Text.Pandoc.Shared: - + Remove `backslashEscapes`, `escapeStringUsing` [API - change]. Replace these inefficient association list - lookups with more efficient escaping functions in the - writers that used them (for a 10-25% performance boost + + Remove `backslashEscapes`, `escapeStringUsing` [API change]. Replace + these inefficient association list lookups with more efficient escaping + functions in the writers that used them (for a 10-25% performance boost in org, haddock, rtf, texinfo writers). - + Remove `ToString`, `ToText` typeclasses [API change]. - These were needed for the transition from String to Text, - but they are no longer used and may clash with other - things. + + Remove `ToString`, `ToText` typeclasses [API change]. These were needed + for the transition from String to Text, but they are no longer used and + may clash with other things. + Simplify `compactDL`. - * Text.Pandoc.Parsing: Change type of `readWithM` so that it - is no longer polymorphic. The `ToText` class has been - removed, and now that we've completed the transition to Text - we no longer need this to operate on Strings. + * Text.Pandoc.Parsing: + + + Change type of `readWithM` so that it is no longer polymorphic + [API change]. The `ToText` class has been removed, and now that we've + completed the transition to Text we no longer need this to operate + on Strings. + + Remove `F` type synonym [API change]. Muse and Org were defining their + own `F` anyway. + + * Text.Pandoc.Readers.Metadata: + + + Export `yamlMetaBlock` [API change]. + + Make `yamlBsToMeta`, `yamlBsToRefs` polymorphic on the parser state + [API change]. * Markdown reader: Fix regression with `tex_math_backslash` (#7155). @@ -50,6 +65,11 @@ * RST reader: fix logic for ending comments (#7134). Previously comments sometimes got extended too far. + * DocBook writer: include Header attributes as XML attributes on + section (Erik Rask). Attributes with key names that are not allowed + as XML attributes are dropped, as are attributes with invalid values + and `xml:id` (DocBook 5) and `id` (DocBook 4). + * Docx writer: + Make `nsid` in `abstractNum` deterministic. Previously we assigned @@ -82,17 +102,16 @@ * Text.Pandoc.Logging: Add `DuplicateAttribute` constructor to `LogMessage`. [API change] - * Use `-j4` for linux release build. This speeds up the build - dramatically on arm. + * Use `-j4` for linux release build. This speeds up the build dramatically + on arm. - * cabal.project: remove ghcoptions. Move flags to top level, so - they can be set differently on the command line. + * cabal.project: remove ghcoptions. Move flags to top level, so they can + be set differently on the command line. * Require latest texmath, skylighting, citeproc, jira-wiki-markup. - (The latest skylighting fixes a bad bug with Haskell syntax - highlighting.) Narrow version bounds for texmath, - skylighting, and citeproc, since the test output depend on - them. + (The latest skylighting fixes a bad bug with Haskell syntax highlighting.) + Narrow version bounds for texmath, skylighting, and citeproc, since + the test output depend on them. * Use doclayout 0.3.0.2. This significantly reduces the time and memory needed to compile pandoc. @@ -107,22 +126,20 @@ * Test improvements: - + Use `getExecutablePath` from base, avoiding the dependency - on `executable-path`. - + Factor out `setupEnvironment` in Helpers, to avoid code - duplication. - + Fix finding of data files by setting teh `pandoc_datadir` - environment variable when we shell out to pandoc. This - avoids the need to use `--data-dir` for the tests, which - caused problems finding `pandoc.lua` when compiling - without the `embed_data_files` flag (#7163). + + Use `getExecutablePath` from base, avoiding the dependency on + `executable-path`. + + Factor out `setupEnvironment` in Helpers, to avoid code duplication. + + Fix finding of data files by setting teh `pandoc_datadir` environment + variable when we shell out to pandoc. This avoids the need to use + `--data-dir` for the tests, which caused problems finding `pandoc.lua` + when compiling without the `embed_data_files` flag (#7163). * Benchmark improvements: + Build `+RTS -A8m -RTS` into default ghc-options for benchmark. - This is necessary to get accurate benchmark results; otherwise - we are largely measuring garbage collecting, some not related - to the current benchmark. + This is necessary to get accurate benchmark results; otherwise we + are largely measuring garbage collecting, some not related to the + current benchmark. + Allow specifying BASELINE file in 'make bench' for comparison (otherwise the latest benchmark is chosen by default). + Force `readFile` in benchmarks early (Bodigrim). -- cgit v1.2.3 From 052056289fc6f884a2a8799dacca64a16248a5c2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 21 Mar 2021 23:38:47 -0700 Subject: Simplify T.P.Asciify and export toAsciiText [API change]. Instead of encoding a giant (and incomplete) map, we now just use unicode-transforms to normalize the text to a canonical decomposition, and manipulate the result. The new `toAsciiText` is equivalent to the old `T.pack . mapMaybe toAsciiChar . T.unpack` but should be faster. --- src/Text/Pandoc/Asciify.hs | 403 ++------------------------------------------- src/Text/Pandoc/Parsing.hs | 6 +- src/Text/Pandoc/Shared.hs | 4 +- 3 files changed, 18 insertions(+), 395 deletions(-) diff --git a/src/Text/Pandoc/Asciify.hs b/src/Text/Pandoc/Asciify.hs index 01a7b624a..620546c13 100644 --- a/src/Text/Pandoc/Asciify.hs +++ b/src/Text/Pandoc/Asciify.hs @@ -10,396 +10,19 @@ Function to convert accented latin letters to their unaccented ascii equivalents (used in constructing HTML identifiers). -} -module Text.Pandoc.Asciify (toAsciiChar) +module Text.Pandoc.Asciify (toAsciiChar, toAsciiText) where -import Data.Char (isAscii) -import qualified Data.Map as M +import Data.Char (isAscii, isMark) +import qualified Data.Text.Normalize as TN +import Data.Text (Text) +import qualified Data.Text as T -toAsciiChar :: Char -> Maybe Char -toAsciiChar c | isAscii c = Just c - | otherwise = M.lookup c asciiMap +toAsciiText :: Text -> Text +toAsciiText = T.filter isAscii . TN.normalize (TN.NFD) -asciiMap :: M.Map Char Char -asciiMap = M.fromList - [('\192','A') - ,('\193','A') - ,('\194','A') - ,('\195','A') - ,('\196','A') - ,('\197','A') - ,('\199','C') - ,('\200','E') - ,('\201','E') - ,('\202','E') - ,('\203','E') - ,('\204','I') - ,('\205','I') - ,('\206','I') - ,('\207','I') - ,('\209','N') - ,('\210','O') - ,('\211','O') - ,('\212','O') - ,('\213','O') - ,('\214','O') - ,('\217','U') - ,('\218','U') - ,('\219','U') - ,('\220','U') - ,('\221','Y') - ,('\224','a') - ,('\225','a') - ,('\226','a') - ,('\227','a') - ,('\228','a') - ,('\229','a') - ,('\231','c') - ,('\232','e') - ,('\233','e') - ,('\234','e') - ,('\235','e') - ,('\236','i') - ,('\237','i') - ,('\238','i') - ,('\239','i') - ,('\241','n') - ,('\242','o') - ,('\243','o') - ,('\244','o') - ,('\245','o') - ,('\246','o') - ,('\249','u') - ,('\250','u') - ,('\251','u') - ,('\252','u') - ,('\253','y') - ,('\255','y') - ,('\256','A') - ,('\257','a') - ,('\258','A') - ,('\259','a') - ,('\260','A') - ,('\261','a') - ,('\262','C') - ,('\263','c') - ,('\264','C') - ,('\265','c') - ,('\266','C') - ,('\267','c') - ,('\268','C') - ,('\269','c') - ,('\270','D') - ,('\271','d') - ,('\274','E') - ,('\275','e') - ,('\276','E') - ,('\277','e') - ,('\278','E') - ,('\279','e') - ,('\280','E') - ,('\281','e') - ,('\282','E') - ,('\283','e') - ,('\284','G') - ,('\285','g') - ,('\286','G') - ,('\287','g') - ,('\288','G') - ,('\289','g') - ,('\290','G') - ,('\291','g') - ,('\292','H') - ,('\293','h') - ,('\296','I') - ,('\297','i') - ,('\298','I') - ,('\299','i') - ,('\300','I') - ,('\301','i') - ,('\302','I') - ,('\303','i') - ,('\304','I') - ,('\305','i') - ,('\308','J') - ,('\309','j') - ,('\310','K') - ,('\311','k') - ,('\313','L') - ,('\314','l') - ,('\315','L') - ,('\316','l') - ,('\317','L') - ,('\318','l') - ,('\323','N') - ,('\324','n') - ,('\325','N') - ,('\326','n') - ,('\327','N') - ,('\328','n') - ,('\332','O') - ,('\333','o') - ,('\334','O') - ,('\335','o') - ,('\336','O') - ,('\337','o') - ,('\340','R') - ,('\341','r') - ,('\342','R') - ,('\343','r') - ,('\344','R') - ,('\345','r') - ,('\346','S') - ,('\347','s') - ,('\348','S') - ,('\349','s') - ,('\350','S') - ,('\351','s') - ,('\352','S') - ,('\353','s') - ,('\354','T') - ,('\355','t') - ,('\356','T') - ,('\357','t') - ,('\360','U') - ,('\361','u') - ,('\362','U') - ,('\363','u') - ,('\364','U') - ,('\365','u') - ,('\366','U') - ,('\367','u') - ,('\368','U') - ,('\369','u') - ,('\370','U') - ,('\371','u') - ,('\372','W') - ,('\373','w') - ,('\374','Y') - ,('\375','y') - ,('\376','Y') - ,('\377','Z') - ,('\378','z') - ,('\379','Z') - ,('\380','z') - ,('\381','Z') - ,('\382','z') - ,('\416','O') - ,('\417','o') - ,('\431','U') - ,('\432','u') - ,('\461','A') - ,('\462','a') - ,('\463','I') - ,('\464','i') - ,('\465','O') - ,('\466','o') - ,('\467','U') - ,('\468','u') - ,('\486','G') - ,('\487','g') - ,('\488','K') - ,('\489','k') - ,('\490','O') - ,('\491','o') - ,('\496','j') - ,('\500','G') - ,('\501','g') - ,('\504','N') - ,('\505','n') - ,('\512','A') - ,('\513','a') - ,('\514','A') - ,('\515','a') - ,('\516','E') - ,('\517','e') - ,('\518','E') - ,('\519','e') - ,('\520','I') - ,('\521','i') - ,('\522','I') - ,('\523','i') - ,('\524','O') - ,('\525','o') - ,('\526','O') - ,('\527','o') - ,('\528','R') - ,('\529','r') - ,('\530','R') - ,('\531','r') - ,('\532','U') - ,('\533','u') - ,('\534','U') - ,('\535','u') - ,('\536','S') - ,('\537','s') - ,('\538','T') - ,('\539','t') - ,('\542','H') - ,('\543','h') - ,('\550','A') - ,('\551','a') - ,('\552','E') - ,('\553','e') - ,('\558','O') - ,('\559','o') - ,('\562','Y') - ,('\563','y') - ,('\894',';') - ,('\7680','A') - ,('\7681','a') - ,('\7682','B') - ,('\7683','b') - ,('\7684','B') - ,('\7685','b') - ,('\7686','B') - ,('\7687','b') - ,('\7690','D') - ,('\7691','d') - ,('\7692','D') - ,('\7693','d') - ,('\7694','D') - ,('\7695','d') - ,('\7696','D') - ,('\7697','d') - ,('\7698','D') - ,('\7699','d') - ,('\7704','E') - ,('\7705','e') - ,('\7706','E') - ,('\7707','e') - ,('\7710','F') - ,('\7711','f') - ,('\7712','G') - ,('\7713','g') - ,('\7714','H') - ,('\7715','h') - ,('\7716','H') - ,('\7717','h') - ,('\7718','H') - ,('\7719','h') - ,('\7720','H') - ,('\7721','h') - ,('\7722','H') - ,('\7723','h') - ,('\7724','I') - ,('\7725','i') - ,('\7728','K') - ,('\7729','k') - ,('\7730','K') - ,('\7731','k') - ,('\7732','K') - ,('\7733','k') - ,('\7734','L') - ,('\7735','l') - ,('\7738','L') - ,('\7739','l') - ,('\7740','L') - ,('\7741','l') - ,('\7742','M') - ,('\7743','m') - ,('\7744','M') - ,('\7745','m') - ,('\7746','M') - ,('\7747','m') - ,('\7748','N') - ,('\7749','n') - ,('\7750','N') - ,('\7751','n') - ,('\7752','N') - ,('\7753','n') - ,('\7754','N') - ,('\7755','n') - ,('\7764','P') - ,('\7765','p') - ,('\7766','P') - ,('\7767','p') - ,('\7768','R') - ,('\7769','r') - ,('\7770','R') - ,('\7771','r') - ,('\7774','R') - ,('\7775','r') - ,('\7776','S') - ,('\7777','s') - ,('\7778','S') - ,('\7779','s') - ,('\7786','T') - ,('\7787','t') - ,('\7788','T') - ,('\7789','t') - ,('\7790','T') - ,('\7791','t') - ,('\7792','T') - ,('\7793','t') - ,('\7794','U') - ,('\7795','u') - ,('\7796','U') - ,('\7797','u') - ,('\7798','U') - ,('\7799','u') - ,('\7804','V') - ,('\7805','v') - ,('\7806','V') - ,('\7807','v') - ,('\7808','W') - ,('\7809','w') - ,('\7810','W') - ,('\7811','w') - ,('\7812','W') - ,('\7813','w') - ,('\7814','W') - ,('\7815','w') - ,('\7816','W') - ,('\7817','w') - ,('\7818','X') - ,('\7819','x') - ,('\7820','X') - ,('\7821','x') - ,('\7822','Y') - ,('\7823','y') - ,('\7824','Z') - ,('\7825','z') - ,('\7826','Z') - ,('\7827','z') - ,('\7828','Z') - ,('\7829','z') - ,('\7830','h') - ,('\7831','t') - ,('\7832','w') - ,('\7833','y') - ,('\7840','A') - ,('\7841','a') - ,('\7842','A') - ,('\7843','a') - ,('\7864','E') - ,('\7865','e') - ,('\7866','E') - ,('\7867','e') - ,('\7868','E') - ,('\7869','e') - ,('\7880','I') - ,('\7881','i') - ,('\7882','I') - ,('\7883','i') - ,('\7884','O') - ,('\7885','o') - ,('\7886','O') - ,('\7887','o') - ,('\7908','U') - ,('\7909','u') - ,('\7910','U') - ,('\7911','u') - ,('\7922','Y') - ,('\7923','y') - ,('\7924','Y') - ,('\7925','y') - ,('\7926','Y') - ,('\7927','y') - ,('\7928','Y') - ,('\7929','y') - ,('\8175','`') - ,('\8490','K') - ,('\8800','=') - ,('\8814','<') - ,('\8815','>') - ] +toAsciiChar :: Char -> Maybe Char +toAsciiChar c = case T.unpack (TN.normalize TN.NFD (T.singleton c)) of + (x:xs) | isAscii x + , all isMark xs + -> Just x + _ -> Nothing diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 0c2078721..847fd2e05 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -195,13 +195,13 @@ import Data.Default import Data.Functor (($>)) import Data.List (intercalate, transpose) import qualified Data.Map as M -import Data.Maybe (mapMaybe, fromMaybe) +import Data.Maybe (fromMaybe) import qualified Data.Set as Set import Data.String import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup.Entity (lookupEntity) -import Text.Pandoc.Asciify (toAsciiChar) +import Text.Pandoc.Asciify (toAsciiText) import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad, readFileFromDirs, report) @@ -1380,7 +1380,7 @@ registerHeader (ident,classes,kvs) header' = do then do let id' = uniqueIdent exts (B.toList header') ids let id'' = if Ext_ascii_identifiers `extensionEnabled` exts - then T.pack $ mapMaybe toAsciiChar $ T.unpack id' + then toAsciiText id' else id' updateState $ updateIdentifierList $ Set.insert id' updateState $ updateIdentifierList $ Set.insert id'' diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 23adff909..3292b32f4 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -123,7 +123,7 @@ import Text.HTML.TagSoup (RenderOptions (..), Tag (..), renderOptions, import Text.Pandoc.Builder (Blocks, Inlines, ToMetaValue (..)) import qualified Text.Pandoc.Builder as B import Data.Time -import Text.Pandoc.Asciify (toAsciiChar) +import Text.Pandoc.Asciify (toAsciiText) import Text.Pandoc.Definition import Text.Pandoc.Extensions (Extensions, Extension(..), extensionEnabled) import Text.Pandoc.Generic (bottomUp) @@ -478,7 +478,7 @@ inlineListToIdentifier exts = | otherwise = T.dropWhile (not . isAlpha) filterAscii | extensionEnabled Ext_ascii_identifiers exts - = T.pack . mapMaybe toAsciiChar . T.unpack + = toAsciiText | otherwise = id toIdent | extensionEnabled Ext_gfm_auto_identifiers exts = -- cgit v1.2.3 From 88d3d55909e0cc0612fcc0e7f86d81a51635fee0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 22 Mar 2021 14:28:03 -0700 Subject: Require latest skylighting (fixes a bug in XML syntax highlighting). --- pandoc.cabal | 4 ++-- stack.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index d5ebb2d6e..b60122dbe 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -479,8 +479,8 @@ library random >= 1 && < 1.3, safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, - skylighting >= 0.10.5 && < 0.10.6, - skylighting-core >= 0.10.5 && < 0.10.6, + skylighting >= 0.10.5.1 && < 0.10.6, + skylighting-core >= 0.10.5.1 && < 0.10.6, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, diff --git a/stack.yaml b/stack.yaml index 7264793cd..c57687c41 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,8 +10,8 @@ extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 - jira-wiki-markup-1.3.4 -- skylighting-core-0.10.5 -- skylighting-0.10.5 +- skylighting-core-0.10.5.1 +- skylighting-0.10.5.1 - doclayout-0.3.0.2 - citeproc-0.3.0.9 - texmath-0.12.2 -- cgit v1.2.3 From 56ce1fc12651e9cf60b882a18d72a410967d6540 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 24 Mar 2021 11:57:49 -0700 Subject: Fix DocBook reader mathml regression... ...caused by the switch in XML libraries. Also fixed a similar issue in JATS. Closes #7173. --- src/Text/Pandoc/Readers/DocBook.hs | 3 +- src/Text/Pandoc/Readers/JATS.hs | 8 ++- test/command/7173.md | 137 +++++++++++++++++++++++++++++++++++++ test/docbook-reader.docbook | 4 +- 4 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 test/command/7173.md diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 6f5bb0ad4..4f525cfb1 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1239,7 +1239,8 @@ equation e constructor = where mathMLEquations :: [Text] mathMLEquations = map writeTeX $ rights $ readMath - (\x -> qName (elName x) == "math" && qPrefix (elName x) == Just "mml") + (\x -> qName (elName x) == "math" && + qURI (elName x) == Just "http://www.w3.org/1998/Math/MathML") (readMathML . showElement) latexEquations :: [Text] diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 602f3b4f2..c068f3774 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -491,7 +491,8 @@ parseInline (Elem e) = "disp-formula" -> formula displayMath "inline-formula" -> formula math - "math" | qPrefix (elName e) == Just "mml" -> return . math $ mathML e + "math" | qURI (elName e) == Just "http://www.w3.org/1998/Math/MathML" + -> return . math $ mathML e "tex-math" -> return . math $ textContent e "email" -> return $ link ("mailto:" <> textContent e) "" @@ -514,8 +515,9 @@ parseInline (Elem e) = filterChildren isMathML whereToLook return . mconcat . take 1 . map constructor $ texMaths ++ mathMLs - isMathML x = qName (elName x) == "math" && - qPrefix (elName x) == Just "mml" + isMathML x = qName (elName x) == "math" && + qURI (elName x) == + Just "http://www.w3.org/1998/Math/MathML" removePrefix elname = elname { qPrefix = Nothing } codeWithLang = do let classes' = case attrValue "language" e of diff --git a/test/command/7173.md b/test/command/7173.md new file mode 100644 index 000000000..2599dc19b --- /dev/null +++ b/test/command/7173.md @@ -0,0 +1,137 @@ +``` +% pandoc -f docbook -t latex + + + + +
+ Untitled Document + Word + + 1+2 + + + 1+2 + + + + +
+^D +Word + +\[1 + 2\] +``` + +``` +% pandoc -f docbook -t latex + + + + +
+ Untitled Document + Word + + 1+2 + + + 1+2 + + + + +
+^D +Word + +\[1 + 2\] +``` + +``` +% pandoc -f docbook -t latex + + + + +
+ Untitled Document + Word + + 1+2 + + + 1+2 + + + + +
+^D +Word + +\[1 + 2\] +``` + +``` +% pandoc -f docbook -t latex + + + + +
+ Untitled Document + Word + + 1+2 + + + 1+2 + + + + +
+^D +Word + +\[1 + 2\] +``` + +``` +% pandoc -f docbook -t latex + + + + +
+ Untitled Document + Word + + 1+2 + + + 1+2 + + + + +
+^D +Word + +\[1 + 2\] +``` + diff --git a/test/docbook-reader.docbook b/test/docbook-reader.docbook index 5717d78d0..51e62942b 100644 --- a/test/docbook-reader.docbook +++ b/test/docbook-reader.docbook @@ -6,7 +6,9 @@ ]> -
+
Pandoc Test Suite -- cgit v1.2.3 From d2495d0c755b05ae35a31768da791e85e213df49 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 24 Mar 2021 14:36:29 -0700 Subject: Allow attoparsec 0.14.x. --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index b60122dbe..c3ff4dfb2 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -438,7 +438,7 @@ library SHA >= 1.6 && < 1.7, aeson >= 0.7 && < 1.6, aeson-pretty >= 0.8.5 && < 0.9, - attoparsec >= 0.12 && < 0.14, + attoparsec >= 0.12 && < 0.15, base64-bytestring >= 0.1 && < 1.3, binary >= 0.7 && < 0.11, blaze-html >= 0.9 && < 0.10, -- cgit v1.2.3 From 69d44b2c82b6a5428463b4f879f785e939f51280 Mon Sep 17 00:00:00 2001 From: TatianaPorras <52352806+TatianaPorras@users.noreply.github.com> Date: Sun, 28 Mar 2021 02:14:58 -0500 Subject: Fixed "The this" typo in lua-filters.md The documentation for Lua filters said "The this module defines..." This has been fixed to say "This module defines..." --- doc/lua-filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua-filters.md b/doc/lua-filters.md index df55dc44e..bb24723ce 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -3205,7 +3205,7 @@ Usage: # Module pandoc.List -The this module defines pandoc's list type. It comes with useful +This module defines pandoc's list type. It comes with useful methods and convenience functions. ## Constructor -- cgit v1.2.3 From a02ab4622d7ac301b6231bf450df1ae3e5a7a514 Mon Sep 17 00:00:00 2001 From: TatianaPorras <52352806+TatianaPorras@users.noreply.github.com> Date: Sat, 27 Mar 2021 19:48:45 -0500 Subject: Fixed typo #7159 This commit fixes the typo in the Lua filters documentation, see #7159 --- doc/lua-filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua-filters.md b/doc/lua-filters.md index bb24723ce..e2a65fe1e 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -109,7 +109,7 @@ element filtering function. In other words, filter entries will be called for each corresponding element in the document, getting the respective element as input. -The return of a filter function must one of the following: +The return of a filter function must be one of the following: - nil: this means that the object should remain unchanged. - a pandoc object: this must be of the same type as the input -- cgit v1.2.3 From e22d1fbb144d63d595c5db9225dd912b09fd938f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 29 Mar 2021 14:56:44 -0700 Subject: Powerpoint writer: allow monofont to be specified in metadata... ...not just using `--variable` on the command line (as in other writers). Closes #7187. --- src/Text/Pandoc/Writers/Powerpoint.hs | 2 +- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index ca3b74a1d..e0573beca 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -39,5 +39,5 @@ writePowerpoint opts (Pandoc meta blks) = do let blks' = walk fixDisplayMath blks let (pres, logMsgs) = documentToPresentation opts (Pandoc meta blks') mapM_ report logMsgs - archv <- presentationToArchive opts pres + archv <- presentationToArchive opts meta pres return $ fromArchive archv diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 0e515b3c2..f2f54a91c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -38,17 +38,19 @@ import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Options import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL +import Text.Pandoc.Writers.Shared (metaToContext) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isJust) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob -import Text.DocTemplates (FromContext(lookupContext)) +import Text.DocTemplates (FromContext(lookupContext), Context) +import Text.DocLayout (literal) import Text.TeXMath import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation -import Text.Pandoc.Shared (tshow) +import Text.Pandoc.Shared (tshow, stringify) import Skylighting (fromColor) import Data.List.NonEmpty (nonEmpty) @@ -97,6 +99,7 @@ data WriterEnv = WriterEnv { envRefArchive :: Archive , envDistArchive :: Archive , envUTCTime :: UTCTime , envOpts :: WriterOptions + , envContext :: Context Text , envPresentationSize :: (Integer, Integer) , envSlideHasHeader :: Bool , envInList :: Bool @@ -122,6 +125,7 @@ instance Default WriterEnv where , envDistArchive = emptyArchive , envUTCTime = posixSecondsToUTCTime 0 , envOpts = def + , envContext = mempty , envPresentationSize = (720, 540) , envSlideHasHeader = False , envInList = False @@ -168,7 +172,7 @@ runP env st p = evalStateT (runReaderT p env) st monospaceFont :: Monad m => P m T.Text monospaceFont = do - vars <- writerVariables <$> asks envOpts + vars <- asks envContext case lookupContext "monofont" vars of Just s -> return s Nothing -> return "Courier" @@ -304,8 +308,9 @@ makeSpeakerNotesMap (Presentation _ slides) = then Nothing else Just n -presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive -presentationToArchive opts pres = do +presentationToArchive :: PandocMonad m + => WriterOptions -> Meta -> Presentation -> m Archive +presentationToArchive opts meta pres = do distArchive <- toArchive . BL.fromStrict <$> P.readDefaultDataFile "reference.pptx" refArchive <- case writerReferenceDoc opts of @@ -321,10 +326,18 @@ presentationToArchive opts pres = do PandocSomeError "Could not determine presentation size" + -- note, we need writerTemplate to be Just _ or metaToContext does + -- nothing + context <- metaToContext opts{ writerTemplate = + writerTemplate opts <|> Just mempty } + (return . literal . stringify) + (return . literal . stringify) meta + let env = def { envRefArchive = refArchive , envDistArchive = distArchive , envUTCTime = utctime , envOpts = opts + , envContext = context , envPresentationSize = presSize , envSlideIdMap = makeSlideIdMap pres , envSpeakerNotesIdMap = makeSpeakerNotesMap pres -- cgit v1.2.3 From 7e887c0a226a96c7fdc358e9690e2b764db39dce Mon Sep 17 00:00:00 2001 From: Anti-Distinctlyminty Date: Tue, 30 Mar 2021 17:34:44 +0200 Subject: Update org.md (#7189) SELECT_TAGS supported since ac83b9c37c39a49878e7d864fb276c0e4caed338 --- doc/org.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/org.md b/doc/org.md index 522ccc23c..9ed20eafe 100644 --- a/doc/org.md +++ b/doc/org.md @@ -25,8 +25,7 @@ The following export keywords are supported: - LANGUAGE: document language; included as plain-text metadata field `lang`. The value should be a [BCP47 language tag]. -- SELECT_TAGS: tags which select a tree for export. Currently - *unsupported*. +- SELECT_TAGS: tags which select a tree for export. - EXCLUDE\_TAGS: tags which prevent a subtree from being exported. Fully supported. -- cgit v1.2.3 From fd0873c907ea0bc1fee9ecbca5f2d05e29c5fe5b Mon Sep 17 00:00:00 2001 From: Roman Beránek Date: Wed, 31 Mar 2021 22:26:09 +0200 Subject: Require text for trypandoc (#7193) --- pandoc.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index c3ff4dfb2..b2d7fa460 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -729,7 +729,7 @@ executable trypandoc main-is: trypandoc.hs hs-source-dirs: trypandoc if flag(trypandoc) - build-depends: aeson, http-types, wai >= 0.3, wai-extra >= 3.0.24 + build-depends: aeson, http-types, text, wai >= 0.3, wai-extra >= 3.0.24 buildable: True else buildable: False -- cgit v1.2.3 From 40da6c402beb64e47838254c04228098129165df Mon Sep 17 00:00:00 2001 From: niszet Date: Thu, 1 Apr 2021 08:44:34 +0900 Subject: Treat tabs as spaces in ODT Reader. (#7185) --- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 8 +++++++- test/Tests/Readers/Odt.hs | 1 + test/odt/native/tab.native | 1 + test/odt/odt/tab.odt | Bin 0 -> 10170 bytes 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/odt/native/tab.native create mode 100644 test/odt/odt/tab.odt diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index df90880fa..c4220b0db 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -577,7 +577,10 @@ read_spaces = matchingElement NsText "s" ( read_line_break :: InlineMatcher read_line_break = matchingElement NsText "line-break" $ returnV linebreak - +-- +read_tab :: InlineMatcher +read_tab = matchingElement NsText "tab" + $ returnV space -- read_span :: InlineMatcher read_span = matchingElement NsText "span" @@ -585,6 +588,7 @@ read_span = matchingElement NsText "span" $ matchChildContent [ read_span , read_spaces , read_line_break + , read_tab , read_link , read_note , read_citation @@ -604,6 +608,7 @@ read_paragraph = matchingElement NsText "p" $ matchChildContent [ read_span , read_spaces , read_line_break + , read_tab , read_link , read_note , read_citation @@ -630,6 +635,7 @@ read_header = matchingElement NsText "h" children <- ( matchChildContent [ read_span , read_spaces , read_line_break + , read_tab , read_link , read_note , read_citation diff --git a/test/Tests/Readers/Odt.hs b/test/Tests/Readers/Odt.hs index 9b5ec6b9e..edff4fe2c 100644 --- a/test/Tests/Readers/Odt.hs +++ b/test/Tests/Readers/Odt.hs @@ -176,6 +176,7 @@ namesOfTestsComparingToNative = [ "blockquote" , "referenceToText" , "simpleTable" , "simpleTableWithCaption" + , "tab" -- , "table" , "textMixedStyles" , "tableWithContents" diff --git a/test/odt/native/tab.native b/test/odt/native/tab.native new file mode 100644 index 000000000..948e81cd5 --- /dev/null +++ b/test/odt/native/tab.native @@ -0,0 +1 @@ +[Para [Str "Three",Space,Str "tabs",Space,Str "between",Space,Str "A",Space,Str "and",Space,Str "B",Space,Str "will",Space,Str "be",Space,Str "converted",Space,Str "to",Space,Str "one",Space,Str "Space:",Space,Str "A",Space,Str "B."]] \ No newline at end of file diff --git a/test/odt/odt/tab.odt b/test/odt/odt/tab.odt new file mode 100644 index 000000000..a2c80f45c Binary files /dev/null and b/test/odt/odt/tab.odt differ -- cgit v1.2.3 From 4371223d132ef822cff0ddee6099a5fe295385cb Mon Sep 17 00:00:00 2001 From: tecosaur Date: Fri, 2 Apr 2021 05:36:02 +0800 Subject: Org writer: Use LaTeX style maths deliminators (#7196) Org works better with LaTeX-style delimiters. --- src/Text/Pandoc/Writers/Org.hs | 4 ++-- test/writer.org | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 88a2b8314..d0c9813da 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -404,8 +404,8 @@ inlineToOrg (Str str) = return . literal $ escapeString str inlineToOrg (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath - then "$" <> literal str <> "$" - else "$$" <> literal str <> "$$" + then "\\(" <> literal str <> "\\)" + else "\\[" <> literal str <> "\\]" inlineToOrg il@(RawInline f str) | isRawFormat f = return $ literal str | otherwise = do diff --git a/test/writer.org b/test/writer.org index 34dd51f43..df9d2b3a5 100644 --- a/test/writer.org +++ b/test/writer.org @@ -600,14 +600,14 @@ Ellipses...and...and.... :END: - \cite[22-23]{smith.1899} -- $2+2=4$ -- $x \in y$ -- $\alpha \wedge \omega$ -- $223$ -- $p$-Tree +- \(2+2=4\) +- \(x \in y\) +- \(\alpha \wedge \omega\) +- \(223\) +- \(p\)-Tree - Here's some display math: - $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ -- Here's one that has a line break in it: $\alpha + \omega \times x^2$. + \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\] +- Here's one that has a line break in it: \(\alpha + \omega \times x^2\). These shouldn't be math: -- cgit v1.2.3 From 935d10769df682fa08d947a873ca7665136e0b79 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 2 Apr 2021 17:04:55 -0700 Subject: Fix "phrase" in DocBook: take classes from "role" not "class". Closes #7195. Revises #6438. --- src/Text/Pandoc/Readers/DocBook.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 4f525cfb1..ac3caa2c0 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1075,7 +1075,7 @@ parseInline (Elem e) = return $ spanWith (attrValue "id" e, [], []) mempty "phrase" -> do let ident = attrValue "id" e - let classes = T.words $ attrValue "class" e + let classes = T.words $ attrValue "role" e if ident /= "" || classes /= [] then innerInlines (spanWith (ident,classes,[])) else innerInlines id -- cgit v1.2.3 From 08598dedb1d83736b8e0c2d23577ceb2cdc431f5 Mon Sep 17 00:00:00 2001 From: obcat Date: Sun, 4 Apr 2021 01:36:54 +0900 Subject: Fix typo (#7200) --- doc/using-the-pandoc-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/using-the-pandoc-api.md b/doc/using-the-pandoc-api.md index df9380313..19ce8b274 100644 --- a/doc/using-the-pandoc-api.md +++ b/doc/using-the-pandoc-api.md @@ -14,7 +14,7 @@ and types is available at Pandoc is structured as a set of *readers*, which translate various input formats into an abstract syntax tree (the Pandoc AST) representing a structured document, and a set of -*writers*, which render this AST into various input formats. +*writers*, which render this AST into various output formats. Pictorially: ``` -- cgit v1.2.3 From 65a9d3a8786c23f79de9dcdf56ab7efb789726ff Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Apr 2021 08:49:03 -0700 Subject: SelfContained: use application/octet-stream for unknown mime types... instead of halting with an error. Closes #7202. --- src/Text/Pandoc/SelfContained.hs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index c9e20cad0..17c1e18c9 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -244,11 +244,10 @@ getData mimetype src let raw' = if ext `elem` [".gz", ".svgz"] then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw] else raw - mime <- case (mimetype, respMime) of - ("",Nothing) -> throwError $ PandocSomeError - $ "Could not determine mime type for `" <> src <> "'" - (x, Nothing) -> return x - (_, Just x ) -> return x + let mime = case (mimetype, respMime) of + ("",Nothing) -> "application/octet-stream" + (x, Nothing) -> x + (_, Just x ) -> x result <- if "text/css" `T.isPrefixOf` mime then do oldInputs <- getInputFiles -- cgit v1.2.3 From 038261ea529bc4516d7cee501db70020938dbf2b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 5 Apr 2021 21:45:52 +0200 Subject: JATS writer: escape disallows chars in identifiers XML identifiers must start with an underscore or letter, and can contain only a limited set of punctuation characters. Any IDs not adhering to these rules are rewritten by writing the offending characters as Uxxxx, where `xxxx` is the character's hex code. --- src/Text/Pandoc/Writers/JATS.hs | 27 ++-- src/Text/Pandoc/Writers/JATS/References.hs | 5 +- src/Text/Pandoc/Writers/JATS/Table.hs | 4 +- src/Text/Pandoc/XML.hs | 30 ++++- test/Tests/Writers/JATS.hs | 205 ++++++++++++++++------------- 5 files changed, 162 insertions(+), 109 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index a9369db7a..26f94cb03 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -239,7 +239,7 @@ languageFor classes = codeAttr :: Attr -> (Text, [(Text, Text)]) codeAttr (ident,classes,kvs) = (lang, attr) where - attr = [("id",ident) | not (T.null ident)] ++ + attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("language",lang) | not (T.null lang)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["code-type", "code-version", "executable", @@ -251,7 +251,8 @@ codeAttr (ident,classes,kvs) = (lang, attr) blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text) blockToJATS _ Null = return empty blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do - let idAttr = [("id", writerIdentifierPrefix opts <> id') | not (T.null id')] + let idAttr = [ ("id", writerIdentifierPrefix opts <> escapeNCName id') + | not (T.null id')] let otherAttrs = ["sec-type", "specific-use"] let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs] title' <- inlinesToJATS opts ils @@ -260,7 +261,7 @@ blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do inTagsSimple "title" title' $$ contents -- Bibliography reference: blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident = - inTags True "ref" [("id", ident)] . + inTags True "ref" [("id", escapeNCName ident)] . inTagsSimple "mixed-citation" <$> inlinesToJATS opts lst blockToJATS opts (Div ("refs",_,_) xs) = do @@ -271,14 +272,14 @@ blockToJATS opts (Div ("refs",_,_) xs) = do return $ inTagsIndented "ref-list" contents blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do contents <- blocksToJATS opts bs - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] return $ inTags True cls attr contents blockToJATS opts (Div (ident,_,kvs) bs) = do contents <- blocksToJATS opts bs - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] @@ -296,7 +297,7 @@ blockToJATS opts (Para [Image (ident,_,kvs) txt let capt = if null txt then empty else inTagsSimple "caption" $ inTagsSimple "p" alt - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["fig-type", "orientation", "position", "specific-use"]] let graphicattr = [("mimetype",maintype), @@ -307,7 +308,7 @@ blockToJATS opts (Para [Image (ident,_,kvs) txt capt $$ selfClosingTag "graphic" graphicattr blockToJATS _ (Para [Image (ident,_,kvs) _ (src, tit)]) = do let (maintype, subtype) = imageMimeType src kvs - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("mimetype", maintype), ("mime-subtype", subtype), ("xlink:href", src)] ++ @@ -434,7 +435,7 @@ inlineToJATS opts (Note contents) = do let notenum = case notes of (n, _):_ -> n + 1 [] -> 1 - thenote <- inTags True "fn" [("id","fn" <> tshow notenum)] + thenote <- inTags True "fn" [("id", "fn" <> tshow notenum)] <$> wrappedBlocksToJATS (not . isPara) opts (walk demoteHeaderAndRefs contents) modify $ \st -> st{ jatsNotes = (notenum, thenote) : notes } @@ -447,7 +448,7 @@ inlineToJATS opts (Cite _ lst) = inlineToJATS opts (Span ("",_,[]) ils) = inlinesToJATS opts ils inlineToJATS opts (Span (ident,_,kvs) ils) = do contents <- inlinesToJATS opts ils - let attr = [("id",ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs , k `elem` ["content-type", "rationale", @@ -488,9 +489,9 @@ inlineToJATS _ (Link _attr [Str t] (T.stripPrefix "mailto:" -> Just email, _)) return $ inTagsSimple "email" $ literal (escapeStringForXML email) inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do let attr = mconcat - [ [("id", ident) | not (T.null ident)] + [ [("id", escapeNCName ident) | not (T.null ident)] , [("alt", stringify txt) | not (null txt)] - , [("rid", src)] + , [("rid", escapeNCName src)] , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src] ] @@ -500,7 +501,7 @@ inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do contents <- inlinesToJATS opts txt return $ inTags False "xref" attr contents inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("ext-link-type", "uri"), ("xlink:href", src)] ++ [("xlink:title", tit) | not (T.null tit)] ++ @@ -518,7 +519,7 @@ inlineToJATS _ (Image (ident,_,kvs) _ (src, tit)) = do let subtype = fromMaybe "" $ lookup "mime-subtype" kvs `mplus` (T.drop 1 . T.dropWhile (/='/') <$> mbMT) - let attr = [("id", ident) | not (T.null ident)] ++ + let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("mimetype", maintype), ("mime-subtype", subtype), ("xlink:href", src)] ++ diff --git a/src/Text/Pandoc/Writers/JATS/References.hs b/src/Text/Pandoc/Writers/JATS/References.hs index 903144128..5b19fd034 100644 --- a/src/Text/Pandoc/Writers/JATS/References.hs +++ b/src/Text/Pandoc/Writers/JATS/References.hs @@ -29,7 +29,7 @@ import Text.Pandoc.Builder (Inlines) import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.JATS.Types -import Text.Pandoc.XML (escapeStringForXML, inTags) +import Text.Pandoc.XML (escapeNCName, escapeStringForXML, inTags) import qualified Data.Text as T referencesToJATS :: PandocMonad m @@ -46,7 +46,8 @@ referenceToJATS :: PandocMonad m referenceToJATS _opts ref = do let refType = referenceType ref let pubType = [("publication-type", refType) | not (T.null refType)] - let wrap = inTags True "ref" [("id", "ref-" <> unItemId (referenceId ref))] + let ident = escapeNCName $ "ref-" <> unItemId (referenceId ref) + let wrap = inTags True "ref" [("id", ident)] . inTags True "element-citation" pubType return . wrap . vcat $ [ authors diff --git a/src/Text/Pandoc/Writers/JATS/Table.hs b/src/Text/Pandoc/Writers/JATS/Table.hs index 465480f59..2e34900d2 100644 --- a/src/Text/Pandoc/Writers/JATS/Table.hs +++ b/src/Text/Pandoc/Writers/JATS/Table.hs @@ -24,7 +24,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.JATS.Types -import Text.Pandoc.XML (inTags, inTagsIndented, selfClosingTag) +import Text.Pandoc.XML (escapeNCName, inTags, inTagsIndented, selfClosingTag) import qualified Data.Text as T import qualified Text.Pandoc.Writers.AnnotatedTable as Ann @@ -216,7 +216,7 @@ cellToJats opts celltype (Ann.Cell (colspec :| _) _colNum cell) = toAttribs :: Attr -> [Text] -> [(Text, Text)] toAttribs (ident, _classes, kvs) knownAttribs = - (if T.null ident then id else (("id", ident) :)) $ + (if T.null ident then id else (("id", escapeNCName ident) :)) $ filter ((`elem` knownAttribs) . fst) kvs tableCellToJats :: PandocMonad m diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index 6dbbce1d2..79b4768ec 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -13,6 +13,7 @@ Functions for escaping and formatting XML. -} module Text.Pandoc.XML ( escapeCharForXML, escapeStringForXML, + escapeNCName, inTags, selfClosingTag, inTagsSimple, @@ -24,7 +25,7 @@ module Text.Pandoc.XML ( escapeCharForXML, html5Attributes, rdfaAttributes ) where -import Data.Char (isAscii, isSpace, ord) +import Data.Char (isAscii, isSpace, ord, isLetter, isDigit) import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup.Entity (lookupEntity, htmlEntities) @@ -119,8 +120,33 @@ html5EntityMap = foldr go mempty htmlEntities where ent' = T.takeWhile (/=';') (T.pack ent) _ -> entmap +-- | Converts a string into an NCName, i.e., an XML name without colons. +-- Disallowed characters are escaped using @ux%x@, where @%x@ is the +-- hexadecimal unicode identifier of the escaped character. +escapeNCName :: Text -> Text +escapeNCName t = case T.uncons t of + Nothing -> T.empty + Just (c, cs) -> escapeStartChar c <> T.concatMap escapeNCNameChar cs + where + escapeStartChar :: Char -> Text + escapeStartChar c = if isLetter c || c == '_' + then T.singleton c + else escapeChar c --- Unescapes XML entities + escapeNCNameChar :: Char -> Text + escapeNCNameChar c = if isNCNameChar c + then T.singleton c + else escapeChar c + + isNCNameChar :: Char -> Bool + isNCNameChar c = isLetter c || c `elem` ("_-.·" :: String) || isDigit c + || '\x0300' <= c && c <= '\x036f' + || '\x203f' <= c && c <= '\x2040' + + escapeChar :: Char -> Text + escapeChar = T.pack . printf "U%04X" . ord + +-- | Unescapes XML entities fromEntities :: Text -> Text fromEntities t = let (x, y) = T.break (== '&') t diff --git a/test/Tests/Writers/JATS.hs b/test/Tests/Writers/JATS.hs index 2f501c890..23c1686dc 100644 --- a/test/Tests/Writers/JATS.hs +++ b/test/Tests/Writers/JATS.hs @@ -1,21 +1,21 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.JATS (tests) where -import Data.Text (unpack) +import Data.Text (Text) import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder +import qualified Data.Text as T -jats :: (ToPandoc a) => a -> String -jats = unpack - . purely (writeJATS def{ writerWrapText = WrapNone }) - . toPandoc +jats :: (ToPandoc a) => a -> Text +jats = purely (writeJATS def{ writerWrapText = WrapNone }) + . toPandoc -jatsArticleAuthoring :: (ToPandoc a) => a -> String -jatsArticleAuthoring = unpack - . purely (writeJatsArticleAuthoring def{ writerWrapText = WrapNone }) +jatsArticleAuthoring :: (ToPandoc a) => a -> Text +jatsArticleAuthoring = + purely (writeJatsArticleAuthoring def{ writerWrapText = WrapNone }) . toPandoc {- @@ -32,89 +32,114 @@ which is in turn shorthand for infix 4 =: (=:) :: (ToString a, ToPandoc a) - => String -> (a, String) -> TestTree + => String -> (a, Text) -> TestTree (=:) = test jats tests :: [TestTree] -tests = [ testGroup "inline code" - [ "basic" =: code "@&" =?> "

@&

" - , "lang" =: codeWith ("", ["c"], []) "@&" =?> "

@&

" - ] - , testGroup "block code" - [ "basic" =: codeBlock "@&" =?> "@&" - , "lang" =: codeBlockWith ("", ["c"], []) "@&" =?> "@&" - ] - , testGroup "images" - [ "basic" =: - image "/url" "title" mempty - =?> "" - ] - , testGroup "inlines" - [ "Emphasis" =: emph "emphasized" - =?> "

emphasized

" +tests = + [ testGroup "inline code" + [ "basic" =: code "@&" =?> "

@&

" + , "lang" =: codeWith ("", ["c"], []) "@&" =?> "

@&

" + ] + , testGroup "block code" + [ "basic" =: codeBlock "@&" =?> "@&" + , "lang" =: codeBlockWith ("", ["c"], []) "@&" =?> "@&" + ] + , testGroup "images" + [ "basic" =: + image "/url" "title" mempty + =?> "" + ] + , testGroup "inlines" + [ "Emphasis" =: emph "emphasized" + =?> "

emphasized

" + + , test jatsArticleAuthoring "footnote in articleauthoring tag set" + ("test" <> note (para "footnote") =?> + unlines [ "

test" + , "

footnote

" + , "

" + ]) + ] + , "bullet list" =: bulletList [ plain $ text "first" + , plain $ text "second" + , plain $ text "third" + ] + =?> "\n\ + \ \n\ + \

first

\n\ + \
\n\ + \ \n\ + \

second

\n\ + \
\n\ + \ \n\ + \

third

\n\ + \
\n\ + \
" + , testGroup "definition lists" + [ "with internal link" =: definitionList [(link "#go" "" (str "testing"), + [plain (text "hi there")])] =?> + "\n\ + \ \n\ + \ testing\n\ + \ \n\ + \

hi there

\n\ + \
\n\ + \
\n\ + \
" + ] + , testGroup "math" + [ "escape |" =: para (math "\\sigma|_{\\{x\\}}") =?> + "

\n\ + \\n\ + \σ|{x}

" + ] + , testGroup "headers" + [ "unnumbered header" =: + headerWith ("foo",["unnumbered"],[]) 1 + (text "Header 1" <> note (plain $ text "note")) =?> + "\n\ + \ Header 1<xref ref-type=\"fn\" rid=\"fn1\">1</xref>\n\ + \" + , "unnumbered sub header" =: + headerWith ("foo",["unnumbered"],[]) 1 + (text "Header") + <> headerWith ("foo",["unnumbered"],[]) 2 + (text "Sub-Header") =?> + "\n\ + \ Header\n\ + \ \n\ + \ Sub-Header\n\ + \ \n\ + \" + , "containing image" =: + header 1 (image "imgs/foo.jpg" "" (text "Alt text")) =?> + "\n\ + \ <inline-graphic mimetype=\"image\" mime-subtype=\"jpeg\" xlink:href=\"imgs/foo.jpg\" />\n\ + \" + ] + + , testGroup "ids" + [ "non-ASCII in header ID" =: + headerWith ("smørbrød",[],[]) 1 (text "smørbrød") =?> + T.unlines [ "" + , " smørbrød" + , "" + ] + + , "disallowed symbol in header id" =: + headerWith ("i/o",[],[]) 1 (text "I/O") =?> + T.unlines [ "" + , " I/O" + , "" + ] + + , "disallowed symbols in internal link target" =: + link "#foo:bar" "" "baz" =?> + "

baz

" - , test jatsArticleAuthoring "footnote in articleauthoring tag set" - ("test" <> note (para "footnote") =?> - unlines [ "

test" - , "

footnote

" - , "

" - ]) - ] - , "bullet list" =: bulletList [ plain $ text "first" - , plain $ text "second" - , plain $ text "third" - ] - =?> "\n\ - \ \n\ - \

first

\n\ - \
\n\ - \ \n\ - \

second

\n\ - \
\n\ - \ \n\ - \

third

\n\ - \
\n\ - \
" - , testGroup "definition lists" - [ "with internal link" =: definitionList [(link "#go" "" (str "testing"), - [plain (text "hi there")])] =?> - "\n\ - \ \n\ - \ testing\n\ - \ \n\ - \

hi there

\n\ - \
\n\ - \
\n\ - \
" - ] - , testGroup "math" - [ "escape |" =: para (math "\\sigma|_{\\{x\\}}") =?> - "

\n\ - \\n\ - \σ|{x}

" - ] - , testGroup "headers" - [ "unnumbered header" =: - headerWith ("foo",["unnumbered"],[]) 1 - (text "Header 1" <> note (plain $ text "note")) =?> - "\n\ - \ Header 1<xref ref-type=\"fn\" rid=\"fn1\">1</xref>\n\ - \" - , "unnumbered sub header" =: - headerWith ("foo",["unnumbered"],[]) 1 - (text "Header") - <> headerWith ("foo",["unnumbered"],[]) 2 - (text "Sub-Header") =?> - "\n\ - \ Header\n\ - \ \n\ - \ Sub-Header\n\ - \ \n\ - \" - , "containing image" =: - header 1 (image "imgs/foo.jpg" "" (text "Alt text")) =?> - "\n\ - \ <inline-graphic mimetype=\"image\" mime-subtype=\"jpeg\" xlink:href=\"imgs/foo.jpg\" />\n\ - \" - ] - ] + , "code id starting with a number" =: + codeWith ("7y",[],[]) "print 5" =?> + "

print 5

" + ] + ] -- cgit v1.2.3 From 21fed4a9c2c1c4bd2aedb619c3c76e0211b77a25 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Apr 2021 23:26:54 -0700 Subject: SelfContained: remove unneeded imports. --- src/Text/Pandoc/SelfContained.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 17c1e18c9..3bbab4bbe 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -16,7 +16,6 @@ the HTML using data URIs. module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) -import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) import Data.ByteString (ByteString) import Data.ByteString.Base64 @@ -29,7 +28,6 @@ import System.FilePath (takeDirectory, takeExtension, ()) import Text.HTML.TagSoup import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem, getInputFiles, report, setInputFiles) -import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Shared (isURI, renderTags', trim) -- cgit v1.2.3 From 60974538b25657c9aa37e72cc66ca3957912ddec Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Apr 2021 23:29:22 -0700 Subject: Commonmark writer: Use backslash escapes for `<` and `|`... instead of entities. Closes #7208. --- src/Text/Pandoc/Writers/Markdown.hs | 12 +++++++++++- test/command/7208.md | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/command/7208.md diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 4d9f3d5b0..daf45ed53 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -67,7 +67,17 @@ writePlain opts document = -- | Convert Pandoc to Commonmark. writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCommonMark opts document = - evalMD (pandocToMarkdown opts document) def{ envVariant = Commonmark } def + evalMD (pandocToMarkdown opts' document) def{ envVariant = Commonmark } def + where + opts' = opts{ writerExtensions = + -- These extensions can't be enabled or disabled + -- for commonmark because they're part of the core; + -- we set them here so that escapeText will behave + -- properly. + enableExtension Ext_all_symbols_escapable $ + enableExtension Ext_pipe_tables $ + enableExtension Ext_intraword_underscores $ + writerExtensions opts } pandocTitleBlock :: Doc Text -> [Doc Text] -> Doc Text -> Doc Text pandocTitleBlock tit auths dat = diff --git a/test/command/7208.md b/test/command/7208.md new file mode 100644 index 000000000..e65943ade --- /dev/null +++ b/test/command/7208.md @@ -0,0 +1,6 @@ +``` +% pandoc -t gfm +\ +^D +\ +``` -- cgit v1.2.3 From e227496d3a5d07df8183b8d986ea2aa36c90612c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 8 Apr 2021 22:14:47 +0200 Subject: Lua filter: respect Inlines/Blocks filter functions in pandoc.walk_* --- src/Text/Pandoc/Lua/Filter.hs | 2 ++ src/Text/Pandoc/Lua/Module/Pandoc.hs | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Lua/Filter.hs b/src/Text/Pandoc/Lua/Filter.hs index 90967f295..01bf90efa 100644 --- a/src/Text/Pandoc/Lua/Filter.hs +++ b/src/Text/Pandoc/Lua/Filter.hs @@ -13,7 +13,9 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction , LuaFilter , runFilterFile , walkInlines + , walkInlineLists , walkBlocks + , walkBlockLists , module Text.Pandoc.Lua.Walk ) where import Control.Applicative ((<|>)) diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index 8d30f9a0c..5c14b3a30 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -23,8 +23,10 @@ import Foreign.Lua (Lua, NumResults, Optional, Peekable, Pushable) import System.Exit (ExitCode (..)) import Text.Pandoc.Class.PandocIO (runIO) import Text.Pandoc.Definition (Block, Inline) -import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..)) +import Text.Pandoc.Lua.Filter (LuaFilter, SingletonsList (..), walkInlines, + walkInlineLists, walkBlocks, walkBlockLists) import Text.Pandoc.Lua.Marshaling () +import Text.Pandoc.Lua.Marshaling.List (List (..)) import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua, loadDefaultModule) import Text.Pandoc.Walk (Walkable) @@ -51,9 +53,12 @@ pushModule = do return 1 walkElement :: (Walkable (SingletonsList Inline) a, - Walkable (SingletonsList Block) a) + Walkable (SingletonsList Block) a, + Walkable (List Inline) a, + Walkable (List Block) a) => a -> LuaFilter -> PandocLua a -walkElement x f = liftPandocLua $ walkInlines f x >>= walkBlocks f +walkElement x f = liftPandocLua $ + walkInlines f x >>= walkInlineLists f >>= walkBlocks f >>= walkBlockLists f walk_inline :: Inline -> LuaFilter -> PandocLua Inline walk_inline = walkElement -- cgit v1.2.3 From 20cd33e5a44810b68fed74da00f4f51eb2282147 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Apr 2021 14:47:11 -0700 Subject: Fix regression in grid tables for wide characters. In the translation from String to Text, a char-width-sensitive splitAt' was dropped. This commit reinstates it. Closes #7214. --- src/Text/Pandoc/Shared.hs | 18 +++++++++++++----- test/command/7214.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 test/command/7214.md diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 3292b32f4..95cbdc8b8 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -153,12 +153,20 @@ splitTextBy isSep t in first : splitTextBy isSep (T.dropWhile isSep rest) splitTextByIndices :: [Int] -> T.Text -> [T.Text] -splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) +splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) . T.unpack where - splitTextByRelIndices [] t = [t] - splitTextByRelIndices (x:xs) t = - let (first, rest) = T.splitAt x t - in first : splitTextByRelIndices xs rest + splitTextByRelIndices [] cs = [T.pack cs] + splitTextByRelIndices (x:xs) cs = + let (first, rest) = splitAt' x cs + in T.pack first : splitTextByRelIndices xs rest + +-- Note: don't replace this with T.splitAt, which is not sensitive +-- to character widths! +splitAt' :: Int -> [Char] -> ([Char],[Char]) +splitAt' _ [] = ([],[]) +splitAt' n xs | n <= 0 = ([],xs) +splitAt' n (x:xs) = (x:ys,zs) + where (ys,zs) = splitAt' (n - charWidth x) xs ordNub :: (Ord a) => [a] -> [a] ordNub l = go Set.empty l diff --git a/test/command/7214.md b/test/command/7214.md new file mode 100644 index 000000000..43bf9e4ca --- /dev/null +++ b/test/command/7214.md @@ -0,0 +1,28 @@ +``` +% pandoc ++------------+----------+------------------+ +|日本語 | の文字列 | words in english | ++------------+----------+------------------+ +|abc defghij | def | xyz | ++------------+----------+------------------+ +^D + +++++ + + + + + + + + + + + + +
日本語の文字列words in english
abc defghijdefxyz
+``` -- cgit v1.2.3 From 051b7ffeaffdaf34ed1384a239cf0179aa59b932 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 10 Apr 2021 10:47:10 +0200 Subject: JATS writer: add footnote number as label in backmatter Footnotes in the backmatter are given the footnote's number as a label. The articleauthoring output is unaffected from this change, as footnotes are placed inline there. Closes: #7210 --- src/Text/Pandoc/Writers/JATS.hs | 1 + test/writer.jats_archiving | 14 ++++++++------ test/writer.jats_publishing | 14 ++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 26f94cb03..e78c6dc8f 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -436,6 +436,7 @@ inlineToJATS opts (Note contents) = do (n, _):_ -> n + 1 [] -> 1 thenote <- inTags True "fn" [("id", "fn" <> tshow notenum)] + . (inTagsSimple "label" (literal $ tshow notenum) <>) <$> wrappedBlocksToJATS (not . isPara) opts (walk demoteHeaderAndRefs contents) modify $ \st -> st{ jatsNotes = (notenum, thenote) : notes } diff --git a/test/writer.jats_archiving b/test/writer.jats_archiving index 1a9537a21..332b5d3fd 100644 --- a/test/writer.jats_archiving +++ b/test/writer.jats_archiving @@ -882,11 +882,12 @@ These should not be escaped: \$ \\ \> \[ \{ -

Here is the footnote. It can go anywhere after the footnote reference. - It need not be placed at the end of the document.

+

Here is the footnote. It can go anywhere after the + footnote reference. It need not be placed at the end of the document.

-

Here’s the long note. This one contains multiple blocks.

+

Here’s the long note. This one contains multiple + blocks.

Subsequent blocks are indented to show that they belong to the footnote (as with list items).

@@ -896,16 +897,17 @@ These should not be escaped: \$ \\ \> \[ \{ just indent the first line of each block.

-

This is easier to type. Inline notes may contain +

This is easier to type. Inline notes + may contain links and ] verbatim characters, as well as [bracketed text].

-

In quote.

+

In quote.

-

In list.

+

In list.

diff --git a/test/writer.jats_publishing b/test/writer.jats_publishing index b85816848..f53fd554d 100644 --- a/test/writer.jats_publishing +++ b/test/writer.jats_publishing @@ -882,11 +882,12 @@ These should not be escaped: \$ \\ \> \[ \{ -

Here is the footnote. It can go anywhere after the footnote reference. - It need not be placed at the end of the document.

+

Here is the footnote. It can go anywhere after the + footnote reference. It need not be placed at the end of the document.

-

Here’s the long note. This one contains multiple blocks.

+

Here’s the long note. This one contains multiple + blocks.

Subsequent blocks are indented to show that they belong to the footnote (as with list items).

@@ -896,16 +897,17 @@ These should not be escaped: \$ \\ \> \[ \{ just indent the first line of each block.

-

This is easier to type. Inline notes may contain +

This is easier to type. Inline notes + may contain links and ] verbatim characters, as well as [bracketed text].

-

In quote.

+

In quote.

-

In list.

+

In list.

-- cgit v1.2.3 From 2d60524de43d59ffb1763a33a15cc2ecce613ecf Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 10 Apr 2021 11:38:02 +0200 Subject: JATS writer: convert spans to elements Spans with attributes are converted to `` elements instead of being wrapped with `` and `` elements. Milestone elements are not allowed in documents using the articleauthoring tag set, so this change ensures the creation of valid documents. Closes: #7211 --- src/Text/Pandoc/Writers/JATS.hs | 13 +++++++------ test/Tests/Writers/JATS.hs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index e78c6dc8f..5b3e439d4 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -444,18 +444,19 @@ inlineToJATS opts (Note contents) = do ("rid", "fn" <> tshow notenum)] $ text (show notenum) inlineToJATS opts (Cite _ lst) = - -- TODO revisit this after examining the jats.csl pipeline inlinesToJATS opts lst -inlineToJATS opts (Span ("",_,[]) ils) = inlinesToJATS opts ils inlineToJATS opts (Span (ident,_,kvs) ils) = do contents <- inlinesToJATS opts ils let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs - , k `elem` ["content-type", "rationale", - "rid", "specific-use"]] - return $ selfClosingTag "milestone-start" attr <> contents <> - selfClosingTag "milestone-end" [] + , k `elem` ["alt", "content-type", "rid", "specific-use", + "vocab", "vocab-identifier", "vocab-term", + "vocab-term-identifier"]] + return $ + if null attr + then contents -- unwrap if no relevant attributes are given + else inTags False "named-content" attr contents inlineToJATS _ (Math t str) = do let addPref (Xml.Attr q v) | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v diff --git a/test/Tests/Writers/JATS.hs b/test/Tests/Writers/JATS.hs index 23c1686dc..e90438176 100644 --- a/test/Tests/Writers/JATS.hs +++ b/test/Tests/Writers/JATS.hs @@ -142,4 +142,19 @@ tests = codeWith ("7y",[],[]) "print 5" =?> "

print 5

" ] + + , testGroup "spans" + [ "unwrapped if no attributes given" =: + spanWith nullAttr "text in span" =?> + "

text in span

" + + , "converted to named-content element" =: + spanWith ("a", ["ignored"], [("alt", "aa")]) "text" =?> + "

text

" + + , "unwrapped if named-content element would have no attributes" =: + spanWith ("", ["ignored"], [("hidden", "true")]) "text in span" =?> + "

text in span

" + + ] ] -- cgit v1.2.3 From 2e7fee9c3c48e2492340a38d3a387e4f9ea0c913 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 15 Apr 2021 14:30:33 -0700 Subject: Use latest xml-conduit. --- pandoc.cabal | 2 +- stack.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index b2d7fa460..c8ef3cfb9 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -492,7 +492,7 @@ library unicode-transforms >= 0.3 && < 0.4, unordered-containers >= 0.2 && < 0.3, xml >= 1.3.12 && < 1.4, - xml-conduit >= 1.7 && < 1.10, + xml-conduit >= 1.9.1.1 && < 1.10, zip-archive >= 0.2.3.4 && < 0.5, zlib >= 0.5 && < 0.7 if os(windows) && arch(i386) diff --git a/stack.yaml b/stack.yaml index c57687c41..bf8e76239 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,6 +16,7 @@ extra-deps: - citeproc-0.3.0.9 - texmath-0.12.2 - random-1.2.0 +- xml-conduit-1.9.1.1 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From 5f79a66ed64e9b0cc326e467dcb17239f1596fcc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 16 Apr 2021 22:13:29 +0200 Subject: JATS writer: reduce unnecessary use of

elements for wrapping The `

` element is used for wrapping in cases were the contents would otherwise not be allowed in a certain context. Unnecessary wrapping is avoided, especially around quotes (`` elements). Closes: #7227 --- src/Text/Pandoc/Writers/JATS.hs | 22 +++++---- src/Text/Pandoc/Writers/JATS/Table.hs | 26 +++++++++-- src/Text/Pandoc/Writers/JATS/Types.hs | 15 ++++-- test/command/7041.md | 23 +++++++++ test/writer.jats_archiving | 68 +++++++++++---------------- test/writer.jats_articleauthoring | 88 ++++++++++++++++------------------- test/writer.jats_publishing | 68 +++++++++++---------------- 7 files changed, 166 insertions(+), 144 deletions(-) create mode 100644 test/command/7041.md diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 5b3e439d4..b58ff8aef 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -1,6 +1,7 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.JATS Copyright : 2017-2021 John MacFarlane @@ -80,7 +81,7 @@ writeJats tagSet opts d = do let environment = JATSEnv { jatsTagSet = tagSet , jatsInlinesWriter = inlinesToJATS - , jatsBlockWriter = blockToJATS + , jatsBlockWriter = wrappedBlocksToJATS , jatsReferences = refs } let initialState = JATSState { jatsNotes = [] } @@ -162,11 +163,9 @@ wrappedBlocksToJATS needsWrap opts = wrappedBlockToJATS b = do inner <- blockToJATS opts b return $ - if needsWrap b || isBlockQuote b -- see #7041 + if needsWrap b then inTags True "p" [("specific-use","wrapper")] inner else inner - isBlockQuote (BlockQuote _) = True - isBlockQuote _ = False -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block @@ -324,10 +323,13 @@ blockToJATS opts (LineBlock lns) = blockToJATS opts $ linesToPara lns blockToJATS opts (BlockQuote blocks) = do tagSet <- asks jatsTagSet - let blocksToJats' = if tagSet == TagSetArticleAuthoring - then wrappedBlocksToJATS (not . isPara) - else blocksToJATS - inTagsIndented "disp-quote" <$> blocksToJats' opts blocks + let needsWrap = if tagSet == TagSetArticleAuthoring + then not . isPara + else \case + Header{} -> True + HorizontalRule -> True + _ -> False + inTagsIndented "disp-quote" <$> wrappedBlocksToJATS needsWrap opts blocks blockToJATS _ (CodeBlock a str) = return $ inTags False tag attr (flush (text (T.unpack $ escapeStringForXML str))) where (lang, attr) = codeAttr a diff --git a/src/Text/Pandoc/Writers/JATS/Table.hs b/src/Text/Pandoc/Writers/JATS/Table.hs index 2e34900d2..70569bdcd 100644 --- a/src/Text/Pandoc/Writers/JATS/Table.hs +++ b/src/Text/Pandoc/Writers/JATS/Table.hs @@ -34,13 +34,19 @@ tableToJATS :: PandocMonad m -> JATS m (Doc Text) tableToJATS opts (Ann.Table attr caption colspecs thead tbodies tfoot) = do let (Caption _maybeShortCaption captionBlocks) = caption + -- Only paragraphs are allowed in captions, all other blocks must be + -- wrapped in @

@ elements. + let needsWrapping = \case + Plain{} -> False + Para{} -> False + _ -> True tbl <- captionlessTable opts attr colspecs thead tbodies tfoot captionDoc <- if null captionBlocks then return empty else do blockToJATS <- asks jatsBlockWriter - inTagsIndented "caption" . vcat <$> - mapM (blockToJATS opts) captionBlocks + inTagsIndented "caption" <$> + blockToJATS needsWrapping opts captionBlocks return $ inTags True "table-wrap" [] $ captionDoc $$ tbl captionlessTable :: PandocMonad m @@ -230,7 +236,7 @@ tableCellToJats opts ctype colAlign (Cell attr align rowspan colspan item) = do inlinesToJats <- asks jatsInlinesWriter let cellContents = \case [Plain inlines] -> inlinesToJats opts inlines - blocks -> vcat <$> mapM (blockToJats opts) blocks + blocks -> blockToJats needsWrapInCell opts blocks let tag' = case ctype of BodyCell -> "td" HeaderCell -> "th" @@ -246,3 +252,17 @@ tableCellToJats opts ctype colAlign (Cell attr align rowspan colspan item) = do . maybeCons (colspanAttrib colspan) $ toAttribs attr validAttribs inTags False tag' attribs <$> cellContents item + +-- | Whether the JATS produced from this block should be wrapped in a +-- @

@ element when put directly below a @@ element. +needsWrapInCell :: Block -> Bool +needsWrapInCell = \case + Plain{} -> False -- should be unwrapped anyway + Para{} -> False + BulletList{} -> False + OrderedList{} -> False + DefinitionList{} -> False + HorizontalRule -> False + CodeBlock{} -> False + RawBlock{} -> False -- responsibility of the user + _ -> True diff --git a/src/Text/Pandoc/Writers/JATS/Types.hs b/src/Text/Pandoc/Writers/JATS/Types.hs index 6fdddc0b5..8d8673cf6 100644 --- a/src/Text/Pandoc/Writers/JATS/Types.hs +++ b/src/Text/Pandoc/Writers/JATS/Types.hs @@ -37,11 +37,20 @@ newtype JATSState = JATSState { jatsNotes :: [(Int, Doc Text)] } +-- | Environment containing all information relevant for rendering. data JATSEnv m = JATSEnv - { jatsTagSet :: JATSTagSet + { jatsTagSet :: JATSTagSet -- ^ The tag set that's being ouput + + , jatsBlockWriter :: (Block -> Bool) + -> WriterOptions -> [Block] -> JATS m (Doc Text) + -- ^ Converts a block list to JATS, wrapping top-level blocks into a + -- @

@ element if the property evaluates to @True@. + -- See #7227. + , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text) - , jatsBlockWriter :: WriterOptions -> Block -> JATS m (Doc Text) - , jatsReferences :: [Reference Inlines] + -- ^ Converts an inline list to JATS. + + , jatsReferences :: [Reference Inlines] -- ^ List of references } -- | JATS writer type diff --git a/test/command/7041.md b/test/command/7041.md new file mode 100644 index 000000000..1773963b8 --- /dev/null +++ b/test/command/7041.md @@ -0,0 +1,23 @@ +``` +% pandoc -f html -t jats + + +
Fly, you fools!
+^D + + + + + + + + + + +

+ +

Fly, you fools!

+ +

+
+``` diff --git a/test/writer.jats_archiving b/test/writer.jats_archiving index 332b5d3fd..70e15b6b8 100644 --- a/test/writer.jats_archiving +++ b/test/writer.jats_archiving @@ -78,39 +78,31 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

-

+ +

This is a block quote. It is pretty short.

+ + +

Code in a block quote:

+ sub status { + print "working"; +} +

A list:

+ + +

item one

+
+ +

item two

+
+
+

Nested block quotes:

-

This is a block quote. It is pretty short.

+

nested

-

-

-

Code in a block quote:

- sub status { - print "working"; -} -

A list:

- - -

item one

-
- -

item two

-
-
-

Nested block quotes:

-

- -

nested

-
-

-

- -

nested

- -

+

nested

-

+

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -837,12 +829,10 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

-

- -

Blockquoted: - http://example.com/

- -

+ +

Blockquoted: + http://example.com/

+

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -866,11 +856,9 @@ These should not be escaped: \$ \\ \> \[ \{ not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

-

- -

Notes can go in quotes.4

- -

+ +

Notes can go in quotes.4

+

And in list items.5

diff --git a/test/writer.jats_articleauthoring b/test/writer.jats_articleauthoring index 956a30faa..59485a114 100644 --- a/test/writer.jats_articleauthoring +++ b/test/writer.jats_articleauthoring @@ -67,43 +67,39 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

-

- -

This is a block quote. It is pretty short.

- -

-

- -

Code in a block quote:

-

- sub status { + +

This is a block quote. It is pretty short.

+ + +

Code in a block quote:

+

+ sub status { print "working"; } -

-

A list:

-

- - -

item one

-
- -

item two

-
-
-

-

Nested block quotes:

-

- -

nested

- -

-

- -

nested

- -

- -

+

+

A list:

+

+ + +

item one

+ + +

item two

+
+ +

+

Nested block quotes:

+

+ +

nested

+ +

+

+ +

nested

+ +

+

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -817,12 +813,10 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

-

- -

Blockquoted: - http://example.com/

- -

+ +

Blockquoted: + http://example.com/

+

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -860,13 +854,11 @@ These should not be escaped: \$ \\ \> \[ \{ and ] verbatim characters, as well as [bracketed text].

-

- -

Notes can go in quotes. -

In quote.

-

- -

+ +

Notes can go in quotes. +

In quote.

+

+

And in list items. diff --git a/test/writer.jats_publishing b/test/writer.jats_publishing index f53fd554d..e6db4172a 100644 --- a/test/writer.jats_publishing +++ b/test/writer.jats_publishing @@ -78,39 +78,31 @@ Gruber’s markdown test suite.

Block Quotes

E-mail style:

-

+ +

This is a block quote. It is pretty short.

+ + +

Code in a block quote:

+ sub status { + print "working"; +} +

A list:

+ + +

item one

+
+ +

item two

+
+
+

Nested block quotes:

-

This is a block quote. It is pretty short.

+

nested

-

-

-

Code in a block quote:

- sub status { - print "working"; -} -

A list:

- - -

item one

-
- -

item two

-
-
-

Nested block quotes:

-

- -

nested

-
-

-

- -

nested

- -

+

nested

-

+

This should not be a block quote: 2 > 1.

And a following paragraph.

@@ -837,12 +829,10 @@ These should not be escaped: \$ \\ \> \[ \{

An e-mail address: nobody@nowhere.net

-

- -

Blockquoted: - http://example.com/

- -

+ +

Blockquoted: + http://example.com/

+

Auto-links should not occur here: <http://example.com/>

or here: <http://example.com/> @@ -866,11 +856,9 @@ These should not be escaped: \$ \\ \> \[ \{ not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

-

- -

Notes can go in quotes.4

- -

+ +

Notes can go in quotes.4

+

And in list items.5

-- cgit v1.2.3 From ff5a5048091b765ed15750ccb5ea30f9459ba33a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Apr 2021 11:51:44 -0700 Subject: Use new citeproc + unicode-collation. Add command test for unicode-collation. --- cabal.project | 10 +++ stack.yaml | 6 +- test/command/unicode-collation.md | 130 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 test/command/unicode-collation.md diff --git a/cabal.project b/cabal.project index a0406da3c..fa17a20a6 100644 --- a/cabal.project +++ b/cabal.project @@ -2,6 +2,16 @@ packages: pandoc.cabal tests: True flags: +embed_data_files +source-repository-package + type: git + location: https://github.com/jgm/citeproc + tag: b42857be658b8f2649e989e061978e304986f853 + +source-repository-package + type: git + location: https://github.com/jgm/unicode-collation + tag: 9d229a5c6bcbaf53d7022575234eb223cfa90d55 + -- source-repository-package -- type: git -- location: https://github.com/jgm/citeproc diff --git a/stack.yaml b/stack.yaml index bf8e76239..159bf74b9 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,10 +13,14 @@ extra-deps: - skylighting-core-0.10.5.1 - skylighting-0.10.5.1 - doclayout-0.3.0.2 -- citeproc-0.3.0.9 - texmath-0.12.2 - random-1.2.0 - xml-conduit-1.9.1.1 +# - citeproc-0.3.0.9 +- git: https://github.com/jgm/citeproc + commit: b42857be658b8f2649e989e061978e304986f853 +- git: https://github.com/jgm/unicode-collation + commit: 9d229a5c6bcbaf53d7022575234eb223cfa90d55 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 diff --git a/test/command/unicode-collation.md b/test/command/unicode-collation.md new file mode 100644 index 000000000..a53ed7577 --- /dev/null +++ b/test/command/unicode-collation.md @@ -0,0 +1,130 @@ +``` +% pandoc --citeproc -t plain +--- +lang: en-US +csl: command/apa.csl +references: +- id: a1 + type: book + author: + - family: Ubina + given: A. John + issued: 1985 +- id: a2 + type: book + author: + - family: Über + given: Aglaia + issued: 1996 +- id: a3 + type: book + author: + - family: Oñate + given: José + issued: 1985 +- id: a4 + type: book + author: + - family: Onush + given: Frank + issued: 2002 +- id: a5 + type: book + author: + - family: O'Neil + given: Timothy + issued: 2010 +--- + +[@a1;@a2;@a3;@a4;@a5] +^D +(O’Neil, 2010; Oñate, 1985; Onush, 2002; Über, 1996; Ubina, 1985) + +O’Neil, T. (2010). + +Oñate, J. (1985). + +Onush, F. (2002). + +Über, A. (1996). + +Ubina, A. J. (1985). +``` + +``` +% pandoc --citeproc -t plain +--- +lang: es +csl: command/apa.csl +references: +- id: a1 + type: book + author: + - family: Ubina + given: A. John + issued: 1985 +- id: a2 + type: book + author: + - family: Über + given: Aglaia + issued: 1996 +- id: a3 + type: book + author: + - family: Oñate + given: José + issued: 1985 +- id: a4 + type: book + author: + - family: Onush + given: Frank + issued: 2002 +- id: a5 + type: book + author: + - family: O'Neil + given: Timothy + issued: 2010 +--- + +[@a1;@a2;@a3;@a4;@a5] +^D +(O’Neil, 2010; Onush, 2002; Oñate, 1985; Über, 1996; Ubina, 1985) + +O’Neil, T. (2010). + +Onush, F. (2002). + +Oñate, J. (1985). + +Über, A. (1996). + +Ubina, A. J. (1985). +``` + +``` +% pandoc -C -t plain +--- +nocite: '@*' +lang: fr +references: +- id: cote + author: cote +- id: côte + author: côte +- id: coté + author: coté +- id: côté + author: côté +... +^D +cote. s. d. + +côte. s. d. + +coté. s. d. + +côté. s. d. +``` -- cgit v1.2.3 From 7ba8c0d2a5e2b89ae1547759510b2ee21de88cb1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Apr 2021 21:28:19 -0700 Subject: Move getLang from BCP47 -> T.P.Writers.Shared. [API change] --- pandoc.cabal | 1 + src/Text/Pandoc/BCP47.hs | 13 ---- src/Text/Pandoc/Citeproc/Data.hs | 5 +- src/Text/Pandoc/Shared.hs | 1 - src/Text/Pandoc/Writers/LaTeX/Lang.hs | 117 +++++++++++++++++----------------- src/Text/Pandoc/Writers/Shared.hs | 14 ++++ 6 files changed, 77 insertions(+), 74 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index c8ef3cfb9..b6cbb0d7a 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -493,6 +493,7 @@ library unordered-containers >= 0.2 && < 0.3, xml >= 1.3.12 && < 1.4, xml-conduit >= 1.9.1.1 && < 1.10, + unicode-collation >= 0.1 && < 0.2, zip-archive >= 0.2.3.4 && < 0.5, zlib >= 0.5 && < 0.7 if os(windows) && arch(i386) diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index 69824aa57..1ecf0bf73 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -37,19 +37,6 @@ renderLang :: Lang -> T.Text renderLang lang = T.intercalate "-" (langLanguage lang : filter (not . T.null) ([langScript lang, langRegion lang] ++ langVariants lang)) --- | Get the contents of the `lang` metadata field or variable. -getLang :: WriterOptions -> Meta -> Maybe T.Text -getLang opts meta = - case lookupContext "lang" (writerVariables opts) of - Just s -> Just s - _ -> - case lookupMeta "lang" meta of - Just (MetaBlocks [Para [Str s]]) -> Just s - Just (MetaBlocks [Plain [Str s]]) -> Just s - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing - -- | Parse a BCP 47 string as a Lang. Currently we parse -- extensions and private-use fields as "variants," even -- though officially they aren't. diff --git a/src/Text/Pandoc/Citeproc/Data.hs b/src/Text/Pandoc/Citeproc/Data.hs index dfdaf2598..40430b0f5 100644 --- a/src/Text/Pandoc/Citeproc/Data.hs +++ b/src/Text/Pandoc/Citeproc/Data.hs @@ -10,7 +10,7 @@ import qualified Data.Text.Encoding as TE import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.Citeproc.Util (toIETF) -import Citeproc (Lang(..), parseLang) +import UnicodeCollation.Lang (Lang(..), parseLang) biblatexLocalizations :: [(FilePath, ByteString)] biblatexLocalizations = $(embedDir "citeproc/biblatex-localization") @@ -21,7 +21,8 @@ biblatexStringMap :: M.Map Text (M.Map Text (Text, Text)) biblatexStringMap = foldr go mempty biblatexLocalizations where go (fp, bs) = - let Lang lang _ = parseLang (toIETF $ T.takeWhile (/= '.') $ T.pack fp) + let Lang lang _ _ _ _ _ = parseLang + (toIETF $ T.takeWhile (/= '.') $ T.pack fp) ls = T.lines $ TE.decodeUtf8 bs in if length ls > 4 then M.insert lang (toStringMap $ map (T.splitOn "|") ls) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 95cbdc8b8..e389c1727 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -852,7 +852,6 @@ filteredFilesFromArchive zf f = fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString) fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e) - -- -- IANA URIs -- diff --git a/src/Text/Pandoc/Writers/LaTeX/Lang.hs b/src/Text/Pandoc/Writers/LaTeX/Lang.hs index 41aafee48..871b2692a 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Lang.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Lang.hs @@ -15,7 +15,7 @@ module Text.Pandoc.Writers.LaTeX.Lang toBabel ) where import Data.Text (Text) -import Text.Pandoc.BCP47 (Lang (..)) +import UnicodeCollation.Lang (Lang(..)) -- In environments \Arabic instead of \arabic is used @@ -25,88 +25,89 @@ toPolyglossiaEnv l = ("arabic", o) -> ("Arabic", o) x -> x --- Takes a list of the constituents of a BCP 47 language code and +-- Takes a list of the constituents of a BCP47 language code and -- converts it to a Polyglossia (language, options) tuple -- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf toPolyglossia :: Lang -> (Text, Text) -toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria") -toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq") -toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq") -toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq") -toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya") -toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco") -toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania") -toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq") -toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq") -toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia") -toPolyglossia (Lang "de" _ _ vars) - | "1901" `elem` vars = ("german", "spelling=old") -toPolyglossia (Lang "de" _ "AT" vars) - | "1901" `elem` vars = ("german", "variant=austrian, spelling=old") -toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian") -toPolyglossia (Lang "de" _ "CH" vars) - | "1901" `elem` vars = ("german", "variant=swiss, spelling=old") -toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss") -toPolyglossia (Lang "de" _ _ _) = ("german", "") -toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "") -toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly") -toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian") -toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian") -toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british") -toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand") -toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british") -toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american") -toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient") -toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "") -toPolyglossia (Lang "la" _ _ vars) - | "x-classic" `elem` vars = ("latin", "variant=classic") -toPolyglossia (Lang "pt" _ "BR" _) = ("portuguese", "variant=brazilian") -toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "") +toPolyglossia (Lang "ar" _ (Just "DZ") _ _ _) = ("arabic", "locale=algeria") +toPolyglossia (Lang "ar" _ (Just "IQ") _ _ _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ (Just "JO") _ _ _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ (Just "LB") _ _ _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ (Just "LY") _ _ _) = ("arabic", "locale=libya") +toPolyglossia (Lang "ar" _ (Just "MA") _ _ _) = ("arabic", "locale=morocco") +toPolyglossia (Lang "ar" _ (Just "MR") _ _ _) = ("arabic", "locale=mauritania") +toPolyglossia (Lang "ar" _ (Just "PS") _ _ _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ (Just "SY") _ _ _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ (Just "TN") _ _ _) = ("arabic", "locale=tunisia") +toPolyglossia (Lang "de" _ _ vars _ _) + | "1901" `elem` vars = ("german", "spelling=old") +toPolyglossia (Lang "de" _ (Just "AT") vars _ _) + | "1901" `elem` vars = ("german", "variant=austrian, spelling=old") +toPolyglossia (Lang "de" _ (Just "AT") _ _ _) = ("german", "variant=austrian") +toPolyglossia (Lang "de" _ (Just "CH") vars _ _) + | "1901" `elem` vars = ("german", "variant=swiss, spelling=old") +toPolyglossia (Lang "de" _ (Just "CH") _ _ _ _) = ("german", "variant=swiss") +toPolyglossia (Lang "de" _ _ _ _ _) = ("german", "") +toPolyglossia (Lang "dsb" _ _ _ _ _) = ("lsorbian", "") +toPolyglossia (Lang "el" _ _ vars _ _) + | "polyton" `elem` vars = ("greek", "variant=poly") +toPolyglossia (Lang "en" _ (Just "AU") _ _ _) = ("english", "variant=australian") +toPolyglossia (Lang "en" _ (Just "CA") _ _ _) = ("english", "variant=canadian") +toPolyglossia (Lang "en" _ (Just "GB") _ _ _) = ("english", "variant=british") +toPolyglossia (Lang "en" _ (Just "NZ") _ _ _) = ("english", "variant=newzealand") +toPolyglossia (Lang "en" _ (Just "UK") _ _ _) = ("english", "variant=british") +toPolyglossia (Lang "en" _ (Just "US") _ _ _) = ("english", "variant=american") +toPolyglossia (Lang "grc" _ _ _ _ _) = ("greek", "variant=ancient") +toPolyglossia (Lang "hsb" _ _ _ _ _) = ("usorbian", "") +toPolyglossia (Lang "la" _ _ vars _ _) + | "x-classic" `elem` vars = ("latin", "variant=classic") +toPolyglossia (Lang "pt" _ "BR" _ _ _) = ("portuguese", "variant=brazilian") +toPolyglossia (Lang "sl" _ _ _ _ _) = ("slovenian", "") toPolyglossia x = (commonFromBcp47 x, "") --- Takes a list of the constituents of a BCP 47 language code and +-- Takes a list of the constituents of a BCP47 language code and -- converts it to a Babel language string. -- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf -- List of supported languages (slightly outdated): -- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf toBabel :: Lang -> Text -toBabel (Lang "de" _ "AT" vars) +toBabel (Lang "de" _ (Just "AT") vars _ _) | "1901" `elem` vars = "austrian" | otherwise = "naustrian" -toBabel (Lang "de" _ "CH" vars) +toBabel (Lang "de" _ (Just "CH") vars _ _) | "1901" `elem` vars = "swissgerman" | otherwise = "nswissgerman" -toBabel (Lang "de" _ _ vars) +toBabel (Lang "de" _ _ vars _ _) | "1901" `elem` vars = "german" | otherwise = "ngerman" -toBabel (Lang "dsb" _ _ _) = "lowersorbian" +toBabel (Lang "dsb" _ _ _ _ _) = "lowersorbian" toBabel (Lang "el" _ _ vars) | "polyton" `elem` vars = "polutonikogreek" -toBabel (Lang "en" _ "AU" _) = "australian" -toBabel (Lang "en" _ "CA" _) = "canadian" -toBabel (Lang "en" _ "GB" _) = "british" -toBabel (Lang "en" _ "NZ" _) = "newzealand" -toBabel (Lang "en" _ "UK" _) = "british" -toBabel (Lang "en" _ "US" _) = "american" -toBabel (Lang "fr" _ "CA" _) = "canadien" -toBabel (Lang "fra" _ _ vars) +toBabel (Lang "en" _ (Just "AU") _ _ _) = "australian" +toBabel (Lang "en" _ (Just "CA") _ _ _) = "canadian" +toBabel (Lang "en" _ (Just "GB") _ _ _) = "british" +toBabel (Lang "en" _ (Just "NZ") _ _ _) = "newzealand" +toBabel (Lang "en" _ (Just "UK") _ _ _) = "british" +toBabel (Lang "en" _ (Just "US") _ _ _) = "american" +toBabel (Lang "fr" _ (Just "CA") _ _ _) = "canadien" +toBabel (Lang "fra" _ _ vars _ _) | "aca" `elem` vars = "acadian" -toBabel (Lang "grc" _ _ _) = "polutonikogreek" -toBabel (Lang "hsb" _ _ _) = "uppersorbian" -toBabel (Lang "la" _ _ vars) +toBabel (Lang "grc" _ _ _ _ _) = "polutonikogreek" +toBabel (Lang "hsb" _ _ _ _ _) = "uppersorbian" +toBabel (Lang "la" _ _ vars _ _) | "x-classic" `elem` vars = "classiclatin" -toBabel (Lang "pt" _ "BR" _) = "brazilian" -toBabel (Lang "sl" _ _ _) = "slovene" +toBabel (Lang "pt" _ (Just "BR") _ _ _) = "brazilian" +toBabel (Lang "sl" _ _ _ _ _) = "slovene" toBabel x = commonFromBcp47 x --- Takes a list of the constituents of a BCP 47 language code +-- Takes a list of the constituents of a BCP47 language code -- and converts it to a string shared by Babel and Polyglossia. -- https://tools.ietf.org/html/bcp47#section-2.1 commonFromBcp47 :: Lang -> Text -commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc" -commonFromBcp47 (Lang "zh" "Latn" _ vars) - | "pinyin" `elem` vars = "pinyin" -commonFromBcp47 (Lang l _ _ _) = fromIso l +commonFromBcp47 (Lang "sr" (Just "Cyrl") _ _ _ _) = "serbianc" +commonFromBcp47 (Lang "zh" (Just "Latn") _ vars _ _) + | "pinyin" `elem` vars = "pinyin" +commonFromBcp47 (Lang l _ _ _ _ _) = fromIso l where fromIso "af" = "afrikaans" fromIso "am" = "amharic" diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 91ecb310b..fcb47bd5a 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -20,6 +20,7 @@ module Text.Pandoc.Writers.Shared ( , setField , resetField , defField + , getLang , tagWithAttrs , isDisplayMath , fixDisplayMath @@ -147,6 +148,19 @@ defField field val (Context m) = where f _newval oldval = oldval +-- | Get the contents of the `lang` metadata field or variable. +getLang :: WriterOptions -> Meta -> Maybe Text +getLang opts meta = + case lookupContext "lang" (writerVariables opts) of + Just s -> Just s + _ -> + case lookupMeta "lang" meta of + Just (MetaBlocks [Para [Str s]]) -> Just s + Just (MetaBlocks [Plain [Str s]]) -> Just s + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing + -- | Produce an HTML tag with the given pandoc attributes. tagWithAttrs :: HasChars a => T.Text -> Attr -> Doc a tagWithAttrs tag (ident,classes,kvs) = hsep -- cgit v1.2.3 From aecbf8156eb7c36c4b41de27797e262c23728db5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Apr 2021 21:28:48 -0700 Subject: Remove Text.Pandoc.BCP47 module. [API change] Use Lang from UnicodeCollation.Lang instead. This is a richer implementation of BCP 47. --- pandoc.cabal | 1 - src/Text/Pandoc/App.hs | 6 +- src/Text/Pandoc/BCP47.hs | 99 ------------- src/Text/Pandoc/Citeproc.hs | 10 +- src/Text/Pandoc/Citeproc/BibTeX.hs | 20 +-- src/Text/Pandoc/Citeproc/Data.hs | 12 +- src/Text/Pandoc/Class/CommonState.hs | 2 +- src/Text/Pandoc/Class/PandocMonad.hs | 4 +- src/Text/Pandoc/Readers/BibTeX.hs | 9 +- src/Text/Pandoc/Readers/LaTeX.hs | 2 +- src/Text/Pandoc/Readers/LaTeX/Lang.hs | 241 ++++++++++++++++---------------- src/Text/Pandoc/Writers/BibTeX.hs | 2 +- src/Text/Pandoc/Writers/ConTeXt.hs | 48 +++---- src/Text/Pandoc/Writers/CslJson.hs | 7 +- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- src/Text/Pandoc/Writers/LaTeX/Lang.hs | 8 +- src/Text/Pandoc/Writers/ODT.hs | 7 +- src/Text/Pandoc/Writers/OpenDocument.hs | 8 +- src/Text/Pandoc/Writers/Shared.hs | 2 +- 20 files changed, 198 insertions(+), 294 deletions(-) delete mode 100644 src/Text/Pandoc/BCP47.hs diff --git a/pandoc.cabal b/pandoc.cabal index b6cbb0d7a..8816767e9 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -601,7 +601,6 @@ library Text.Pandoc.Asciify, Text.Pandoc.Emoji, Text.Pandoc.ImageSize, - Text.Pandoc.BCP47, Text.Pandoc.Class, Text.Pandoc.Citeproc other-modules: Text.Pandoc.App.CommandLineOptions, diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6b45e5418..67d3cce7d 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -55,7 +55,7 @@ import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts, import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs, options) import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings) -import Text.Pandoc.BCP47 (Lang (..), parseBCP47) +import UnicodeCollation.Lang (Lang (..), parseLang) import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.SelfContained (makeSelfContained) @@ -200,8 +200,8 @@ convertWithOpts opts = do Just f -> readFileStrict f case lookupMetaString "lang" (optMetadata opts) of - "" -> setTranslations $ Lang "en" "" "US" [] - l -> case parseBCP47 l of + "" -> setTranslations $ Lang "en" Nothing (Just "US") [] [] [] + l -> case parseLang l of Left _ -> report $ InvalidLang l Right l' -> setTranslations l' diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs deleted file mode 100644 index 1ecf0bf73..000000000 --- a/src/Text/Pandoc/BCP47.hs +++ /dev/null @@ -1,99 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{- | - Module : Text.Pandoc.BCP47 - Copyright : Copyright (C) 2017-2021 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - -Functions for parsing and rendering BCP47 language identifiers. --} -module Text.Pandoc.BCP47 ( - getLang - , parseBCP47 - , Lang(..) - , renderLang - ) -where -import Control.Monad (guard) -import Data.Char (isAlphaNum, isAscii, isLetter, isLower, isUpper) -import Text.Pandoc.Definition -import Text.Pandoc.Options -import Text.DocTemplates (FromContext(..)) -import qualified Data.Text as T -import qualified Text.Parsec as P - --- | Represents BCP 47 language/country code. -data Lang = Lang{ langLanguage :: T.Text - , langScript :: T.Text - , langRegion :: T.Text - , langVariants :: [T.Text] } - deriving (Eq, Ord, Show) - --- | Render a Lang as BCP 47. -renderLang :: Lang -> T.Text -renderLang lang = T.intercalate "-" (langLanguage lang : filter (not . T.null) - ([langScript lang, langRegion lang] ++ langVariants lang)) - --- | Parse a BCP 47 string as a Lang. Currently we parse --- extensions and private-use fields as "variants," even --- though officially they aren't. -parseBCP47 :: T.Text -> Either T.Text Lang -parseBCP47 lang = - case P.parse bcp47 "lang" lang of - Right r -> Right r - Left e -> Left $ T.pack $ show e - where bcp47 = do - language <- pLanguage - script <- P.option "" pScript - region <- P.option "" pRegion - variants <- P.many (pVariant P.<|> pExtension P.<|> pPrivateUse) - P.eof - return Lang{ langLanguage = language - , langScript = script - , langRegion = region - , langVariants = variants } - asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) - pLanguage = do - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return $ T.toLower $ T.pack cs - pScript = P.try $ do - P.char '-' - x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) - xs <- P.count 3 - (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) - return $ T.toLower $ T.pack (x:xs) - pRegion = P.try $ do - P.char '-' - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return $ T.toUpper $ T.pack cs - pVariant = P.try $ do - P.char '-' - ds <- P.option "" (P.count 1 P.digit) - cs <- P.many1 asciiLetter - let var = ds ++ cs - lv = length var - guard $ if null ds - then lv >= 5 && lv <= 8 - else lv == 4 - return $ T.toLower $ T.pack var - pExtension = P.try $ do - P.char '-' - cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c) - let lcs = length cs - guard $ lcs >= 2 && lcs <= 8 - return $ T.toLower $ T.pack cs - pPrivateUse = P.try $ do - P.char '-' - P.char 'x' - P.char '-' - cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c) - guard $ not (null cs) && length cs <= 8 - let var = "x-" ++ cs - return $ T.toLower $ T.pack var diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index af302f782..c9f1806e4 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -18,7 +18,6 @@ import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences) import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..)) import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText) import Text.Pandoc.Readers.Markdown (yamlToRefs) -import qualified Text.Pandoc.BCP47 as BCP47 import Text.Pandoc.Builder (Inlines, Many(..), deleteMeta, setMeta) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition as Pandoc @@ -630,13 +629,8 @@ removeFinalPeriod ils = bcp47LangToIETF :: PandocMonad m => Text -> m (Maybe Lang) bcp47LangToIETF bcplang = - case BCP47.parseBCP47 bcplang of + case parseLang bcplang of Left _ -> do report $ InvalidLang bcplang return Nothing - Right lang -> - return $ Just - $ Lang (BCP47.langLanguage lang) - (if T.null (BCP47.langRegion lang) - then Nothing - else Just (BCP47.langRegion lang)) + Right lang -> return $ Just lang diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index c0752dadc..510e56f9c 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -205,10 +205,13 @@ writeBibtexString opts variant mblang ref = [ (", " <>) <$> nameGiven name, nameDroppingParticle name ] - mblang' = (parseLang <$> getVariableAsText "language") <|> mblang + mblang' = case getVariableAsText "language" of + Just l -> either (const Nothing) Just $ parseLang l + Nothing -> mblang titlecase = case mblang' of - Just (Lang "en" _) -> titlecase' + Just lang | langLanguage lang == "en" + -> titlecase' Nothing -> titlecase' _ -> case variant of @@ -331,7 +334,7 @@ writeBibtexString opts variant mblang ref = renderFields = mconcat . intersperse ("," <> cr) . mapMaybe renderField defaultLang :: Lang -defaultLang = Lang "en" (Just "US") +defaultLang = Lang "en" Nothing (Just "US") [] [] [] -- a map of bibtex "string" macros type StringMap = Map.Map Text Text @@ -351,9 +354,7 @@ itemToReference locale variant item = do bib item $ do let lang = fromMaybe defaultLang $ localeLanguage locale modify $ \st -> st{ localeLang = lang, - untitlecase = case lang of - (Lang "en" _) -> True - _ -> False } + untitlecase = langLanguage lang == "en" } id' <- asks identifier otherIds <- (Just <$> getRawField "ids") @@ -711,7 +712,7 @@ itemToReference locale variant item = do bib :: Item -> Bib a -> BibParser a -bib entry m = fst <$> evalRWST m entry (BibState True (Lang "en" (Just "US"))) +bib entry m = fst <$> evalRWST m entry (BibState True defaultLang) resolveCrossRefs :: Variant -> [Item] -> [Item] resolveCrossRefs variant entries = @@ -1456,8 +1457,9 @@ resolveKey lang ils = Walk.walk go ils go x = x resolveKey' :: Lang -> Text -> Text -resolveKey' lang@(Lang l _) k = - case Map.lookup l biblatexStringMap >>= Map.lookup (T.toLower k) of +resolveKey' lang k = + case Map.lookup (langLanguage lang) biblatexStringMap >>= + Map.lookup (T.toLower k) of Nothing -> k Just (x, _) -> either (const k) stringify $ parseLaTeX lang x diff --git a/src/Text/Pandoc/Citeproc/Data.hs b/src/Text/Pandoc/Citeproc/Data.hs index 40430b0f5..388b9ba62 100644 --- a/src/Text/Pandoc/Citeproc/Data.hs +++ b/src/Text/Pandoc/Citeproc/Data.hs @@ -21,12 +21,12 @@ biblatexStringMap :: M.Map Text (M.Map Text (Text, Text)) biblatexStringMap = foldr go mempty biblatexLocalizations where go (fp, bs) = - let Lang lang _ _ _ _ _ = parseLang - (toIETF $ T.takeWhile (/= '.') $ T.pack fp) - ls = T.lines $ TE.decodeUtf8 bs - in if length ls > 4 - then M.insert lang (toStringMap $ map (T.splitOn "|") ls) - else id + let ls = T.lines $ TE.decodeUtf8 bs + in case parseLang (toIETF $ T.takeWhile (/= '.') $ T.pack fp) of + Right lang | length ls > 4 + -> M.insert (langLanguage lang) + (toStringMap $ map (T.splitOn "|") ls) + _ -> id toStringMap = foldr go' mempty go' [term, x, y] = M.insert term (x, y) go' _ = id diff --git a/src/Text/Pandoc/Class/CommonState.hs b/src/Text/Pandoc/Class/CommonState.hs index 7e1735c2b..0fd094d99 100644 --- a/src/Text/Pandoc/Class/CommonState.hs +++ b/src/Text/Pandoc/Class/CommonState.hs @@ -19,7 +19,7 @@ where import Data.Default (Default (def)) import Data.Text (Text) -import Text.Pandoc.BCP47 (Lang) +import UnicodeCollation.Lang (Lang) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Logging (LogMessage, Verbosity (WARNING)) import Text.Pandoc.Translations (Translations) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 293a822a0..76f1fa32b 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -70,7 +70,7 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, import System.FilePath ((), (<.>), takeExtension, dropExtension, isRelative, splitDirectories) import System.Random (StdGen) -import Text.Pandoc.BCP47 (Lang(..), parseBCP47, renderLang) +import UnicodeCollation.Lang (Lang(..), parseLang, renderLang) import Text.Pandoc.Class.CommonState (CommonState (..)) import Text.Pandoc.Definition import Text.Pandoc.Error @@ -285,7 +285,7 @@ readFileFromDirs (d:ds) f = catchError toLang :: PandocMonad m => Maybe T.Text -> m (Maybe Lang) toLang Nothing = return Nothing toLang (Just s) = - case parseBCP47 s of + case parseLang s of Left _ -> do report $ InvalidLang s return Nothing diff --git a/src/Text/Pandoc/Readers/BibTeX.hs b/src/Text/Pandoc/Readers/BibTeX.hs index 956b9f1f7..b82a81350 100644 --- a/src/Text/Pandoc/Readers/BibTeX.hs +++ b/src/Text/Pandoc/Readers/BibTeX.hs @@ -48,11 +48,14 @@ readBibLaTeX = readBibTeX' BibTeX.Biblatex readBibTeX' :: PandocMonad m => Variant -> ReaderOptions -> Text -> m Pandoc readBibTeX' variant _opts t = do - lang <- maybe (Lang "en" (Just "US")) parseLang - <$> lookupEnv "LANG" + mblangEnv <- lookupEnv "LANG" + let defaultLang = Lang "en" Nothing (Just "US") [] [] [] + let lang = case mblangEnv of + Nothing -> defaultLang + Just l -> either (const defaultLang) id $ parseLang l locale <- case getLocale lang of Left e -> - case getLocale (Lang "en" (Just "US")) of + case getLocale (Lang "en" Nothing (Just "US") [] [] []) of Right l -> return l Left _ -> throwError $ PandocCiteprocError e Right l -> return l diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 851756065..83caf742a 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -33,7 +33,7 @@ import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import System.FilePath (addExtension, replaceExtension, takeExtension) -import Text.Pandoc.BCP47 (renderLang) +import UnicodeCollation.Lang (renderLang) import Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocPure (PandocPure) import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getResourcePath, diff --git a/src/Text/Pandoc/Readers/LaTeX/Lang.hs b/src/Text/Pandoc/Readers/LaTeX/Lang.hs index 08e217bdb..b92e6ab57 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Lang.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Lang.hs @@ -23,7 +23,7 @@ import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Shared (extractSpaces) -import Text.Pandoc.BCP47 (Lang(..), renderLang) +import UnicodeCollation.Lang (Lang(..), renderLang) import Text.Pandoc.Class (PandocMonad(..), setTranslations) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Parsing (updateState, option, getState, QuoteContext(..), @@ -99,133 +99,136 @@ setDefaultLanguage = do polyglossiaLangToBCP47 :: M.Map T.Text (T.Text -> Lang) polyglossiaLangToBCP47 = M.fromList [ ("arabic", \o -> case T.filter (/=' ') o of - "locale=algeria" -> Lang "ar" "" "DZ" [] - "locale=mashriq" -> Lang "ar" "" "SY" [] - "locale=libya" -> Lang "ar" "" "LY" [] - "locale=morocco" -> Lang "ar" "" "MA" [] - "locale=mauritania" -> Lang "ar" "" "MR" [] - "locale=tunisia" -> Lang "ar" "" "TN" [] - _ -> Lang "ar" "" "" []) + "locale=algeria" -> Lang "ar" Nothing (Just "DZ") [] [] [] + "locale=mashriq" -> Lang "ar" Nothing (Just "SY") [] [] [] + "locale=libya" -> Lang "ar" Nothing (Just "LY") [] [] [] + "locale=morocco" -> Lang "ar" Nothing (Just "MA") [] [] [] + "locale=mauritania" -> Lang "ar" Nothing (Just "MR") [] [] [] + "locale=tunisia" -> Lang "ar" Nothing (Just "TN") [] [] [] + _ -> Lang "ar" Nothing (Just "") [] [] []) , ("german", \o -> case T.filter (/=' ') o of - "spelling=old" -> Lang "de" "" "DE" ["1901"] + "spelling=old" -> Lang "de" Nothing (Just "DE") ["1901"] [] [] "variant=austrian,spelling=old" - -> Lang "de" "" "AT" ["1901"] - "variant=austrian" -> Lang "de" "" "AT" [] + -> Lang "de" Nothing (Just "AT") ["1901"] [] [] + "variant=austrian" -> Lang "de" Nothing (Just "AT") [] [] [] "variant=swiss,spelling=old" - -> Lang "de" "" "CH" ["1901"] - "variant=swiss" -> Lang "de" "" "CH" [] - _ -> Lang "de" "" "" []) - , ("lsorbian", \_ -> Lang "dsb" "" "" []) + -> Lang "de" Nothing (Just "CH") ["1901"] [] [] + "variant=swiss" -> Lang "de" Nothing (Just "CH") [] [] [] + _ -> Lang "de" Nothing Nothing [] [] []) + , ("lsorbian", \_ -> Lang "dsb" Nothing Nothing [] [] []) , ("greek", \o -> case T.filter (/=' ') o of - "variant=poly" -> Lang "el" "" "polyton" [] - "variant=ancient" -> Lang "grc" "" "" [] - _ -> Lang "el" "" "" []) + "variant=poly" -> Lang "el" Nothing (Just "polyton") [] [] [] + "variant=ancient" -> Lang "grc" Nothing Nothing [] [] [] + _ -> Lang "el" Nothing Nothing [] [] []) , ("english", \o -> case T.filter (/=' ') o of - "variant=australian" -> Lang "en" "" "AU" [] - "variant=canadian" -> Lang "en" "" "CA" [] - "variant=british" -> Lang "en" "" "GB" [] - "variant=newzealand" -> Lang "en" "" "NZ" [] - "variant=american" -> Lang "en" "" "US" [] - _ -> Lang "en" "" "" []) - , ("usorbian", \_ -> Lang "hsb" "" "" []) + "variant=australian" -> Lang "en" Nothing (Just "AU") [] [] [] + "variant=canadian" -> Lang "en" Nothing (Just "CA") [] [] [] + "variant=british" -> Lang "en" Nothing (Just "GB") [] [] [] + "variant=newzealand" -> Lang "en" Nothing (Just "NZ") [] [] [] + "variant=american" -> Lang "en" Nothing (Just "US") [] [] [] + _ -> Lang "en" Nothing (Just "") [] [] []) + , ("usorbian", \_ -> Lang "hsb" Nothing Nothing [] [] []) , ("latin", \o -> case T.filter (/=' ') o of - "variant=classic" -> Lang "la" "" "" ["x-classic"] - _ -> Lang "la" "" "" []) - , ("slovenian", \_ -> Lang "sl" "" "" []) - , ("serbianc", \_ -> Lang "sr" "cyrl" "" []) - , ("pinyin", \_ -> Lang "zh" "Latn" "" ["pinyin"]) - , ("afrikaans", \_ -> Lang "af" "" "" []) - , ("amharic", \_ -> Lang "am" "" "" []) - , ("assamese", \_ -> Lang "as" "" "" []) - , ("asturian", \_ -> Lang "ast" "" "" []) - , ("bulgarian", \_ -> Lang "bg" "" "" []) - , ("bengali", \_ -> Lang "bn" "" "" []) - , ("tibetan", \_ -> Lang "bo" "" "" []) - , ("breton", \_ -> Lang "br" "" "" []) - , ("catalan", \_ -> Lang "ca" "" "" []) - , ("welsh", \_ -> Lang "cy" "" "" []) - , ("czech", \_ -> Lang "cs" "" "" []) - , ("coptic", \_ -> Lang "cop" "" "" []) - , ("danish", \_ -> Lang "da" "" "" []) - , ("divehi", \_ -> Lang "dv" "" "" []) - , ("esperanto", \_ -> Lang "eo" "" "" []) - , ("spanish", \_ -> Lang "es" "" "" []) - , ("estonian", \_ -> Lang "et" "" "" []) - , ("basque", \_ -> Lang "eu" "" "" []) - , ("farsi", \_ -> Lang "fa" "" "" []) - , ("finnish", \_ -> Lang "fi" "" "" []) - , ("french", \_ -> Lang "fr" "" "" []) - , ("friulan", \_ -> Lang "fur" "" "" []) - , ("irish", \_ -> Lang "ga" "" "" []) - , ("scottish", \_ -> Lang "gd" "" "" []) - , ("ethiopic", \_ -> Lang "gez" "" "" []) - , ("galician", \_ -> Lang "gl" "" "" []) - , ("hebrew", \_ -> Lang "he" "" "" []) - , ("hindi", \_ -> Lang "hi" "" "" []) - , ("croatian", \_ -> Lang "hr" "" "" []) - , ("magyar", \_ -> Lang "hu" "" "" []) - , ("armenian", \_ -> Lang "hy" "" "" []) - , ("interlingua", \_ -> Lang "ia" "" "" []) - , ("indonesian", \_ -> Lang "id" "" "" []) - , ("icelandic", \_ -> Lang "is" "" "" []) - , ("italian", \_ -> Lang "it" "" "" []) - , ("japanese", \_ -> Lang "jp" "" "" []) - , ("khmer", \_ -> Lang "km" "" "" []) - , ("kurmanji", \_ -> Lang "kmr" "" "" []) - , ("kannada", \_ -> Lang "kn" "" "" []) - , ("korean", \_ -> Lang "ko" "" "" []) - , ("lao", \_ -> Lang "lo" "" "" []) - , ("lithuanian", \_ -> Lang "lt" "" "" []) - , ("latvian", \_ -> Lang "lv" "" "" []) - , ("malayalam", \_ -> Lang "ml" "" "" []) - , ("mongolian", \_ -> Lang "mn" "" "" []) - , ("marathi", \_ -> Lang "mr" "" "" []) - , ("dutch", \_ -> Lang "nl" "" "" []) - , ("nynorsk", \_ -> Lang "nn" "" "" []) - , ("norsk", \_ -> Lang "no" "" "" []) - , ("nko", \_ -> Lang "nqo" "" "" []) - , ("occitan", \_ -> Lang "oc" "" "" []) - , ("panjabi", \_ -> Lang "pa" "" "" []) - , ("polish", \_ -> Lang "pl" "" "" []) - , ("piedmontese", \_ -> Lang "pms" "" "" []) - , ("portuguese", \_ -> Lang "pt" "" "" []) - , ("romansh", \_ -> Lang "rm" "" "" []) - , ("romanian", \_ -> Lang "ro" "" "" []) - , ("russian", \_ -> Lang "ru" "" "" []) - , ("sanskrit", \_ -> Lang "sa" "" "" []) - , ("samin", \_ -> Lang "se" "" "" []) - , ("slovak", \_ -> Lang "sk" "" "" []) - , ("albanian", \_ -> Lang "sq" "" "" []) - , ("serbian", \_ -> Lang "sr" "" "" []) - , ("swedish", \_ -> Lang "sv" "" "" []) - , ("syriac", \_ -> Lang "syr" "" "" []) - , ("tamil", \_ -> Lang "ta" "" "" []) - , ("telugu", \_ -> Lang "te" "" "" []) - , ("thai", \_ -> Lang "th" "" "" []) - , ("turkmen", \_ -> Lang "tk" "" "" []) - , ("turkish", \_ -> Lang "tr" "" "" []) - , ("ukrainian", \_ -> Lang "uk" "" "" []) - , ("urdu", \_ -> Lang "ur" "" "" []) - , ("vietnamese", \_ -> Lang "vi" "" "" []) + "variant=classic" -> Lang "la" Nothing Nothing ["x-classic"] [] [] + _ -> Lang "la" Nothing Nothing [] [] []) + , ("slovenian", \_ -> Lang "sl" Nothing Nothing [] [] []) + , ("serbianc", \_ -> Lang "sr" (Just "Cyrl") Nothing [] [] []) + , ("pinyin", \_ -> Lang "zh" (Just "Latn") Nothing ["pinyin"] [] []) + , ("afrikaans", \_ -> simpleLang "af") + , ("amharic", \_ -> simpleLang "am") + , ("assamese", \_ -> simpleLang "as") + , ("asturian", \_ -> simpleLang "ast") + , ("bulgarian", \_ -> simpleLang "bg") + , ("bengali", \_ -> simpleLang "bn") + , ("tibetan", \_ -> simpleLang "bo") + , ("breton", \_ -> simpleLang "br") + , ("catalan", \_ -> simpleLang "ca") + , ("welsh", \_ -> simpleLang "cy") + , ("czech", \_ -> simpleLang "cs") + , ("coptic", \_ -> simpleLang "cop") + , ("danish", \_ -> simpleLang "da") + , ("divehi", \_ -> simpleLang "dv") + , ("esperanto", \_ -> simpleLang "eo") + , ("spanish", \_ -> simpleLang "es") + , ("estonian", \_ -> simpleLang "et") + , ("basque", \_ -> simpleLang "eu") + , ("farsi", \_ -> simpleLang "fa") + , ("finnish", \_ -> simpleLang "fi") + , ("french", \_ -> simpleLang "fr") + , ("friulan", \_ -> simpleLang "fur") + , ("irish", \_ -> simpleLang "ga") + , ("scottish", \_ -> simpleLang "gd") + , ("ethiopic", \_ -> simpleLang "gez") + , ("galician", \_ -> simpleLang "gl") + , ("hebrew", \_ -> simpleLang "he") + , ("hindi", \_ -> simpleLang "hi") + , ("croatian", \_ -> simpleLang "hr") + , ("magyar", \_ -> simpleLang "hu") + , ("armenian", \_ -> simpleLang "hy") + , ("interlingua", \_ -> simpleLang "ia") + , ("indonesian", \_ -> simpleLang "id") + , ("icelandic", \_ -> simpleLang "is") + , ("italian", \_ -> simpleLang "it") + , ("japanese", \_ -> simpleLang "jp") + , ("khmer", \_ -> simpleLang "km") + , ("kurmanji", \_ -> simpleLang "kmr") + , ("kannada", \_ -> simpleLang "kn") + , ("korean", \_ -> simpleLang "ko") + , ("lao", \_ -> simpleLang "lo") + , ("lithuanian", \_ -> simpleLang "lt") + , ("latvian", \_ -> simpleLang "lv") + , ("malayalam", \_ -> simpleLang "ml") + , ("mongolian", \_ -> simpleLang "mn") + , ("marathi", \_ -> simpleLang "mr") + , ("dutch", \_ -> simpleLang "nl") + , ("nynorsk", \_ -> simpleLang "nn") + , ("norsk", \_ -> simpleLang "no") + , ("nko", \_ -> simpleLang "nqo") + , ("occitan", \_ -> simpleLang "oc") + , ("panjabi", \_ -> simpleLang "pa") + , ("polish", \_ -> simpleLang "pl") + , ("piedmontese", \_ -> simpleLang "pms") + , ("portuguese", \_ -> simpleLang "pt") + , ("romansh", \_ -> simpleLang "rm") + , ("romanian", \_ -> simpleLang "ro") + , ("russian", \_ -> simpleLang "ru") + , ("sanskrit", \_ -> simpleLang "sa") + , ("samin", \_ -> simpleLang "se") + , ("slovak", \_ -> simpleLang "sk") + , ("albanian", \_ -> simpleLang "sq") + , ("serbian", \_ -> simpleLang "sr") + , ("swedish", \_ -> simpleLang "sv") + , ("syriac", \_ -> simpleLang "syr") + , ("tamil", \_ -> simpleLang "ta") + , ("telugu", \_ -> simpleLang "te") + , ("thai", \_ -> simpleLang "th") + , ("turkmen", \_ -> simpleLang "tk") + , ("turkish", \_ -> simpleLang "tr") + , ("ukrainian", \_ -> simpleLang "uk") + , ("urdu", \_ -> simpleLang "ur") + , ("vietnamese", \_ -> simpleLang "vi") ] +simpleLang :: Text -> Lang +simpleLang l = Lang l Nothing Nothing [] [] [] + babelLangToBCP47 :: T.Text -> Maybe Lang babelLangToBCP47 s = case s of - "austrian" -> Just $ Lang "de" "" "AT" ["1901"] - "naustrian" -> Just $ Lang "de" "" "AT" [] - "swissgerman" -> Just $ Lang "de" "" "CH" ["1901"] - "nswissgerman" -> Just $ Lang "de" "" "CH" [] - "german" -> Just $ Lang "de" "" "DE" ["1901"] - "ngerman" -> Just $ Lang "de" "" "DE" [] - "lowersorbian" -> Just $ Lang "dsb" "" "" [] - "uppersorbian" -> Just $ Lang "hsb" "" "" [] - "polutonikogreek" -> Just $ Lang "el" "" "" ["polyton"] - "slovene" -> Just $ Lang "sl" "" "" [] - "australian" -> Just $ Lang "en" "" "AU" [] - "canadian" -> Just $ Lang "en" "" "CA" [] - "british" -> Just $ Lang "en" "" "GB" [] - "newzealand" -> Just $ Lang "en" "" "NZ" [] - "american" -> Just $ Lang "en" "" "US" [] - "classiclatin" -> Just $ Lang "la" "" "" ["x-classic"] + "austrian" -> Just $ Lang "de" Nothing (Just "AT") ["1901"] [] [] + "naustrian" -> Just $ Lang "de" Nothing (Just "AT") [] [] [] + "swissgerman" -> Just $ Lang "de" Nothing (Just "CH") ["1901"] [] [] + "nswissgerman" -> Just $ Lang "de" Nothing (Just "CH") [] [] [] + "german" -> Just $ Lang "de" Nothing (Just "DE") ["1901"] [] [] + "ngerman" -> Just $ Lang "de" Nothing (Just "DE") [] [] [] + "lowersorbian" -> Just $ Lang "dsb" Nothing Nothing [] [] [] + "uppersorbian" -> Just $ Lang "hsb" Nothing Nothing [] [] [] + "polutonikogreek" -> Just $ Lang "el" Nothing Nothing ["polyton"] [] [] + "slovene" -> Just $ simpleLang "sl" + "australian" -> Just $ Lang "en" Nothing (Just "AU") [] [] [] + "canadian" -> Just $ Lang "en" Nothing (Just "CA") [] [] [] + "british" -> Just $ Lang "en" Nothing (Just "GB") [] [] [] + "newzealand" -> Just $ Lang "en" Nothing (Just "NZ") [] [] [] + "american" -> Just $ Lang "en" Nothing (Just "US") [] [] [] + "classiclatin" -> Just $ Lang "la" Nothing Nothing ["x-classic"] [] [] _ -> ($ "") <$> M.lookup s polyglossiaLangToBCP47 diff --git a/src/Text/Pandoc/Writers/BibTeX.hs b/src/Text/Pandoc/Writers/BibTeX.hs index b9ae0c13a..95de6b71f 100644 --- a/src/Text/Pandoc/Writers/BibTeX.hs +++ b/src/Text/Pandoc/Writers/BibTeX.hs @@ -43,7 +43,7 @@ writeBibTeX' :: PandocMonad m => Variant -> WriterOptions -> Pandoc -> m Text writeBibTeX' variant opts (Pandoc meta _) = do let mblang = case lookupMetaString "lang" meta of "" -> Nothing - t -> Just $ parseLang t + t -> either (const Nothing) Just $ parseLang t let refs = case lookupMeta "references" meta of Just (MetaList xs) -> mapMaybe metaValueToReference xs _ -> [] diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 3c9975be8..f352c84bc 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -21,7 +21,7 @@ import Data.Maybe (mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) -import Text.Pandoc.BCP47 +import UnicodeCollation.Lang (Lang(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.ImageSize @@ -555,26 +555,26 @@ fromBCP47 mbs = fromBCP47' <$> toLang mbs -- https://tools.ietf.org/html/bcp47#section-2.1 -- http://wiki.contextgarden.net/Language_Codes fromBCP47' :: Maybe Lang -> Maybe Text -fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy" -fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq" -fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo" -fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb" -fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz" -fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma" -fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo" -fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de" -fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at" -fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch" -fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr" -fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us" -fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb" -fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr" -fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr" -fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba" -fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il" -fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja" -fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua" -fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn" -fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn" -fromBCP47' (Just (Lang l _ _ _) ) = Just l -fromBCP47' Nothing = Nothing +fromBCP47' (Just (Lang "ar" _ (Just "SY") _ _ _)) = Just "ar-sy" +fromBCP47' (Just (Lang "ar" _ (Just "IQ") _ _ _)) = Just "ar-iq" +fromBCP47' (Just (Lang "ar" _ (Just "JO") _ _ _)) = Just "ar-jo" +fromBCP47' (Just (Lang "ar" _ (Just "LB") _ _ _)) = Just "ar-lb" +fromBCP47' (Just (Lang "ar" _ (Just "DZ") _ _ _)) = Just "ar-dz" +fromBCP47' (Just (Lang "ar" _ (Just "MA") _ _ _)) = Just "ar-ma" +fromBCP47' (Just (Lang "de" _ _ ["1901"] _ _)) = Just "deo" +fromBCP47' (Just (Lang "de" _ (Just "DE") _ _ _)) = Just "de-de" +fromBCP47' (Just (Lang "de" _ (Just "AT") _ _ _)) = Just "de-at" +fromBCP47' (Just (Lang "de" _ (Just "CH") _ _ _)) = Just "de-ch" +fromBCP47' (Just (Lang "el" _ _ ["poly"] _ _)) = Just "agr" +fromBCP47' (Just (Lang "en" _ (Just "US") _ _ _)) = Just "en-us" +fromBCP47' (Just (Lang "en" _ (Just "GB") _ _ _)) = Just "en-gb" +fromBCP47' (Just (Lang "grc"_ _ _ _ _)) = Just "agr" +fromBCP47' (Just (Lang "el" _ _ _ _ _)) = Just "gr" +fromBCP47' (Just (Lang "eu" _ _ _ _ _)) = Just "ba" +fromBCP47' (Just (Lang "he" _ _ _ _ _)) = Just "il" +fromBCP47' (Just (Lang "jp" _ _ _ _ _)) = Just "ja" +fromBCP47' (Just (Lang "uk" _ _ _ _ _)) = Just "ua" +fromBCP47' (Just (Lang "vi" _ _ _ _ _)) = Just "vn" +fromBCP47' (Just (Lang "zh" _ _ _ _ _)) = Just "cn" +fromBCP47' (Just (Lang l _ _ _ _ _)) = Just l +fromBCP47' Nothing = Nothing diff --git a/src/Text/Pandoc/Writers/CslJson.hs b/src/Text/Pandoc/Writers/CslJson.hs index a10def95e..395335667 100644 --- a/src/Text/Pandoc/Writers/CslJson.hs +++ b/src/Text/Pandoc/Writers/CslJson.hs @@ -34,15 +34,16 @@ import Control.Monad.Identity import Citeproc.Locale (getLocale) import Citeproc.CslJson import Text.Pandoc.Options (WriterOptions) -import Data.Maybe (mapMaybe) +import Data.Maybe (mapMaybe, fromMaybe) import Data.Aeson.Encode.Pretty (Config (..), Indent (Spaces), NumberFormat (Generic), defConfig, encodePretty') writeCslJson :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCslJson _opts (Pandoc meta _) = do - let lang = maybe (Lang "en" (Just "US")) parseLang - (lookupMeta "lang" meta >>= metaValueToText) + let lang = fromMaybe (Lang "en" Nothing (Just "US") [] [] []) + (lookupMeta "lang" meta >>= metaValueToText >>= + either (const Nothing) Just . parseLang) locale <- case getLocale lang of Left e -> throwError $ PandocCiteprocError e Right l -> return l diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 20bcd0324..7781df8e7 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -36,7 +36,7 @@ import qualified Data.Text.Lazy as TL import Data.Time.Clock.POSIX import Data.Digest.Pure.SHA (sha1, showDigest) import Skylighting -import Text.Pandoc.BCP47 (getLang, renderLang) +import UnicodeCollation.Lang (renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 1c970e6ad..e99bad738 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -29,7 +29,7 @@ import qualified Data.Text as T import Network.URI (unEscapeString) import Text.DocTemplates (FromContext(lookupContext), renderTemplate, Val(..), Context(..)) -import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang) +import UnicodeCollation.Lang (Lang (..), renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, diff --git a/src/Text/Pandoc/Writers/LaTeX/Lang.hs b/src/Text/Pandoc/Writers/LaTeX/Lang.hs index 871b2692a..437b84120 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Lang.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Lang.hs @@ -46,7 +46,7 @@ toPolyglossia (Lang "de" _ (Just "AT") vars _ _) toPolyglossia (Lang "de" _ (Just "AT") _ _ _) = ("german", "variant=austrian") toPolyglossia (Lang "de" _ (Just "CH") vars _ _) | "1901" `elem` vars = ("german", "variant=swiss, spelling=old") -toPolyglossia (Lang "de" _ (Just "CH") _ _ _ _) = ("german", "variant=swiss") +toPolyglossia (Lang "de" _ (Just "CH") _ _ _) = ("german", "variant=swiss") toPolyglossia (Lang "de" _ _ _ _ _) = ("german", "") toPolyglossia (Lang "dsb" _ _ _ _ _) = ("lsorbian", "") toPolyglossia (Lang "el" _ _ vars _ _) @@ -61,9 +61,9 @@ toPolyglossia (Lang "grc" _ _ _ _ _) = ("greek", "variant=ancient") toPolyglossia (Lang "hsb" _ _ _ _ _) = ("usorbian", "") toPolyglossia (Lang "la" _ _ vars _ _) | "x-classic" `elem` vars = ("latin", "variant=classic") -toPolyglossia (Lang "pt" _ "BR" _ _ _) = ("portuguese", "variant=brazilian") +toPolyglossia (Lang "pt" _ (Just "BR") _ _ _) = ("portuguese", "variant=brazilian") toPolyglossia (Lang "sl" _ _ _ _ _) = ("slovenian", "") -toPolyglossia x = (commonFromBcp47 x, "") +toPolyglossia x = (commonFromBcp47 x, "") -- Takes a list of the constituents of a BCP47 language code and -- converts it to a Babel language string. @@ -81,7 +81,7 @@ toBabel (Lang "de" _ _ vars _ _) | "1901" `elem` vars = "german" | otherwise = "ngerman" toBabel (Lang "dsb" _ _ _ _ _) = "lowersorbian" -toBabel (Lang "el" _ _ vars) +toBabel (Lang "el" _ _ vars _ _) | "polyton" `elem` vars = "polutonikogreek" toBabel (Lang "en" _ (Just "AU") _ _ _) = "australian" toBabel (Lang "en" _ (Just "CA") _ _ _) = "canadian" diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 101b236aa..6fd4cdeb4 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -16,6 +16,7 @@ import Codec.Archive.Zip import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B +import Data.Maybe (fromMaybe) import Data.Generics (everywhere', mkT) import Data.List (isPrefixOf) import qualified Data.Map as Map @@ -23,7 +24,7 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Time import System.FilePath (takeDirectory, takeExtension, (<.>)) -import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang) +import UnicodeCollation.Lang (Lang (..), renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition @@ -35,7 +36,7 @@ import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.DocLayout import Text.Pandoc.Shared (stringify, pandocVersion, tshow) import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks, - fixDisplayMath) + fixDisplayMath, getLang) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) @@ -194,7 +195,7 @@ addLang lang = everywhere' (mkT updateLangAttr) where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) = Attr n (langLanguage lang) updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) - = Attr n (langRegion lang) + = Attr n (fromMaybe "" $ langRegion lang) updateLangAttr x = x -- | transform both Image and Math elements diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index cf42f2228..6c265090c 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -25,7 +25,7 @@ import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T -import Text.Pandoc.BCP47 (Lang (..), parseBCP47) +import UnicodeCollation.Lang (Lang (..), parseLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, translateTerm, setTranslations, toLang) import Text.Pandoc.Definition @@ -236,7 +236,7 @@ handleSpaces s = case T.uncons s of -- | Convert Pandoc document to string in OpenDocument format. writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOpenDocument opts (Pandoc meta blocks) = do - let defLang = Lang "en" "US" "" [] + let defLang = Lang "en" (Just "US") Nothing [] [] [] lang <- case lookupMetaString "lang" meta of "" -> pure defLang s -> fromMaybe defLang <$> toLang (Just s) @@ -893,7 +893,7 @@ textStyleAttr m s Map.insert "style:font-name-complex" "Courier New" $ m | Language lang <- s = Map.insert "fo:language" (langLanguage lang) . - Map.insert "fo:country" (langRegion lang) $ m + maybe id (Map.insert "fo:country") (langRegion lang) $ m | otherwise = m withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a @@ -901,7 +901,7 @@ withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> - case parseBCP47 l of + case parseLang l of Right lang -> withTextStyle (Language lang) action Left _ -> do report $ InvalidLang l diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index fcb47bd5a..a09d18571 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -149,7 +149,7 @@ defField field val (Context m) = f _newval oldval = oldval -- | Get the contents of the `lang` metadata field or variable. -getLang :: WriterOptions -> Meta -> Maybe Text +getLang :: WriterOptions -> Meta -> Maybe T.Text getLang opts meta = case lookupContext "lang" (writerVariables opts) of Just s -> Just s -- cgit v1.2.3 From 099ac9985b93befb32e01718e5b242d5c7b4f080 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 15 Apr 2021 17:36:18 -0700 Subject: Use BCP47 language codes in citeproc tests. --- test/command/pandoc-citeproc-65.md | 2 +- test/command/pandoc-citeproc-70.md | 4 ++-- test/command/unicode-collation.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/command/pandoc-citeproc-65.md b/test/command/pandoc-citeproc-65.md index 7b07f7ecf..93335e901 100644 --- a/test/command/pandoc-citeproc-65.md +++ b/test/command/pandoc-citeproc-65.md @@ -14,7 +14,7 @@ references: id: 'stotz:1996handbuch' issued: literal: 1996_2004 - language: German + language: de number-of-volumes: 5 publisher: Beck publisher-place: Munich diff --git a/test/command/pandoc-citeproc-70.md b/test/command/pandoc-citeproc-70.md index fb96add82..e1132b98d 100644 --- a/test/command/pandoc-citeproc-70.md +++ b/test/command/pandoc-citeproc-70.md @@ -22,7 +22,7 @@ references: issued: date-parts: - - 2009 - language: French + language: fr page: '155--174' publisher: 'Fédération Internationale des Instituts d''Études Médiévales' @@ -42,7 +42,7 @@ references: issued: date-parts: - - 1955 - language: Latin + language: la note: 'ArticleType: research-article / Full publication date: 1955 / Copyright © 1955 Fordham University' page: '163--193' diff --git a/test/command/unicode-collation.md b/test/command/unicode-collation.md index a53ed7577..190e383d6 100644 --- a/test/command/unicode-collation.md +++ b/test/command/unicode-collation.md @@ -108,7 +108,7 @@ Ubina, A. J. (1985). % pandoc -C -t plain --- nocite: '@*' -lang: fr +lang: fr-FR-u-kb-true references: - id: cote author: cote -- cgit v1.2.3 From 7a7fefce5ef395f1c88db8b984618a4bd3c7d916 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 15 Apr 2021 17:45:28 -0700 Subject: Use document's lang for the lang parameter of citeproc... even if it differs from localeLanguage. (It is designed to be possible to override the locale language, and this is especially useful when one wants to use the unicode extension syntx, e.g. fr-u-kb.) --- src/Text/Pandoc/Citeproc.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index c9f1806e4..2f4936fa6 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -74,8 +74,7 @@ processCitations (Pandoc meta bs) = do let linkCites = maybe False truish $ lookupMeta "link-citations" meta let opts = defaultCiteprocOptions{ linkCitations = linkCites } - let result = Citeproc.citeproc opts style (localeLanguage locale) - refs citations + let result = Citeproc.citeproc opts style mblang refs citations mapM_ (report . CiteprocWarning) (resultWarnings result) let sopts = styleOptions style let classes = "references" : -- TODO remove this or keep for compatibility? -- cgit v1.2.3 From a478a5c4c8753fd0bf272cd540ca197ae146a196 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 17 Apr 2021 11:47:54 -0700 Subject: Update to released unicode-collation, latest citeproc dev version. Update citeproc test. --- cabal.project | 11 +---------- src/Text/Pandoc/App.hs | 2 +- src/Text/Pandoc/Citeproc/Data.hs | 2 +- src/Text/Pandoc/Class/CommonState.hs | 2 +- src/Text/Pandoc/Class/PandocMonad.hs | 2 +- src/Text/Pandoc/Readers/LaTeX.hs | 2 +- src/Text/Pandoc/Readers/LaTeX/Lang.hs | 2 +- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/Docbook.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- src/Text/Pandoc/Writers/LaTeX/Lang.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- stack.yaml | 5 ++--- test/command/pandoc-citeproc-320a.md | 8 ++++---- 16 files changed, 20 insertions(+), 30 deletions(-) diff --git a/cabal.project b/cabal.project index fa17a20a6..77bc8ef88 100644 --- a/cabal.project +++ b/cabal.project @@ -5,14 +5,5 @@ flags: +embed_data_files source-repository-package type: git location: https://github.com/jgm/citeproc - tag: b42857be658b8f2649e989e061978e304986f853 + tag: f9439e07e9271c7c2674a51efcad2fb8c663b2c8 -source-repository-package - type: git - location: https://github.com/jgm/unicode-collation - tag: 9d229a5c6bcbaf53d7022575234eb223cfa90d55 - --- source-repository-package --- type: git --- location: https://github.com/jgm/citeproc --- tag: d44e24696ab444090d0e63e321c3a573f68b2e74 diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 67d3cce7d..4e8c9f2ab 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -55,7 +55,7 @@ import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts, import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs, options) import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings) -import UnicodeCollation.Lang (Lang (..), parseLang) +import Text.Collate.Lang (Lang (..), parseLang) import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.SelfContained (makeSelfContained) diff --git a/src/Text/Pandoc/Citeproc/Data.hs b/src/Text/Pandoc/Citeproc/Data.hs index 388b9ba62..848a83a1e 100644 --- a/src/Text/Pandoc/Citeproc/Data.hs +++ b/src/Text/Pandoc/Citeproc/Data.hs @@ -10,7 +10,7 @@ import qualified Data.Text.Encoding as TE import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.Citeproc.Util (toIETF) -import UnicodeCollation.Lang (Lang(..), parseLang) +import Text.Collate.Lang (Lang(..), parseLang) biblatexLocalizations :: [(FilePath, ByteString)] biblatexLocalizations = $(embedDir "citeproc/biblatex-localization") diff --git a/src/Text/Pandoc/Class/CommonState.hs b/src/Text/Pandoc/Class/CommonState.hs index 0fd094d99..796a4afd5 100644 --- a/src/Text/Pandoc/Class/CommonState.hs +++ b/src/Text/Pandoc/Class/CommonState.hs @@ -19,7 +19,7 @@ where import Data.Default (Default (def)) import Data.Text (Text) -import UnicodeCollation.Lang (Lang) +import Text.Collate.Lang (Lang) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Logging (LogMessage, Verbosity (WARNING)) import Text.Pandoc.Translations (Translations) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 76f1fa32b..7559cd7cd 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -70,7 +70,7 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, import System.FilePath ((), (<.>), takeExtension, dropExtension, isRelative, splitDirectories) import System.Random (StdGen) -import UnicodeCollation.Lang (Lang(..), parseLang, renderLang) +import Text.Collate.Lang (Lang(..), parseLang, renderLang) import Text.Pandoc.Class.CommonState (CommonState (..)) import Text.Pandoc.Definition import Text.Pandoc.Error diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 83caf742a..203dab83c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -33,7 +33,7 @@ import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import System.FilePath (addExtension, replaceExtension, takeExtension) -import UnicodeCollation.Lang (renderLang) +import Text.Collate.Lang (renderLang) import Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocPure (PandocPure) import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getResourcePath, diff --git a/src/Text/Pandoc/Readers/LaTeX/Lang.hs b/src/Text/Pandoc/Readers/LaTeX/Lang.hs index b92e6ab57..6a8327904 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Lang.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Lang.hs @@ -23,7 +23,7 @@ import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Shared (extractSpaces) -import UnicodeCollation.Lang (Lang(..), renderLang) +import Text.Collate.Lang (Lang(..), renderLang) import Text.Pandoc.Class (PandocMonad(..), setTranslations) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Parsing (updateState, option, getState, QuoteContext(..), diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index f352c84bc..f14b1d894 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -21,7 +21,7 @@ import Data.Maybe (mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) -import UnicodeCollation.Lang (Lang(..)) +import Text.Collate.Lang (Lang(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.ImageSize diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 1f10c9d04..02b141250 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -493,4 +493,4 @@ isSectionAttr DocBook4 ("os",_) = True isSectionAttr DocBook4 ("revision",_) = True isSectionAttr DocBook4 ("security",_) = True isSectionAttr DocBook4 ("vendor",_) = True -isSectionAttr _ (_,_) = False \ No newline at end of file +isSectionAttr _ (_,_) = False diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 7781df8e7..749ad9a21 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -36,7 +36,7 @@ import qualified Data.Text.Lazy as TL import Data.Time.Clock.POSIX import Data.Digest.Pure.SHA (sha1, showDigest) import Skylighting -import UnicodeCollation.Lang (renderLang) +import Text.Collate.Lang (renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index e99bad738..8b1f3df1d 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -29,7 +29,7 @@ import qualified Data.Text as T import Network.URI (unEscapeString) import Text.DocTemplates (FromContext(lookupContext), renderTemplate, Val(..), Context(..)) -import UnicodeCollation.Lang (Lang (..), renderLang) +import Text.Collate.Lang (Lang (..), renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, diff --git a/src/Text/Pandoc/Writers/LaTeX/Lang.hs b/src/Text/Pandoc/Writers/LaTeX/Lang.hs index 437b84120..0ba68b74e 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Lang.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Lang.hs @@ -15,7 +15,7 @@ module Text.Pandoc.Writers.LaTeX.Lang toBabel ) where import Data.Text (Text) -import UnicodeCollation.Lang (Lang(..)) +import Text.Collate.Lang (Lang(..)) -- In environments \Arabic instead of \arabic is used diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 6fd4cdeb4..e4eb4fd25 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -24,7 +24,7 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Time import System.FilePath (takeDirectory, takeExtension, (<.>)) -import UnicodeCollation.Lang (Lang (..), renderLang) +import Text.Collate.Lang (Lang (..), renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 6c265090c..34a3a4aa5 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -25,7 +25,7 @@ import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T -import UnicodeCollation.Lang (Lang (..), parseLang) +import Text.Collate.Lang (Lang (..), parseLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, translateTerm, setTranslations, toLang) import Text.Pandoc.Definition diff --git a/stack.yaml b/stack.yaml index 159bf74b9..7bc33fa43 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,11 +16,10 @@ extra-deps: - texmath-0.12.2 - random-1.2.0 - xml-conduit-1.9.1.1 +- unicode-collation-0.1 # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc - commit: b42857be658b8f2649e989e061978e304986f853 -- git: https://github.com/jgm/unicode-collation - commit: 9d229a5c6bcbaf53d7022575234eb223cfa90d55 + commit: f9439e07e9271c7c2674a51efcad2fb8c663b2c8 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 diff --git a/test/command/pandoc-citeproc-320a.md b/test/command/pandoc-citeproc-320a.md index 1c3b47de0..79dacfa10 100644 --- a/test/command/pandoc-citeproc-320a.md +++ b/test/command/pandoc-citeproc-320a.md @@ -56,6 +56,10 @@ n.d.; al-'Udhrī, n.d.; Uch, n.d.; Uebel, n.d.; Zzz, n.d.). Uch, Ann. n.d. ::: +::: {#ref-item4 .csl-entry} +'Udhrī, Jamīl al-. n.d. +::: + ::: {#ref-item1 .csl-entry} ʾUdhrī, Jamīl al-. n.d. ::: @@ -68,10 +72,6 @@ Uch, Ann. n.d. \'Udhrī, Jamīl al-. n.d. ::: -::: {#ref-item4 .csl-entry} -'Udhrī, Jamīl al-. n.d. -::: - ::: {#ref-item5 .csl-entry} 'Udhrī, Jamīl al-. n.d. ::: -- cgit v1.2.3 From 4f858a6a490f6d66f2f929876dc75d9bdd13efa2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 18 Apr 2021 09:09:53 -0700 Subject: MANUAL: Add information about `lang` and bibliography sorting. --- MANUAL.txt | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 845d1dbba..4ddacb9a2 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5541,10 +5541,23 @@ A few other metadata fields affect bibliography formatting: `lang` : The `lang` field will affect how the style is localized, - for example in the translation of labels and the use - of quotation marks. (For backwards compatibility, - `locale` may be used instead of `lang`, but this use - is deprecated.) + for example in the translation of labels, the use + of quotation marks, and the way items are sorted. + (For backwards compatibility, `locale` may be used instead + of `lang`, but this use is deprecated.) + + A BCP 47 language tag is expected: for example, `en`, + `de`, `en-US`, `fr-CA`, `ug-Cyrl`. The unicode extension + syntax (after `-u-`) may be used to specify options for + collation (sorting) more precisely. Here are some examples: + + - `zh-u-co-pinyin` -- Chinese with the Pinyin collation. + - `es-u-co-trad` -- Spanish with the traditional collation + (with `Ch` sorting after `C`). + - `fr-u-kb` -- French with "backwards" accent sorting + (with `coté` sorting after `côte`). + - `en-US-u-kf-upper` -- English with uppercase letters sorting + before lower (default is lower before upper). `notes-after-punctuation` : If true (the default), pandoc will put footnote citations @@ -5556,12 +5569,6 @@ A few other metadata fields affect bibliography formatting: punctuation. - - - - - - # Slide shows You can use pandoc to produce an HTML + JavaScript slide presentation -- cgit v1.2.3 From b7a263218454b8f9e53330c4256a5c751111d41f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 18 Apr 2021 11:34:12 -0700 Subject: Use lastest unicode-collation. --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 8816767e9..744f3f095 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -493,7 +493,7 @@ library unordered-containers >= 0.2 && < 0.3, xml >= 1.3.12 && < 1.4, xml-conduit >= 1.9.1.1 && < 1.10, - unicode-collation >= 0.1 && < 0.2, + unicode-collation >= 0.1.1 && < 0.2, zip-archive >= 0.2.3.4 && < 0.5, zlib >= 0.5 && < 0.7 if os(windows) && arch(i386) diff --git a/stack.yaml b/stack.yaml index 7bc33fa43..ac0c987ac 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,7 +16,7 @@ extra-deps: - texmath-0.12.2 - random-1.2.0 - xml-conduit-1.9.1.1 -- unicode-collation-0.1 +- unicode-collation-0.1.1 # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc commit: f9439e07e9271c7c2674a51efcad2fb8c663b2c8 -- cgit v1.2.3 From 9d6c3fa41f8e84c700b1e97c8ba82f3d17e04b8e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 18 Apr 2021 11:35:09 -0700 Subject: Use lastest citeproc. --- cabal.project | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 77bc8ef88..2b82eb70c 100644 --- a/cabal.project +++ b/cabal.project @@ -5,5 +5,5 @@ flags: +embed_data_files source-repository-package type: git location: https://github.com/jgm/citeproc - tag: f9439e07e9271c7c2674a51efcad2fb8c663b2c8 + tag: 46066cb8939e907e7e3a7126304919c8ed32aed0 diff --git a/stack.yaml b/stack.yaml index ac0c987ac..689f792bd 100644 --- a/stack.yaml +++ b/stack.yaml @@ -19,7 +19,7 @@ extra-deps: - unicode-collation-0.1.1 # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc - commit: f9439e07e9271c7c2674a51efcad2fb8c663b2c8 + commit: 46066cb8939e907e7e3a7126304919c8ed32aed0 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From 8d584b5826d43298c9b86f57c429a2a16092ef97 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 18 Apr 2021 21:58:55 -0700 Subject: CI: Use cabal 2.4 even with ghc 8.0.2 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64dda5302..bb5c743fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,8 @@ jobs: matrix: versions: - ghc: '8.0.2' - cabal: '2.2' - prefix: 'new-' + cabal: '2.4' + prefix: 'v2-' cabalopts: '' testopts: '' - ghc: '8.2.2' -- cgit v1.2.3 From 73d394ca2adff31a384404ae25665b36c7d0bba0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 18 Apr 2021 22:01:12 -0700 Subject: Use MetaInlines not MetaBlocks for multimarkdown metadata fields. This gives better results in converting to e.g. pandoc markdown. Ref: --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- test/command/mmd-metadata.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test/command/mmd-metadata.md diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 6c3947a81..4b20e3a8b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -298,7 +298,7 @@ kvPair allowEmpty = try $ do (try $ newline >> lookAhead (blankline <|> nonspaceChar)) guard $ allowEmpty || not (T.null val) let key' = T.concat $ T.words $ T.toLower key - let val' = MetaBlocks $ B.toList $ B.plain $ B.text val + let val' = MetaInlines $ B.toList $ B.text val return (key',val') parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc diff --git a/test/command/mmd-metadata.md b/test/command/mmd-metadata.md new file mode 100644 index 000000000..3cda34873 --- /dev/null +++ b/test/command/mmd-metadata.md @@ -0,0 +1,20 @@ +``` +% pandoc -f markdown_mmd -t markdown -s +Title: Blah blah blah +Author: Doo de Doo +Base Header Level: 1 +Bibliography: Pubs.bib +Lang: en-GB + +body +^D +--- +author: Doo de Doo +baseheaderlevel: 1 +bibliography: Pubs.bib +lang: en-GB +title: Blah blah blah +--- + +body +``` -- cgit v1.2.3 From 16d372abcb991caf26077c7f1cd0be4680894170 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 19 Apr 2021 08:38:31 -0700 Subject: Issue error message when reader or writer format is malformed. Previously we exited with an error status but (due to a bug) no message. Closes #7231. --- src/Text/Pandoc/Readers.hs | 6 +++--- src/Text/Pandoc/Writers.hs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index ac70f7d4c..7ae9db34f 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -65,6 +65,7 @@ import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.Text (Text) import qualified Data.Text as T +import Text.Pandoc.Shared (tshow) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Error @@ -101,7 +102,6 @@ import Text.Pandoc.Readers.CSV import Text.Pandoc.Readers.CslJson import Text.Pandoc.Readers.BibTeX import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Parsec.Error data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) @@ -152,8 +152,8 @@ readers = [ ("native" , TextReader readNative) getReader :: PandocMonad m => Text -> m (Reader m, Extensions) getReader s = case parseFormatSpec s of - Left e -> throwError $ PandocAppError - $ T.intercalate "\n" [T.pack m | Message m <- errorMessages e] + Left e -> throwError $ PandocAppError $ + "Error parsing reader format " <> tshow s <> ": " <> tshow e Right (readerName, extsToEnable, extsToDisable) -> case lookup readerName readers of Nothing -> throwError $ PandocUnknownReaderError diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 95d6270b5..c348477c2 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -81,6 +81,7 @@ import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.Text (Text) import qualified Data.Text as T +import Text.Pandoc.Shared (tshow) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Options @@ -122,7 +123,6 @@ import Text.Pandoc.Writers.Texinfo import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.XWiki import Text.Pandoc.Writers.ZimWiki -import Text.Parsec.Error data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text) | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) @@ -196,8 +196,8 @@ writers = [ getWriter :: PandocMonad m => Text -> m (Writer m, Extensions) getWriter s = case parseFormatSpec s of - Left e -> throwError $ PandocAppError - $ T.intercalate "\n" [T.pack m | Message m <- errorMessages e] + Left e -> throwError $ PandocAppError $ + "Error parsing writer format " <> tshow s <> ": " <> tshow e Right (writerName, extsToEnable, extsToDisable) -> case lookup writerName writers of Nothing -> throwError $ -- cgit v1.2.3 From 3ab08fe2fb0ba7e786e7d2e89219d254b7753dd3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 19 Apr 2021 12:59:11 -0700 Subject: Fix nightly build. --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c038bca2c..784c57276 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -22,10 +22,10 @@ jobs: ghc --version cabal --version cabal v2-update - cabal v2-build --dependencies-only all + cabal v2-build --dependencies-only - name: Build run: | - cabal v2-install all + cabal v2-install strip $HOME/.cabal/bin/pandoc - name: Install artifact run: | -- cgit v1.2.3 From 0b74bbbdaa643a473e26ea14d0a94efac6078d8c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 20 Apr 2021 10:54:46 +0200 Subject: Docx writer: extract Table handling into separate module --- pandoc.cabal | 2 + src/Text/Pandoc/Writers/Docx.hs | 226 +--------------------------------- src/Text/Pandoc/Writers/Docx/Table.hs | 114 +++++++++++++++++ 3 files changed, 121 insertions(+), 221 deletions(-) create mode 100644 src/Text/Pandoc/Writers/Docx/Table.hs diff --git a/pandoc.cabal b/pandoc.cabal index 744f3f095..14d2da0ae 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -657,6 +657,8 @@ library Text.Pandoc.Readers.Metadata, Text.Pandoc.Readers.Roff, Text.Pandoc.Writers.Docx.StyleMap, + Text.Pandoc.Writers.Docx.Table, + Text.Pandoc.Writers.Docx.Types, Text.Pandoc.Writers.JATS.References, Text.Pandoc.Writers.JATS.Table, Text.Pandoc.Writers.JATS.Types, diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 749ad9a21..7064ded09 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -22,7 +22,6 @@ import Control.Applicative ((<|>)) import Control.Monad.Except (catchError, throwError) import Control.Monad.Reader import Control.Monad.State.Strict -import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Data.Char (isSpace, isLetter) import Data.List (intercalate, isPrefixOf, isSuffixOf) @@ -47,123 +46,24 @@ import Text.Pandoc.Highlighting (highlight) import Text.Pandoc.Error import Text.Pandoc.ImageSize import Text.Pandoc.Logging -import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType, - getMimeTypeDef) +import Text.Pandoc.MIME (extensionFromMimeType, getMimeType, getMimeTypeDef) import Text.Pandoc.Options import Text.Pandoc.Writers.Docx.StyleMap +import Text.Pandoc.Writers.Docx.Table +import Text.Pandoc.Writers.Docx.Types import Text.Pandoc.Shared import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared -import Text.Printf (printf) import Text.TeXMath import Text.Pandoc.Writers.OOXML import Text.Pandoc.XML.Light as XML import Data.Generics (mkT, everywhere) -data ListMarker = NoMarker - | BulletMarker - | NumberMarker ListNumberStyle ListNumberDelim Int - deriving (Show, Read, Eq, Ord) - -listMarkerToId :: ListMarker -> Text -listMarkerToId NoMarker = "990" -listMarkerToId BulletMarker = "991" -listMarkerToId (NumberMarker sty delim n) = T.pack $ - '9' : '9' : styNum : delimNum : show n - where styNum = case sty of - DefaultStyle -> '2' - Example -> '3' - Decimal -> '4' - LowerRoman -> '5' - UpperRoman -> '6' - LowerAlpha -> '7' - UpperAlpha -> '8' - delimNum = case delim of - DefaultDelim -> '0' - Period -> '1' - OneParen -> '2' - TwoParens -> '3' - -data EnvProps = EnvProps{ styleElement :: Maybe Element - , otherElements :: [Element] - } - -instance Semigroup EnvProps where - EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es') - -instance Monoid EnvProps where - mempty = EnvProps Nothing [] - mappend = (<>) - squashProps :: EnvProps -> [Element] squashProps (EnvProps Nothing es) = es squashProps (EnvProps (Just e) es) = e : es -data WriterEnv = WriterEnv{ envTextProperties :: EnvProps - , envParaProperties :: EnvProps - , envRTL :: Bool - , envListLevel :: Int - , envListNumId :: Int - , envInDel :: Bool - , envChangesAuthor :: Text - , envChangesDate :: Text - , envPrintWidth :: Integer - } - -defaultWriterEnv :: WriterEnv -defaultWriterEnv = WriterEnv{ envTextProperties = mempty - , envParaProperties = mempty - , envRTL = False - , envListLevel = -1 - , envListNumId = 1 - , envInDel = False - , envChangesAuthor = "unknown" - , envChangesDate = "1969-12-31T19:00:00Z" - , envPrintWidth = 1 - } - -data WriterState = WriterState{ - stFootnotes :: [Element] - , stComments :: [([(Text, Text)], [Inline])] - , stSectionIds :: Set.Set Text - , stExternalLinks :: M.Map Text Text - , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString) - , stLists :: [ListMarker] - , stInsId :: Int - , stDelId :: Int - , stStyleMaps :: StyleMaps - , stFirstPara :: Bool - , stInTable :: Bool - , stInList :: Bool - , stTocTitle :: [Inline] - , stDynamicParaProps :: Set.Set ParaStyleName - , stDynamicTextProps :: Set.Set CharStyleName - , stCurId :: Int - } - -defaultWriterState :: WriterState -defaultWriterState = WriterState{ - stFootnotes = defaultFootnotes - , stComments = [] - , stSectionIds = Set.empty - , stExternalLinks = M.empty - , stImages = M.empty - , stLists = [NoMarker] - , stInsId = 1 - , stDelId = 1 - , stStyleMaps = StyleMaps M.empty M.empty - , stFirstPara = False - , stInTable = False - , stInList = False - , stTocTitle = [Str "Table of Contents"] - , stDynamicParaProps = Set.empty - , stDynamicTextProps = Set.empty - , stCurId = 20 - } - -type WS m = ReaderT WriterEnv (StateT WriterState m) - renumIdMap :: Int -> [Element] -> M.Map Text Text renumIdMap _ [] = M.empty renumIdMap n (e:es) @@ -858,12 +758,6 @@ separateTables (x@Table{}:xs@(Table{}:_)) = x : RawBlock (Format "openxml") "" : separateTables xs separateTables (x:xs) = x : separateTables xs -pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element -pStyleM styleName = do - pStyleMap <- gets (smParaStyle . stStyleMaps) - let sty' = getStyleIdFromName styleName pStyleMap - return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] () - rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element rStyleM styleName = do cStyleMap <- gets (smCharStyle . stStyleMaps) @@ -995,78 +889,8 @@ blockToOpenXML' _ HorizontalRule = do $ mknode "v:rect" [("style","width:0;height:1.5pt"), ("o:hralign","center"), ("o:hrstd","t"),("o:hr","t")] () ] -blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do - let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot - setFirstPara - modify $ \s -> s { stInTable = True } - let captionStr = stringify caption - caption' <- if null caption - then return [] - else withParaPropM (pStyleM "Table Caption") - $ blockToOpenXML opts (Para caption) - let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] () - -- Table cells require a element, even an empty one! - -- Not in the spec but in Word 2007, 2010. See #4953. And - -- apparently the last element must be a , see #6983. - let cellToOpenXML (al, cell) = do - es <- withParaProp (alignmentFor al) $ blocksToOpenXML opts cell - return $ - case reverse (onlyElems es) of - b:e:_ | qName (elName b) == "bookmarkEnd" - , qName (elName e) == "p" -> es - e:_ | qName (elName e) == "p" -> es - _ -> es ++ [Elem $ mknode "w:p" [] ()] - headers' <- mapM cellToOpenXML $ zip aligns headers - rows' <- mapM (mapM cellToOpenXML . zip aligns) rows - compactStyle <- pStyleM "Compact" - let emptyCell' = [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] - let mkcell contents = mknode "w:tc" [] - $ if null contents - then emptyCell' - else contents - let mkrow cells = - mknode "w:tr" [] $ - map mkcell cells - let textwidth = 7920 -- 5.5 in in twips, 1/20 pt - let fullrow = 5000 -- 100% specified in pct - let (rowwidth :: Int) = round $ fullrow * sum widths - let mkgridcol w = mknode "w:gridCol" - [("w:w", tshow (floor (textwidth * w) :: Integer))] () - let hasHeader = not $ all null headers - modify $ \s -> s { stInTable = False } - -- for compatibility with Word <= 2007, we include a val with a bitmask - -- 0×0020 Apply first row conditional formatting - -- 0×0040 Apply last row conditional formatting - -- 0×0080 Apply first column conditional formatting - -- 0×0100 Apply last column conditional formatting - -- 0×0200 Do not apply row banding conditional formatting - -- 0×0400 Do not apply column banding conditional formattin - let tblLookVal :: Int - tblLookVal = if hasHeader then 0x20 else 0 - return $ - caption' ++ - [Elem $ - mknode "w:tbl" [] - ( mknode "w:tblPr" [] - ( mknode "w:tblStyle" [("w:val","Table")] () : - mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () : - mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") - ,("w:lastRow","0") - ,("w:firstColumn","0") - ,("w:lastColumn","0") - ,("w:noHBand","0") - ,("w:noVBand","0") - ,("w:val", T.pack $ printf "%04x" tblLookVal) - ] () : - [ mknode "w:tblCaption" [("w:val", captionStr)] () - | not (null caption) ] ) - : mknode "w:tblGrid" [] - (if all (==0) widths - then [] - else map mkgridcol widths) - : [ mkrow headers' | hasHeader ] ++ - map mkrow rows' - )] +blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = + tableToOpenXML (blocksToOpenXML opts) blkCapt specs thead tbody tfoot blockToOpenXML' opts el | BulletList lst <- el = addOpenXMLList BulletMarker lst | OrderedList (start, numstyle, numdelim) lst <- el @@ -1121,13 +945,6 @@ listItemToOpenXML opts numid (first:rest) = do modify $ \st -> st{ stInList = oldInList } return $ first'' ++ rest'' -alignmentToString :: Alignment -> Text -alignmentToString alignment = case alignment of - AlignLeft -> "left" - AlignRight -> "right" - AlignCenter -> "center" - AlignDefault -> "left" - -- | Convert a list of inline elements to OpenXML. inlinesToOpenXML :: PandocMonad m => WriterOptions -> [Inline] -> WS m [Content] inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst @@ -1138,10 +955,6 @@ withNumId numid = local $ \env -> env{ envListNumId = numid } asList :: (PandocMonad m) => WS m a -> WS m a asList = local $ \env -> env{ envListLevel = envListLevel env + 1 } -isStyle :: Element -> Bool -isStyle e = isElem [] "w" "rStyle" e || - isElem [] "w" "pStyle" e - getTextProps :: (PandocMonad m) => WS m [Element] getTextProps = do props <- asks envTextProperties @@ -1170,16 +983,6 @@ getParaProps displayMathPara = do [] -> [] ps -> [mknode "w:pPr" [] ps] -withParaProp :: PandocMonad m => Element -> WS m a -> WS m a -withParaProp d p = - local (\env -> env {envParaProperties = ep <> envParaProperties env}) p - where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d] - -withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a -withParaPropM md p = do - d <- md - withParaProp d p - formattedString :: PandocMonad m => Text -> WS m [Element] formattedString str = -- properly handle soft hyphens @@ -1200,9 +1003,6 @@ formattedRun els = do props <- getTextProps return [ mknode "w:r" [] $ props ++ els ] -setFirstPara :: PandocMonad m => WS m () -setFirstPara = modify $ \s -> s { stFirstPara = True } - -- | Convert an inline element to OpenXML. inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Content] inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il @@ -1494,22 +1294,6 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do br :: Element br = mknode "w:r" [] [mknode "w:br" [] ()] --- Word will insert these footnotes into the settings.xml file --- (whether or not they're visible in the document). If they're in the --- file, but not in the footnotes.xml file, it will produce --- problems. So we want to make sure we insert them into our document. -defaultFootnotes :: [Element] -defaultFootnotes = [ mknode "w:footnote" - [("w:type", "separator"), ("w:id", "-1")] - [ mknode "w:p" [] - [mknode "w:r" [] - [ mknode "w:separator" [] ()]]] - , mknode "w:footnote" - [("w:type", "continuationSeparator"), ("w:id", "0")] - [ mknode "w:p" [] - [ mknode "w:r" [] - [ mknode "w:continuationSeparator" [] ()]]]] - withDirection :: PandocMonad m => WS m a -> WS m a withDirection x = do diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs new file mode 100644 index 000000000..a6b137fc4 --- /dev/null +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -0,0 +1,114 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{- | +Module : Text.Pandoc.Writers.Docx +Copyright : Copyright (C) 2012-2021 John MacFarlane +License : GNU GPL, version 2 or above +Maintainer : John MacFarlane + +Conversion of table blocks to docx. +-} +module Text.Pandoc.Writers.Docx.Table + ( tableToOpenXML + ) where + +import Control.Monad.State.Strict +import Data.Text (Text) +import Text.Pandoc.Definition +import Text.Pandoc.Class.PandocMonad (PandocMonad) +import Text.Pandoc.Writers.Docx.Types +import Text.Pandoc.Shared +import Text.Pandoc.Writers.Shared +import Text.Printf (printf) +import Text.Pandoc.Writers.OOXML +import Text.Pandoc.XML.Light as XML +import qualified Data.Text as T + +tableToOpenXML :: PandocMonad m + => ([Block] -> WS m [Content]) + -> Caption + -> [ColSpec] + -> TableHead + -> [TableBody] + -> TableFoot + -> WS m [Content] +tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do + let (caption, aligns, widths, headers, rows) = + toLegacyTable blkCapt specs thead tbody tfoot + setFirstPara + modify $ \s -> s { stInTable = True } + let captionStr = stringify caption + caption' <- if null caption + then return [] + else withParaPropM (pStyleM "Table Caption") + $ blocksToOpenXML [Para caption] + let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] () + -- Table cells require a element, even an empty one! + -- Not in the spec but in Word 2007, 2010. See #4953. And + -- apparently the last element must be a , see #6983. + let cellToOpenXML (al, cell) = do + es <- withParaProp (alignmentFor al) $ blocksToOpenXML cell + return $ + case reverse (onlyElems es) of + b:e:_ | qName (elName b) == "bookmarkEnd" + , qName (elName e) == "p" -> es + e:_ | qName (elName e) == "p" -> es + _ -> es ++ [Elem $ mknode "w:p" [] ()] + headers' <- mapM cellToOpenXML $ zip aligns headers + rows' <- mapM (mapM cellToOpenXML . zip aligns) rows + compactStyle <- pStyleM "Compact" + let emptyCell' = [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] + let mkcell contents = mknode "w:tc" [] + $ if null contents + then emptyCell' + else contents + let mkrow cells = + mknode "w:tr" [] $ + map mkcell cells + let textwidth = 7920 -- 5.5 in in twips, 1/20 pt + let fullrow = 5000 -- 100% specified in pct + let (rowwidth :: Int) = round $ fullrow * sum widths + let mkgridcol w = mknode "w:gridCol" + [("w:w", tshow (floor (textwidth * w) :: Integer))] () + let hasHeader = not $ all null headers + modify $ \s -> s { stInTable = False } + -- for compatibility with Word <= 2007, we include a val with a bitmask + -- 0×0020 Apply first row conditional formatting + -- 0×0040 Apply last row conditional formatting + -- 0×0080 Apply first column conditional formatting + -- 0×0100 Apply last column conditional formatting + -- 0×0200 Do not apply row banding conditional formatting + -- 0×0400 Do not apply column banding conditional formattin + let tblLookVal :: Int + tblLookVal = if hasHeader then 0x20 else 0 + return $ + caption' ++ + [Elem $ + mknode "w:tbl" [] + ( mknode "w:tblPr" [] + ( mknode "w:tblStyle" [("w:val","Table")] () : + mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () : + mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") + ,("w:lastRow","0") + ,("w:firstColumn","0") + ,("w:lastColumn","0") + ,("w:noHBand","0") + ,("w:noVBand","0") + ,("w:val", T.pack $ printf "%04x" tblLookVal) + ] () : + [ mknode "w:tblCaption" [("w:val", captionStr)] () + | not (null caption) ] ) + : mknode "w:tblGrid" [] + (if all (==0) widths + then [] + else map mkgridcol widths) + : [ mkrow headers' | hasHeader ] ++ + map mkrow rows' + )] + +alignmentToString :: Alignment -> Text +alignmentToString alignment = case alignment of + AlignLeft -> "left" + AlignRight -> "right" + AlignCenter -> "center" + AlignDefault -> "left" -- cgit v1.2.3 From dc0ba7294d9c40987b016ac886440a85ade8ae44 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 20 Apr 2021 13:38:16 +0200 Subject: Docx writer: add missing file --- src/Text/Pandoc/Writers/Docx/Types.hs | 181 ++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/Text/Pandoc/Writers/Docx/Types.hs diff --git a/src/Text/Pandoc/Writers/Docx/Types.hs b/src/Text/Pandoc/Writers/Docx/Types.hs new file mode 100644 index 000000000..006584c30 --- /dev/null +++ b/src/Text/Pandoc/Writers/Docx/Types.hs @@ -0,0 +1,181 @@ +{-# LANGUAGE OverloadedStrings #-} +{- | +Module : Text.Pandoc.Writers.Docx +Copyright : Copyright (C) 2012-2021 John MacFarlane +License : GNU GPL, version 2 or above +Maintainer : John MacFarlane + +Conversion of table blocks to docx. +-} +module Text.Pandoc.Writers.Docx.Types + ( EnvProps (..) + , WriterEnv (..) + , defaultWriterEnv + , WriterState (..) + , defaultWriterState + , WS + , ListMarker (..) + , listMarkerToId + , pStyleM + , isStyle + , setFirstPara + , withParaProp + , withParaPropM + ) where + +import Control.Applicative ((<|>)) +import Control.Monad.Reader +import Control.Monad.State.Strict +import Data.Text (Text) +import Text.Pandoc.Class.PandocMonad (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.Writers.Docx.StyleMap +import Text.Pandoc.Writers.OOXML +import Text.Pandoc.XML.Light as XML +import qualified Data.ByteString as B +import qualified Data.Map as M +import qualified Data.Set as Set +import qualified Data.Text as T + +data ListMarker = NoMarker + | BulletMarker + | NumberMarker ListNumberStyle ListNumberDelim Int + deriving (Show, Read, Eq, Ord) + +listMarkerToId :: ListMarker -> Text +listMarkerToId NoMarker = "990" +listMarkerToId BulletMarker = "991" +listMarkerToId (NumberMarker sty delim n) = T.pack $ + '9' : '9' : styNum : delimNum : show n + where styNum = case sty of + DefaultStyle -> '2' + Example -> '3' + Decimal -> '4' + LowerRoman -> '5' + UpperRoman -> '6' + LowerAlpha -> '7' + UpperAlpha -> '8' + delimNum = case delim of + DefaultDelim -> '0' + Period -> '1' + OneParen -> '2' + TwoParens -> '3' + + +data EnvProps = EnvProps{ styleElement :: Maybe Element + , otherElements :: [Element] + } + +instance Semigroup EnvProps where + EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es') + +instance Monoid EnvProps where + mempty = EnvProps Nothing [] + mappend = (<>) + +data WriterEnv = WriterEnv + { envTextProperties :: EnvProps + , envParaProperties :: EnvProps + , envRTL :: Bool + , envListLevel :: Int + , envListNumId :: Int + , envInDel :: Bool + , envChangesAuthor :: Text + , envChangesDate :: Text + , envPrintWidth :: Integer + } + +defaultWriterEnv :: WriterEnv +defaultWriterEnv = WriterEnv + { envTextProperties = mempty + , envParaProperties = mempty + , envRTL = False + , envListLevel = -1 + , envListNumId = 1 + , envInDel = False + , envChangesAuthor = "unknown" + , envChangesDate = "1969-12-31T19:00:00Z" + , envPrintWidth = 1 + } + + +data WriterState = WriterState{ + stFootnotes :: [Element] + , stComments :: [([(Text, Text)], [Inline])] + , stSectionIds :: Set.Set Text + , stExternalLinks :: M.Map Text Text + , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString) + , stLists :: [ListMarker] + , stInsId :: Int + , stDelId :: Int + , stStyleMaps :: StyleMaps + , stFirstPara :: Bool + , stInTable :: Bool + , stInList :: Bool + , stTocTitle :: [Inline] + , stDynamicParaProps :: Set.Set ParaStyleName + , stDynamicTextProps :: Set.Set CharStyleName + , stCurId :: Int + } + +defaultWriterState :: WriterState +defaultWriterState = WriterState{ + stFootnotes = defaultFootnotes + , stComments = [] + , stSectionIds = Set.empty + , stExternalLinks = M.empty + , stImages = M.empty + , stLists = [NoMarker] + , stInsId = 1 + , stDelId = 1 + , stStyleMaps = StyleMaps M.empty M.empty + , stFirstPara = False + , stInTable = False + , stInList = False + , stTocTitle = [Str "Table of Contents"] + , stDynamicParaProps = Set.empty + , stDynamicTextProps = Set.empty + , stCurId = 20 + } + +setFirstPara :: PandocMonad m => WS m () +setFirstPara = modify $ \s -> s { stFirstPara = True } + +type WS m = ReaderT WriterEnv (StateT WriterState m) + +-- Word will insert these footnotes into the settings.xml file +-- (whether or not they're visible in the document). If they're in the +-- file, but not in the footnotes.xml file, it will produce +-- problems. So we want to make sure we insert them into our document. +defaultFootnotes :: [Element] +defaultFootnotes = [ mknode "w:footnote" + [("w:type", "separator"), ("w:id", "-1")] + [ mknode "w:p" [] + [mknode "w:r" [] + [ mknode "w:separator" [] ()]]] + , mknode "w:footnote" + [("w:type", "continuationSeparator"), ("w:id", "0")] + [ mknode "w:p" [] + [ mknode "w:r" [] + [ mknode "w:continuationSeparator" [] ()]]]] + +pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element +pStyleM styleName = do + pStyleMap <- gets (smParaStyle . stStyleMaps) + let sty' = getStyleIdFromName styleName pStyleMap + return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] () + +withParaProp :: PandocMonad m => Element -> WS m a -> WS m a +withParaProp d p = + local (\env -> env {envParaProperties = ep <> envParaProperties env}) p + where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d] + +withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a +withParaPropM md p = do + d <- md + withParaProp d p + +isStyle :: Element -> Bool +isStyle e = isElem [] "w" "rStyle" e || + isElem [] "w" "pStyle" e -- cgit v1.2.3 From ac62b082ee784bdac6e2db625fd069f59b0b2ead Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 20 Apr 2021 14:59:38 -0700 Subject: stack.yaml - use latest unicode-collation. --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 689f792bd..b3e29ba93 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,7 +16,7 @@ extra-deps: - texmath-0.12.2 - random-1.2.0 - xml-conduit-1.9.1.1 -- unicode-collation-0.1.1 +- unicode-collation-0.1.2 # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc commit: 46066cb8939e907e7e3a7126304919c8ed32aed0 -- cgit v1.2.3 From 7f4850c9de086a2f6df072bf15357cd05335cffd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 10:29:03 -0700 Subject: Remove biblatex-nussbaum.md test. It is basically the same as biblaetx-quotes.md. --- test/command/biblatex-nussbaum.md | 63 --------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 test/command/biblatex-nussbaum.md diff --git a/test/command/biblatex-nussbaum.md b/test/command/biblatex-nussbaum.md deleted file mode 100644 index 5be575a32..000000000 --- a/test/command/biblatex-nussbaum.md +++ /dev/null @@ -1,63 +0,0 @@ -``` -% pandoc -f biblatex -t markdown -s -@comment{ - -Adapted from biblatex-example.bib - - -Formatted with pandoc and chicago-author-date.csl, 2013-10-23: - -(Nussbaum 1978) - -Nussbaum, Martha. 1978. *Aristotle’s “De Motu Animalium”*. Princeton: -Princeton University Press. - - -Formatted with pandoc and apa.csl, 2013-10-23: - -(Nussbaum, 1978) - -Nussbaum, M. (1978). *Aristotle’s “De Motu Animalium”*. Princeton: -Princeton University Press. - - -} - -@string{ pup = {Princeton University Press} } - -@Book{nussbaum, - author = {Nussbaum, Martha}, - title = {Aristotle's \mkbibquote{De Motu Animalium}}, - date = 1978, - publisher = pup, - location = {Princeton}, - keywords = {secondary}, - hyphenation = {american}, - sorttitle = {Aristotle's De Motu Animalium}, - indexsorttitle= {Aristotle's De Motu Animalium}, - annotation = {A book entry. Note the sorttitle and - indexsorttitle fields and the markup of the quotes in - the database file}, -} - -^D ---- -nocite: "[@*]" -references: -- annote: A book entry. Note the sorttitle and indexsorttitle fields and - the markup of the quotes in the database file - author: - - family: Nussbaum - given: Martha - id: nussbaum - issued: 1978 - keyword: secondary - language: en-US - publisher: Princeton University Press - publisher-place: Princeton - title: "Aristotle's \"De Motu Animalium\"" - type: book ---- - - -``` -- cgit v1.2.3 From 547bc2cdf83b8be926de55521674c0e8fab12db5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 10:31:33 -0700 Subject: Add quotes properly in markdown YAML metadata fields. This fixes a bug, which caused the writer to look at the LAST rather than the FIRST character in determining whether quotes were needed. So we got spurious quotes in some cases and didn't get necessary quotes in others. Closes #7245. Updated a number of test cases accordingly. --- src/Text/Pandoc/Writers/Markdown.hs | 11 +++++------ test/command/1279.md | 2 +- test/command/biblatex-inproceedings.md | 2 +- test/command/biblatex-jaffe.md | 4 ++-- test/command/biblatex-moraux.md | 2 +- test/command/biblatex-quotes.md | 2 +- test/command/biblatex-spiegelberg.md | 4 ++-- test/command/biblatex-test-case-conversion.md | 4 ++-- test/command/biblatex-textnormal.md | 2 +- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index daf45ed53..3295d9e6c 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -26,7 +26,7 @@ import Data.Default import Data.List (intersperse, sortOn, transpose) import Data.List.NonEmpty (nonEmpty, NonEmpty(..)) import qualified Data.Map as M -import Data.Maybe (fromMaybe, mapMaybe) +import Data.Maybe (fromMaybe, mapMaybe, isNothing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T @@ -142,18 +142,17 @@ valToYaml (SimpleVal x) | otherwise = if hasNewlines x then hang 0 ("|" <> cr) x - else if fst $ foldr needsDoubleQuotes (False, True) x + else if isNothing $ foldM needsDoubleQuotes True x then "\"" <> fmap escapeInDoubleQuotes x <> "\"" else x where - needsDoubleQuotes t (positive, isFirst) + needsDoubleQuotes isFirst t = if T.any isBadAnywhere t || (isFirst && T.any isYamlPunct (T.take 1 t)) - then (True, False) - else (positive, False) + then Nothing + else Just False isBadAnywhere '#' = True isBadAnywhere ':' = True - isBadAnywhere '`' = False isBadAnywhere _ = False hasNewlines NewLine = True hasNewlines BlankLines{} = True diff --git a/test/command/1279.md b/test/command/1279.md index acad6859e..d3572f245 100644 --- a/test/command/1279.md +++ b/test/command/1279.md @@ -9,7 +9,7 @@ title: My Article [^1]: Dept. of This and That ^D --- -author: "John Doe[^1]" +author: John Doe[^1] date: 2014 title: My Article --- diff --git a/test/command/biblatex-inproceedings.md b/test/command/biblatex-inproceedings.md index 29a1e0298..76b584800 100644 --- a/test/command/biblatex-inproceedings.md +++ b/test/command/biblatex-inproceedings.md @@ -63,7 +63,7 @@ references: publisher-place: Cambridge title: "Le *De Anima* dans la tradition grècque: Quelques aspects de l'interpretation du traité, de Theophraste à Themistius" - title-short: *De Anima* dans la tradition grècque + title-short: "*De Anima* dans la tradition grècque" type: paper-conference - author: - family: Salam diff --git a/test/command/biblatex-jaffe.md b/test/command/biblatex-jaffe.md index f172c6ae7..e0381772b 100644 --- a/test/command/biblatex-jaffe.md +++ b/test/command/biblatex-jaffe.md @@ -72,8 +72,8 @@ references: language: la number-of-volumes: 2 publisher-place: Leipzig - title: "Regesta Pontificum Romanorum ab condita ecclesia ad annum post - Christum natum [mcxcviii]{.smallcaps}" + title: Regesta Pontificum Romanorum ab condita ecclesia ad annum post + Christum natum [mcxcviii]{.smallcaps} title-short: Regesta Pontificum Romanorum type: book --- diff --git a/test/command/biblatex-moraux.md b/test/command/biblatex-moraux.md index 8a1b084f8..1624ad960 100644 --- a/test/command/biblatex-moraux.md +++ b/test/command/biblatex-moraux.md @@ -86,7 +86,7 @@ references: publisher-place: Cambridge title: "Le *De Anima* dans la tradition grècque: Quelques aspects de l'interpretation du traité, de Theophraste à Themistius" - title-short: *De Anima* dans la tradition grècque + title-short: "*De Anima* dans la tradition grècque" type: paper-conference --- diff --git a/test/command/biblatex-quotes.md b/test/command/biblatex-quotes.md index 9d868cb76..50055316f 100644 --- a/test/command/biblatex-quotes.md +++ b/test/command/biblatex-quotes.md @@ -36,7 +36,7 @@ references: language: en-US publisher: Princeton University Press publisher-place: Princeton - title: "Aristotle's \"De Motu Animalium\"" + title: Aristotle's "De Motu Animalium" type: book --- diff --git a/test/command/biblatex-spiegelberg.md b/test/command/biblatex-spiegelberg.md index 59d651958..59338ae88 100644 --- a/test/command/biblatex-spiegelberg.md +++ b/test/command/biblatex-spiegelberg.md @@ -63,8 +63,8 @@ references: issued: 1969 language: de-DE page: 189-216 - title: "Intention" und "Intentionalität" in der Scholastik, bei - Brentano und Husserl + title: "\"Intention\" und \"Intentionalität\" in der Scholastik, bei + Brentano und Husserl" title-short: Intention und Intentionalität type: article-journal volume: 29 diff --git a/test/command/biblatex-test-case-conversion.md b/test/command/biblatex-test-case-conversion.md index aa30767ec..e69cb9336 100644 --- a/test/command/biblatex-test-case-conversion.md +++ b/test/command/biblatex-test-case-conversion.md @@ -63,9 +63,9 @@ references: id: item1 issued: 2013 language: en-US - title: "A title, in English, with a Proper Name and an ACRONYM and a + title: A title, in English, with a Proper Name and an ACRONYM and a [camelCase]{.nocase} word and some units, 400 [nm]{.nocase}, - 3 [cm]{.nocase}, and a quote, *Alea [iacta est]{.nocase}*" + 3 [cm]{.nocase}, and a quote, *Alea [iacta est]{.nocase}* type: article-journal --- diff --git a/test/command/biblatex-textnormal.md b/test/command/biblatex-textnormal.md index 0aa5550c3..a4e6107ec 100644 --- a/test/command/biblatex-textnormal.md +++ b/test/command/biblatex-textnormal.md @@ -9,7 +9,7 @@ nocite: "[@*]" references: - id: item1 - title: "The title [of this book]{.nodecor}" + title: The title [of this book]{.nodecor} type: book --- -- cgit v1.2.3 From e9c0f9f97ba6459530c7bb2ffb55d432a1ba7884 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 25 Apr 2021 19:36:06 +0200 Subject: Markdown writer: Cleaner (code)blocks with single class (#7242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a block only has a single class and no other attributes, it is not necessary to wrap the class attribute in curly braces – the class name can be placed after the opening mark as is. This will result in bit cleaner output when pandoc is used as a markdown pretty-printer. --- src/Text/Pandoc/Writers/Markdown.hs | 10 ++++++++-- test/command/5304.md | 2 +- test/command/5519.md | 2 +- test/command/6925.md | 4 ++-- test/command/toc.md | 4 ++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 3295d9e6c..2ad9eabd9 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -278,6 +278,12 @@ attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] escAttrChar '\\' = literal "\\\\" escAttrChar c = literal $ T.singleton c +-- | (Code) blocks with a single class can just use it standalone, +-- no need to bother with curly braces. +classOrAttrsToMarkdown :: Attr -> Doc Text +classOrAttrsToMarkdown ("",[cls],_) = literal cls +classOrAttrsToMarkdown attrs = attrsToMarkdown attrs + linkAttributes :: WriterOptions -> Attr -> Doc Text linkAttributes opts attr = if isEnabled Ext_link_attributes opts && attr /= nullAttr @@ -343,7 +349,7 @@ blockToMarkdown' opts (Div attrs ils) = do case () of _ | isEnabled Ext_fenced_divs opts && attrs /= nullAttr -> - nowrap (literal ":::" <+> attrsToMarkdown attrs) $$ + nowrap (literal ":::" <+> classOrAttrsToMarkdown attrs) $$ chomp contents $$ literal ":::" <> blankline | isEnabled Ext_native_divs opts || @@ -512,7 +518,7 @@ blockToMarkdown' opts (CodeBlock attribs str) = do backticks = endline '`' tildes = endline '~' attrs = if isEnabled Ext_fenced_code_attributes opts - then nowrap $ " " <> attrsToMarkdown attribs + then nowrap $ " " <> classOrAttrsToMarkdown attribs else case attribs of (_,cls:_,_) -> " " <> literal cls _ -> empty diff --git a/test/command/5304.md b/test/command/5304.md index 62b2b9ddd..70f32a96a 100644 --- a/test/command/5304.md +++ b/test/command/5304.md @@ -11,7 +11,7 @@ ... ``` ^D -``` {.markdown} +``` markdown `«sträng»` `` «sträng» `` diff --git a/test/command/5519.md b/test/command/5519.md index a175ce9f9..ecde184f1 100644 --- a/test/command/5519.md +++ b/test/command/5519.md @@ -6,7 +6,7 @@ ``` `````` ^D -```` {.attr} +```` attr ``` code ``` diff --git a/test/command/6925.md b/test/command/6925.md index 458a0b91d..e0d8e6870 100644 --- a/test/command/6925.md +++ b/test/command/6925.md @@ -20,13 +20,13 @@ a \end{thm2} \end{document} ^D -::: {.thm} +::: thm **Theorem 1**. *a* ![image](1.png) ::: -::: {.thm2} +::: thm2 **Theorem 1**. a ![image](1.png) diff --git a/test/command/toc.md b/test/command/toc.md index 794af7690..543f97ba9 100644 --- a/test/command/toc.md +++ b/test/command/toc.md @@ -31,7 +31,7 @@ ## b -::: {.interior} +::: interior # C ## cc @@ -39,7 +39,7 @@ # D ::: -::: {.blue} +::: blue # E ## e -- cgit v1.2.3 From c56d080a253c1364ca55ae97b8d43ed638acf723 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 25 Apr 2021 21:19:07 +0200 Subject: Writers: Recognize custom syntax definitions (#7241) Languages defined using `--syntax-definition` were not recognized by `languagesByExtension`. This patch corrects that, allowing the writers to see all custom definitions. The LaTeX still uses the default syntax map, but that's okay in that context, since `--syntax-definition` won't create new listings styles. --- src/Text/Pandoc/Highlighting.hs | 10 +++++----- src/Text/Pandoc/Readers/LaTeX.hs | 3 ++- src/Text/Pandoc/Writers/Docbook.hs | 7 ++++--- src/Text/Pandoc/Writers/JATS.hs | 24 +++++++++++++----------- src/Text/Pandoc/Writers/TEI.hs | 7 ++++--- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 0bb6ed319..62a261e50 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -52,12 +52,12 @@ highlightingStyles = ("breezedark", breezeDark), ("haddock", haddock)] -languages :: [T.Text] -languages = [T.toLower (sName s) | s <- M.elems defaultSyntaxMap] +languages :: SyntaxMap -> [T.Text] +languages syntaxmap = [T.toLower (sName s) | s <- M.elems syntaxmap] -languagesByExtension :: T.Text -> [T.Text] -languagesByExtension ext = - [T.toLower (sName s) | s <- syntaxesByExtension defaultSyntaxMap (T.unpack ext)] +languagesByExtension :: SyntaxMap -> T.Text -> [T.Text] +languagesByExtension syntaxmap ext = + [T.toLower (sName s) | s <- syntaxesByExtension syntaxmap (T.unpack ext)] highlight :: SyntaxMap -> (FormatOptions -> [SourceLine] -> a) -- ^ Formatter diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 203dab83c..19c257a48 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -32,6 +32,7 @@ import Data.Maybe (fromMaybe, maybeToList) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T +import Skylighting (defaultSyntaxMap) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Collate.Lang (renderLang) import Text.Pandoc.Builder as B @@ -1170,7 +1171,7 @@ inputListing = do let (ident,classes,kvs) = parseListingsOptions options let classes' = (case listingsLanguage options of - Nothing -> (take 1 (languagesByExtension (T.pack $ takeExtension $ T.unpack f)) <>) + Nothing -> (take 1 (languagesByExtension defaultSyntaxMap (T.pack $ takeExtension $ T.unpack f)) <>) Just _ -> id) classes let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead let lastline = fromMaybe (length codeLines) $ diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 02b141250..25bd308bf 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -253,17 +253,18 @@ blockToDocbook opts (LineBlock lns) = blockToDocbook opts $ linesToPara lns blockToDocbook opts (BlockQuote blocks) = inTagsIndented "blockquote" <$> blocksToDocbook opts blocks -blockToDocbook _ (CodeBlock (_,classes,_) str) = return $ +blockToDocbook opts (CodeBlock (_,classes,_) str) = return $ literal (" lang <> ">") <> cr <> flush (literal (escapeStringForXML str) <> cr <> literal "") where lang = if null langs then "" else " language=\"" <> escapeStringForXML (head langs) <> "\"" - isLang l = T.toLower l `elem` map T.toLower languages + syntaxMap = writerSyntaxMap opts + isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] - else languagesByExtension . T.toLower $ s + else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes blockToDocbook opts (BulletList lst) = do let attribs = [("spacing", "compact") | isTightList lst] diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index b58ff8aef..0bcfa0df4 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -224,19 +224,21 @@ imageMimeType src kvs = (T.drop 1 . T.dropWhile (/='/') <$> mbMT) in (maintype, subtype) -languageFor :: [Text] -> Text -languageFor classes = +languageFor :: WriterOptions -> [Text] -> Text +languageFor opts classes = case langs of (l:_) -> escapeStringForXML l [] -> "" - where isLang l = T.toLower l `elem` map T.toLower languages + where + syntaxMap = writerSyntaxMap opts + isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] - else languagesByExtension . T.toLower $ s + else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes -codeAttr :: Attr -> (Text, [(Text, Text)]) -codeAttr (ident,classes,kvs) = (lang, attr) +codeAttr :: WriterOptions -> Attr -> (Text, [(Text, Text)]) +codeAttr opts (ident,classes,kvs) = (lang, attr) where attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("language",lang) | not (T.null lang)] ++ @@ -244,7 +246,7 @@ codeAttr (ident,classes,kvs) = (lang, attr) "code-version", "executable", "language-version", "orientation", "platforms", "position", "specific-use"]] - lang = languageFor classes + lang = languageFor opts classes -- | Convert a Pandoc block element to JATS. blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text) @@ -330,9 +332,9 @@ blockToJATS opts (BlockQuote blocks) = do HorizontalRule -> True _ -> False inTagsIndented "disp-quote" <$> wrappedBlocksToJATS needsWrap opts blocks -blockToJATS _ (CodeBlock a str) = return $ +blockToJATS opts (CodeBlock a str) = return $ inTags False tag attr (flush (text (T.unpack $ escapeStringForXML str))) - where (lang, attr) = codeAttr a + where (lang, attr) = codeAttr opts a tag = if T.null lang then "preformat" else "code" blockToJATS _ (BulletList []) = return empty blockToJATS opts (BulletList lst) = @@ -412,9 +414,9 @@ inlineToJATS opts (Quoted SingleQuote lst) = do inlineToJATS opts (Quoted DoubleQuote lst) = do contents <- inlinesToJATS opts lst return $ char '“' <> contents <> char '”' -inlineToJATS _ (Code a str) = +inlineToJATS opts (Code a str) = return $ inTags False tag attr $ literal (escapeStringForXML str) - where (lang, attr) = codeAttr a + where (lang, attr) = codeAttr opts a tag = if T.null lang then "monospace" else "code" inlineToJATS _ il@(RawInline f x) | f == "jats" = return $ literal x diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index b926c48a1..18015259d 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -146,16 +146,17 @@ blockToTEI opts (LineBlock lns) = blockToTEI opts $ linesToPara lns blockToTEI opts (BlockQuote blocks) = inTagsIndented "quote" <$> blocksToTEI opts blocks -blockToTEI _ (CodeBlock (_,classes,_) str) = +blockToTEI opts (CodeBlock (_,classes,_) str) = return $ literal ("") <> cr <> flush (literal (escapeStringForXML str) <> cr <> text "") where lang = if null langs then "" else escapeStringForXML (head langs) - isLang l = T.toLower l `elem` map T.toLower languages + syntaxMap = writerSyntaxMap opts + isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] - else languagesByExtension . T.toLower $ s + else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes blockToTEI opts (BulletList lst) = do let attribs = [("type", "unordered")] -- cgit v1.2.3 From 3a98f7a0c767c3b13c7b358ca75d2bddab7cd4d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 12:21:00 -0700 Subject: Minor code reformatting. Also taking this opportunity to note, for the record, that the commit for #7241 should be marked [API change]. It changes the type of `languagesByExtension` in Highlighting, adding a parameter for a `SyntaxMap`. --- src/Text/Pandoc/Readers/LaTeX.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 19c257a48..9ad168293 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1171,7 +1171,8 @@ inputListing = do let (ident,classes,kvs) = parseListingsOptions options let classes' = (case listingsLanguage options of - Nothing -> (take 1 (languagesByExtension defaultSyntaxMap (T.pack $ takeExtension $ T.unpack f)) <>) + Nothing -> (take 1 (languagesByExtension defaultSyntaxMap + (T.pack $ takeExtension $ T.unpack f)) <>) Just _ -> id) classes let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead let lastline = fromMaybe (length codeLines) $ -- cgit v1.2.3 From 16b64bda3821a47fc6b3a215f5a74db00a892b4e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 21:56:54 -0700 Subject: Use dev texmath, citeproc. --- cabal.project | 6 +++++- pandoc.cabal | 2 +- stack.yaml | 8 +++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cabal.project b/cabal.project index 2b82eb70c..fa4d9353a 100644 --- a/cabal.project +++ b/cabal.project @@ -5,5 +5,9 @@ flags: +embed_data_files source-repository-package type: git location: https://github.com/jgm/citeproc - tag: 46066cb8939e907e7e3a7126304919c8ed32aed0 + tag: f33b370bd1ab5ad5140720369c0586041e433a47 +source-repository-package +- type: git + location: https://github.com/jm/texmath + tag: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d diff --git a/pandoc.cabal b/pandoc.cabal index 14d2da0ae..29a5bfc7a 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -445,7 +445,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.3.0.9 && < 0.3.1, + citeproc >= 0.4 && < 0.4.1, commonmark >= 0.1.1.3 && < 0.2, commonmark-extensions >= 0.2.0.4 && < 0.3, commonmark-pandoc >= 0.2 && < 0.3, diff --git a/stack.yaml b/stack.yaml index b3e29ba93..32369895f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,13 +13,15 @@ extra-deps: - skylighting-core-0.10.5.1 - skylighting-0.10.5.1 - doclayout-0.3.0.2 -- texmath-0.12.2 - random-1.2.0 - xml-conduit-1.9.1.1 -- unicode-collation-0.1.2 +- unicode-collation-0.1.3 +# - texmath-0.12.2 +- git: https://github.com/jgm/texmath + commit: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc - commit: 46066cb8939e907e7e3a7126304919c8ed32aed0 + commit: f33b370bd1ab5ad5140720369c0586041e433a47 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From 1f72a506270c44207878ede2021ec512dcd70d6a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 22:52:07 -0700 Subject: Fix typo in cabal.project. --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index fa4d9353a..5603fc2d1 100644 --- a/cabal.project +++ b/cabal.project @@ -8,6 +8,6 @@ source-repository-package tag: f33b370bd1ab5ad5140720369c0586041e433a47 source-repository-package -- type: git + type: git location: https://github.com/jm/texmath tag: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d -- cgit v1.2.3 From 853f24f5cacdf50c6ed4c60ac9a09f100a25e3b4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Apr 2021 23:11:53 -0700 Subject: Fixed another typo in cabal.project. --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index 5603fc2d1..e06c375b9 100644 --- a/cabal.project +++ b/cabal.project @@ -9,5 +9,5 @@ source-repository-package source-repository-package type: git - location: https://github.com/jm/texmath + location: https://github.com/jgm/texmath tag: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d -- cgit v1.2.3 From b159daa8e9dc467acfe480afedfb0cb63ff60c74 Mon Sep 17 00:00:00 2001 From: Charanjit Singh Date: Mon, 26 Apr 2021 15:24:25 +0530 Subject: docs/org.md: Add note about default export behavior of org-mode in (#7238) Since it seem to be a common source of confusion, this commit adds a note in org-mode documentation referring users to consult org-mode's export documentation when they find themselves in a pinch. --- doc/org.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/org.md b/doc/org.md index 9ed20eafe..edaaa8c50 100644 --- a/doc/org.md +++ b/doc/org.md @@ -36,6 +36,21 @@ The following export keywords are supported: defaults to stdout unless a target has to be given as a command line option. +::: {.alert .alert-info} +Pandoc tries to be compatible with org-mode when exporting an org document. If +you find some behavior confusing, please do refer to org-mode +[Export-Settings](https://orgmode.org/manual/Export-Settings.html) +documentation. For example, a common confusion +([#3214](https://github.com/jgm/pandoc/issues/3214 "Problem with headers lower +then 3 in org-mode reader"), [#5169](https://github.com/jgm/pandoc/issues/5169 +"org mode headings past level three converted to numbered outline list"), +[#6145](https://github.com/jgm/pandoc/issues/6145 "Headers 4 levels deep render +differently"), [#7236](https://github.com/jgm/pandoc/issues/7236 "In Org mode, +Header with level > 3 are not recognized as headers")) is treatment of headers +with level > 3 differently because org-mode sets `org-export-headline-levels` +(configurable with `#+OPTIONS: H:3`) to 3 by default. +::: + [BCP47 language tag]: https://tools.ietf.org/html/bcp47 Format-specific options -- cgit v1.2.3 From d0d8bb981861a5c076d1c525240791f631140f80 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 26 Apr 2021 09:29:47 -0700 Subject: Add unicode-collation to list of libraries developed to support pandoc. --- doc/libraries.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/libraries.md b/doc/libraries.md index 828d453a8..397dff83d 100644 --- a/doc/libraries.md +++ b/doc/libraries.md @@ -15,6 +15,9 @@ pandoc: [texmath] : Conversion of math between tex, Word equation, MathML, and GNU eqn. +[unicode-collation] +: Proper Unicode collation (sorting). + [doclayout] : Combinators for laying out a textual document, with support for line wrapping, tabular layout, and more. @@ -56,4 +59,5 @@ pandoc: [rfc5051]: https://hackage.haskell.org/package/rfc5051 [emojis]: https://hackage.haskell.org/package/emojis [jira-wiki-markup]: https://hackage.haskell.org/package/jira-wiki-markup +[unicode-collation]: https://hackage.haskell.org/package/unicode-collation -- cgit v1.2.3 From 0921b82d98b6ec7fa80ffd522c129b3828b9c00b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 26 Apr 2021 18:07:01 +0200 Subject: Docx writer: autoset table width if no column has an explicit width. --- src/Text/Pandoc/Writers/Docx/Table.hs | 18 +++++++++++------- test/docx/golden/table_one_row.docx | Bin 9903 -> 9904 bytes test/docx/golden/table_with_list_cell.docx | Bin 10212 -> 10212 bytes test/docx/golden/tables.docx | Bin 10238 -> 10239 bytes 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index a6b137fc4..349f3a4ce 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -1,7 +1,8 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | -Module : Text.Pandoc.Writers.Docx +Module : Text.Pandoc.Writers.Docx.Table Copyright : Copyright (C) 2012-2021 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane @@ -87,7 +88,10 @@ tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do mknode "w:tbl" [] ( mknode "w:tblPr" [] ( mknode "w:tblStyle" [("w:val","Table")] () : - mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () : + mknode "w:tblW" (if all (== 0) widths + then [("w:type", "auto"), ("w:w", "0")] + else [("w:type", "pct"), ("w:w", tshow rowwidth)]) + () : mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") ,("w:lastRow","0") ,("w:firstColumn","0") @@ -107,8 +111,8 @@ tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do )] alignmentToString :: Alignment -> Text -alignmentToString alignment = case alignment of - AlignLeft -> "left" - AlignRight -> "right" - AlignCenter -> "center" - AlignDefault -> "left" +alignmentToString = \case + AlignLeft -> "left" + AlignRight -> "right" + AlignCenter -> "center" + AlignDefault -> "left" diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index 6eaea2ac2..cab3fc31c 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 45a97ccaa..9238c7e20 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 115a16a48..6f0379def 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ -- cgit v1.2.3 From 85f379e474be72ae0f7a53ebc5efe2ad4f8165b4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 28 Apr 2021 12:46:52 +0200 Subject: JATS writer: use either styled-content or named-content for spans. If the element has a content-type attribute, or at least one class, then that value is used as `content-type` and the span is put inside a `` element. Otherwise a `` element is used instead. Closes: #7211 --- src/Text/Pandoc/Writers/JATS.hs | 36 ++++++++++++++++++++++++++---------- test/Tests/Writers/JATS.hs | 14 +++++++++----- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 0bcfa0df4..9db8723d1 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -21,12 +21,13 @@ module Text.Pandoc.Writers.JATS , writeJatsPublishing , writeJatsArticleAuthoring ) where +import Control.Applicative ((<|>)) import Control.Monad.Reader import Control.Monad.State import Data.Generics (everywhere, mkT) import Data.List (partition) import qualified Data.Map as M -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, listToMaybe) import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime) import qualified Data.Text as T import Data.Text (Text) @@ -449,18 +450,33 @@ inlineToJATS opts (Note contents) = do $ text (show notenum) inlineToJATS opts (Cite _ lst) = inlinesToJATS opts lst -inlineToJATS opts (Span (ident,_,kvs) ils) = do +inlineToJATS opts (Span (ident,classes,kvs) ils) = do contents <- inlinesToJATS opts ils - let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ - [("xml:lang",l) | ("lang",l) <- kvs] ++ - [(k,v) | (k,v) <- kvs - , k `elem` ["alt", "content-type", "rid", "specific-use", - "vocab", "vocab-identifier", "vocab-term", - "vocab-term-identifier"]] + let commonAttr = [("id", escapeNCName ident) | not (T.null ident)] ++ + [("xml:lang",l) | ("lang",l) <- kvs] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["alt", "specific-use"]] + -- A named-content element is a good fit for spans, but requires a + -- content-type attribute to be present. We use either the explicit + -- attribute or the first class as content type. If neither is + -- available, then we fall back to using a @styled-content@ element. + let (tag, specificAttr) = + case lookup "content-type" kvs <|> listToMaybe classes of + Just ct -> ( "named-content" + , ("content-type", ct) : + [(k, v) | (k, v) <- kvs + , k `elem` ["rid", "vocab", "vocab-identifier", + "vocab-term", "vocab-term-identifier"]]) + -- Fall back to styled-content + Nothing -> ("styled-content" + , [(k, v) | (k,v) <- kvs + , k `elem` ["style", "style-type", "style-detail", + "toggle"]]) + let attr = commonAttr ++ specificAttr + -- unwrap if wrapping element would have no attributes return $ if null attr - then contents -- unwrap if no relevant attributes are given - else inTags False "named-content" attr contents + then contents + else inTags False tag attr contents inlineToJATS _ (Math t str) = do let addPref (Xml.Attr q v) | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v diff --git a/test/Tests/Writers/JATS.hs b/test/Tests/Writers/JATS.hs index e90438176..5b96ed2ed 100644 --- a/test/Tests/Writers/JATS.hs +++ b/test/Tests/Writers/JATS.hs @@ -148,13 +148,17 @@ tests = spanWith nullAttr "text in span" =?> "

text in span

" - , "converted to named-content element" =: - spanWith ("a", ["ignored"], [("alt", "aa")]) "text" =?> - "

text

" + , "converted to named-content element if class given" =: + spanWith ("a", ["genus-species"], [("alt", "aa")]) "C. elegans" =?> + ("

" + <> "C. elegans

") - , "unwrapped if named-content element would have no attributes" =: - spanWith ("", ["ignored"], [("hidden", "true")]) "text in span" =?> + , "unwrapped if styled-content element would have no attributes" =: + spanWith ("", [], [("hidden", "true")]) "text in span" =?> "

text in span

" + , "use content-type attribute if present" =: + spanWith ("", [], [("content-type", "species")]) "E. coli" =?> + "

E. coli

" ] ] -- cgit v1.2.3 From 8fe7e8dd5c2667f0cac5af67ff1a02872eeb9933 Mon Sep 17 00:00:00 2001 From: Julien Dutant <34026710+jdutant@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:06:13 +0100 Subject: list of figures before list of tables in LaTeX and ConTeXt templates (#7235) --- data/templates/default.context | 6 +++--- data/templates/default.latex | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/templates/default.context b/data/templates/default.context index 1588406fa..64de448ca 100644 --- a/data/templates/default.context +++ b/data/templates/default.context @@ -160,12 +160,12 @@ $endfor$ $if(toc)$ \completecontent $endif$ -$if(lot)$ -\completelistoftables -$endif$ $if(lof)$ \completelistoffigures $endif$ +$if(lot)$ +\completelistoftables +$endif$ $body$ diff --git a/data/templates/default.latex b/data/templates/default.latex index 830bf8012..a7f46b950 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -472,12 +472,12 @@ $endif$ } $endif$ $endif$ -$if(lot)$ -\listoftables -$endif$ $if(lof)$ \listoffigures $endif$ +$if(lot)$ +\listoftables +$endif$ $if(linestretch)$ \setstretch{$linestretch$} $endif$ -- cgit v1.2.3 From 80e2e88287f43d88ea92a77779b25e161c81f67b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 28 Apr 2021 23:30:16 -0700 Subject: Smarter smart quotes. Treat a leading " with no closing " as a left curly quote. This supports the practice, in fiction, of continuing paragraphs quoting the same speaker without an end quote. It also helps with quotes that break over lines in line blocks. Closes #7216. --- src/Text/Pandoc/Parsing.hs | 45 +++++++++++++++++++++++------------- src/Text/Pandoc/Readers/Markdown.hs | 18 ++++++++------- src/Text/Pandoc/Readers/RST.hs | 19 +-------------- src/Text/Pandoc/Readers/TWiki.hs | 22 +----------------- test/Tests/Readers/Markdown.hs | 2 +- test/command/7216.md | 19 +++++++++++++++ test/command/pandoc-citeproc-320a.md | 12 +++++----- 7 files changed, 67 insertions(+), 70 deletions(-) create mode 100644 test/command/7216.md diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 847fd2e05..2f6189104 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -105,8 +105,9 @@ module Text.Pandoc.Parsing ( take1WhileP, singleQuoteEnd, doubleQuoteStart, doubleQuoteEnd, - ellipses, apostrophe, + doubleCloseQuote, + ellipses, dash, nested, citeKey, @@ -1398,10 +1399,7 @@ smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext -> ParserT s st m Inlines smartPunctuation inlineParser = do guardEnabled Ext_smart - choice [ quoted inlineParser, apostrophe, dash, ellipses ] - -apostrophe :: Stream s m Char => ParserT s st m Inlines -apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\x2019") + choice [ quoted inlineParser, apostrophe, doubleCloseQuote, dash, ellipses ] quoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) => ParserT s st m Inlines @@ -1411,16 +1409,22 @@ quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) => ParserT s st m Inlines -> ParserT s st m Inlines -singleQuoted inlineParser = try $ B.singleQuoted . mconcat - <$ singleQuoteStart - <*> withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd) - -doubleQuoted :: (HasQuoteContext st m, Stream s m Char) +singleQuoted inlineParser = do + singleQuoteStart + (B.singleQuoted . mconcat <$> + try + (withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd))) + <|> pure "\8217" + +doubleQuoted :: (HasQuoteContext st m, HasLastStrPosition st, Stream s m Char) => ParserT s st m Inlines -> ParserT s st m Inlines -doubleQuoted inlineParser = try $ B.doubleQuoted . mconcat - <$ doubleQuoteStart - <*> withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd) +doubleQuoted inlineParser = do + doubleQuoteStart + (B.doubleQuoted . mconcat <$> + try + (withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd))) + <|> pure (B.str "\8220") failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t) => QuoteContext @@ -1443,7 +1447,7 @@ singleQuoteStart = do guard =<< notAfterString try $ do charOrRef "'\8216\145" - notFollowedBy (oneOf [' ', '\t', '\n']) + notFollowedBy (satisfy isSpaceChar) singleQuoteEnd :: Stream s m Char => ParserT s st m () @@ -1451,17 +1455,26 @@ singleQuoteEnd = try $ do charOrRef "'\8217\146" notFollowedBy alphaNum -doubleQuoteStart :: (HasQuoteContext st m, Stream s m Char) +doubleQuoteStart :: (HasLastStrPosition st, + HasQuoteContext st m, + Stream s m Char) => ParserT s st m () doubleQuoteStart = do failIfInQuoteContext InDoubleQuote + guard =<< notAfterString try $ do charOrRef "\"\8220\147" - notFollowedBy (oneOf [' ', '\t', '\n']) + notFollowedBy (satisfy isSpaceChar) doubleQuoteEnd :: Stream s m Char => ParserT s st m () doubleQuoteEnd = void (charOrRef "\"\8221\148") +apostrophe :: Stream s m Char => ParserT s st m Inlines +apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\8217") + +doubleCloseQuote :: Stream s m Char => ParserT s st m Inlines +doubleCloseQuote = B.str "\8221" <$ char '"' + ellipses :: Stream s m Char => ParserT s st m Inlines ellipses = try (string "..." >> return (B.str "\8230")) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 4b20e3a8b..ba8ed147e 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2199,25 +2199,27 @@ citation = try $ do smart :: PandocMonad m => MarkdownParser m (F Inlines) smart = do guardEnabled Ext_smart - doubleQuoted <|> singleQuoted <|> - choice (map (return <$>) [apostrophe, dash, ellipses]) + doubleQuoted <|> singleQuoted <|> (return <$> doubleCloseQuote) <|> + (return <$> apostrophe) <|> (return <$> dash) <|> (return <$> ellipses) singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) -singleQuoted = try $ do +singleQuoted = do singleQuoteStart - withQuoteContext InSingleQuote $ + (try (withQuoteContext InSingleQuote $ fmap B.singleQuoted . trimInlinesF . mconcat <$> - many1Till inline singleQuoteEnd + many1Till inline singleQuoteEnd)) + <|> (return (return (B.str "\8217"))) -- doubleQuoted will handle regular double-quoted sections, as well -- as dialogues with an open double-quote without a close double-quote -- in the same paragraph. doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) -doubleQuoted = try $ do +doubleQuoted = do doubleQuoteStart - withQuoteContext InDoubleQuote $ + (try (withQuoteContext InDoubleQuote $ fmap B.doubleQuoted . trimInlinesF . mconcat <$> - many1Till inline doubleQuoteEnd + many1Till inline doubleQuoteEnd)) + <|> (return (return (B.str "\8220"))) toRow :: [Blocks] -> Row toRow = Row nullAttr . map B.simpleCell diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 514e3b88d..ac4c0b6cb 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1658,21 +1658,4 @@ note = try $ do return $ B.note contents smart :: PandocMonad m => RSTParser m Inlines -smart = do - guardEnabled Ext_smart - doubleQuoted <|> singleQuoted <|> - choice [apostrophe, dash, ellipses] - -singleQuoted :: PandocMonad m => RSTParser m Inlines -singleQuoted = try $ do - singleQuoteStart - withQuoteContext InSingleQuote $ - B.singleQuoted . trimInlines . mconcat <$> - many1Till inline singleQuoteEnd - -doubleQuoted :: PandocMonad m => RSTParser m Inlines -doubleQuoted = try $ do - doubleQuoteStart - withQuoteContext InDoubleQuote $ - B.doubleQuoted . trimInlines . mconcat <$> - many1Till inline doubleQuoteEnd +smart = smartPunctuation inline diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 484a6c923..c4d7bcc93 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -469,27 +469,7 @@ symbol :: PandocMonad m => TWParser m B.Inlines symbol = B.str <$> countChar 1 nonspaceChar smart :: PandocMonad m => TWParser m B.Inlines -smart = do - guardEnabled Ext_smart - doubleQuoted <|> singleQuoted <|> - choice [ apostrophe - , dash - , ellipses - ] - -singleQuoted :: PandocMonad m => TWParser m B.Inlines -singleQuoted = try $ do - singleQuoteStart - withQuoteContext InSingleQuote - (B.singleQuoted . B.trimInlines . mconcat <$> many1Till inline singleQuoteEnd) - -doubleQuoted :: PandocMonad m => TWParser m B.Inlines -doubleQuoted = try $ do - doubleQuoteStart - contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) - withQuoteContext InDoubleQuote (doubleQuoteEnd >> - return (B.doubleQuoted $ B.trimInlines contents)) - <|> return (B.str "\8220" B.<> contents) +smart = smartPunctuation inline link :: PandocMonad m => TWParser m B.Inlines link = try $ do diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index 0930deae6..6e38da21a 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -358,7 +358,7 @@ tests = [ testGroup "inline code" para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.") , test markdownSmart "unclosed double quote" ("**this should \"be bold**" - =?> para (strong "this should \"be bold")) + =?> para (strong "this should \8220be bold")) ] , testGroup "footnotes" [ "indent followed by newline and flush-left text" =: diff --git a/test/command/7216.md b/test/command/7216.md new file mode 100644 index 000000000..cab3b9689 --- /dev/null +++ b/test/command/7216.md @@ -0,0 +1,19 @@ +``` +pandoc -t latex +"This is some text in quotes. Another paragraph by the same speaker follows. The first paragraph should have no close quote. + +"The second paragraph should have open and close quotes." + +| "Open quote on this line, +| Close quote on the next line." +| "Quotes on the same line." +^D +``This is some text in quotes. Another paragraph by the same speaker +follows. The first paragraph should have no close quote. + +``The second paragraph should have open and close quotes.'' + +``Open quote on this line,\\ +Close quote on the next line.''\\ +``Quotes on the same line.'' +``` diff --git a/test/command/pandoc-citeproc-320a.md b/test/command/pandoc-citeproc-320a.md index 79dacfa10..e894a2250 100644 --- a/test/command/pandoc-citeproc-320a.md +++ b/test/command/pandoc-citeproc-320a.md @@ -49,17 +49,13 @@ references: Foo [@item1; @item2; @item3; @item4; @item5; @item6; @item7; @item8]. ^D Foo (al-ʾUdhrī, n.d.; al-ʿUdhrī, n.d.; al-\'Udhrī, n.d.; al-'Udhrī, -n.d.; al-'Udhrī, n.d.; Uch, n.d.; Uebel, n.d.; Zzz, n.d.). +n.d.a, n.d.b; Uch, n.d.; Uebel, n.d.; Zzz, n.d.). ::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item6 .csl-entry} Uch, Ann. n.d. ::: -::: {#ref-item4 .csl-entry} -'Udhrī, Jamīl al-. n.d. -::: - ::: {#ref-item1 .csl-entry} ʾUdhrī, Jamīl al-. n.d. ::: @@ -72,8 +68,12 @@ Uch, Ann. n.d. \'Udhrī, Jamīl al-. n.d. ::: +::: {#ref-item4 .csl-entry} +'Udhrī, Jamīl al-. n.d.a. +::: + ::: {#ref-item5 .csl-entry} -'Udhrī, Jamīl al-. n.d. +---------. n.d.b. ::: ::: {#ref-item7 .csl-entry} -- cgit v1.2.3 From d14c5f94df490996d12f8a56e29f927b9f9549e8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Apr 2021 08:48:49 -0700 Subject: Further improvements in smart quotes. Improves heuristic for detection of an "open double quote." Closes #2103. --- src/Text/Pandoc/Parsing.hs | 4 ++-- test/command/2103.md | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/command/2103.md diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 2f6189104..37ab0adaa 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1447,7 +1447,7 @@ singleQuoteStart = do guard =<< notAfterString try $ do charOrRef "'\8216\145" - notFollowedBy (satisfy isSpaceChar) + void $ lookAhead (satisfy (not . isSpaceChar)) singleQuoteEnd :: Stream s m Char => ParserT s st m () @@ -1463,7 +1463,7 @@ doubleQuoteStart = do failIfInQuoteContext InDoubleQuote guard =<< notAfterString try $ do charOrRef "\"\8220\147" - notFollowedBy (satisfy isSpaceChar) + void $ lookAhead (satisfy (not . isSpaceChar)) doubleQuoteEnd :: Stream s m Char => ParserT s st m () diff --git a/test/command/2103.md b/test/command/2103.md new file mode 100644 index 000000000..14a522a5e --- /dev/null +++ b/test/command/2103.md @@ -0,0 +1,8 @@ +``` +pandoc -t latex +| A happy pandoc user said "fix this bug please +| or I'll go crazy!" +^D +A happy pandoc user said ``fix this bug please\\ +or I'll go crazy!'' +``` -- cgit v1.2.3 From b6a65445e10d74dfe384763c37438338bd395372 Mon Sep 17 00:00:00 2001 From: mbrackeantidot <65160241+mbrackeantidot@users.noreply.github.com> Date: Thu, 29 Apr 2021 18:11:44 +0200 Subject: Docx reader: add handling of vml image objects (jgm#4735) (#7257) They represent images, the same way as other images in vml format. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 11 +++++++++-- test/Tests/Readers/Docx.hs | 4 ++++ test/docx/image_vml_as_object.docx | Bin 0 -> 74199 bytes test/docx/image_vml_as_object.native | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/docx/image_vml_as_object.docx create mode 100644 test/docx/image_vml_as_object.native diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index f8ed248d7..7325ff300 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -709,7 +709,8 @@ elemToParPart ns element case drawing of Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem) Nothing -> throwError WrongElem --- The below is an attempt to deal with images in deprecated vml format. +-- The two cases below are an attempt to deal with images in deprecated vml format. +-- Todo: check out title and attr for deprecated format. elemToParPart ns element | isElem ns "w" "r" element , Just _ <- findChildByName ns "w" "pict" element = @@ -717,9 +718,15 @@ elemToParPart ns element >>= findAttrByName ns "r" "id" in case drawing of - -- Todo: check out title and attr for deprecated format. Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp "" "" bs Nothing) Nothing -> throwError WrongElem +elemToParPart ns element + | isElem ns "w" "r" element + , Just objectElem <- findChildByName ns "w" "object" element + , Just shapeElem <- findChildByName ns "v" "shape" objectElem + , Just imagedataElem <- findChildByName ns "v" "imagedata" shapeElem + , Just drawingId <- findAttrByName ns "r" "id" imagedataElem + = expandDrawingId drawingId >>= (\(fp, bs) -> return $ Drawing fp "" "" bs Nothing) -- Chart elemToParPart ns element | isElem ns "w" "r" element diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 263e04173..2cce70cc5 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -155,6 +155,10 @@ tests = [ testGroup "document" "VML image" "docx/image_vml.docx" "docx/image_vml.native" + , testCompare + "VML image as object" + "docx/image_vml_as_object.docx" + "docx/image_vml_as_object.native" , testCompare "inline image in links" "docx/inline_images.docx" diff --git a/test/docx/image_vml_as_object.docx b/test/docx/image_vml_as_object.docx new file mode 100644 index 000000000..7e1e4d2ca Binary files /dev/null and b/test/docx/image_vml_as_object.docx differ diff --git a/test/docx/image_vml_as_object.native b/test/docx/image_vml_as_object.native new file mode 100644 index 000000000..6e689486a --- /dev/null +++ b/test/docx/image_vml_as_object.native @@ -0,0 +1,2 @@ +[Para [Str "Test",Space,Str "with",Space,Str "object",Space,Str "as",Space,Str "image:"] +,Para [Image ("",[],[]) [] ("media/image1.emf","")]] -- cgit v1.2.3 From b9b22e3ce4282499c2710cb974190d775898405f Mon Sep 17 00:00:00 2001 From: badumont <52835146+badumont@users.noreply.github.com> Date: Fri, 30 Apr 2021 03:47:23 +0200 Subject: Update default.latex (#7234) Fix bad vertical spacing after the bibliography. --- data/templates/default.latex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index a7f46b950..81b2d91fe 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -384,7 +384,10 @@ $if(csl-refs)$ {% don't indent paragraphs \setlength{\parindent}{0pt} % turn on hanging indent if param 1 is 1 - \ifodd #1 \everypar{\setlength{\hangindent}{\cslhangindent}}\ignorespaces\fi + \ifodd #1 + \let\oldpar\par + \def\par{\hangindent=\cslhangindent\oldpar} + \fi % set entry spacing \ifnum #2 > 0 \setlength{\parskip}{#2\baselineskip} -- cgit v1.2.3 From 96d384ac9e4458d6067a85a9347bf9d79d131f2d Mon Sep 17 00:00:00 2001 From: Florian Kohrt Date: Fri, 30 Apr 2021 03:49:17 +0200 Subject: Support toc-title in revealjs (#7171) * Support toc-title in revealjs * Add semantic HTML "nav" tag Closes #7170. As with default.html5 --- data/templates/default.revealjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs index e6d6939cd..e90b32899 100644 --- a/data/templates/default.revealjs +++ b/data/templates/default.revealjs @@ -59,7 +59,12 @@ $endif$ $endif$ $if(toc)$
+
$endif$ -- cgit v1.2.3 From dc0e482da7db598037893684904e49300207631b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 30 Apr 2021 03:54:36 +0200 Subject: INSTALL: add note that parallel installations should be avoided. (#7147) Closes: #6865 --- INSTALL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index d23c349c1..f226f4aed 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -34,6 +34,10 @@ without SVG support), [Python] (to use Pandoc filters), and choco install rsvg-convert python miktex +Using multiple installation methods can result in two separate +installations of pandoc; it is recommended to properly uninstall +pandoc before switching to an alternative installation method. + By default, Pandoc creates PDFs using LaTeX. We recommend installing it via [MiKTeX]. -- cgit v1.2.3 From 6b16f3bb0d4c3a0ddc051ff9948c67d5cac72bcb Mon Sep 17 00:00:00 2001 From: tecosaur Date: Fri, 30 Apr 2021 16:23:28 +0800 Subject: Org writer: inline latex envs need newlines (#7259) Closes #7252 As specified in https://orgmode.org/manual/LaTeX-fragments.html, an inline \begin{}...\end{} LaTeX block must start on a new line. --- src/Text/Pandoc/Writers/Org.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index d0c9813da..d404f1c8d 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -407,6 +407,8 @@ inlineToOrg (Math t str) = do then "\\(" <> literal str <> "\\)" else "\\[" <> literal str <> "\\]" inlineToOrg il@(RawInline f str) + | elem f ["tex", "latex"] && T.isPrefixOf "\\begin" str = + return $ cr <> literal str <> cr | isRawFormat f = return $ literal str | otherwise = do report $ InlineNotRendered il -- cgit v1.2.3 From 56a0d874c76fccfd1cbd1ede2c464446e889d302 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 30 Apr 2021 08:35:52 -0700 Subject: Add instructions for installing pandoc-types before compiling filter. --- doc/filters.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/filters.md b/doc/filters.md index 46db09f78..004a83b7d 100644 --- a/doc/filters.md +++ b/doc/filters.md @@ -394,6 +394,8 @@ automatically assign it `Just` the target format or `Nothing`. We compile our script: + # first, make sure pandoc-types is installed: + cabal install --lib pandoc-types --package-env . ghc --make handleRuby Then run it: -- cgit v1.2.3 From 3da919e35d02ec1a7e3719e2fdfd699a69d74921 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 1 May 2021 18:52:24 +0200 Subject: Add new internal module Text.Pandoc.Writers.GridTable --- pandoc.cabal | 2 + src/Text/Pandoc/Writers/GridTable.hs | 157 +++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/Text/Pandoc/Writers/GridTable.hs diff --git a/pandoc.cabal b/pandoc.cabal index 29a5bfc7a..8ea3aa681 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -438,6 +438,7 @@ library SHA >= 1.6 && < 1.7, aeson >= 0.7 && < 1.6, aeson-pretty >= 0.8.5 && < 0.9, + array >= 0.5 && < 0.6, attoparsec >= 0.12 && < 0.15, base64-bytestring >= 0.1 && < 1.3, binary >= 0.7 && < 0.11, @@ -659,6 +660,7 @@ library Text.Pandoc.Writers.Docx.StyleMap, Text.Pandoc.Writers.Docx.Table, Text.Pandoc.Writers.Docx.Types, + Text.Pandoc.Writers.GridTable Text.Pandoc.Writers.JATS.References, Text.Pandoc.Writers.JATS.Table, Text.Pandoc.Writers.JATS.Types, diff --git a/src/Text/Pandoc/Writers/GridTable.hs b/src/Text/Pandoc/Writers/GridTable.hs new file mode 100644 index 000000000..c6f4cf456 --- /dev/null +++ b/src/Text/Pandoc/Writers/GridTable.hs @@ -0,0 +1,157 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TupleSections #-} + +{- | +Module : Text.Pandoc.Writers.GridTable +Copyright : © 2020-2021 Albert Krewinkel +License : GNU GPL, version 2 or above + +Maintainer : Albert Krewinkel + +Grid representation of pandoc tables. +-} +module Text.Pandoc.Writers.GridTable + ( Table (..) + , GridCell (..) + , RowIndex (..) + , ColIndex (..) + , CellIndex + , Part (..) + , toTable + , rowArray + ) where + +import Control.Monad (forM_) +import Control.Monad.ST +import Data.Array +import Data.Array.MArray +import Data.Array.ST +import Data.Maybe (listToMaybe) +import Data.STRef +import Text.Pandoc.Definition hiding (Table) +import qualified Text.Pandoc.Builder as B + +-- | A grid cell contains either a real table cell, or is the +-- continuation of a column or row-spanning cell. In the latter case, +-- the index of the continued cell is provided. +data GridCell + = ContentCell Attr Alignment RowSpan ColSpan [Block] + | ContinuationCell CellIndex + deriving (Show) + +-- | Row index in a table part. +newtype RowIndex = RowIndex Int deriving (Enum, Eq, Ix, Ord, Show) +-- | Column index in a table part. +newtype ColIndex = ColIndex Int deriving (Enum, Eq, Ix, Ord, Show) + +-- | Index to a cell in a table part. +type CellIndex = (RowIndex, ColIndex) + +-- | Cells are placed on a grid. Row attributes are stored in a separate +-- array. +data Part = Part + { partAttr :: Attr + , partCellArray :: Array (RowIndex,ColIndex) GridCell + , partRowAttrs :: Array RowIndex Attr + } + +data Table = Table + { tableAttr :: Attr + , tableCaption :: Caption + , tableColSpecs :: Array ColIndex ColSpec + , tableRowHeads :: RowHeadColumns + , tableHead :: Part + , tableBodies :: [Part] + , tableFoot :: Part + } + +toTable + :: B.Attr + -> B.Caption + -> [B.ColSpec] + -> B.TableHead + -> [B.TableBody] + -> B.TableFoot + -> Table +toTable attr caption colSpecs thead tbodies tfoot = + Table attr caption colSpecs' rowHeads thGrid tbGrids tfGrid + where + colSpecs' = listArray (ColIndex 1, ColIndex $ length colSpecs) colSpecs + rowHeads = case listToMaybe tbodies of + Nothing -> RowHeadColumns 0 + Just (TableBody _attr rowHeadCols _headerRows _rows) -> rowHeadCols + thGrid = let (TableHead headAttr rows) = thead + in rowsToPart headAttr rows + tbGrids = map bodyToGrid tbodies + tfGrid = let (TableFoot footAttr rows) = tfoot + in rowsToPart footAttr rows + bodyToGrid (TableBody bodyAttr _rowHeadCols _headRows rows) = + rowsToPart bodyAttr rows + +data BuilderCell + = FilledCell GridCell + | FreeCell + +fromBuilderCell :: BuilderCell -> GridCell +fromBuilderCell = \case + FilledCell c -> c + FreeCell -> error "Found an unassigned cell." + +rowsToPart :: Attr -> [B.Row] -> Part +rowsToPart attr = \case + [] -> Part + attr + (listArray ((RowIndex 1, ColIndex 1), (RowIndex 0, ColIndex 0)) []) + (listArray (RowIndex 1, RowIndex 0) []) + rows@(Row _attr firstRow:_) -> + let nrows = length rows + ncols = sum $ map (\(Cell _ _ _ (ColSpan cs) _) -> cs) firstRow + gbounds = ((RowIndex 1, ColIndex 1), (RowIndex nrows, ColIndex ncols)) + mutableGrid :: ST s (STArray s CellIndex GridCell) + mutableGrid = do + grid <- newArray gbounds FreeCell + ridx <- newSTRef (RowIndex 1) + forM_ rows $ \(Row _attr cells) -> do + cidx <- newSTRef (ColIndex 1) + forM_ cells $ \(Cell cellAttr align rs cs blks) -> do + ridx' <- readSTRef ridx + let nextFreeInRow colindex@(ColIndex c) = do + readArray grid (ridx', colindex) >>= \case + FreeCell -> pure colindex + _ -> nextFreeInRow $ ColIndex (c + 1) + cidx' <- readSTRef cidx >>= nextFreeInRow + writeArray grid (ridx', cidx') . FilledCell $ + ContentCell cellAttr align rs cs blks + forM_ (continuationIndices ridx' cidx' rs cs) $ \idx -> do + writeArray grid idx . FilledCell $ + ContinuationCell (ridx', cidx') + -- go to new column + writeSTRef cidx cidx' + -- go to next row + modifySTRef ridx (incrRowIndex 1) + -- Swap BuilderCells with normal GridCells. + mapArray fromBuilderCell grid + in Part + { partCellArray = runSTArray mutableGrid + , partRowAttrs = listArray (RowIndex 1, RowIndex nrows) $ + map (\(Row rowAttr _) -> rowAttr) rows + , partAttr = attr + } + +continuationIndices :: RowIndex -> ColIndex -> RowSpan -> ColSpan -> [CellIndex] +continuationIndices (RowIndex ridx) (ColIndex cidx) rowspan colspan = + let (RowSpan rs) = rowspan + (ColSpan cs) = colspan + in [ (RowIndex r, ColIndex c) | r <- [ridx..(ridx + rs - 1)] + , c <- [cidx..(cidx + cs - 1)] + , (r, c) /= (ridx, cidx)] + +rowArray :: RowIndex -> Array CellIndex GridCell -> Array ColIndex GridCell +rowArray ridx grid = + let ((_minRidx, minCidx), (_maxRidx, maxCidx)) = bounds grid + in ixmap (minCidx, maxCidx) (ridx,) grid + +incrRowIndex :: RowSpan -> RowIndex -> RowIndex +incrRowIndex (RowSpan n) (RowIndex r) = RowIndex $ r + n -- cgit v1.2.3 From ddbf83f62c8bb6516203c99acd894c404351b5ae Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 1 May 2021 18:52:24 +0200 Subject: Docx writer: support colspans and rowspans in tables See: #6315 --- src/Text/Pandoc/Writers/Docx.hs | 6 +- src/Text/Pandoc/Writers/Docx/Table.hs | 200 +++++++++++++++++++---------- src/Text/Pandoc/Writers/GridTable.hs | 4 +- test/docx/golden/table_one_row.docx | Bin 9904 -> 9925 bytes test/docx/golden/table_with_list_cell.docx | Bin 10212 -> 10230 bytes test/docx/golden/tables.docx | Bin 10239 -> 10271 bytes 6 files changed, 140 insertions(+), 70 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 7064ded09..e11961bfd 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -53,6 +53,7 @@ import Text.Pandoc.Writers.Docx.Table import Text.Pandoc.Writers.Docx.Types import Text.Pandoc.Shared import Text.Pandoc.Walk +import qualified Text.Pandoc.Writers.GridTable as Grid import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.TeXMath @@ -889,8 +890,9 @@ blockToOpenXML' _ HorizontalRule = do $ mknode "v:rect" [("style","width:0;height:1.5pt"), ("o:hralign","center"), ("o:hrstd","t"),("o:hr","t")] () ] -blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = - tableToOpenXML (blocksToOpenXML opts) blkCapt specs thead tbody tfoot +blockToOpenXML' opts (Table attr caption colspecs thead tbodies tfoot) = + tableToOpenXML (blocksToOpenXML opts) + (Grid.toTable attr caption colspecs thead tbodies tfoot) blockToOpenXML' opts el | BulletList lst <- el = addOpenXMLList BulletMarker lst | OrderedList (start, numstyle, numdelim) lst <- el diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index 349f3a4ce..bb931bf08 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -14,65 +14,39 @@ module Text.Pandoc.Writers.Docx.Table ) where import Control.Monad.State.Strict +import Data.Array import Data.Text (Text) import Text.Pandoc.Definition import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Writers.Docx.Types import Text.Pandoc.Shared -import Text.Pandoc.Writers.Shared import Text.Printf (printf) +import Text.Pandoc.Writers.GridTable hiding (Table) import Text.Pandoc.Writers.OOXML -import Text.Pandoc.XML.Light as XML +import Text.Pandoc.XML.Light as XML hiding (Attr) import qualified Data.Text as T +import qualified Text.Pandoc.Writers.GridTable as Grid tableToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) - -> Caption - -> [ColSpec] - -> TableHead - -> [TableBody] - -> TableFoot + -> Grid.Table -> WS m [Content] -tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do - let (caption, aligns, widths, headers, rows) = - toLegacyTable blkCapt specs thead tbody tfoot +tableToOpenXML blocksToOpenXML gridTable = do setFirstPara modify $ \s -> s { stInTable = True } - let captionStr = stringify caption - caption' <- if null caption - then return [] - else withParaPropM (pStyleM "Table Caption") - $ blocksToOpenXML [Para caption] - let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] () - -- Table cells require a element, even an empty one! - -- Not in the spec but in Word 2007, 2010. See #4953. And - -- apparently the last element must be a , see #6983. - let cellToOpenXML (al, cell) = do - es <- withParaProp (alignmentFor al) $ blocksToOpenXML cell - return $ - case reverse (onlyElems es) of - b:e:_ | qName (elName b) == "bookmarkEnd" - , qName (elName e) == "p" -> es - e:_ | qName (elName e) == "p" -> es - _ -> es ++ [Elem $ mknode "w:p" [] ()] - headers' <- mapM cellToOpenXML $ zip aligns headers - rows' <- mapM (mapM cellToOpenXML . zip aligns) rows - compactStyle <- pStyleM "Compact" - let emptyCell' = [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] - let mkcell contents = mknode "w:tc" [] - $ if null contents - then emptyCell' - else contents - let mkrow cells = - mknode "w:tr" [] $ - map mkcell cells - let textwidth = 7920 -- 5.5 in in twips, 1/20 pt - let fullrow = 5000 -- 100% specified in pct - let (rowwidth :: Int) = round $ fullrow * sum widths - let mkgridcol w = mknode "w:gridCol" - [("w:w", tshow (floor (textwidth * w) :: Integer))] () - let hasHeader = not $ all null headers - modify $ \s -> s { stInTable = False } + let (Grid.Table _attr caption colspecs _rowheads thead tbodies tfoot) = + gridTable + let (Caption _maybeShortCaption captionBlocks) = caption + let captionStr = stringify captionBlocks + captionXml <- if null captionBlocks + then return [] + else withParaPropM (pStyleM "Table Caption") + $ blocksToOpenXML captionBlocks + head' <- cellGridToOpenXML blocksToOpenXML thead + bodies <- mapM (cellGridToOpenXML blocksToOpenXML) tbodies + foot' <- cellGridToOpenXML blocksToOpenXML tfoot + + let hasHeader = not . null . indices . partRowAttrs $ thead -- for compatibility with Word <= 2007, we include a val with a bitmask -- 0×0020 Apply first row conditional formatting -- 0×0040 Apply last row conditional formatting @@ -80,18 +54,12 @@ tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do -- 0×0100 Apply last column conditional formatting -- 0×0200 Do not apply row banding conditional formatting -- 0×0400 Do not apply column banding conditional formattin - let tblLookVal :: Int - tblLookVal = if hasHeader then 0x20 else 0 - return $ - caption' ++ - [Elem $ - mknode "w:tbl" [] - ( mknode "w:tblPr" [] - ( mknode "w:tblStyle" [("w:val","Table")] () : - mknode "w:tblW" (if all (== 0) widths - then [("w:type", "auto"), ("w:w", "0")] - else [("w:type", "pct"), ("w:w", tshow rowwidth)]) - () : + let tblLookVal = if hasHeader then (0x20 :: Int) else 0 + let (gridCols, tblWattr) = tableLayout (elems colspecs) + let tbl = mknode "w:tbl" [] + ( mknode "w:tblPr" [] + ( mknode "w:tblStyle" [("w:val","Table")] () : + mknode "w:tblW" tblWattr () : mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") ,("w:lastRow","0") ,("w:firstColumn","0") @@ -100,15 +68,14 @@ tableToOpenXML blocksToOpenXML blkCapt specs thead tbody tfoot = do ,("w:noVBand","0") ,("w:val", T.pack $ printf "%04x" tblLookVal) ] () : - [ mknode "w:tblCaption" [("w:val", captionStr)] () - | not (null caption) ] ) - : mknode "w:tblGrid" [] - (if all (==0) widths - then [] - else map mkgridcol widths) - : [ mkrow headers' | hasHeader ] ++ - map mkrow rows' - )] + [ mknode "w:tblCaption" [("w:val", captionStr)] () + | not (T.null captionStr) ] + ) + : mknode "w:tblGrid" [] gridCols + : head' ++ mconcat bodies ++ foot' + ) + modify $ \s -> s { stInTable = False } + return $ captionXml ++ [Elem tbl] alignmentToString :: Alignment -> Text alignmentToString = \case @@ -116,3 +83,104 @@ alignmentToString = \case AlignRight -> "right" AlignCenter -> "center" AlignDefault -> "left" + +tableLayout :: [ColSpec] -> ([Element], [(Text, Text)]) +tableLayout specs = + let + textwidth = 7920 -- 5.5 in in twips (1 twip == 1/20 pt) + fullrow = 5000 -- 100% specified in pct (1 pct == 1/50th of a percent) + ncols = length specs + getWidth = \case + ColWidth n -> n + _ -> 0 + widths = map (getWidth . snd) specs + rowwidth = round (fullrow * sum widths) :: Int + widthToTwips w = floor (textwidth * w) :: Int + mkGridCol w = mknode "w:gridCol" [("w:w", tshow (widthToTwips w))] () + in if all (== 0) widths + then ( replicate ncols $ mkGridCol (1.0 / fromIntegral ncols) + , [ ("w:type", "auto"), ("w:w", "0")]) + else ( map mkGridCol widths + , [ ("w:type", "pct"), ("w:w", tshow rowwidth) ]) + +cellGridToOpenXML :: PandocMonad m + => ([Block] -> WS m [Content]) + -> Part + -> WS m [Element] +cellGridToOpenXML blocksToOpenXML part@(Part _ _ rowAttrs) = + if null (indices rowAttrs) + then return mempty + else mapM (rowToOpenXML blocksToOpenXML) $ partToRows part + +data OOXMLCell + = OOXMLCell Attr Alignment RowSpan ColSpan [Block] + | OOXMLCellMerge ColSpan + +data OOXMLRow = OOXMLRow Attr [OOXMLCell] + +partToRows :: Part -> [OOXMLRow] +partToRows part = + let + toOOXMLCell :: RowIndex -> ColIndex -> GridCell -> [OOXMLCell] + toOOXMLCell ridx cidx = \case + ContentCell attr align rowspan colspan blocks -> + [OOXMLCell attr align rowspan colspan blocks] + ContinuationCell idx'@(ridx',cidx') | ridx /= ridx', cidx == cidx' -> + case (partCellArray part)!idx' of + (ContentCell _ _ _ colspan _) -> [OOXMLCellMerge colspan] + x -> error $ "Content cell expected, got, " ++ show x ++ + " at index " ++ show idx' + _ -> mempty + mkRow :: (RowIndex, Attr) -> OOXMLRow + mkRow (ridx, attr) = OOXMLRow attr + . concatMap (uncurry $ toOOXMLCell ridx) + . assocs + . rowArray ridx + $ partCellArray part + in map mkRow $ assocs (partRowAttrs part) + +rowToOpenXML :: PandocMonad m + => ([Block] -> WS m [Content]) + -> OOXMLRow + -> WS m Element +rowToOpenXML blocksToOpenXML (OOXMLRow _attr cells) = do + xmlcells <- mapM (ooxmlCellToOpenXML blocksToOpenXML) cells + -- let align' = case align of + -- AlignDefault -> colAlign + -- _ -> align + return $ mknode "w:tr" [] xmlcells + +ooxmlCellToOpenXML :: PandocMonad m + => ([Block] -> WS m [Content]) + -> OOXMLCell + -> WS m Element +ooxmlCellToOpenXML blocksToOpenXML = \case + OOXMLCellMerge (ColSpan colspan) -> do + return $ mknode "w:tc" [] + [ mknode "w:tcPr" [] [ mknode "w:gridSpan" [("w:val", tshow colspan)] () + , mknode "w:vMerge" [("w:val", "continue")] () ] + , mknode "w:p" [] [mknode "w:pPr" [] ()]] + OOXMLCell _attr align rowspan (ColSpan colspan) contents -> do + -- we handle rowspans via 'leftpad', so we can ignore those here + + compactStyle <- pStyleM "Compact" + es <- withParaProp (alignmentFor align) $ blocksToOpenXML contents + -- Table cells require a element, even an empty one! + -- Not in the spec but in Word 2007, 2010. See #4953. And + -- apparently the last element must be a , see #6983. + return . mknode "w:tc" [] $ + Elem + (mknode "w:tcPr" [] ([ mknode "w:gridSpan" [("w:val", tshow colspan)] () + | colspan > 1] ++ + [ mknode "w:vMerge" [("w:val", "restart")] () + | rowspan > RowSpan 1 ])) : + if null contents + then [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] + else case reverse (onlyElems es) of + b:e:_ | qName (elName b) == "bookmarkEnd" -- y tho? + , qName (elName e) == "p" -> es + e:_ | qName (elName e) == "p" -> es + _ -> es ++ [Elem $ mknode "w:p" [] ()] + +alignmentFor :: Alignment -> Element +alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] () diff --git a/src/Text/Pandoc/Writers/GridTable.hs b/src/Text/Pandoc/Writers/GridTable.hs index c6f4cf456..bc468febc 100644 --- a/src/Text/Pandoc/Writers/GridTable.hs +++ b/src/Text/Pandoc/Writers/GridTable.hs @@ -87,8 +87,8 @@ toTable attr caption colSpecs thead tbodies tfoot = tbGrids = map bodyToGrid tbodies tfGrid = let (TableFoot footAttr rows) = tfoot in rowsToPart footAttr rows - bodyToGrid (TableBody bodyAttr _rowHeadCols _headRows rows) = - rowsToPart bodyAttr rows + bodyToGrid (TableBody bodyAttr _rowHeadCols headRows rows) = + rowsToPart bodyAttr (headRows ++ rows) data BuilderCell = FilledCell GridCell diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index cab3fc31c..e60bb303f 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 9238c7e20..a4037cf32 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 6f0379def..bc1bc27f8 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ -- cgit v1.2.3 From 8357b835d9c6d17f32bded56aa24059c2f6e0678 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 5 May 2021 19:00:53 +0200 Subject: App: allow tabs expansion even if file-scope is used Tabs in plain-text inputs are now handled correctly, even if the `--file-scope` flag is used. Closes: #6709 --- src/Text/Pandoc/App.hs | 18 +++++++++++------- test/command/6709.md | 11 +++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 test/command/6709.md diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 4e8c9f2ab..96e4b5f47 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -255,13 +255,17 @@ convertWithOpts opts = do let sourceToDoc :: [FilePath] -> PandocIO Pandoc sourceToDoc sources' = case reader of - TextReader r - | optFileScope opts || readerNameBase == "json" -> - mconcat <$> mapM (readSource >=> r readerOpts) sources' - | otherwise -> - readSources sources' >>= r readerOpts - ByteStringReader r -> - mconcat <$> mapM (readFile' >=> r readerOpts) sources' + TextReader r + | readerNameBase == "json" -> + mconcat <$> mapM (readSource >=> r readerOpts) sources' + | optFileScope opts -> + -- Read source and convert tabs (see #6709) + let readSource' = fmap convertTabs . readSource + in mconcat <$> mapM (readSource' >=> r readerOpts) sources' + | otherwise -> + readSources sources' >>= r readerOpts + ByteStringReader r -> + mconcat <$> mapM (readFile' >=> r readerOpts) sources' when (readerNameBase == "markdown_github" || diff --git a/test/command/6709.md b/test/command/6709.md new file mode 100644 index 000000000..d6d514552 --- /dev/null +++ b/test/command/6709.md @@ -0,0 +1,11 @@ +Tabs must be expanded even if --file-scope is used +```` +% pandoc -t native --file-scope --tab-stop=2 +``` +if true; then + echo "yup" +fi +``` +^D +[CodeBlock ("",[],[]) "if true; then\n echo \"yup\"\nfi"] +```` -- cgit v1.2.3 From 295d93e96b1853c2ff4658aa7206ea1329024fab Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 7 May 2021 17:10:56 +0200 Subject: ConTeXt writer: support blank lines in line blocks. Fixes: #6564 Thanks to @denismaier. --- src/Text/Pandoc/Writers/ConTeXt.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index f14b1d894..b694437d8 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -178,8 +178,12 @@ blockToConTeXt (Para lst) = do contents <- inlineListToConTeXt lst return $ contents <> blankline blockToConTeXt (LineBlock lns) = do - doclines <- nowrap . vcat <$> mapM inlineListToConTeXt lns - return $ "\\startlines" $$ doclines $$ "\\stoplines" <> blankline + let emptyToBlankline doc = if isEmpty doc + then blankline + else doc + doclines <- mapM inlineListToConTeXt lns + let contextLines = vcat . map emptyToBlankline $ doclines + return $ "\\startlines" $$ contextLines $$ "\\stoplines" <> blankline blockToConTeXt (BlockQuote lst) = do contents <- blockListToConTeXt lst return $ "\\startblockquote" $$ nest 0 contents $$ "\\stopblockquote" <> blankline -- cgit v1.2.3 From 6e45607f9948f45b2e94f54b4825b667ca0d5441 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 1 May 2021 13:17:45 -0700 Subject: Change reader types, allowing better tracking of source positions. Previously, when multiple file arguments were provided, pandoc simply concatenated them and passed the contents to the readers, which took a Text argument. As a result, the readers had no way of knowing which file was the source of any particular bit of text. This meant that we couldn't report accurate source positions on errors or include accurate source positions as attributes in the AST. More seriously, it meant that we couldn't resolve resource paths relative to the files containing them (see e.g. #5501, #6632, #6384, #3752). Add Text.Pandoc.Sources (exported module), with a `Sources` type and a `ToSources` class. A `Sources` wraps a list of `(SourcePos, Text)` pairs. [API change] A parsec `Stream` instance is provided for `Sources`. The module also exports versions of parsec's `satisfy` and other Char parsers that track source positions accurately from a `Sources` stream (or any instance of the new `UpdateSourcePos` class). Text.Pandoc.Parsing now exports these modified Char parsers instead of the ones parsec provides. Modified parsers to use a `Sources` as stream [API change]. The readers that previously took a `Text` argument have been modified to take any instance of `ToSources`. So, they may still be used with a `Text`, but they can also be used with a `Sources` object. In Text.Pandoc.Error, modified the constructor PandocParsecError to take a `Sources` rather than a `Text` as first argument, so parse error locations can be accurately reported. T.P.Error: showPos, do not print "-" as source name. --- .hlint.yaml | 1 + pandoc.cabal | 1 + src/Text/Pandoc/App.hs | 8 +- src/Text/Pandoc/App/Opt.hs | 2 +- src/Text/Pandoc/Citeproc/BibTeX.hs | 14 +- src/Text/Pandoc/Error.hs | 39 ++- src/Text/Pandoc/Logging.hs | 8 +- src/Text/Pandoc/Parsing.hs | 474 ++++++++++++++++++------------ src/Text/Pandoc/Readers.hs | 97 +++--- src/Text/Pandoc/Readers/BibTeX.hs | 13 +- src/Text/Pandoc/Readers/CSV.hs | 14 +- src/Text/Pandoc/Readers/CommonMark.hs | 70 +++-- src/Text/Pandoc/Readers/Creole.hs | 11 +- src/Text/Pandoc/Readers/CslJson.hs | 9 +- src/Text/Pandoc/Readers/DocBook.hs | 11 +- src/Text/Pandoc/Readers/DokuWiki.hs | 17 +- src/Text/Pandoc/Readers/FB2.hs | 9 +- src/Text/Pandoc/Readers/HTML.hs | 33 ++- src/Text/Pandoc/Readers/Haddock.hs | 12 +- src/Text/Pandoc/Readers/Ipynb.hs | 8 +- src/Text/Pandoc/Readers/JATS.hs | 11 +- src/Text/Pandoc/Readers/Jira.hs | 16 +- src/Text/Pandoc/Readers/LaTeX.hs | 23 +- src/Text/Pandoc/Readers/LaTeX/Citation.hs | 2 +- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 12 +- src/Text/Pandoc/Readers/LaTeX/Types.hs | 15 +- src/Text/Pandoc/Readers/Man.hs | 29 +- src/Text/Pandoc/Readers/Markdown.hs | 49 +-- src/Text/Pandoc/Readers/MediaWiki.hs | 13 +- src/Text/Pandoc/Readers/Metadata.hs | 26 +- src/Text/Pandoc/Readers/Muse.hs | 17 +- src/Text/Pandoc/Readers/Native.hs | 12 +- src/Text/Pandoc/Readers/OPML.hs | 13 +- src/Text/Pandoc/Readers/Org.hs | 11 +- src/Text/Pandoc/Readers/Org/Inlines.hs | 3 +- src/Text/Pandoc/Readers/Org/Parsing.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 76 ++--- src/Text/Pandoc/Readers/Roff.hs | 34 +-- src/Text/Pandoc/Readers/TWiki.hs | 12 +- src/Text/Pandoc/Readers/Textile.hs | 172 +++++------ src/Text/Pandoc/Readers/TikiWiki.hs | 12 +- src/Text/Pandoc/Readers/Txt2Tags.hs | 12 +- src/Text/Pandoc/Readers/Vimwiki.hs | 19 +- src/Text/Pandoc/Shared.hs | 1 + src/Text/Pandoc/Sources.hs | 195 ++++++++++++ test/Tests/Readers/Markdown.hs | 4 +- 46 files changed, 1025 insertions(+), 617 deletions(-) create mode 100644 src/Text/Pandoc/Sources.hs diff --git a/.hlint.yaml b/.hlint.yaml index 350794803..ad0f7ddb9 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -9,6 +9,7 @@ # Ignore some builtin hints # - ignore: {name: "Avoid lambda"} +- ignore: {name: "Use bimap"} - ignore: {name: "Eta reduce"} - ignore: {name: "Evaluate"} - ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained diff --git a/pandoc.cabal b/pandoc.cabal index 8ea3aa681..de7951c54 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -513,6 +513,7 @@ library Text.Pandoc.Options, Text.Pandoc.Extensions, Text.Pandoc.Shared, + Text.Pandoc.Sources, Text.Pandoc.MediaBag, Text.Pandoc.Error, Text.Pandoc.Filter, diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 96e4b5f47..98b072ffb 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -160,9 +160,11 @@ convertWithOpts opts = do else optTabStop opts) - let readSources :: [FilePath] -> PandocIO Text - readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$> - mapM readSource srcs + let readSources :: [FilePath] -> PandocIO [(FilePath, Text)] + readSources srcs = + mapM (\fp -> do + t <- readSource fp + return (if fp == "-" then "" else fp, convertTabs t)) srcs outputSettings <- optToOutputSettings opts diff --git a/src/Text/Pandoc/App/Opt.hs b/src/Text/Pandoc/App/Opt.hs index c72f63464..d54d932b7 100644 --- a/src/Text/Pandoc/App/Opt.hs +++ b/src/Text/Pandoc/App/Opt.hs @@ -687,7 +687,7 @@ yamlToMeta (Mapping _ _ m) = where pMetaString = pure . MetaString <$> P.manyChar P.anyChar runEverything p = - runPure (P.readWithM p (def :: P.ParserState) "") + runPure (P.readWithM p (def :: P.ParserState) ("" :: Text)) >>= fmap (Meta . flip P.runF def) yamlToMeta _ = return mempty diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 510e56f9c..f6833000c 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -59,10 +59,11 @@ data Variant = Bibtex | Biblatex deriving (Show, Eq, Ord) -- | Parse BibTeX or BibLaTeX into a list of 'Reference's. -readBibtexString :: Variant -- ^ bibtex or biblatex +readBibtexString :: ToSources a + => Variant -- ^ bibtex or biblatex -> Locale -- ^ Locale -> (Text -> Bool) -- ^ Filter on citation ids - -> Text -- ^ bibtex/biblatex text + -> a -- ^ bibtex/biblatex text -> Either ParseError [Reference Inlines] readBibtexString variant locale idpred contents = do case runParser (((resolveCrossRefs variant <$> bibEntries) <* eof) >>= @@ -70,7 +71,7 @@ readBibtexString variant locale idpred contents = do filter (\item -> idpred (identifier item) && entryType item /= "xdata")) (fromMaybe defaultLang $ localeLanguage locale, Map.empty) - "" contents of + "" (toSources contents) of Left err -> Left err Right xs -> return xs @@ -339,7 +340,7 @@ defaultLang = Lang "en" Nothing (Just "US") [] [] [] -- a map of bibtex "string" macros type StringMap = Map.Map Text Text -type BibParser = Parser Text (Lang, StringMap) +type BibParser = Parser Sources (Lang, StringMap) data Item = Item{ identifier :: Text , sourcePos :: SourcePos @@ -804,7 +805,7 @@ bibEntries = do (bibComment <|> bibPreamble <|> bibString)) bibSkip :: BibParser () -bibSkip = () <$ take1WhileP (/='@') +bibSkip = skipMany1 (satisfy (/='@')) bibComment :: BibParser () bibComment = do @@ -829,6 +830,9 @@ bibString = do updateState (\(l,m) -> (l, Map.insert k v m)) return () +take1WhileP :: Monad m => (Char -> Bool) -> ParserT Sources u m Text +take1WhileP f = T.pack <$> many1 (satisfy f) + inBraces :: BibParser Text inBraces = do char '{' diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 8102f04cc..81eb41f85 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -23,26 +23,27 @@ import Control.Exception (Exception, displayException) import Data.Typeable (Typeable) import Data.Word (Word8) import Data.Text (Text) +import Data.List (sortOn) import qualified Data.Text as T +import Data.Ord (Down(..)) import GHC.Generics (Generic) import Network.HTTP.Client (HttpException) import System.Exit (ExitCode (..), exitWith) import System.IO (stderr) import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Sources (Sources(..)) import Text.Printf (printf) import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) import Text.Pandoc.Shared (tshow) import Citeproc (CiteprocError, prettyCiteprocError) -type Input = Text - data PandocError = PandocIOError Text IOError | PandocHttpError Text HttpException | PandocShouldNeverHappenError Text | PandocSomeError Text | PandocParseError Text - | PandocParsecError Input ParseError + | PandocParsecError Sources ParseError | PandocMakePDFError Text | PandocOptionError Text | PandocSyntaxMapError Text @@ -81,22 +82,28 @@ renderError e = "Please report this to pandoc's developers: " <> s PandocSomeError s -> s PandocParseError s -> s - PandocParsecError input err' -> + PandocParsecError (Sources inputs) err' -> let errPos = errorPos err' errLine = sourceLine errPos errColumn = sourceColumn errPos - ls = T.lines input <> [""] - errorInFile = if length ls > errLine - 1 - then T.concat ["\n", ls !! (errLine - 1) - ,"\n", T.replicate (errColumn - 1) " " - ,"^"] - else "" - in "\nError at " <> tshow err' <> - -- if error comes from a chunk or included file, - -- then we won't get the right text this way: - if sourceName errPos == "source" - then errorInFile - else "" + errFile = sourceName errPos + errorInFile = + case sortOn (Down . sourceLine . fst) + [ (pos,t) + | (pos,t) <- inputs + , sourceName pos == errFile + , sourceLine pos <= errLine + ] of + [] -> "" + ((pos,txt):_) -> + let ls = T.lines txt <> [""] + ln = errLine - sourceLine pos + in if length ls > ln - 1 + then T.concat ["\n", ls !! (ln - 1) + ,"\n", T.replicate (errColumn - 1) " " + ,"^"] + else "" + in "\nError at " <> tshow err' <> errorInFile PandocMakePDFError s -> s PandocOptionError s -> s PandocSyntaxMapError s -> s diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index efd2188f1..8c7292b69 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -241,9 +241,11 @@ instance ToJSON LogMessage where showPos :: SourcePos -> Text showPos pos = Text.pack $ sn ++ "line " ++ show (sourceLine pos) ++ " column " ++ show (sourceColumn pos) - where sn = if sourceName pos == "source" || sourceName pos == "" - then "" - else sourceName pos ++ " " + where + sn' = sourceName pos + sn = if sn' == "source" || sn' == "" || sn' == "-" + then "" + else sn' ++ " " encodeLogMessages :: [LogMessage] -> BL.ByteString encodeLogMessages ms = diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 37ab0adaa..11c4c7a62 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -5,7 +5,6 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Parsing @@ -19,8 +18,7 @@ A utility library with parsers used in pandoc readers. -} -module Text.Pandoc.Parsing ( take1WhileP, - takeP, +module Text.Pandoc.Parsing ( module Text.Pandoc.Sources, countChar, textStr, anyLine, @@ -134,22 +132,10 @@ module Text.Pandoc.Parsing ( take1WhileP, getInput, setInput, unexpected, - char, - letter, - digit, - alphaNum, skipMany, skipMany1, - spaces, - space, - anyChar, - satisfy, - newline, - string, count, eof, - noneOf, - oneOf, lookAhead, notFollowedBy, many, @@ -174,6 +160,8 @@ module Text.Pandoc.Parsing ( take1WhileP, SourcePos, getPosition, setPosition, + sourceName, + setSourceName, sourceColumn, sourceLine, setSourceColumn, @@ -189,16 +177,25 @@ module Text.Pandoc.Parsing ( take1WhileP, where import Control.Monad.Identity + ( guard, + join, + unless, + when, + void, + liftM2, + liftM, + Identity(..), + MonadPlus(mzero) ) import Control.Monad.Reader + ( asks, runReader, MonadReader(ask), Reader, ReaderT(ReaderT) ) import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isAsciiLower, isPunctuation, isSpace, ord, toLower, toUpper) -import Data.Default +import Data.Default ( Default(..) ) import Data.Functor (($>)) import Data.List (intercalate, transpose) import qualified Data.Map as M import Data.Maybe (fromMaybe) import qualified Data.Set as Set -import Data.String import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup.Entity (lookupEntity) @@ -207,22 +204,108 @@ import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad, readFileFromDirs, report) import Text.Pandoc.Definition + ( Target, + nullMeta, + nullAttr, + Meta, + ColWidth(ColWidthDefault, ColWidth), + TableFoot(TableFoot), + TableBody(TableBody), + Attr, + TableHead(TableHead), + Row(..), + Alignment(..), + Inline(Str), + ListNumberDelim(..), + ListAttributes, + ListNumberStyle(..) ) import Text.Pandoc.Logging + ( LogMessage(CouldNotLoadIncludeFile, DuplicateIdentifier) ) import Text.Pandoc.Options + ( extensionEnabled, + Extension(Ext_old_dashes, Ext_tex_math_dollars, + Ext_tex_math_single_backslash, Ext_tex_math_double_backslash, + Ext_auto_identifiers, Ext_ascii_identifiers, Ext_smart), + ReaderOptions(readerTabStop, readerColumns, readerExtensions) ) import Text.Pandoc.Readers.LaTeX.Types (Macro) import Text.Pandoc.Shared + ( uniqueIdent, + tshow, + mapLeft, + compactify, + trim, + trimr, + splitTextByIndices, + safeRead, + trimMath, + schemes, + escapeURI ) +import Text.Pandoc.Sources import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) import Text.Pandoc.XML (fromEntities) -import Text.Parsec hiding (token) -import Text.Parsec.Pos (initialPos, newPos, updatePosString) - -import Control.Monad.Except +import Text.Parsec + ( between, + setSourceName, + Parsec, + Column, + Line, + incSourceLine, + incSourceColumn, + setSourceLine, + setSourceColumn, + sourceLine, + sourceColumn, + sourceName, + setSourceName, + setPosition, + getPosition, + updateState, + setState, + getState, + optionMaybe, + optional, + option, + endBy1, + endBy, + sepEndBy1, + sepEndBy, + sepBy1, + sepBy, + try, + choice, + (), + (<|>), + manyTill, + many1, + many, + notFollowedBy, + lookAhead, + eof, + count, + skipMany1, + skipMany, + unexpected, + setInput, + getInput, + anyToken, + tokenPrim, + parse, + runParserT, + runParser, + ParseError, + ParsecT, + SourcePos, + Stream(..) ) +import Text.Parsec.Pos (initialPos, newPos) +import Control.Monad.Except ( MonadError(throwError) ) import Text.Pandoc.Error + ( PandocError(PandocParseError, PandocParsecError) ) type Parser t s = Parsec t s type ParserT = ParsecT + -- | Reader monad wrapping the parser state. This is used to possibly delay -- evaluation until all relevant information has been parsed and made available -- in the parser state. @@ -251,70 +334,48 @@ instance (Semigroup a, Monoid a) => Monoid (Future s a) where mappend = (<>) -- | Like @count@, but packs its result -countChar :: (Stream s m Char, Monad m) +countChar :: (Stream s m Char, UpdateSourcePos s Char, Monad m) => Int -> ParsecT s st m Char -> ParsecT s st m Text countChar n = fmap T.pack . count n -- | Like @string@, but uses @Text@. -textStr :: Stream s m Char => Text -> ParsecT s u m Text +textStr :: (Stream s m Char, UpdateSourcePos s Char) + => Text -> ParsecT s u m Text textStr t = string (T.unpack t) $> t --- | Parse characters while a predicate is true. -take1WhileP :: Monad m - => (Char -> Bool) - -> ParserT Text st m Text -take1WhileP f = do - -- needed to persuade parsec that this won't match an empty string: - c <- satisfy f - inp <- getInput - pos <- getPosition - let (t, rest) = T.span f inp - setInput rest - setPosition $ - if f '\t' || f '\n' - then updatePosString pos $ T.unpack t - else incSourceColumn pos (T.length t) - return $ T.singleton c <> t - --- Parse n characters of input (or the rest of the input if --- there aren't n characters). -takeP :: Monad m => Int -> ParserT Text st m Text -takeP n = do - guard (n > 0) - -- faster than 'count n anyChar' - inp <- getInput - pos <- getPosition - let (xs, rest) = T.splitAt n inp - -- needed to persuade parsec that this won't match an empty string: - anyChar - setInput rest - setPosition $ updatePosString pos $ T.unpack xs - return xs - --- | Parse any line of text -anyLine :: Monad m => ParserT Text st m Text + +-- | Parse any line of text, returning the contents without the +-- final newline. +anyLine :: Monad m => ParserT Sources st m Text anyLine = do -- This is much faster than: -- manyTill anyChar newline inp <- getInput - pos <- getPosition - case T.break (=='\n') inp of - (this, T.uncons -> Just ('\n', rest)) -> do - -- needed to persuade parsec that this won't match an empty string: - anyChar - setInput rest - setPosition $ incSourceLine (setSourceColumn pos 1) 1 - return this - _ -> mzero + case inp of + Sources [] -> mzero + Sources ((fp,t):inps) -> + -- we assume that lines don't span different input files + case T.break (=='\n') t of + (this, rest) + | T.null rest + , not (null inps) -> + -- line may span different input files, so do it + -- character by character + T.pack <$> manyTill anyChar newline + | otherwise -> do -- either end of inputs or newline in rest + setInput $ Sources ((fp, rest):inps) + char '\n' -- needed so parsec knows we won't match empty string + -- and so source pos is updated + return this -- | Parse any line, include the final newline in the output -anyLineNewline :: Monad m => ParserT Text st m Text +anyLineNewline :: Monad m => ParserT Sources st m Text anyLineNewline = (<> "\n") <$> anyLine -- | Parse indent by specified number of spaces (or equiv. tabs) -indentWith :: Stream s m Char +indentWith :: (Stream s m Char, UpdateSourcePos s Char) => HasReaderOptions st => Int -> ParserT s st m Text indentWith num = do @@ -399,11 +460,13 @@ notFollowedBy' p = try $ join $ do a <- try p return (return ()) -- (This version due to Andrew Pimlott on the Haskell mailing list.) -oneOfStrings' :: Stream s m Char => (Char -> Char -> Bool) -> [Text] -> ParserT s st m Text +oneOfStrings' :: (Stream s m Char, UpdateSourcePos s Char) + => (Char -> Char -> Bool) -> [Text] -> ParserT s st m Text oneOfStrings' f = fmap T.pack . oneOfStrings'' f . fmap T.unpack -- TODO: This should be re-implemented in a Text-aware way -oneOfStrings'' :: Stream s m Char => (Char -> Char -> Bool) -> [String] -> ParserT s st m String +oneOfStrings'' :: (Stream s m Char, UpdateSourcePos s Char) + => (Char -> Char -> Bool) -> [String] -> ParserT s st m String oneOfStrings'' _ [] = Prelude.fail "no strings" oneOfStrings'' matches strs = try $ do c <- anyChar @@ -418,14 +481,16 @@ oneOfStrings'' matches strs = try $ do -- | Parses one of a list of strings. If the list contains -- two strings one of which is a prefix of the other, the longer -- string will be matched if possible. -oneOfStrings :: Stream s m Char => [Text] -> ParserT s st m Text +oneOfStrings :: (Stream s m Char, UpdateSourcePos s Char) + => [Text] -> ParserT s st m Text oneOfStrings = oneOfStrings' (==) -- | Parses one of a list of strings (tried in order), case insensitive. -- TODO: This will not be accurate with general Unicode (neither -- Text.toLower nor Text.toCaseFold can be implemented with a map) -oneOfStringsCI :: Stream s m Char => [Text] -> ParserT s st m Text +oneOfStringsCI :: (Stream s m Char, UpdateSourcePos s Char) + => [Text] -> ParserT s st m Text oneOfStringsCI = oneOfStrings' ciMatch where ciMatch x y = toLower' x == toLower' y -- this optimizes toLower by checking common ASCII case @@ -436,11 +501,13 @@ oneOfStringsCI = oneOfStrings' ciMatch | otherwise = toLower c -- | Parses a space or tab. -spaceChar :: Stream s m Char => ParserT s st m Char +spaceChar :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m Char spaceChar = satisfy $ \c -> c == ' ' || c == '\t' -- | Parses a nonspace, nonnewline character. -nonspaceChar :: Stream s m Char => ParserT s st m Char +nonspaceChar :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m Char nonspaceChar = satisfy (not . isSpaceChar) isSpaceChar :: Char -> Bool @@ -451,21 +518,24 @@ isSpaceChar '\r' = True isSpaceChar _ = False -- | Skips zero or more spaces or tabs. -skipSpaces :: Stream s m Char => ParserT s st m () +skipSpaces :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m () skipSpaces = skipMany spaceChar -- | Skips zero or more spaces or tabs, then reads a newline. -blankline :: Stream s m Char => ParserT s st m Char +blankline :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m Char blankline = try $ skipSpaces >> newline -- | Parses one or more blank lines and returns a string of newlines. -blanklines :: Stream s m Char => ParserT s st m Text +blanklines :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m Text blanklines = T.pack <$> many1 blankline -- | Gobble n spaces; if tabs are encountered, expand them -- and gobble some or all of their spaces, leaving the rest. gobbleSpaces :: (HasReaderOptions st, Monad m) - => Int -> ParserT Text st m () + => Int -> ParserT Sources st m () gobbleSpaces 0 = return () gobbleSpaces n | n < 0 = error "gobbleSpaces called with negative number" @@ -473,18 +543,26 @@ gobbleSpaces n char ' ' <|> eatOneSpaceOfTab gobbleSpaces (n - 1) -eatOneSpaceOfTab :: (HasReaderOptions st, Monad m) => ParserT Text st m Char +eatOneSpaceOfTab :: (HasReaderOptions st, Monad m) => ParserT Sources st m Char eatOneSpaceOfTab = do - char '\t' + lookAhead (char '\t') + pos <- getPosition tabstop <- getOption readerTabStop + -- replace the tab on the input stream with spaces + let numSpaces = tabstop - ((sourceColumn pos - 1) `mod` tabstop) inp <- getInput - setInput $ T.replicate (tabstop - 1) " " <> inp - return ' ' + setInput $ + case inp of + Sources [] -> error "eatOneSpaceOfTab - empty Sources list" + Sources ((fp,t):rest) -> + -- drop the tab and add spaces + Sources ((fp, T.replicate numSpaces " " <> T.drop 1 t):rest) + char ' ' -- | Gobble up to n spaces; if tabs are encountered, expand them -- and gobble some or all of their spaces, leaving the rest. gobbleAtMostSpaces :: (HasReaderOptions st, Monad m) - => Int -> ParserT Text st m Int + => Int -> ParserT Sources st m Int gobbleAtMostSpaces 0 = return 0 gobbleAtMostSpaces n | n < 0 = error "gobbleAtMostSpaces called with negative number" @@ -493,7 +571,8 @@ gobbleAtMostSpaces n (+ 1) <$> gobbleAtMostSpaces (n - 1) -- | Parses material enclosed between start and end parsers. -enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser +enclosed :: (Show end, Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m t -- ^ start parser -> ParserT s st m end -- ^ end parser -> ParserT s st m a -- ^ content parser (to be used repeatedly) -> ParserT s st m [a] @@ -501,39 +580,41 @@ enclosed start end parser = try $ start >> notFollowedBy space >> many1Till parser end -- | Parse string, case insensitive. -stringAnyCase :: Stream s m Char => Text -> ParserT s st m Text +stringAnyCase :: (Stream s m Char, UpdateSourcePos s Char) + => Text -> ParserT s st m Text stringAnyCase = fmap T.pack . stringAnyCase' . T.unpack -stringAnyCase' :: Stream s m Char => String -> ParserT s st m String +stringAnyCase' :: (Stream s m Char, UpdateSourcePos s Char) + => String -> ParserT s st m String stringAnyCase' [] = string "" stringAnyCase' (x:xs) = do firstChar <- char (toUpper x) <|> char (toLower x) rest <- stringAnyCase' xs return (firstChar:rest) +-- TODO rewrite by just adding to Sources stream? -- | Parse contents of 'str' using 'parser' and return result. -parseFromString :: (Stream s m Char, IsString s) - => ParserT s st m r +parseFromString :: Monad m + => ParserT Sources st m r -> Text - -> ParserT s st m r + -> ParserT Sources st m r parseFromString parser str = do oldPos <- getPosition - setPosition $ initialPos " chunk" + setPosition $ initialPos "chunk" oldInput <- getInput - setInput $ fromString $ T.unpack str + setInput $ toSources str result <- parser spaces - eof setInput oldInput setPosition oldPos return result -- | Like 'parseFromString' but specialized for 'ParserState'. -- This resets 'stateLastStrPos', which is almost always what we want. -parseFromString' :: (Stream s m Char, IsString s, HasLastStrPosition u) - => ParserT s u m a +parseFromString' :: (Monad m, HasLastStrPosition u) + => ParserT Sources u m a -> Text - -> ParserT s u m a + -> ParserT Sources u m a parseFromString' parser str = do oldLastStrPos <- getLastStrPos <$> getState updateState $ setLastStrPos Nothing @@ -542,7 +623,7 @@ parseFromString' parser str = do return res -- | Parse raw line block up to and including blank lines. -lineClump :: Monad m => ParserT Text st m Text +lineClump :: Monad m => ParserT Sources st m Text lineClump = blanklines <|> (T.unlines <$> many1 (notFollowedBy blankline >> anyLine)) @@ -551,7 +632,7 @@ lineClump = blanklines -- pairs of open and close, which must be different. For example, -- @charsInBalanced '(' ')' anyChar@ will parse "(hello (there))" -- and return "hello (there)". -charsInBalanced :: Stream s m Char => Char -> Char -> ParserT s st m Char +charsInBalanced :: (Stream s m Char, UpdateSourcePos s Char) => Char -> Char -> ParserT s st m Char -> ParserT s st m Text charsInBalanced open close parser = try $ do char open @@ -570,7 +651,7 @@ charsInBalanced open close parser = try $ do -- Auxiliary functions for romanNumeral: -- | Parses a roman numeral (uppercase or lowercase), returns number. -romanNumeral :: Stream s m Char => Bool -- ^ Uppercase if true +romanNumeral :: (Stream s m Char, UpdateSourcePos s Char) => Bool -- ^ Uppercase if true -> ParserT s st m Int romanNumeral upperCase = do let rchar uc = char $ if upperCase then uc else toLower uc @@ -606,7 +687,7 @@ romanNumeral upperCase = do -- | Parses an email address; returns original and corresponding -- escaped mailto: URI. -emailAddress :: Stream s m Char => ParserT s st m (Text, Text) +emailAddress :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (Text, Text) emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) where toResult mbox dom = let full = fromEntities $ T.pack $ mbox ++ '@':dom in (full, escapeURI $ "mailto:" <> full) @@ -630,11 +711,11 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) isEmailPunct c = T.any (== c) "!\"#$%&'*+-/=?^_{|}~;" -uriScheme :: Stream s m Char => ParserT s st m Text +uriScheme :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Text uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. -uri :: Stream s m Char => ParserT s st m (Text, Text) +uri :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (Text, Text) uri = try $ do scheme <- uriScheme char ':' @@ -677,7 +758,7 @@ uri = try $ do uriChunkBetween l r = try $ do chunk <- between (char l) (char r) uriChunk return (T.pack $ [l] ++ chunk ++ [r]) -mathInlineWith :: Stream s m Char => Text -> Text -> ParserT s st m Text +mathInlineWith :: (Stream s m Char, UpdateSourcePos s Char) => Text -> Text -> ParserT s st m Text mathInlineWith op cl = try $ do textStr op when (op == "$") $ notFollowedBy space @@ -698,10 +779,10 @@ mathInlineWith op cl = try $ do notFollowedBy digit -- to prevent capture of $5 return $ trimMath $ T.concat words' where - inBalancedBraces :: Stream s m Char => Int -> Text -> ParserT s st m Text + inBalancedBraces :: (Stream s m Char, UpdateSourcePos s Char) => Int -> Text -> ParserT s st m Text inBalancedBraces n = fmap T.pack . inBalancedBraces' n . T.unpack - inBalancedBraces' :: Stream s m Char => Int -> String -> ParserT s st m String + inBalancedBraces' :: (Stream s m Char, UpdateSourcePos s Char) => Int -> String -> ParserT s st m String inBalancedBraces' 0 "" = do c <- anyChar if c == '{' @@ -718,13 +799,13 @@ mathInlineWith op cl = try $ do '{' -> inBalancedBraces' (numOpen + 1) (c:xs) _ -> inBalancedBraces' numOpen (c:xs) -mathDisplayWith :: Stream s m Char => Text -> Text -> ParserT s st m Text +mathDisplayWith :: (Stream s m Char, UpdateSourcePos s Char) => Text -> Text -> ParserT s st m Text mathDisplayWith op cl = try $ fmap T.pack $ do textStr op many1Till (satisfy (/= '\n') <|> (newline <* notFollowedBy' blankline)) (try $ textStr cl) -mathDisplay :: (HasReaderOptions st, Stream s m Char) +mathDisplay :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Text mathDisplay = (guardEnabled Ext_tex_math_dollars >> mathDisplayWith "$$" "$$") @@ -733,7 +814,7 @@ mathDisplay = <|> (guardEnabled Ext_tex_math_double_backslash >> mathDisplayWith "\\\\[" "\\\\]") -mathInline :: (HasReaderOptions st , Stream s m Char) +mathInline :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Text mathInline = (guardEnabled Ext_tex_math_dollars >> mathInlineWith "$" "$") @@ -746,7 +827,7 @@ mathInline = -- displacement (the difference between the source column at the end -- and the source column at the beginning). Vertical displacement -- (source row) is ignored. -withHorizDisplacement :: Stream s m Char +withHorizDisplacement :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m a -- ^ Parser to apply -> ParserT s st m (a, Int) -- ^ (result, displacement) withHorizDisplacement parser = do @@ -758,30 +839,37 @@ withHorizDisplacement parser = do -- | Applies a parser and returns the raw string that was parsed, -- along with the value produced by the parser. withRaw :: Monad m - => ParsecT Text st m a - -> ParsecT Text st m (a, Text) + => ParsecT Sources st m a + -> ParsecT Sources st m (a, Text) withRaw parser = do - pos1 <- getPosition - inp <- getInput + inps1 <- getInput result <- parser - pos2 <- getPosition - let (l1,c1) = (sourceLine pos1, sourceColumn pos1) - let (l2,c2) = (sourceLine pos2, sourceColumn pos2) - let inplines = take ((l2 - l1) + 1) $ T.lines inp - let raw = case inplines of - [] -> "" - [l] -> T.take (c2 - c1) l - ls -> T.unlines (init ls) <> T.take (c2 - 1) (last ls) - return (result, raw) + inps2 <- getInput + -- 'raw' is the difference between inps1 and inps2 + return (result, sourcesDifference inps1 inps2) + +sourcesDifference :: Sources -> Sources -> Text +sourcesDifference (Sources is1) (Sources is2) = go is1 is2 + where + go inps1 inps2 = + case (inps1, inps2) of + ([], _) -> mempty + (_, []) -> mconcat $ map snd inps1 + ((p1,t1):rest1, (p2, t2):rest2) + | p1 == p2 + , t1 == t2 -> go rest1 rest2 + | p1 == p2 + , t1 /= t2 -> fromMaybe mempty $ T.stripSuffix t2 t1 + | otherwise -> t1 <> go rest1 inps2 -- | Parses backslash, then applies character parser. -escaped :: Stream s m Char +escaped :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Char -- ^ Parser for character to escape -> ParserT s st m Char escaped parser = try $ char '\\' >> parser -- | Parse character entity. -characterReference :: Stream s m Char => ParserT s st m Char +characterReference :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Char characterReference = try $ do char '&' ent <- many1Till nonspaceChar (char ';') @@ -794,19 +882,19 @@ characterReference = try $ do _ -> Prelude.fail "entity not found" -- | Parses an uppercase roman numeral and returns (UpperRoman, number). -upperRoman :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +upperRoman :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) upperRoman = do num <- romanNumeral True return (UpperRoman, num) -- | Parses a lowercase roman numeral and returns (LowerRoman, number). -lowerRoman :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +lowerRoman :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) lowerRoman = do num <- romanNumeral False return (LowerRoman, num) -- | Parses a decimal numeral and returns (Decimal, number). -decimal :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +decimal :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) decimal = do num <- many1 digit return (Decimal, fromMaybe 1 $ safeRead $ T.pack num) @@ -815,7 +903,7 @@ decimal = do -- returns (DefaultStyle, [next example number]). The next -- example number is incremented in parser state, and the label -- (if present) is added to the label table. -exampleNum :: Stream s m Char +exampleNum :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s ParserState m (ListNumberStyle, Int) exampleNum = do char '@' @@ -834,37 +922,37 @@ exampleNum = do return (Example, num) -- | Parses a '#' returns (DefaultStyle, 1). -defaultNum :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +defaultNum :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) defaultNum = do char '#' return (DefaultStyle, 1) -- | Parses a lowercase letter and returns (LowerAlpha, number). -lowerAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +lowerAlpha :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) lowerAlpha = do ch <- satisfy isAsciiLower return (LowerAlpha, ord ch - ord 'a' + 1) -- | Parses an uppercase letter and returns (UpperAlpha, number). -upperAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +upperAlpha :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) upperAlpha = do ch <- satisfy isAsciiUpper return (UpperAlpha, ord ch - ord 'A' + 1) -- | Parses a roman numeral i or I -romanOne :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) +romanOne :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) romanOne = (char 'i' >> return (LowerRoman, 1)) <|> (char 'I' >> return (UpperRoman, 1)) -- | Parses an ordered list marker and returns list attributes. -anyOrderedListMarker :: Stream s m Char => ParserT s ParserState m ListAttributes +anyOrderedListMarker :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s ParserState m ListAttributes anyOrderedListMarker = choice [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens], numParser <- [decimal, exampleNum, defaultNum, romanOne, lowerAlpha, lowerRoman, upperAlpha, upperRoman]] -- | Parses a list number (num) followed by a period, returns list attributes. -inPeriod :: Stream s m Char +inPeriod :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) -> ParserT s st m ListAttributes inPeriod num = try $ do @@ -876,7 +964,7 @@ inPeriod num = try $ do return (start, style, delim) -- | Parses a list number (num) followed by a paren, returns list attributes. -inOneParen :: Stream s m Char +inOneParen :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) -> ParserT s st m ListAttributes inOneParen num = try $ do @@ -885,7 +973,7 @@ inOneParen num = try $ do return (start, style, OneParen) -- | Parses a list number (num) enclosed in parens, returns list attributes. -inTwoParens :: Stream s m Char +inTwoParens :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int) -> ParserT s st m ListAttributes inTwoParens num = try $ do @@ -896,7 +984,7 @@ inTwoParens num = try $ do -- | Parses an ordered list marker with a given style and delimiter, -- returns number. -orderedListMarker :: Stream s m Char +orderedListMarker :: (Stream s m Char, UpdateSourcePos s Char) => ListNumberStyle -> ListNumberDelim -> ParserT s ParserState m Int @@ -919,10 +1007,10 @@ orderedListMarker style delim = do return start -- | Parses a character reference and returns a Str element. -charRef :: Stream s m Char => ParserT s st m Inline +charRef :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inline charRef = Str . T.singleton <$> characterReference -lineBlockLine :: Monad m => ParserT Text st m Text +lineBlockLine :: Monad m => ParserT Sources st m Text lineBlockLine = try $ do char '|' char ' ' @@ -932,11 +1020,11 @@ lineBlockLine = try $ do continuations <- many (try $ char ' ' >> anyLine) return $ white <> T.unwords (line : continuations) -blankLineBlockLine :: Stream s m Char => ParserT s st m Char +blankLineBlockLine :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Char blankLineBlockLine = try (char '|' >> blankline) -- | Parses an RST-style line block and returns a list of strings. -lineBlockLines :: Monad m => ParserT Text st m [Text] +lineBlockLines :: Monad m => ParserT Sources st m [Text] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> (T.singleton <$> blankLineBlockLine)) skipMany blankline @@ -944,7 +1032,8 @@ lineBlockLines = try $ do -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. -tableWith :: (Stream s m Char, HasReaderOptions st, Monad mf) +tableWith :: (Stream s m Char, UpdateSourcePos s Char, + HasReaderOptions st, Monad mf) => ParserT s st m (mf [Blocks], [Alignment], [Int]) -> ([Int] -> ParserT s st m (mf [Blocks])) -> ParserT s st m sep @@ -964,7 +1053,8 @@ tableWith headerParser rowParser lineParser footerParser = try $ do type TableComponents mf = ([Alignment], [Double], mf [Row], mf [Row]) -tableWith' :: (Stream s m Char, HasReaderOptions st, Monad mf) +tableWith' :: (Stream s m Char, UpdateSourcePos s Char, + HasReaderOptions st, Monad mf) => ParserT s st m (mf [Blocks], [Alignment], [Int]) -> ([Int] -> ParserT s st m (mf [Blocks])) -> ParserT s st m sep @@ -1013,20 +1103,19 @@ widthsFromIndices numColumns' indices = -- (which may be grid), then the rows, -- which may be grid, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). -gridTableWith :: (Stream s m Char, HasReaderOptions st, HasLastStrPosition st, - Monad mf, IsString s) - => ParserT s st m (mf Blocks) -- ^ Block list parser +gridTableWith :: (Monad m, HasReaderOptions st, HasLastStrPosition st, Monad mf) + => ParserT Sources st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table - -> ParserT s st m (mf Blocks) + -> ParserT Sources st m (mf Blocks) gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter -gridTableWith' :: (Stream s m Char, HasReaderOptions st, HasLastStrPosition st, - Monad mf, IsString s) - => ParserT s st m (mf Blocks) -- ^ Block list parser +gridTableWith' :: (Monad m, HasReaderOptions st, HasLastStrPosition st, + Monad mf) + => ParserT Sources st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table - -> ParserT s st m (TableComponents mf) + -> ParserT Sources st m (TableComponents mf) gridTableWith' blocks headless = tableWith' (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter @@ -1035,7 +1124,7 @@ gridTableSplitLine :: [Int] -> Text -> [Text] gridTableSplitLine indices line = map removeFinalBar $ tail $ splitTextByIndices (init indices) $ trimr line -gridPart :: Stream s m Char => Char -> ParserT s st m ((Int, Int), Alignment) +gridPart :: Monad m => Char -> ParserT Sources st m ((Int, Int), Alignment) gridPart ch = do leftColon <- option False (True <$ char ':') dashes <- many1 (char ch) @@ -1050,7 +1139,7 @@ gridPart ch = do (False, False) -> AlignDefault return ((lengthDashes, lengthDashes + 1), alignment) -gridDashedLines :: Stream s m Char => Char -> ParserT s st m [((Int, Int), Alignment)] +gridDashedLines :: Monad m => Char -> ParserT Sources st m [((Int, Int), Alignment)] gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline removeFinalBar :: Text -> Text @@ -1059,14 +1148,14 @@ removeFinalBar = T.dropWhileEnd go . T.dropWhileEnd (=='|') go c = T.any (== c) " \t" -- | Separator between rows of grid table. -gridTableSep :: Stream s m Char => Char -> ParserT s st m Char +gridTableSep :: Monad m => Char -> ParserT Sources st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: (Stream s m Char, Monad mf, IsString s, HasLastStrPosition st) +gridTableHeader :: (Monad m, Monad mf, HasLastStrPosition st) => Bool -- ^ Headerless table - -> ParserT s st m (mf Blocks) - -> ParserT s st m (mf [Blocks], [Alignment], [Int]) + -> ParserT Sources st m (mf Blocks) + -> ParserT Sources st m (mf [Blocks], [Alignment], [Int]) gridTableHeader True _ = do optional blanklines dashes <- gridDashedLines '-' @@ -1089,17 +1178,17 @@ gridTableHeader False blocks = try $ do heads <- sequence <$> mapM (parseFromString' blocks . trim) rawHeads return (heads, aligns, indices) -gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [Text] +gridTableRawLine :: (Stream s m Char, UpdateSourcePos s Char) => [Int] -> ParserT s st m [Text] gridTableRawLine indices = do char '|' line <- many1Till anyChar newline return (gridTableSplitLine indices $ T.pack line) -- | Parse row of grid table. -gridTableRow :: (Stream s m Char, Monad mf, IsString s, HasLastStrPosition st) - => ParserT s st m (mf Blocks) +gridTableRow :: (Monad m, Monad mf, HasLastStrPosition st) + => ParserT Sources st m (mf Blocks) -> [Int] - -> ParserT s st m (mf [Blocks]) + -> ParserT Sources st m (mf [Blocks]) gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((<> "\n") . T.unlines . removeOneLeadingSpace) $ @@ -1120,34 +1209,38 @@ removeOneLeadingSpace xs = Just (c, _) -> c == ' ' -- | Parse footer for a grid table. -gridTableFooter :: Stream s m Char => ParserT s st m () +gridTableFooter :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () gridTableFooter = optional blanklines --- -- | Removes the ParsecT layer from the monad transformer stack -readWithM :: Monad m - => ParserT Text st m a -- ^ parser - -> st -- ^ initial state - -> Text -- ^ input +readWithM :: (Monad m, ToSources t) + => ParserT Sources st m a -- ^ parser + -> st -- ^ initial state + -> t -- ^ input -> m (Either PandocError a) readWithM parser state input = - mapLeft (PandocParsecError input) <$> runParserT parser state "source" input + mapLeft (PandocParsecError sources) + <$> runParserT parser state (initialSourceName sources) sources + where + sources = toSources input -- | Parse a string with a given parser and state -readWith :: Parser Text st a +readWith :: ToSources t + => Parser Sources st a -> st - -> Text + -> t -> Either PandocError a readWith p t inp = runIdentity $ readWithM p t inp -- | Parse a string with @parser@ (for testing). testStringWith :: Show a - => ParserT Text ParserState Identity a + => ParserT Sources ParserState Identity a -> Text -> IO () testStringWith parser str = UTF8.putStrLn $ tshow $ - readWith parser defaultParserState str + readWith parser defaultParserState (toSources str) -- | Parsing options. data ParserState = ParserState @@ -1394,19 +1487,23 @@ registerHeader (ident,classes,kvs) header' = do updateState $ updateIdentifierList $ Set.insert ident return (ident,classes,kvs) -smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) +smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, + HasQuoteContext st m, + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines -> ParserT s st m Inlines smartPunctuation inlineParser = do guardEnabled Ext_smart choice [ quoted inlineParser, apostrophe, doubleCloseQuote, dash, ellipses ] -quoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) +quoted :: (HasLastStrPosition st, HasQuoteContext st m, + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines -> ParserT s st m Inlines quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser -singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) +singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines -> ParserT s st m Inlines singleQuoted inlineParser = do @@ -1416,7 +1513,8 @@ singleQuoted inlineParser = do (withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd))) <|> pure "\8217" -doubleQuoted :: (HasQuoteContext st m, HasLastStrPosition st, Stream s m Char) +doubleQuoted :: (HasQuoteContext st m, HasLastStrPosition st, + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines -> ParserT s st m Inlines doubleQuoted inlineParser = do @@ -1433,13 +1531,14 @@ failIfInQuoteContext context = do context' <- getQuoteContext when (context' == context) $ Prelude.fail "already inside quotes" -charOrRef :: Stream s m Char => [Char] -> ParserT s st m Char +charOrRef :: (Stream s m Char, UpdateSourcePos s Char) => [Char] -> ParserT s st m Char charOrRef cs = oneOf cs <|> try (do c <- characterReference guard (c `elem` cs) return c) -singleQuoteStart :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) +singleQuoteStart :: (HasLastStrPosition st, HasQuoteContext st m, + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () singleQuoteStart = do failIfInQuoteContext InSingleQuote @@ -1449,7 +1548,7 @@ singleQuoteStart = do charOrRef "'\8216\145" void $ lookAhead (satisfy (not . isSpaceChar)) -singleQuoteEnd :: Stream s m Char +singleQuoteEnd :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () singleQuoteEnd = try $ do charOrRef "'\8217\146" @@ -1457,7 +1556,7 @@ singleQuoteEnd = try $ do doubleQuoteStart :: (HasLastStrPosition st, HasQuoteContext st m, - Stream s m Char) + Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () doubleQuoteStart = do failIfInQuoteContext InDoubleQuote @@ -1465,21 +1564,21 @@ doubleQuoteStart = do try $ do charOrRef "\"\8220\147" void $ lookAhead (satisfy (not . isSpaceChar)) -doubleQuoteEnd :: Stream s m Char +doubleQuoteEnd :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () doubleQuoteEnd = void (charOrRef "\"\8221\148") -apostrophe :: Stream s m Char => ParserT s st m Inlines +apostrophe :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\8217") -doubleCloseQuote :: Stream s m Char => ParserT s st m Inlines +doubleCloseQuote :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines doubleCloseQuote = B.str "\8221" <$ char '"' -ellipses :: Stream s m Char +ellipses :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines ellipses = try (string "..." >> return (B.str "\8230")) -dash :: (HasReaderOptions st, Stream s m Char) +dash :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines dash = try $ do oldDashes <- extensionEnabled Ext_old_dashes <$> getOption readerExtensions @@ -1506,7 +1605,7 @@ nested p = do updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } return res -citeKey :: (Stream s m Char, HasLastStrPosition st) +citeKey :: (Stream s m Char, UpdateSourcePos s Char, HasLastStrPosition st) => ParserT s st m (Bool, Text) citeKey = try $ do guard =<< notAfterString @@ -1575,10 +1674,11 @@ insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) insertIncludedFile blocks totoks dirs f = runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f +-- TODO: replace this with something using addToSources. -- | Parse content of include file as future blocks. Circular includes result in -- an @PandocParseError@. insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st) - => ParserT Text st m (Future st Blocks) + => ParserT Sources st m (Future st Blocks) -> [FilePath] -> FilePath - -> ParserT Text st m (Future st Blocks) -insertIncludedFileF p = insertIncludedFile' p id + -> ParserT Sources st m (Future st Blocks) +insertIncludedFileF p = insertIncludedFile' p (\t -> Sources [(initialPos "",t)]) diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 7ae9db34f..5106f8058 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -1,4 +1,5 @@ {-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {- | @@ -72,6 +73,7 @@ import Text.Pandoc.Error import Text.Pandoc.Extensions import Text.Pandoc.Options import Text.Pandoc.Readers.CommonMark +import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.Creole import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx @@ -84,7 +86,6 @@ import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.JATS (readJATS) import Text.Pandoc.Readers.Jira (readJira) import Text.Pandoc.Readers.LaTeX -import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.MediaWiki import Text.Pandoc.Readers.Muse import Text.Pandoc.Readers.Native @@ -102,50 +103,52 @@ import Text.Pandoc.Readers.CSV import Text.Pandoc.Readers.CslJson import Text.Pandoc.Readers.BibTeX import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Sources (ToSources(..), sourcesToText) -data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) +data Reader m = TextReader (forall a . ToSources a => + ReaderOptions -> a -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) -- | Association list of formats and readers. readers :: PandocMonad m => [(Text, Reader m)] -readers = [ ("native" , TextReader readNative) - ,("json" , TextReader readJSON) - ,("markdown" , TextReader readMarkdown) - ,("markdown_strict" , TextReader readMarkdown) - ,("markdown_phpextra" , TextReader readMarkdown) - ,("markdown_github" , TextReader readMarkdown) - ,("markdown_mmd", TextReader readMarkdown) - ,("commonmark" , TextReader readCommonMark) - ,("commonmark_x" , TextReader readCommonMark) - ,("creole" , TextReader readCreole) - ,("dokuwiki" , TextReader readDokuWiki) - ,("gfm" , TextReader readCommonMark) - ,("rst" , TextReader readRST) - ,("mediawiki" , TextReader readMediaWiki) - ,("vimwiki" , TextReader readVimwiki) - ,("docbook" , TextReader readDocBook) - ,("opml" , TextReader readOPML) - ,("org" , TextReader readOrg) - ,("textile" , TextReader readTextile) -- TODO : textile+lhs - ,("html" , TextReader readHtml) - ,("jats" , TextReader readJATS) - ,("jira" , TextReader readJira) - ,("latex" , TextReader readLaTeX) - ,("haddock" , TextReader readHaddock) - ,("twiki" , TextReader readTWiki) - ,("tikiwiki" , TextReader readTikiWiki) - ,("docx" , ByteStringReader readDocx) - ,("odt" , ByteStringReader readOdt) - ,("t2t" , TextReader readTxt2Tags) - ,("epub" , ByteStringReader readEPUB) - ,("muse" , TextReader readMuse) - ,("man" , TextReader readMan) - ,("fb2" , TextReader readFB2) - ,("ipynb" , TextReader readIpynb) - ,("csv" , TextReader readCSV) - ,("csljson" , TextReader readCslJson) - ,("bibtex" , TextReader readBibTeX) - ,("biblatex" , TextReader readBibLaTeX) +readers = [("native" , TextReader readNative) + ,("json" , TextReader readJSON) + ,("markdown" , TextReader readMarkdown) + ,("markdown_strict" , TextReader readMarkdown) + ,("markdown_phpextra" , TextReader readMarkdown) + ,("markdown_github" , TextReader readMarkdown) + ,("markdown_mmd", TextReader readMarkdown) + ,("commonmark" , TextReader readCommonMark) + ,("commonmark_x" , TextReader readCommonMark) + ,("creole" , TextReader readCreole) + ,("dokuwiki" , TextReader readDokuWiki) + ,("gfm" , TextReader readCommonMark) + ,("rst" , TextReader readRST) + ,("mediawiki" , TextReader readMediaWiki) + ,("vimwiki" , TextReader readVimwiki) + ,("docbook" , TextReader readDocBook) + ,("opml" , TextReader readOPML) + ,("org" , TextReader readOrg) + ,("textile" , TextReader readTextile) -- TODO : textile+lhs + ,("html" , TextReader readHtml) + ,("jats" , TextReader readJATS) + ,("jira" , TextReader readJira) + ,("latex" , TextReader readLaTeX) + ,("haddock" , TextReader readHaddock) + ,("twiki" , TextReader readTWiki) + ,("tikiwiki" , TextReader readTikiWiki) + ,("docx" , ByteStringReader readDocx) + ,("odt" , ByteStringReader readOdt) + ,("t2t" , TextReader readTxt2Tags) + ,("epub" , ByteStringReader readEPUB) + ,("muse" , TextReader readMuse) + ,("man" , TextReader readMan) + ,("fb2" , TextReader readFB2) + ,("ipynb" , TextReader readIpynb) + ,("csv" , TextReader readCSV) + ,("csljson" , TextReader readCslJson) + ,("bibtex" , TextReader readBibTeX) + ,("biblatex" , TextReader readBibLaTeX) ] -- | Retrieve reader, extensions based on formatSpec (format+extensions). @@ -173,9 +176,13 @@ getReader s = return (r, exts) -- | Read pandoc document from JSON format. -readJSON :: PandocMonad m - => ReaderOptions -> Text -> m Pandoc -readJSON _ t = - case eitherDecode' . BL.fromStrict . UTF8.fromText $ t of +readJSON :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc +readJSON _ s = + case eitherDecode' . BL.fromStrict . UTF8.fromText + . sourcesToText . toSources $ s of Right doc -> return doc - Left e -> throwError $ PandocParseError ("JSON parse error: " <> T.pack e) + Left e -> throwError $ PandocParseError ("JSON parse error: " + <> T.pack e) diff --git a/src/Text/Pandoc/Readers/BibTeX.hs b/src/Text/Pandoc/Readers/BibTeX.hs index b82a81350..318afda85 100644 --- a/src/Text/Pandoc/Readers/BibTeX.hs +++ b/src/Text/Pandoc/Readers/BibTeX.hs @@ -23,30 +23,33 @@ where import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Builder (setMeta, cite, str) -import Data.Text (Text) import Citeproc (Lang(..), parseLang) import Citeproc.Locale (getLocale) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Class (PandocMonad, lookupEnv) import Text.Pandoc.Citeproc.BibTeX as BibTeX import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue) +import Text.Pandoc.Sources (ToSources(..)) import Control.Monad.Except (throwError) -- | Read BibTeX from an input string and return a Pandoc document. -- The document will have only metadata, with an empty body. -- The metadata will contain a `references` field with the -- bibliography entries, and a `nocite` field with the wildcard `[@*]`. -readBibTeX :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readBibTeX :: (PandocMonad m, ToSources a) + => ReaderOptions -> a -> m Pandoc readBibTeX = readBibTeX' BibTeX.Bibtex -- | Read BibLaTeX from an input string and return a Pandoc document. -- The document will have only metadata, with an empty body. -- The metadata will contain a `references` field with the -- bibliography entries, and a `nocite` field with the wildcard `[@*]`. -readBibLaTeX :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readBibLaTeX :: (PandocMonad m, ToSources a) + => ReaderOptions -> a -> m Pandoc readBibLaTeX = readBibTeX' BibTeX.Biblatex -readBibTeX' :: PandocMonad m => Variant -> ReaderOptions -> Text -> m Pandoc +readBibTeX' :: (PandocMonad m, ToSources a) + => Variant -> ReaderOptions -> a -> m Pandoc readBibTeX' variant _opts t = do mblangEnv <- lookupEnv "LANG" let defaultLang = Lang "en" Nothing (Just "US") [] [] [] @@ -60,7 +63,7 @@ readBibTeX' variant _opts t = do Left _ -> throwError $ PandocCiteprocError e Right l -> return l case BibTeX.readBibtexString variant locale (const True) t of - Left e -> throwError $ PandocParsecError t e + Left e -> throwError $ PandocParsecError (toSources t) e Right refs -> return $ setMeta "references" (map referenceToMetaValue refs) . setMeta "nocite" diff --git a/src/Text/Pandoc/Readers/CSV.hs b/src/Text/Pandoc/Readers/CSV.hs index 2958d6180..eca8f9425 100644 --- a/src/Text/Pandoc/Readers/CSV.hs +++ b/src/Text/Pandoc/Readers/CSV.hs @@ -13,23 +13,23 @@ Conversion from CSV to a 'Pandoc' table. -} module Text.Pandoc.Readers.CSV ( readCSV ) where -import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.CSV (parseCSV, defaultCSVOptions) import Text.Pandoc.Definition import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) -import Text.Pandoc.Shared (crFilter) import Text.Pandoc.Error +import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Text.Pandoc.Options (ReaderOptions) import Control.Monad.Except (throwError) -readCSV :: PandocMonad m +readCSV :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ Text to parse (assuming @'\n'@ line endings) + -> a -> m Pandoc -readCSV _opts s = - case parseCSV defaultCSVOptions (crFilter s) of +readCSV _opts s = do + let txt = sourcesToText $ toSources s + case parseCSV defaultCSVOptions txt of Right (r:rs) -> return $ B.doc $ B.table capt (zip aligns widths) (TableHead nullAttr hdrs) @@ -45,4 +45,4 @@ readCSV _opts s = aligns = replicate numcols AlignDefault widths = replicate numcols ColWidthDefault Right [] -> return $ B.doc mempty - Left e -> throwError $ PandocParsecError s e + Left e -> throwError $ PandocParsecError (toSources [("",txt)]) e diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 244f77940..b099a9b50 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -30,45 +30,55 @@ import Text.Pandoc.Readers.Metadata (yamlMetaBlock) import Control.Monad.Except import Data.Functor.Identity (runIdentity) import Data.Typeable -import Text.Pandoc.Parsing (runParserT, getPosition, sourceLine, - runF, defaultParserState, take1WhileP, option) +import Text.Pandoc.Parsing (runParserT, getPosition, + runF, defaultParserState, option, many1, anyChar, + Sources(..), ToSources(..), ParserT, Future, + sourceName) import qualified Data.Text as T -- | Parse a CommonMark formatted string into a 'Pandoc' structure. -readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readCommonMark :: (PandocMonad m, ToSources a) + => ReaderOptions -> a -> m Pandoc readCommonMark opts s - | isEnabled Ext_yaml_metadata_block opts - , "---" `T.isPrefixOf` s = do - let metaValueParser = do - inp <- option "" $ take1WhileP (const True) - case runIdentity - (commonmarkWith (specFor opts) "metadata value" inp) of - Left _ -> mzero - Right (Cm bls :: Cm () Blocks) - -> return $ return $ B.toMetaValue bls - res <- runParserT (do meta <- yamlMetaBlock metaValueParser - pos <- getPosition - return (meta, pos)) - defaultParserState "YAML metadata" s - case res of - Left _ -> readCommonMarkBody opts s - Right (meta, pos) -> do - let dropLines 0 = id - dropLines n = dropLines (n - 1) . T.drop 1 . T.dropWhile (/='\n') - let metaLines = sourceLine pos - 1 - let body = T.replicate metaLines "\n" <> dropLines metaLines s - Pandoc _ bs <- readCommonMarkBody opts body - return $ Pandoc (runF meta defaultParserState) bs - | otherwise = readCommonMarkBody opts s + | isEnabled Ext_yaml_metadata_block opts = do + let sources = toSources s + let toks = concatMap sourceToToks (unSources sources) + res <- runParserT (do meta <- yamlMetaBlock (metaValueParser opts) + pos <- getPosition + return (meta, pos)) + defaultParserState "YAML metadata" (toSources s) + case res of + Left _ -> readCommonMarkBody opts sources toks + Right (meta, pos) -> do + -- strip off metadata section and parse body + let body = dropWhile (\t -> tokPos t < pos) toks + Pandoc _ bs <- readCommonMarkBody opts sources body + return $ Pandoc (runF meta defaultParserState) bs + | otherwise = do + let sources = toSources s + let toks = concatMap sourceToToks (unSources sources) + readCommonMarkBody opts sources toks -readCommonMarkBody :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readCommonMarkBody opts s +sourceToToks :: (SourcePos, Text) -> [Tok] +sourceToToks (pos, s) = tokenize (sourceName pos) s + +metaValueParser :: Monad m + => ReaderOptions -> ParserT Sources st m (Future st MetaValue) +metaValueParser opts = do + inp <- option "" $ T.pack <$> many1 anyChar + let toks = concatMap sourceToToks (unSources (toSources inp)) + case runIdentity (parseCommonmarkWith (specFor opts) toks) of + Left _ -> mzero + Right (Cm bls :: Cm () Blocks) -> return $ return $ B.toMetaValue bls + +readCommonMarkBody :: PandocMonad m => ReaderOptions -> Sources -> [Tok] -> m Pandoc +readCommonMarkBody opts s toks | isEnabled Ext_sourcepos opts = - case runIdentity (commonmarkWith (specFor opts) "" s) of + case runIdentity (parseCommonmarkWith (specFor opts) toks) of Left err -> throwError $ PandocParsecError s err Right (Cm bls :: Cm SourceRange Blocks) -> return $ B.doc bls | otherwise = - case runIdentity (commonmarkWith (specFor opts) "" s) of + case runIdentity (parseCommonmarkWith (specFor opts) toks) of Left err -> throwError $ PandocParsecError s err Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index 2658dfea2..ad848ada7 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -23,21 +23,20 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed) -import Text.Pandoc.Shared (crFilter) - -- | Read creole from an input string and return a Pandoc document. -readCreole :: PandocMonad m +readCreole :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readCreole opts s = do - res <- readWithM parseCreole def{ stateOptions = opts } $ crFilter s <> "\n\n" + let sources = ensureFinalNewlines 2 (toSources s) + res <- readWithM parseCreole def{ stateOptions = opts } sources case res of Left e -> throwError e Right d -> return d -type CRLParser = ParserT Text ParserState +type CRLParser = ParserT Sources ParserState -- -- Utility functions diff --git a/src/Text/Pandoc/Readers/CslJson.hs b/src/Text/Pandoc/Readers/CslJson.hs index 30bb19483..a0af5c325 100644 --- a/src/Text/Pandoc/Readers/CslJson.hs +++ b/src/Text/Pandoc/Readers/CslJson.hs @@ -24,21 +24,22 @@ import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Builder (setMeta, cite, str) import qualified Text.Pandoc.UTF8 as UTF8 -import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences) import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue) import Control.Monad.Except (throwError) +import Text.Pandoc.Sources (ToSources(..), sourcesToText) -- | Read CSL JSON from an input string and return a Pandoc document. -- The document will have only metadata, with an empty body. -- The metadata will contain a `references` field with the -- bibliography entries, and a `nocite` field with the wildcard `[@*]`. -readCslJson :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readCslJson _opts t = - case cslJsonToReferences (UTF8.fromText t) of +readCslJson :: (PandocMonad m, ToSources a) + => ReaderOptions -> a -> m Pandoc +readCslJson _opts x = + case cslJsonToReferences (UTF8.fromText $ sourcesToText $ toSources x) of Left e -> throwError $ PandocParseError $ T.pack e Right refs -> return $ setMeta "references" (map referenceToMetaValue refs) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index ac3caa2c0..3db459cfd 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -30,7 +30,8 @@ import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Options import Text.Pandoc.Logging (LogMessage(..)) -import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) +import Text.Pandoc.Shared (safeRead, extractSpaces) +import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Text.TeXMath (readMathML, writeTeX) import Text.Pandoc.XML.Light @@ -539,11 +540,15 @@ instance Default DBState where , dbContent = [] } -readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readDocBook :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc readDocBook _ inp = do + let sources = toSources inp tree <- either (throwError . PandocXMLError "") return $ parseXMLContents - (TL.fromStrict . handleInstructions $ crFilter inp) + (TL.fromStrict . handleInstructions . sourcesToText $ sources) (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree return $ Pandoc (dbMeta st') (toList . mconcat $ bs) diff --git a/src/Text/Pandoc/Readers/DokuWiki.hs b/src/Text/Pandoc/Readers/DokuWiki.hs index dedc1f03f..db98ac8de 100644 --- a/src/Text/Pandoc/Readers/DokuWiki.hs +++ b/src/Text/Pandoc/Readers/DokuWiki.hs @@ -29,26 +29,27 @@ import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (PandocParsecError)) import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, nested) -import Text.Pandoc.Shared (crFilter, trim, stringify, tshow) +import Text.Pandoc.Shared (trim, stringify, tshow) -- | Read DokuWiki from an input string and return a Pandoc document. -readDokuWiki :: PandocMonad m +readDokuWiki :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readDokuWiki opts s = do - let input = crFilter s - res <- runParserT parseDokuWiki def {stateOptions = opts } "source" input + let sources = toSources s + res <- runParserT parseDokuWiki def {stateOptions = opts } + (initialSourceName sources) sources case res of - Left e -> throwError $ PandocParsecError input e + Left e -> throwError $ PandocParsecError sources e Right d -> return d -type DWParser = ParserT Text ParserState +type DWParser = ParserT Sources ParserState -- * Utility functions -- | Parse end-of-line, which can be either a newline or end-of-file. -eol :: Stream s m Char => ParserT s st m () +eol :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () eol = void newline <|> eof nested :: PandocMonad m => DWParser m a -> DWParser m a diff --git a/src/Text/Pandoc/Readers/FB2.hs b/src/Text/Pandoc/Readers/FB2.hs index 66e390bd7..84e5278db 100644 --- a/src/Text/Pandoc/Readers/FB2.hs +++ b/src/Text/Pandoc/Readers/FB2.hs @@ -40,9 +40,9 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad, insertMedia, report) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Shared (crFilter) import Text.Pandoc.XML.Light import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Sources (ToSources(..), sourcesToText) type FB2 m = StateT FB2State m @@ -63,9 +63,12 @@ instance HasMeta FB2State where setMeta field v s = s {fb2Meta = setMeta field v (fb2Meta s)} deleteMeta field s = s {fb2Meta = deleteMeta field (fb2Meta s)} -readFB2 :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readFB2 :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc readFB2 _ inp = - case parseXMLElement $ TL.fromStrict $ crFilter inp of + case parseXMLElement $ TL.fromStrict $ sourcesToText $ toSources inp of Left msg -> throwError $ PandocXMLError "" msg Right el -> do (bs, st) <- runStateT (parseRootElement el) def diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index c3e68afd8..f5c8a2277 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -62,21 +62,21 @@ import Text.Pandoc.Options ( extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Shared ( - addMetaField, blocksToInlines', crFilter, escapeURI, extractSpaces, + addMetaField, blocksToInlines', escapeURI, extractSpaces, htmlSpanLikeElements, renderTags', safeRead, tshow) import Text.Pandoc.Walk import Text.Parsec.Error import Text.TeXMath (readMathML, writeTeX) -- | Convert HTML-formatted string to 'Pandoc' document. -readHtml :: PandocMonad m +readHtml :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assumes @'\n'@ line endings) + -> a -- ^ Input to parse -> m Pandoc readHtml opts inp = do let tags = stripPrefixes $ canonicalizeTags $ parseTagsOptions parseOptions{ optTagPosition = True } - (crFilter inp) + (sourcesToText $ toSources inp) parseDoc = do blocks <- fixPlains False . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState @@ -830,17 +830,19 @@ pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline pTagText :: PandocMonad m => TagParser m Inlines pTagText = try $ do + pos <- getPosition (TagText str) <- pSatisfy isTagText st <- getState qu <- ask parsed <- lift $ lift $ - flip runReaderT qu $ runParserT (many pTagContents) st "text" str + flip runReaderT qu $ runParserT (many pTagContents) st "text" + (Sources [(pos, str)]) case parsed of Left _ -> throwError $ PandocParseError $ "Could not parse `" <> str <> "'" Right result -> return $ mconcat result -type InlinesParser m = HTMLParser m Text +type InlinesParser m = HTMLParser m Sources pTagContents :: PandocMonad m => InlinesParser m Inlines pTagContents = @@ -970,13 +972,14 @@ isCommentTag = tagComment (const True) -- | Matches a stretch of HTML in balanced tags. htmlInBalanced :: Monad m => (Tag Text -> Bool) - -> ParserT Text st m Text + -> ParserT Sources st m Text htmlInBalanced f = try $ do lookAhead (char '<') - inp <- getInput - let ts = canonicalizeTags $ - parseTagsOptions parseOptions{ optTagWarning = True, - optTagPosition = True } inp + sources <- getInput + let ts = canonicalizeTags + $ parseTagsOptions parseOptions{ optTagWarning = True, + optTagPosition = True } + $ sourcesToText sources case ts of (TagPosition sr sc : t@(TagOpen tn _) : rest) -> do guard $ f t @@ -1018,15 +1021,17 @@ hasTagWarning _ = False -- | Matches a tag meeting a certain condition. htmlTag :: (HasReaderOptions st, Monad m) => (Tag Text -> Bool) - -> ParserT Text st m (Tag Text, Text) + -> ParserT Sources st m (Tag Text, Text) htmlTag f = try $ do lookAhead (char '<') startpos <- getPosition - inp <- getInput + sources <- getInput + let inp = sourcesToText sources let ts = canonicalizeTags $ parseTagsOptions parseOptions{ optTagWarning = False , optTagPosition = True } - (inp <> " ") -- add space to ensure that + (inp <> " ") + -- add space to ensure that -- we get a TagPosition after the tag (next, ln, col) <- case ts of (TagPosition{} : next : TagPosition ln col : _) diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 48454e353..35eaac0a9 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -19,7 +19,7 @@ import Control.Monad.Except (throwError) import Data.List (intersperse) import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) -import Data.Text (Text, unpack) +import Data.Text (unpack) import qualified Data.Text as T import Documentation.Haddock.Parser import Documentation.Haddock.Types as H @@ -29,15 +29,17 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options -import Text.Pandoc.Shared (crFilter, splitTextBy, trim) +import Text.Pandoc.Sources (ToSources(..), sourcesToText) +import Text.Pandoc.Shared (splitTextBy, trim) -- | Parse Haddock markup and return a 'Pandoc' document. -readHaddock :: PandocMonad m +readHaddock :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc -readHaddock opts s = case readHaddockEither opts (unpack (crFilter s)) of +readHaddock opts s = case readHaddockEither opts + (unpack . sourcesToText . toSources $ s) of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Ipynb.hs b/src/Text/Pandoc/Readers/Ipynb.hs index 70296bb6b..cd1093109 100644 --- a/src/Text/Pandoc/Readers/Ipynb.hs +++ b/src/Text/Pandoc/Readers/Ipynb.hs @@ -39,10 +39,12 @@ import Data.Aeson as Aeson import Control.Monad.Except (throwError) import Text.Pandoc.Readers.Markdown (readMarkdown) import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Sources (ToSources(..), sourcesToText) -readIpynb :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readIpynb opts t = do - let src = BL.fromStrict (TE.encodeUtf8 t) +readIpynb :: (PandocMonad m, ToSources a) + => ReaderOptions -> a -> m Pandoc +readIpynb opts x = do + let src = BL.fromStrict . TE.encodeUtf8 . sourcesToText $ toSources x case eitherDecode src of Right (notebook4 :: Notebook NbV4) -> notebookToPandoc opts notebook4 Left _ -> diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index c068f3774..9cdbf1611 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -29,11 +29,12 @@ import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options -import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces) +import Text.Pandoc.Shared (safeRead, extractSpaces) import Text.TeXMath (readMathML, writeTeX) import Text.Pandoc.XML.Light import qualified Data.Set as S (fromList, member) import Data.Set ((\\)) +import Text.Pandoc.Sources (ToSources(..), sourcesToText) type JATS m = StateT JATSState m @@ -52,10 +53,14 @@ instance Default JATSState where , jatsContent = [] } -readJATS :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readJATS :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc readJATS _ inp = do + let sources = toSources inp tree <- either (throwError . PandocXMLError "") return $ - parseXMLContents (TL.fromStrict $ crFilter inp) + parseXMLContents (TL.fromStrict . sourcesToText $ sources) (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree return $ Pandoc (jatsMeta st') (toList . mconcat $ bs) diff --git a/src/Text/Pandoc/Readers/Jira.hs b/src/Text/Pandoc/Readers/Jira.hs index 89aecbf56..a3b415f09 100644 --- a/src/Text/Pandoc/Readers/Jira.hs +++ b/src/Text/Pandoc/Readers/Jira.hs @@ -20,18 +20,20 @@ import Text.Pandoc.Builder hiding (cell) import Text.Pandoc.Error (PandocError (PandocParseError)) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Shared (stringify) - +import Text.Pandoc.Sources (ToSources(..), sourcesToText) import qualified Text.Jira.Markup as Jira -- | Read Jira wiki markup. -readJira :: PandocMonad m +readJira :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc -readJira _opts s = case parse s of - Right d -> return $ jiraToPandoc d - Left e -> throwError . PandocParseError $ - "Jira parse error" `append` pack (show e) +readJira _opts inp = do + let sources = toSources inp + case parse (sourcesToText sources) of + Right d -> return $ jiraToPandoc d + Left e -> throwError . PandocParseError $ + "Jira parse error" `append` pack (show e) jiraToPandoc :: Jira.Doc -> Pandoc jiraToPandoc (Jira.Doc blks) = doc $ foldMap jiraToPandocBlocks blks diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9ad168293..f90d562ae 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -77,16 +77,17 @@ import Data.List.NonEmpty (nonEmpty) -- import Debug.Trace (traceShowId) -- | Parse LaTeX from string and return 'Pandoc' document. -readLaTeX :: PandocMonad m +readLaTeX :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assumes @'\n'@ line endings) + -> a -- ^ Input to parse -> m Pandoc readLaTeX opts ltx = do + let sources = toSources ltx parsed <- runParserT parseLaTeX def{ sOptions = opts } "source" - (tokenize "source" (crFilter ltx)) + (tokenizeSources sources) case parsed of Right result -> return result - Left e -> throwError $ PandocParsecError ltx e + Left e -> throwError $ PandocParsecError sources e parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do @@ -132,11 +133,11 @@ resolveRefs _ x = x rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => ParserT Text s m Text + => ParserT Sources s m Text rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) inp <- getInput - let toks = tokenize "source" inp + let toks = tokenizeSources inp snd <$> (rawLaTeXParser toks False (macroDef (const mempty)) blocks <|> rawLaTeXParser toks True (do choice (map controlSeq @@ -163,11 +164,11 @@ beginOrEndCommand = try $ do (txt <> untokenize rawargs) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => ParserT Text s m Text + => ParserT Sources s m Text rawLaTeXInline = do lookAhead (try (char '\\' >> letter)) inp <- getInput - let toks = tokenize "source" inp + let toks = tokenizeSources inp raw <- snd <$> ( rawLaTeXParser toks True (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced)) @@ -178,11 +179,11 @@ rawLaTeXInline = do finalbraces <- mconcat <$> many (try (string "{}")) -- see #5439 return $ raw <> T.pack finalbraces -inlineCommand :: PandocMonad m => ParserT Text ParserState m Inlines +inlineCommand :: PandocMonad m => ParserT Sources ParserState m Inlines inlineCommand = do lookAhead (try (char '\\' >> letter)) inp <- getInput - let toks = tokenize "source" inp + let toks = tokenizeSources inp fst <$> rawLaTeXParser toks True (inlineEnvironment <|> inlineCommand') inlines @@ -641,7 +642,7 @@ opt = do parsed <- runParserT (mconcat <$> many inline) st "bracketed option" toks case parsed of Right result -> return result - Left e -> throwError $ PandocParsecError (untokenize toks) e + Left e -> throwError $ PandocParsecError (toSources toks) e -- block elements: diff --git a/src/Text/Pandoc/Readers/LaTeX/Citation.hs b/src/Text/Pandoc/Readers/LaTeX/Citation.hs index 655823dab..af97125c6 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Citation.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Citation.hs @@ -120,7 +120,7 @@ simpleCiteArgs inline = try $ do runParserT (mconcat <$> many inline) st "bracketed option" toks case parsed of Right result -> return result - Left e -> throwError $ PandocParsecError (untokenize toks) e + Left e -> throwError $ PandocParsecError (toSources toks) e diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index db58b333d..35ce3509d 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -27,6 +27,7 @@ module Text.Pandoc.Readers.LaTeX.Parsing , rawLaTeXParser , applyMacros , tokenize + , tokenizeSources , untokenize , untoken , totoks @@ -248,7 +249,7 @@ withVerbatimMode parser = do rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) => [Tok] -> Bool -> LP m a -> LP m a - -> ParserT Text s m (a, Text) + -> ParserT Sources s m (a, Text) rawLaTeXParser toks retokenize parser valParser = do pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate } @@ -268,7 +269,7 @@ rawLaTeXParser toks retokenize parser valParser = do Left _ -> mzero Right ((val, raw), st) -> do updateState (updateMacros (sMacros st <>)) - _ <- takeP (T.length (untokenize toks')) + void $ count (T.length (untokenize toks')) anyChar let result = untokenize raw -- ensure we end with space if input did, see #4442 let result' = @@ -281,7 +282,7 @@ rawLaTeXParser toks retokenize parser valParser = do return (val, result') applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => Text -> ParserT Text s m Text + => Text -> ParserT Sources s m Text applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> do let retokenize = untokenize <$> many (satisfyTok (const True)) pstate <- getState @@ -301,6 +302,11 @@ QuickCheck property: > let t = T.pack s in untokenize (tokenize "random" t) == t -} +tokenizeSources :: Sources -> [Tok] +tokenizeSources = concatMap tokenizeSource . unSources + where + tokenizeSource (pos, t) = totoks pos t + tokenize :: SourceName -> Text -> [Tok] tokenize sourcename = totoks (initialPos sourcename) diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs index f8c214318..c20b72bc5 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Readers.LaTeX.Types Copyright : Copyright (C) 2017-2021 John MacFarlane @@ -18,7 +19,9 @@ module Text.Pandoc.Readers.LaTeX.Types ( Tok(..) ) where import Data.Text (Text) -import Text.Parsec.Pos (SourcePos) +import Text.Parsec.Pos (SourcePos, sourceName) +import Text.Pandoc.Sources +import Data.List (groupBy) data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | Esc1 | Esc2 | Arg Int @@ -27,6 +30,16 @@ data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | data Tok = Tok SourcePos TokType Text deriving (Eq, Ord, Show) +instance ToSources [Tok] where + toSources = Sources + . map (\ts -> case ts of + Tok p _ _ : _ -> (p, mconcat $ map tokToText ts) + _ -> error "toSources [Tok] encountered empty group") + . groupBy (\(Tok p1 _ _) (Tok p2 _ _) -> sourceName p1 == sourceName p2) + +tokToText :: Tok -> Text +tokToText (Tok _ _ t) = t + data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed deriving (Eq, Ord, Show) diff --git a/src/Text/Pandoc/Readers/Man.hs b/src/Text/Pandoc/Readers/Man.hs index 21b8feaab..1141af66f 100644 --- a/src/Text/Pandoc/Readers/Man.hs +++ b/src/Text/Pandoc/Readers/Man.hs @@ -20,7 +20,7 @@ import Control.Monad (liftM, mzero, guard, void) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError) import Data.Maybe (catMaybes, isJust) -import Data.List (intersperse, intercalate) +import Data.List (intersperse) import qualified Data.Text as T import Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad(..), report) @@ -29,9 +29,8 @@ import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Walk (query) -import Text.Pandoc.Shared (crFilter, mapLeft) +import Text.Pandoc.Shared (mapLeft) import Text.Pandoc.Readers.Roff -- TODO explicit imports -import Text.Parsec hiding (tokenPrim) import qualified Text.Parsec as Parsec import Text.Parsec.Pos (updatePosString) import qualified Data.Foldable as Foldable @@ -50,13 +49,20 @@ type ManParser m = ParserT [RoffToken] ManState m -- | Read man (troff) from an input string and return a Pandoc document. -readMan :: PandocMonad m => ReaderOptions -> T.Text -> m Pandoc -readMan opts txt = do - tokenz <- lexRoff (initialPos "input") (crFilter txt) +readMan :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc +readMan opts s = do + let Sources inps = toSources s + tokenz <- mconcat <$> mapM (uncurry lexRoff) inps let state = def {readerOptions = opts} :: ManState + let fixError (PandocParsecError _ e) = PandocParsecError (Sources inps) e + fixError e = e eitherdoc <- readWithMTokens parseMan state (Foldable.toList . unRoffTokens $ tokenz) - either throwError return eitherdoc + either (throwError . fixError) return eitherdoc + readWithMTokens :: PandocMonad m => ParserT [RoffToken] ManState m a -- ^ parser @@ -64,9 +70,10 @@ readWithMTokens :: PandocMonad m -> [RoffToken] -- ^ input -> m (Either PandocError a) readWithMTokens parser state input = - let leftF = PandocParsecError . T.pack . intercalate "\n" $ show <$> input + let leftF = PandocParsecError mempty in mapLeft leftF `liftM` runParserT parser state "source" input + parseMan :: PandocMonad m => ManParser m Pandoc parseMan = do bs <- many parseBlock <* eof @@ -89,7 +96,7 @@ parseBlock = choice [ parseList parseTable :: PandocMonad m => ManParser m Blocks parseTable = do - modifyState $ \st -> st { tableCellsPlain = True } + updateState $ \st -> st { tableCellsPlain = True } let isTbl Tbl{} = True isTbl _ = False Tbl _opts rows pos <- msatisfy isTbl @@ -135,7 +142,7 @@ parseTable = do case res' of Left _ -> Prelude.fail "Could not parse table cell" Right x -> do - modifyState $ \s -> s{ tableCellsPlain = False } + updateState $ \s -> s{ tableCellsPlain = False } return x Right x -> return x @@ -222,7 +229,7 @@ parseTitle = do setMeta "section" (linePartsToInlines y) [x] -> setMeta "title" (linePartsToInlines x) [] -> id - modifyState $ \st -> st{ metadata = adjustMeta $ metadata st } + updateState $ \st -> st{ metadata = adjustMeta $ metadata st } return mempty linePartsToInlines :: [LinePart] -> Inlines diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index ba8ed147e..69dd51bc4 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -47,19 +47,20 @@ import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared import Text.Pandoc.XML (fromEntities) import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock) +-- import Debug.Trace (traceShowId) -type MarkdownParser m = ParserT Text ParserState m +type MarkdownParser m = ParserT Sources ParserState m type F = Future ParserState -- | Read markdown from an input string and return a Pandoc document. -readMarkdown :: PandocMonad m +readMarkdown :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assuming @'\n'@ line endings) + -> a -- ^ Input -> m Pandoc readMarkdown opts s = do parsed <- readWithM parseMarkdown def{ stateOptions = opts } - (crFilter s <> "\n\n") + (ensureFinalNewlines 3 (toSources s)) case parsed of Right result -> return result Left e -> throwError e @@ -80,7 +81,7 @@ yamlToMeta opts mbfp bstr = do meta <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks) bstr setPosition oldPos return $ runF meta defaultParserState - parsed <- readWithM parser def{ stateOptions = opts } "" + parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text) case parsed of Right result -> return result Left e -> throwError e @@ -103,7 +104,7 @@ yamlToRefs idpred opts mbfp bstr = do refs <- yamlBsToRefs (fmap B.toMetaValue <$> parseBlocks) idpred bstr setPosition oldPos return $ runF refs defaultParserState - parsed <- readWithM parser def{ stateOptions = opts } "" + parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text) case parsed of Right result -> return result Left e -> throwError e @@ -146,14 +147,14 @@ inList = do ctx <- stateParserContext <$> getState guard (ctx == ListItemState) -spnl :: PandocMonad m => ParserT Text st m () +spnl :: PandocMonad m => ParserT Sources st m () spnl = try $ do skipSpaces optional newline skipSpaces notFollowedBy (char '\n') -spnl' :: PandocMonad m => ParserT Text st m Text +spnl' :: PandocMonad m => ParserT Sources st m Text spnl' = try $ do xs <- many spaceChar ys <- option "" $ try $ (:) <$> newline @@ -568,7 +569,7 @@ registerImplicitHeader raw attr@(ident, _, _) -- hrule block -- -hrule :: PandocMonad m => ParserT Text st m (F Blocks) +hrule :: PandocMonad m => ParserT Sources st m (F Blocks) hrule = try $ do skipSpaces start <- satisfy isHruleChar @@ -588,7 +589,7 @@ indentedLine = indentSpaces >> anyLineNewline blockDelimiter :: PandocMonad m => (Char -> Bool) -> Maybe Int - -> ParserT Text ParserState m Int + -> ParserT Sources ParserState m Int blockDelimiter f len = try $ do skipNonindentSpaces c <- lookAhead (satisfy f) @@ -732,7 +733,7 @@ lhsCodeBlockBirdWith c = try $ do blanklines return $ T.intercalate "\n" lns' -birdTrackLine :: PandocMonad m => Char -> ParserT Text st m Text +birdTrackLine :: PandocMonad m => Char -> ParserT Sources st m Text birdTrackLine c = try $ do char c -- allow html tags on left margin: @@ -1025,7 +1026,7 @@ para = try $ do option (B.plain <$> result) $ try $ do newline - (blanklines >> return mempty) + (mempty <$ blanklines) <|> (guardDisabled Ext_blank_before_blockquote >> () <$ lookAhead blockQuote) <|> (guardEnabled Ext_backtick_code_blocks >> () <$ lookAhead codeBlockFenced) <|> (guardDisabled Ext_blank_before_header >> () <$ lookAhead header) @@ -1170,7 +1171,7 @@ lineBlock = do -- and the length including trailing space. dashedLine :: PandocMonad m => Char - -> ParserT Text st m (Int, Int) + -> ParserT Sources st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many spaceChar @@ -1239,7 +1240,7 @@ rawTableLine :: PandocMonad m -> MarkdownParser m [Text] rawTableLine indices = do notFollowedBy' (blanklines' <|> tableFooter) - line <- take1WhileP (/='\n') <* newline + line <- anyLine return $ map trim $ tail $ splitTextByIndices (init indices) line @@ -1390,7 +1391,7 @@ pipeTableCell = return $ B.plain <$> result) <|> return mempty -pipeTableHeaderPart :: PandocMonad m => ParserT Text st m (Alignment, Int) +pipeTableHeaderPart :: PandocMonad m => ParserT Sources st m (Alignment, Int) pipeTableHeaderPart = try $ do skipMany spaceChar left <- optionMaybe (char ':') @@ -1406,10 +1407,14 @@ pipeTableHeaderPart = try $ do (Just _,Just _) -> AlignCenter, len) -- Succeed only if current line contains a pipe. -scanForPipe :: PandocMonad m => ParserT Text st m () +scanForPipe :: PandocMonad m => ParserT Sources st m () scanForPipe = do - inp <- getInput - case T.break (\c -> c == '\n' || c == '|') inp of + Sources inps <- getInput + let ln = case inps of + [] -> "" + ((_,t):(_,t'):_) | T.null t -> t' + ((_,t):_) -> t + case T.break (\c -> c == '\n' || c == '|') ln of (_, T.uncons -> Just ('|', _)) -> return () _ -> mzero @@ -1703,13 +1708,13 @@ whitespace = spaceChar >> return <$> (lb <|> regsp) "whitespace" where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak) regsp = skipMany spaceChar >> return B.space -nonEndline :: PandocMonad m => ParserT Text st m Char +nonEndline :: PandocMonad m => ParserT Sources st m Char nonEndline = satisfy (/='\n') str :: PandocMonad m => MarkdownParser m (F Inlines) str = do result <- mconcat <$> many1 - ( take1WhileP isAlphaNum + ( T.pack <$> (many1 alphaNum) <|> "." <$ try (char '.' <* notFollowedBy (char '.')) ) updateLastStrPos (do guardEnabled Ext_smart @@ -1962,7 +1967,7 @@ rawLaTeXInline' = do s <- rawLaTeXInline return $ return $ B.rawInline "tex" s -- "tex" because it might be context -rawConTeXtEnvironment :: PandocMonad m => ParserT Text st m Text +rawConTeXtEnvironment :: PandocMonad m => ParserT Sources st m Text rawConTeXtEnvironment = try $ do string "\\start" completion <- inBrackets (letter <|> digit <|> spaceChar) @@ -1971,7 +1976,7 @@ rawConTeXtEnvironment = try $ do (try $ string "\\stop" >> textStr completion) return $ "\\start" <> completion <> T.concat contents <> "\\stop" <> completion -inBrackets :: PandocMonad m => ParserT Text st m Char -> ParserT Text st m Text +inBrackets :: PandocMonad m => ParserT Sources st m Char -> ParserT Sources st m Text inBrackets parser = do char '[' contents <- manyChar parser diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 9f4d5e170..825e4a2eb 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -36,17 +36,18 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (nested) import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag) -import Text.Pandoc.Shared (crFilter, safeRead, stringify, stripTrailingNewlines, +import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim, splitTextBy, tshow) import Text.Pandoc.Walk (walk) import Text.Pandoc.XML (fromEntities) -- | Read mediawiki from an input string and return a Pandoc document. -readMediaWiki :: PandocMonad m - => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assuming @'\n'@ line endings) +readMediaWiki :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a -> m Pandoc readMediaWiki opts s = do + let sources = toSources s parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts , mwMaxNestingLevel = 4 , mwNextLinkNumber = 1 @@ -55,7 +56,7 @@ readMediaWiki opts s = do , mwLogMessages = [] , mwInTT = False } - (crFilter s <> "\n") + sources case parsed of Right result -> return result Left e -> throwError e @@ -69,7 +70,7 @@ data MWState = MWState { mwOptions :: ReaderOptions , mwInTT :: Bool } -type MWParser m = ParserT Text MWState m +type MWParser m = ParserT Sources MWState m instance HasReaderOptions MWState where extractReaderOptions = mwOptions diff --git a/src/Text/Pandoc/Readers/Metadata.hs b/src/Text/Pandoc/Readers/Metadata.hs index cb141cba5..bbcfe62ea 100644 --- a/src/Text/Pandoc/Readers/Metadata.hs +++ b/src/Text/Pandoc/Readers/Metadata.hs @@ -35,9 +35,9 @@ import qualified Data.Text.Lazy as TL import qualified Text.Pandoc.UTF8 as UTF8 yamlBsToMeta :: (PandocMonad m, HasLastStrPosition st) - => ParserT Text st m (Future st MetaValue) + => ParserT Sources st m (Future st MetaValue) -> BL.ByteString - -> ParserT Text st m (Future st Meta) + -> ParserT Sources st m (Future st Meta) yamlBsToMeta pMetaValue bstr = do case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of Right (YAML.Doc (YAML.Mapping _ _ o):_) @@ -67,10 +67,10 @@ lookupYAML _ _ = Nothing -- Returns filtered list of references. yamlBsToRefs :: (PandocMonad m, HasLastStrPosition st) - => ParserT Text st m (Future st MetaValue) + => ParserT Sources st m (Future st MetaValue) -> (Text -> Bool) -- ^ Filter for id -> BL.ByteString - -> ParserT Text st m (Future st [MetaValue]) + -> ParserT Sources st m (Future st [MetaValue]) yamlBsToRefs pMetaValue idpred bstr = case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of Right (YAML.Doc o@YAML.Mapping{}:_) @@ -108,9 +108,9 @@ nodeToKey (YAML.Scalar _ (YAML.SUnknown _ t)) = Just t nodeToKey _ = Nothing normalizeMetaValue :: (PandocMonad m, HasLastStrPosition st) - => ParserT Text st m (Future st MetaValue) + => ParserT Sources st m (Future st MetaValue) -> Text - -> ParserT Text st m (Future st MetaValue) + -> ParserT Sources st m (Future st MetaValue) normalizeMetaValue pMetaValue x = -- Note: a standard quoted or unquoted YAML value will -- not end in a newline, but a "block" set off with @@ -133,9 +133,9 @@ checkBoolean t | otherwise = Nothing yamlToMetaValue :: (PandocMonad m, HasLastStrPosition st) - => ParserT Text st m (Future st MetaValue) + => ParserT Sources st m (Future st MetaValue) -> YAML.Node YE.Pos - -> ParserT Text st m (Future st MetaValue) + -> ParserT Sources st m (Future st MetaValue) yamlToMetaValue pMetaValue (YAML.Scalar _ x) = case x of YAML.SStr t -> normalizeMetaValue pMetaValue t @@ -156,9 +156,9 @@ yamlToMetaValue pMetaValue (YAML.Mapping _ _ o) = yamlToMetaValue _ _ = return $ return $ MetaString "" yamlMap :: (PandocMonad m, HasLastStrPosition st) - => ParserT Text st m (Future st MetaValue) + => ParserT Sources st m (Future st MetaValue) -> M.Map (YAML.Node YE.Pos) (YAML.Node YE.Pos) - -> ParserT Text st m (Future st (M.Map Text MetaValue)) + -> ParserT Sources st m (Future st (M.Map Text MetaValue)) yamlMap pMetaValue o = do kvs <- forM (M.toList o) $ \(key, v) -> do k <- maybe (throwError $ PandocParseError @@ -177,8 +177,8 @@ yamlMap pMetaValue o = do -- | Parse a YAML metadata block using the supplied 'MetaValue' parser. yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m) - => ParserT Text st m (Future st MetaValue) - -> ParserT Text st m (Future st Meta) + => ParserT Sources st m (Future st MetaValue) + -> ParserT Sources st m (Future st Meta) yamlMetaBlock parser = try $ do string "---" blankline @@ -189,5 +189,5 @@ yamlMetaBlock parser = try $ do optional blanklines yamlBsToMeta parser $ UTF8.fromTextLazy $ TL.fromStrict rawYaml -stopLine :: Monad m => ParserT Text st m () +stopLine :: Monad m => ParserT Sources st m () stopLine = try $ (string "---" <|> string "...") >> blankline >> return () diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 698bfd3d7..a0d4534f1 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -37,18 +37,19 @@ import Text.Pandoc.Error (PandocError (PandocParsecError)) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing -import Text.Pandoc.Shared (crFilter, trimr, tshow) +import Text.Pandoc.Shared (trimr, tshow) -- | Read Muse from an input string and return a Pandoc document. -readMuse :: PandocMonad m +readMuse :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readMuse opts s = do - let input = crFilter s - res <- flip runReaderT def $ runParserT parseMuse def{ museOptions = opts } "source" input + let sources = toSources s + res <- flip runReaderT def $ runParserT parseMuse def{ museOptions = opts } + (initialSourceName sources) sources case res of - Left e -> throwError $ PandocParsecError input e + Left e -> throwError $ PandocParsecError sources e Right d -> return d type F = Future MuseState @@ -82,7 +83,7 @@ instance Default MuseEnv where , museInPara = False } -type MuseParser m = ParserT Text MuseState (ReaderT MuseEnv m) +type MuseParser m = ParserT Sources MuseState (ReaderT MuseEnv m) instance HasReaderOptions MuseState where extractReaderOptions = museOptions @@ -155,7 +156,7 @@ firstColumn = getPosition >>= \pos -> guard (sourceColumn pos == 1) -- * Parsers -- | Parse end-of-line, which can be either a newline or end-of-file. -eol :: Stream s m Char => ParserT s st m () +eol :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m () eol = void newline <|> eof getIndent :: PandocMonad m diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 9c8bc0374..58f235e81 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -21,6 +21,7 @@ import Control.Monad.Except (throwError) import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Error +import Text.Pandoc.Sources (ToSources(..), sourcesToText) -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, @@ -32,14 +33,15 @@ import Text.Pandoc.Error -- -- > Pandoc nullMeta [Plain [Str "hi"]] -- -readNative :: PandocMonad m +readNative :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text -- ^ String to parse (assuming @'\n'@ line endings) + -> a -> m Pandoc readNative _ s = - case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of - Right doc -> return doc - Left _ -> throwError $ PandocParseError "couldn't read native" + let t = sourcesToText . toSources $ s + in case maybe (Pandoc nullMeta <$> readBlocks t) Right (safeRead t) of + Right doc -> return doc + Left _ -> throwError $ PandocParseError "couldn't read native" readBlocks :: Text -> Either PandocError [Block] readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 5f2ddb876..668c9ca11 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -24,7 +24,8 @@ import Text.Pandoc.Options import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) -import Text.Pandoc.Shared (crFilter, blocksToInlines') +import Text.Pandoc.Shared (blocksToInlines') +import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Text.Pandoc.XML.Light import Control.Monad.Except (throwError) @@ -46,10 +47,14 @@ instance Default OPMLState where , opmlOptions = def } -readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readOPML :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc readOPML opts inp = do - (bs, st') <- runStateT - (case parseXMLContents (TL.fromStrict (crFilter inp)) of + let sources = toSources inp + (bs, st') <- + runStateT (case parseXMLContents (TL.fromStrict . sourcesToText $ sources) of Left msg -> throwError $ PandocXMLError "" msg Right ns -> mapM parseBlock ns) def{ opmlOptions = opts } diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index afeb27a87..8823befdd 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -18,22 +18,19 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing (reportLogMessages) -import Text.Pandoc.Shared (crFilter) - +import Text.Pandoc.Sources (ToSources(..), ensureFinalNewlines) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) -import Data.Text (Text) - -- | Parse org-mode string and return a Pandoc document. -readOrg :: PandocMonad m +readOrg :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assuming @'\n'@ line endings) + -> a -> m Pandoc readOrg opts s = do parsed <- flip runReaderT def $ readWithM parseOrg (optionsToParserState opts) - (crFilter s <> "\n\n") + (ensureFinalNewlines 2 (toSources s)) case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 519a6ce04..054f2611a 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -29,6 +29,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline) import Text.TeXMath (DisplayType (..), readTeX, writePandoc) +import Text.Pandoc.Sources (ToSources(..)) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap import Control.Monad (guard, mplus, mzero, unless, void, when) @@ -802,7 +803,7 @@ inlineLaTeX = try $ do parseAsInlineLaTeX :: PandocMonad m => Text -> TeXExport -> OrgParser m (Maybe Inlines) parseAsInlineLaTeX cs = \case - TeXExport -> maybeRight <$> runParserT inlineCommand state "" cs + TeXExport -> maybeRight <$> runParserT inlineCommand state "" (toSources cs) TeXIgnore -> return (Just mempty) TeXVerbatim -> return (Just $ B.str cs) diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 6ed24a602..c7ea02815 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -114,7 +114,7 @@ import Control.Monad (guard) import Control.Monad.Reader (ReaderT) -- | The parser used to read org files. -type OrgParser m = ParserT Text OrgParserState (ReaderT OrgParserLocal m) +type OrgParser m = ParserT Sources OrgParserState (ReaderT OrgParserLocal m) -- -- Adaptions and specializations of parsing utilities diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index ac4c0b6cb..a3fcf028c 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -38,25 +38,24 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Printf (printf) import Data.Time.Format -- TODO: -- [ ] .. parsed-literal -- | Parse reStructuredText string and return Pandoc document. -readRST :: PandocMonad m +readRST :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ Text to parse (assuming @'\n'@ line endings) + -> a -> m Pandoc readRST opts s = do parsed <- readWithM parseRST def{ stateOptions = opts } - (crFilter s <> "\n\n") + (ensureFinalNewlines 2 (toSources s)) case parsed of Right result -> return result Left e -> throwError e -type RSTParser m = ParserT Text ParserState m +type RSTParser m = ParserT Sources ParserState m -- -- Constants and data structure definitions @@ -151,11 +150,19 @@ parseRST = do startPos <- getPosition -- go through once just to get list of reference keys and notes -- docMinusKeys is the raw document with blanks where the keys were... - docMinusKeys <- T.concat <$> - manyTill (referenceKey <|> anchorDef <|> - noteBlock <|> citationBlock <|> - (snd <$> withRaw comment) <|> - headerBlock <|> lineClump) eof + let chunk = referenceKey + <|> anchorDef + <|> noteBlock + <|> citationBlock + <|> (snd <$> withRaw comment) + <|> headerBlock + <|> lineClump + docMinusKeys <- Sources <$> + manyTill (do pos <- getPosition + t <- chunk + return (pos, t)) eof + -- UGLY: we collapse source position information. + -- TODO: fix the parser to use the F monad instead of two passes setInput docMinusKeys setPosition startPos st' <- getState @@ -348,7 +355,7 @@ singleHeader' = try $ do -- hrule block -- -hrule :: Monad m => ParserT Text st m Blocks +hrule :: Monad m => ParserT Sources st m Blocks hrule = try $ do chr <- oneOf underlineChars count 3 (char chr) @@ -363,7 +370,7 @@ hrule = try $ do -- read a line indented by a given string indentedLine :: (HasReaderOptions st, Monad m) - => Int -> ParserT Text st m Text + => Int -> ParserT Sources st m Text indentedLine indents = try $ do lookAhead spaceChar gobbleAtMostSpaces indents @@ -372,7 +379,7 @@ indentedLine indents = try $ do -- one or more indented lines, possibly separated by blank lines. -- any amount of indentation will work. indentedBlock :: (HasReaderOptions st, Monad m) - => ParserT Text st m Text + => ParserT Sources st m Text indentedBlock = try $ do indents <- length <$> lookAhead (many1 spaceChar) lns <- many1 $ try $ do b <- option "" blanklines @@ -381,20 +388,20 @@ indentedBlock = try $ do optional blanklines return $ T.unlines lns -quotedBlock :: Monad m => ParserT Text st m Text +quotedBlock :: Monad m => ParserT Sources st m Text quotedBlock = try $ do quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" lns <- many1 $ lookAhead (char quote) >> anyLine optional blanklines return $ T.unlines lns -codeBlockStart :: Monad m => ParserT Text st m Char +codeBlockStart :: Monad m => ParserT Sources st m Char codeBlockStart = string "::" >> blankline >> blankline -codeBlock :: Monad m => ParserT Text ParserState m Blocks +codeBlock :: Monad m => ParserT Sources ParserState m Blocks codeBlock = try $ codeBlockStart >> codeBlockBody -codeBlockBody :: Monad m => ParserT Text ParserState m Blocks +codeBlockBody :: Monad m => ParserT Sources ParserState m Blocks codeBlockBody = do lang <- stateRstHighlight <$> getState try $ B.codeBlockWith ("", maybeToList lang, []) . stripTrailingNewlines <$> @@ -410,14 +417,14 @@ lhsCodeBlock = try $ do return $ B.codeBlockWith ("", ["haskell","literate"], []) $ T.intercalate "\n" lns -latexCodeBlock :: Monad m => ParserT Text st m [Text] +latexCodeBlock :: Monad m => ParserT Sources st m [Text] latexCodeBlock = try $ do try (latexBlockLine "\\begin{code}") many1Till anyLine (try $ latexBlockLine "\\end{code}") where latexBlockLine s = skipMany spaceChar >> string s >> blankline -birdCodeBlock :: Monad m => ParserT Text st m [Text] +birdCodeBlock :: Monad m => ParserT Sources st m [Text] birdCodeBlock = filterSpace <$> many1 birdTrackLine where filterSpace lns = -- if (as is normal) there is always a space after >, drop it @@ -425,7 +432,7 @@ birdCodeBlock = filterSpace <$> many1 birdTrackLine then map (T.drop 1) lns else lns -birdTrackLine :: Monad m => ParserT Text st m Text +birdTrackLine :: Monad m => ParserT Sources st m Text birdTrackLine = char '>' >> anyLine -- @@ -456,7 +463,6 @@ includeDirective top fields body = do let (startLine :: Maybe Int) = lookup "start-line" fields >>= safeRead let (endLine :: Maybe Int) = lookup "end-line" fields >>= safeRead oldPos <- getPosition - oldInput <- getInput containers <- stateContainers <$> getState when (f `elem` containers) $ throwError $ PandocParseError $ "Include file loop at " <> tshow oldPos @@ -494,15 +500,11 @@ includeDirective top fields body = do Nothing -> case lookup "literal" fields of Just _ -> return $ B.rawBlock "rst" contents' Nothing -> do - setPosition $ newPos (T.unpack f) 1 1 - setInput $ contents' <> "\n" - bs <- optional blanklines >> - (mconcat <$> many block) - setInput oldInput - setPosition oldPos + addToSources (initialPos (T.unpack f)) + (contents' <> "\n") updateState $ \s -> s{ stateContainers = tail $ stateContainers s } - return bs + return mempty -- @@ -526,7 +528,7 @@ definitionList :: PandocMonad m => RSTParser m Blocks definitionList = B.definitionList <$> many1 definitionListItem -- parses bullet list start and returns its length (inc. following whitespace) -bulletListStart :: Monad m => ParserT Text st m Int +bulletListStart :: Monad m => ParserT Sources st m Int bulletListStart = try $ do notFollowedBy' hrule -- because hrules start out just like lists marker <- oneOf bulletListMarkers @@ -1103,7 +1105,7 @@ quotedReferenceName = try $ do -- plus isolated (no two adjacent) internal hyphens, underscores, -- periods, colons and plus signs; no whitespace or other characters -- are allowed. -simpleReferenceName :: Monad m => ParserT Text st m Text +simpleReferenceName :: Monad m => ParserT Sources st m Text simpleReferenceName = do x <- alphaNum xs <- many $ alphaNum @@ -1122,7 +1124,7 @@ referenceKey = do -- return enough blanks to replace key return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n" -targetURI :: Monad m => ParserT Text st m Text +targetURI :: Monad m => ParserT Sources st m Text targetURI = do skipSpaces optional $ try $ newline >> notFollowedBy blankline @@ -1160,8 +1162,10 @@ anonymousKey :: Monad m => RSTParser m () anonymousKey = try $ do oneOfStrings [".. __:", "__"] src <- targetURI - pos <- getPosition - let key = toKey $ "_" <> T.pack (printf "%09d" (sourceLine pos)) + -- we need to ensure that the keys are ordered by occurrence in + -- the document. + numKeys <- M.size . stateKeys <$> getState + let key = toKey $ "_" <> T.pack (show numKeys) updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } @@ -1250,13 +1254,13 @@ headerBlock = do -- Grid tables TODO: -- - column spans -dashedLine :: Monad m => Char -> ParserT Text st m (Int, Int) +dashedLine :: Monad m => Char -> ParserT Sources st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many (char ' ') return (length dashes, length $ dashes ++ sp) -simpleDashedLines :: Monad m => Char -> ParserT Text st m [(Int,Int)] +simpleDashedLines :: Monad m => Char -> ParserT Sources st m [(Int,Int)] simpleDashedLines ch = try $ many1 (dashedLine ch) -- Parse a table row separator @@ -1382,7 +1386,7 @@ hyphens = do -- don't want to treat endline after hyphen or dash as a space return $ B.str result -escapedChar :: Monad m => ParserT Text st m Inlines +escapedChar :: Monad m => ParserT Sources st m Inlines escapedChar = do c <- escaped anyChar return $ if c == ' ' || c == '\n' || c == '\r' -- '\ ' is null in RST diff --git a/src/Text/Pandoc/Readers/Roff.hs b/src/Text/Pandoc/Readers/Roff.hs index 509ce1377..47f16ef4b 100644 --- a/src/Text/Pandoc/Readers/Roff.hs +++ b/src/Text/Pandoc/Readers/Roff.hs @@ -42,7 +42,6 @@ import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared (safeRead) -import Text.Parsec hiding (tokenPrim) import Text.Pandoc.RoffChar (characterCodes, combiningAccents) import qualified Data.Sequence as Seq import qualified Data.Foldable as Foldable @@ -122,16 +121,16 @@ instance Default RoffState where , afterConditional = False } -type RoffLexer m = ParserT T.Text RoffState m +type RoffLexer m = ParserT Sources RoffState m -- -- Lexer: T.Text -> RoffToken -- -eofline :: Stream s m Char => ParsecT s u m () +eofline :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s u m () eofline = void newline <|> eof <|> () <$ lookAhead (string "\\}") -spacetab :: Stream s m Char => ParsecT s u m Char +spacetab :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s u m Char spacetab = char ' ' <|> char '\t' characterCodeMap :: M.Map T.Text Char @@ -303,8 +302,7 @@ expandString = try $ do char '*' cs <- escapeArg <|> countChar 1 anyChar s <- linePartsToText <$> resolveText cs pos - getInput >>= setInput . (s <>) - return () + addToInput s -- Parses: '..' quoteArg :: PandocMonad m => RoffLexer m T.Text @@ -316,7 +314,7 @@ escFont = do font' <- if T.null font || font == "P" then prevFont <$> getState else return $ foldr processFontLetter defaultFontSpec $ T.unpack font - modifyState $ \st -> st{ prevFont = currentFont st + updateState $ \st -> st{ prevFont = currentFont st , currentFont = font' } return [Font font'] where @@ -372,8 +370,8 @@ lexTable pos = do spaces opts <- try tableOptions <|> [] <$ optional (char ';') case lookup "tab" opts of - Just (T.uncons -> Just (c, _)) -> modifyState $ \st -> st{ tableTabChar = c } - _ -> modifyState $ \st -> st{ tableTabChar = '\t' } + Just (T.uncons -> Just (c, _)) -> updateState $ \st -> st{ tableTabChar = c } + _ -> updateState $ \st -> st{ tableTabChar = '\t' } spaces skipMany lexComment spaces @@ -489,18 +487,18 @@ lexConditional mname = do ifPart <- do optional $ try $ char '\\' >> newline lexGroup - <|> do modifyState $ \s -> s{ afterConditional = True } + <|> do updateState $ \s -> s{ afterConditional = True } t <- manToken - modifyState $ \s -> s{ afterConditional = False } + updateState $ \s -> s{ afterConditional = False } return t case mbtest of Nothing -> do - putState st -- reset state, so we don't record macros in skipped section + setState st -- reset state, so we don't record macros in skipped section report $ SkippedContent (T.cons '.' mname) pos return mempty Just True -> return ifPart Just False -> do - putState st + setState st return mempty expression :: PandocMonad m => RoffLexer m (Maybe Bool) @@ -515,7 +513,7 @@ expression = do _ -> Nothing where returnValue v = do - modifyState $ \st -> st{ lastExpression = v } + updateState $ \st -> st{ lastExpression = v } return v lexGroup :: PandocMonad m => RoffLexer m RoffTokens @@ -536,7 +534,7 @@ lexIncludeFile args = do result <- readFileFromDirs dirs $ T.unpack fp case result of Nothing -> report $ CouldNotLoadIncludeFile fp pos - Just s -> getInput >>= setInput . (s <>) + Just s -> addToInput s return mempty [] -> return mempty @@ -564,13 +562,13 @@ lexStringDef args = do -- string definition (x:ys) -> do let ts = singleTok $ TextLine (intercalate [RoffStr " " ] ys) let stringName = linePartsToText x - modifyState $ \st -> + updateState $ \st -> st{ customMacros = M.insert stringName ts (customMacros st) } return mempty lexMacroDef :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens lexMacroDef args = do -- macro definition - modifyState $ \st -> st{ roffMode = CopyMode } + updateState $ \st -> st{ roffMode = CopyMode } (macroName, stopMacro) <- case args of (x : y : _) -> return (linePartsToText x, linePartsToText y) @@ -584,7 +582,7 @@ lexMacroDef args = do -- macro definition _ <- lexArgs return () ts <- mconcat <$> manyTill manToken stop - modifyState $ \st -> + updateState $ \st -> st{ customMacros = M.insert macroName ts (customMacros st) , roffMode = NormalMode } return mempty diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index c4d7bcc93..276d28aaa 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -28,22 +28,22 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) -import Text.Pandoc.Shared (crFilter, tshow) +import Text.Pandoc.Shared (tshow) import Text.Pandoc.XML (fromEntities) -- | Read twiki from an input string and return a Pandoc document. -readTWiki :: PandocMonad m +readTWiki :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readTWiki opts s = do - res <- readWithM parseTWiki def{ stateOptions = opts } - (crFilter s <> "\n\n") + let sources = ensureFinalNewlines 2 (toSources s) + res <- readWithM parseTWiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right d -> return d -type TWParser = ParserT Text ParserState +type TWParser = ParserT Sources ParserState -- -- utility functions diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 8d7900de4..981878206 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -53,30 +53,34 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) -import Text.Pandoc.Shared (crFilter, trim, tshow) +import Text.Pandoc.Shared (trim, tshow) -- | Parse a Textile text and return a Pandoc document. -readTextile :: PandocMonad m +readTextile :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options - -> Text -- ^ String to parse (assuming @'\n'@ line endings) + -> a -> m Pandoc readTextile opts s = do - parsed <- readWithM parseTextile def{ stateOptions = opts } - (crFilter s <> "\n\n") + let sources = ensureFinalNewlines 2 (toSources s) + parsed <- readWithM parseTextile def{ stateOptions = opts } sources case parsed of Right result -> return result Left e -> throwError e +type TextileParser = ParserT Sources ParserState -- | Generate a Pandoc ADT from a textile document -parseTextile :: PandocMonad m => ParserT Text ParserState m Pandoc +parseTextile :: PandocMonad m => TextileParser m Pandoc parseTextile = do many blankline startPos <- getPosition -- go through once just to get list of reference keys and notes -- docMinusKeys is the raw document with blanks where the keys/notes were... - let firstPassParser = noteBlock <|> lineClump - manyTill firstPassParser eof >>= setInput . T.concat + let firstPassParser = do + pos <- getPosition + t <- noteBlock <|> lineClump + return (pos, t) + manyTill firstPassParser eof >>= setInput . Sources setPosition startPos st' <- getState let reversedNotes = stateNotes st' @@ -84,10 +88,10 @@ parseTextile = do -- now parse it for real... Pandoc nullMeta . B.toList <$> parseBlocks -- FIXME -noteMarker :: PandocMonad m => ParserT Text ParserState m Text +noteMarker :: PandocMonad m => TextileParser m Text noteMarker = skipMany spaceChar >> string "fn" >> T.pack <$> manyTill digit (char '.') -noteBlock :: PandocMonad m => ParserT Text ParserState m Text +noteBlock :: PandocMonad m => TextileParser m Text noteBlock = try $ do startPos <- getPosition ref <- noteMarker @@ -102,11 +106,11 @@ noteBlock = try $ do return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n" -- | Parse document blocks -parseBlocks :: PandocMonad m => ParserT Text ParserState m Blocks +parseBlocks :: PandocMonad m => TextileParser m Blocks parseBlocks = mconcat <$> manyTill block eof -- | Block parsers list tried in definition order -blockParsers :: PandocMonad m => [ParserT Text ParserState m Blocks] +blockParsers :: PandocMonad m => [TextileParser m Blocks] blockParsers = [ codeBlock , header , blockQuote @@ -121,22 +125,22 @@ blockParsers = [ codeBlock ] -- | Any block in the order of definition of blockParsers -block :: PandocMonad m => ParserT Text ParserState m Blocks +block :: PandocMonad m => TextileParser m Blocks block = do res <- choice blockParsers "block" trace (T.take 60 $ tshow $ B.toList res) return res -commentBlock :: PandocMonad m => ParserT Text ParserState m Blocks +commentBlock :: PandocMonad m => TextileParser m Blocks commentBlock = try $ do string "###." manyTill anyLine blanklines return mempty -codeBlock :: PandocMonad m => ParserT Text ParserState m Blocks +codeBlock :: PandocMonad m => TextileParser m Blocks codeBlock = codeBlockTextile <|> codeBlockHtml -codeBlockTextile :: PandocMonad m => ParserT Text ParserState m Blocks +codeBlockTextile :: PandocMonad m => TextileParser m Blocks codeBlockTextile = try $ do string "bc." <|> string "pre." extended <- option False (True <$ char '.') @@ -156,7 +160,7 @@ trimTrailingNewlines :: Text -> Text trimTrailingNewlines = T.dropWhileEnd (=='\n') -- | Code Blocks in Textile are between
 and 
-codeBlockHtml :: PandocMonad m => ParserT Text ParserState m Blocks +codeBlockHtml :: PandocMonad m => TextileParser m Blocks codeBlockHtml = try $ do (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True)) result' <- T.pack <$> manyTill anyChar (htmlTag (tagClose (=="pre"))) @@ -174,7 +178,7 @@ codeBlockHtml = try $ do return $ B.codeBlockWith (ident,classes,kvs) result''' -- | Header of the form "hN. content" with N in 1..6 -header :: PandocMonad m => ParserT Text ParserState m Blocks +header :: PandocMonad m => TextileParser m Blocks header = try $ do char 'h' level <- digitToInt <$> oneOf "123456" @@ -186,14 +190,14 @@ header = try $ do return $ B.headerWith attr' level name -- | Blockquote of the form "bq. content" -blockQuote :: PandocMonad m => ParserT Text ParserState m Blocks +blockQuote :: PandocMonad m => TextileParser m Blocks blockQuote = try $ do string "bq" >> attributes >> char '.' >> whitespace B.blockQuote <$> para -- Horizontal rule -hrule :: PandocMonad m => ParserT Text st m Blocks +hrule :: PandocMonad m => TextileParser m Blocks hrule = try $ do skipSpaces start <- oneOf "-*" @@ -208,39 +212,39 @@ hrule = try $ do -- | Can be a bullet list or an ordered list. This implementation is -- strict in the nesting, sublist must start at exactly "parent depth -- plus one" -anyList :: PandocMonad m => ParserT Text ParserState m Blocks +anyList :: PandocMonad m => TextileParser m Blocks anyList = try $ anyListAtDepth 1 <* blanklines -- | This allow one type of list to be nested into an other type, -- provided correct nesting -anyListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks +anyListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks anyListAtDepth depth = choice [ bulletListAtDepth depth, orderedListAtDepth depth, definitionList ] -- | Bullet List of given depth, depth being the number of leading '*' -bulletListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks +bulletListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth) -- | Bullet List Item of given depth, depth being the number of -- leading '*' -bulletListItemAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks +bulletListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks bulletListItemAtDepth = genericListItemAtDepth '*' -- | Ordered List of given depth, depth being the number of -- leading '#' -orderedListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks +orderedListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks orderedListAtDepth depth = try $ do items <- many1 (orderedListItemAtDepth depth) return $ B.orderedList items -- | Ordered List Item of given depth, depth being the number of -- leading '#' -orderedListItemAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks +orderedListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks orderedListItemAtDepth = genericListItemAtDepth '#' -- | Common implementation of list items -genericListItemAtDepth :: PandocMonad m => Char -> Int -> ParserT Text ParserState m Blocks +genericListItemAtDepth :: PandocMonad m => Char -> Int -> TextileParser m Blocks genericListItemAtDepth c depth = try $ do count depth (char c) >> attributes >> whitespace contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|> @@ -250,25 +254,25 @@ genericListItemAtDepth c depth = try $ do return $ contents <> sublist -- | A definition list is a set of consecutive definition items -definitionList :: PandocMonad m => ParserT Text ParserState m Blocks +definitionList :: PandocMonad m => TextileParser m Blocks definitionList = try $ B.definitionList <$> many1 definitionListItem -- | List start character. -listStart :: PandocMonad m => ParserT Text ParserState m () +listStart :: PandocMonad m => TextileParser m () listStart = genericListStart '*' <|> () <$ genericListStart '#' <|> () <$ definitionListStart -genericListStart :: PandocMonad m => Char -> ParserT Text st m () +genericListStart :: PandocMonad m => Char -> TextileParser m () genericListStart c = () <$ try (many1 (char c) >> whitespace) -basicDLStart :: PandocMonad m => ParserT Text ParserState m () +basicDLStart :: PandocMonad m => TextileParser m () basicDLStart = do char '-' whitespace notFollowedBy newline -definitionListStart :: PandocMonad m => ParserT Text ParserState m Inlines +definitionListStart :: PandocMonad m => TextileParser m Inlines definitionListStart = try $ do basicDLStart trimInlines . mconcat <$> @@ -281,15 +285,15 @@ definitionListStart = try $ do -- the term defined, then spaces and ":=". The definition follows, on -- the same single line, or spaned on multiple line, after a line -- break. -definitionListItem :: PandocMonad m => ParserT Text ParserState m (Inlines, [Blocks]) +definitionListItem :: PandocMonad m => TextileParser m (Inlines, [Blocks]) definitionListItem = try $ do term <- mconcat . intersperse B.linebreak <$> many1 definitionListStart def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef) return (term, def') - where inlineDef :: PandocMonad m => ParserT Text ParserState m [Blocks] + where inlineDef :: PandocMonad m => TextileParser m [Blocks] inlineDef = liftM (\d -> [B.plain d]) $ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline - multilineDef :: PandocMonad m => ParserT Text ParserState m [Blocks] + multilineDef :: PandocMonad m => TextileParser m [Blocks] multilineDef = try $ do optional whitespace >> newline s <- T.pack <$> many1Till anyChar (try (string "=:" >> newline)) @@ -300,7 +304,7 @@ definitionListItem = try $ do -- raw content -- | A raw Html Block, optionally followed by blanklines -rawHtmlBlock :: PandocMonad m => ParserT Text ParserState m Blocks +rawHtmlBlock :: PandocMonad m => TextileParser m Blocks rawHtmlBlock = try $ do skipMany spaceChar (_,b) <- htmlTag isBlockTag @@ -308,14 +312,14 @@ rawHtmlBlock = try $ do return $ B.rawBlock "html" b -- | Raw block of LaTeX content -rawLaTeXBlock' :: PandocMonad m => ParserT Text ParserState m Blocks +rawLaTeXBlock' :: PandocMonad m => TextileParser m Blocks rawLaTeXBlock' = do guardEnabled Ext_raw_tex B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces) -- | In textile, paragraphs are separated by blank lines. -para :: PandocMonad m => ParserT Text ParserState m Blocks +para :: PandocMonad m => TextileParser m Blocks para = B.para . trimInlines . mconcat <$> many1 inline -- Tables @@ -326,7 +330,7 @@ toAlignment '>' = AlignRight toAlignment '=' = AlignCenter toAlignment _ = AlignDefault -cellAttributes :: PandocMonad m => ParserT Text ParserState m (Bool, Alignment) +cellAttributes :: PandocMonad m => TextileParser m (Bool, Alignment) cellAttributes = try $ do isHeader <- option False (True <$ char '_') -- we just ignore colspan and rowspan markers: @@ -339,7 +343,7 @@ cellAttributes = try $ do return (isHeader, alignment) -- | A table cell spans until a pipe | -tableCell :: PandocMonad m => ParserT Text ParserState m ((Bool, Alignment), Blocks) +tableCell :: PandocMonad m => TextileParser m ((Bool, Alignment), Blocks) tableCell = try $ do char '|' (isHeader, alignment) <- option (False, AlignDefault) cellAttributes @@ -350,7 +354,7 @@ tableCell = try $ do return ((isHeader, alignment), B.plain content) -- | A table row is made of many table cells -tableRow :: PandocMonad m => ParserT Text ParserState m [((Bool, Alignment), Blocks)] +tableRow :: PandocMonad m => TextileParser m [((Bool, Alignment), Blocks)] tableRow = try $ do -- skip optional row attributes optional $ try $ do @@ -360,7 +364,7 @@ tableRow = try $ do many1 tableCell <* char '|' <* blankline -- | A table with an optional header. -table :: PandocMonad m => ParserT Text ParserState m Blocks +table :: PandocMonad m => TextileParser m Blocks table = try $ do -- ignore table attributes caption <- option mempty $ try $ do @@ -388,7 +392,7 @@ table = try $ do (TableFoot nullAttr []) -- | Ignore markers for cols, thead, tfoot. -ignorableRow :: PandocMonad m => ParserT Text ParserState m () +ignorableRow :: PandocMonad m => TextileParser m () ignorableRow = try $ do char '|' oneOf ":^-~" @@ -397,7 +401,7 @@ ignorableRow = try $ do _ <- anyLine return () -explicitBlockStart :: PandocMonad m => Text -> ParserT Text ParserState m () +explicitBlockStart :: PandocMonad m => Text -> TextileParser m () explicitBlockStart name = try $ do string (T.unpack name) attributes @@ -409,8 +413,8 @@ explicitBlockStart name = try $ do -- However, they can be used to set HTML/CSS attributes when needed. maybeExplicitBlock :: PandocMonad m => Text -- ^ block tag name - -> ParserT Text ParserState m Blocks -- ^ implicit block - -> ParserT Text ParserState m Blocks + -> TextileParser m Blocks -- ^ implicit block + -> TextileParser m Blocks maybeExplicitBlock name blk = try $ do optional $ explicitBlockStart name blk @@ -423,11 +427,11 @@ maybeExplicitBlock name blk = try $ do -- | Any inline element -inline :: PandocMonad m => ParserT Text ParserState m Inlines +inline :: PandocMonad m => TextileParser m Inlines inline = choice inlineParsers "inline" -- | Inline parsers tried in order -inlineParsers :: PandocMonad m => [ParserT Text ParserState m Inlines] +inlineParsers :: PandocMonad m => [TextileParser m Inlines] inlineParsers = [ str , whitespace , endline @@ -447,7 +451,7 @@ inlineParsers = [ str ] -- | Inline markups -inlineMarkup :: PandocMonad m => ParserT Text ParserState m Inlines +inlineMarkup :: PandocMonad m => TextileParser m Inlines inlineMarkup = choice [ simpleInline (string "??") (B.cite []) , simpleInline (string "**") B.strong , simpleInline (string "__") B.emph @@ -461,29 +465,29 @@ inlineMarkup = choice [ simpleInline (string "??") (B.cite []) ] -- | Trademark, registered, copyright -mark :: PandocMonad m => ParserT Text st m Inlines +mark :: PandocMonad m => TextileParser m Inlines mark = try $ char '(' >> (try tm <|> try reg <|> copy) -reg :: PandocMonad m => ParserT Text st m Inlines +reg :: PandocMonad m => TextileParser m Inlines reg = do oneOf "Rr" char ')' return $ B.str "\174" -tm :: PandocMonad m => ParserT Text st m Inlines +tm :: PandocMonad m => TextileParser m Inlines tm = do oneOf "Tt" oneOf "Mm" char ')' return $ B.str "\8482" -copy :: PandocMonad m => ParserT Text st m Inlines +copy :: PandocMonad m => TextileParser m Inlines copy = do oneOf "Cc" char ')' return $ B.str "\169" -note :: PandocMonad m => ParserT Text ParserState m Inlines +note :: PandocMonad m => TextileParser m Inlines note = try $ do ref <- char '[' *> many1 digit <* char ']' notes <- stateNotes <$> getState @@ -507,13 +511,13 @@ wordBoundaries :: [Char] wordBoundaries = markupChars <> stringBreakers -- | Parse a hyphened sequence of words -hyphenedWords :: PandocMonad m => ParserT Text ParserState m Text +hyphenedWords :: PandocMonad m => TextileParser m Text hyphenedWords = do x <- wordChunk xs <- many (try $ char '-' >> wordChunk) return $ T.intercalate "-" (x:xs) -wordChunk :: PandocMonad m => ParserT Text ParserState m Text +wordChunk :: PandocMonad m => TextileParser m Text wordChunk = try $ do hd <- noneOf wordBoundaries tl <- many ( noneOf wordBoundaries <|> @@ -522,7 +526,7 @@ wordChunk = try $ do return $ T.pack $ hd:tl -- | Any string -str :: PandocMonad m => ParserT Text ParserState m Inlines +str :: PandocMonad m => TextileParser m Inlines str = do baseStr <- hyphenedWords -- RedCloth compliance : if parsed word is uppercase and immediately @@ -535,11 +539,11 @@ str = do return $ B.str fullStr -- | Some number of space chars -whitespace :: PandocMonad m => ParserT Text st m Inlines +whitespace :: PandocMonad m => TextileParser m Inlines whitespace = many1 spaceChar >> return B.space "whitespace" -- | In Textile, an isolated endline character is a line break -endline :: PandocMonad m => ParserT Text ParserState m Inlines +endline :: PandocMonad m => TextileParser m Inlines endline = try $ do newline notFollowedBy blankline @@ -547,18 +551,18 @@ endline = try $ do notFollowedBy rawHtmlBlock return B.linebreak -rawHtmlInline :: PandocMonad m => ParserT Text ParserState m Inlines +rawHtmlInline :: PandocMonad m => TextileParser m Inlines rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag -- | Raw LaTeX Inline -rawLaTeXInline' :: PandocMonad m => ParserT Text ParserState m Inlines +rawLaTeXInline' :: PandocMonad m => TextileParser m Inlines rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex B.rawInline "latex" <$> rawLaTeXInline -- | Textile standard link syntax is "label":target. But we -- can also have ["label":target]. -link :: PandocMonad m => ParserT Text ParserState m Inlines +link :: PandocMonad m => TextileParser m Inlines link = try $ do bracketed <- (True <$ char '[') <|> return False char '"' *> notFollowedBy (oneOf " \t\n\r") @@ -578,7 +582,7 @@ link = try $ do else B.spanWith attr $ B.link url "" name' -- | image embedding -image :: PandocMonad m => ParserT Text ParserState m Inlines +image :: PandocMonad m => TextileParser m Inlines image = try $ do char '!' >> notFollowedBy space (ident, cls, kvs) <- attributes @@ -590,51 +594,51 @@ image = try $ do char '!' return $ B.imageWith attr src alt (B.str alt) -escapedInline :: PandocMonad m => ParserT Text ParserState m Inlines +escapedInline :: PandocMonad m => TextileParser m Inlines escapedInline = escapedEqs <|> escapedTag -escapedEqs :: PandocMonad m => ParserT Text ParserState m Inlines +escapedEqs :: PandocMonad m => TextileParser m Inlines escapedEqs = B.str . T.pack <$> try (string "==" *> manyTill anyChar' (try $ string "==")) -- | literal text escaped btw tags -escapedTag :: PandocMonad m => ParserT Text ParserState m Inlines +escapedTag :: PandocMonad m => TextileParser m Inlines escapedTag = B.str . T.pack <$> try (string "" *> manyTill anyChar' (try $ string "")) -- | Any special symbol defined in wordBoundaries -symbol :: PandocMonad m => ParserT Text ParserState m Inlines +symbol :: PandocMonad m => TextileParser m Inlines symbol = B.str . T.singleton <$> (notFollowedBy newline *> notFollowedBy rawHtmlBlock *> oneOf wordBoundaries) -- | Inline code -code :: PandocMonad m => ParserT Text ParserState m Inlines +code :: PandocMonad m => TextileParser m Inlines code = code1 <|> code2 -- any character except a newline before a blank line -anyChar' :: PandocMonad m => ParserT Text ParserState m Char +anyChar' :: PandocMonad m => TextileParser m Char anyChar' = satisfy (/='\n') <|> try (char '\n' <* notFollowedBy blankline) -code1 :: PandocMonad m => ParserT Text ParserState m Inlines +code1 :: PandocMonad m => TextileParser m Inlines code1 = B.code . T.pack <$> surrounded (char '@') anyChar' -code2 :: PandocMonad m => ParserT Text ParserState m Inlines +code2 :: PandocMonad m => TextileParser m Inlines code2 = do htmlTag (tagOpen (=="tt") null) B.code . T.pack <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt")) -- | Html / CSS attributes -attributes :: PandocMonad m => ParserT Text ParserState m Attr +attributes :: PandocMonad m => TextileParser m Attr attributes = foldl' (flip ($)) ("",[],[]) <$> try (do special <- option id specialAttribute attrs <- many attribute return (special : attrs)) -specialAttribute :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr) +specialAttribute :: PandocMonad m => TextileParser m (Attr -> Attr) specialAttribute = do alignStr <- ("center" <$ char '=') <|> ("justify" <$ try (string "<>")) <|> @@ -643,11 +647,11 @@ specialAttribute = do notFollowedBy spaceChar return $ addStyle $ T.pack $ "text-align:" ++ alignStr -attribute :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr) +attribute :: PandocMonad m => TextileParser m (Attr -> Attr) attribute = try $ (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar -classIdAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr) +classIdAttr :: PandocMonad m => TextileParser m (Attr -> Attr) classIdAttr = try $ do -- (class class #id) char '(' ws <- T.words `fmap` T.pack <$> manyTill anyChar' (char ')') @@ -659,7 +663,7 @@ classIdAttr = try $ do -- (class class #id) classes' -> return $ \(_,_,keyvals) -> ("",classes',keyvals) -styleAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr) +styleAttr :: PandocMonad m => TextileParser m (Attr -> Attr) styleAttr = do style <- try $ enclosed (char '{') (char '}') anyChar' return $ addStyle $ T.pack style @@ -670,23 +674,23 @@ addStyle style (id',classes,keyvals) = where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"] style' = style <> ";" <> T.concat [v | ("style",v) <- keyvals] -langAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr) +langAttr :: PandocMonad m => TextileParser m (Attr -> Attr) langAttr = do lang <- try $ enclosed (char '[') (char ']') alphaNum return $ \(id',classes,keyvals) -> (id',classes,("lang",T.pack lang):keyvals) -- | Parses material surrounded by a parser. surrounded :: (PandocMonad m, Show t) - => ParserT Text st m t -- ^ surrounding parser - -> ParserT Text st m a -- ^ content parser (to be used repeatedly) - -> ParserT Text st m [a] + => ParserT Sources st m t -- ^ surrounding parser + -> ParserT Sources st m a -- ^ content parser (to be used repeatedly) + -> ParserT Sources st m [a] surrounded border = enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border) simpleInline :: PandocMonad m - => ParserT Text ParserState m t -- ^ surrounding parser + => TextileParser m t -- ^ surrounding parser -> (Inlines -> Inlines) -- ^ Inline constructor - -> ParserT Text ParserState m Inlines -- ^ content parser (to be used repeatedly) + -> TextileParser m Inlines -- ^ content parser (to be used repeatedly) simpleInline border construct = try $ do notAfterString border *> notFollowedBy (oneOf " \t\n\r") @@ -700,7 +704,7 @@ simpleInline border construct = try $ do then body else B.spanWith attr body -groupedInlineMarkup :: PandocMonad m => ParserT Text ParserState m Inlines +groupedInlineMarkup :: PandocMonad m => TextileParser m Inlines groupedInlineMarkup = try $ do char '[' sp1 <- option mempty $ B.space <$ whitespace @@ -709,5 +713,5 @@ groupedInlineMarkup = try $ do char ']' return $ sp1 <> result <> sp2 -eof' :: Monad m => ParserT Text s m Char +eof' :: Monad m => ParserT Sources s m Char eof' = '\n' <$ eof diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs index fb4b662c5..5c414fdec 100644 --- a/src/Text/Pandoc/Readers/TikiWiki.hs +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -30,23 +30,23 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging (Verbosity (..)) import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, nested) -import Text.Pandoc.Shared (crFilter, safeRead) +import Text.Pandoc.Shared (safeRead) import Text.Pandoc.XML (fromEntities) import Text.Printf (printf) -- | Read TikiWiki from an input string and return a Pandoc document. -readTikiWiki :: PandocMonad m +readTikiWiki :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readTikiWiki opts s = do - res <- readWithM parseTikiWiki def{ stateOptions = opts } - (crFilter s <> "\n\n") + let sources = ensureFinalNewlines 2 (toSources s) + res <- readWithM parseTikiWiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right d -> return d -type TikiWikiParser = ParserT Text ParserState +type TikiWikiParser = ParserT Sources ParserState -- -- utility functions diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index d355a4b55..6f92f0063 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -33,9 +33,9 @@ import Data.Time (defaultTimeLocale) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (space, spaces, uri) -import Text.Pandoc.Shared (compactify, compactifyDL, crFilter, escapeURI) +import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI) -type T2T = ParserT Text ParserState (Reader T2TMeta) +type T2T = ParserT Sources ParserState (Reader T2TMeta) -- | An object for the T2T macros meta information -- the contents of each field is simply substituted verbatim into the file @@ -68,15 +68,15 @@ getT2TMeta = do (intercalate ", " inps) outp -- | Read Txt2Tags from an input string returning a Pandoc document -readTxt2Tags :: PandocMonad m +readTxt2Tags :: (PandocMonad m, ToSources a) => ReaderOptions - -> Text + -> a -> m Pandoc readTxt2Tags opts s = do + let sources = ensureFinalNewlines 2 (toSources s) meta <- getT2TMeta let parsed = flip runReader meta $ - readWithM parseT2T (def {stateOptions = opts}) $ - crFilter s <> "\n\n" + readWithM parseT2T (def {stateOptions = opts}) sources case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 74dac5ea7..460f304c4 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -74,23 +74,28 @@ import Text.Pandoc.Parsing (ParserState, ParserT, blanklines, emailAddress, many1Till, orderedListMarker, readWithM, registerHeader, spaceChar, stateMeta, stateOptions, uri, manyTillChar, manyChar, textStr, - many1Char, countChar, many1TillChar) -import Text.Pandoc.Shared (crFilter, splitTextBy, stringify, stripFirstAndLast, + many1Char, countChar, many1TillChar, + alphaNum, anyChar, char, newline, noneOf, oneOf, + space, spaces, string) +import Text.Pandoc.Sources (ToSources(..), Sources) +import Text.Pandoc.Shared (splitTextBy, stringify, stripFirstAndLast, isURI, tshow) -import Text.Parsec.Char (alphaNum, anyChar, char, newline, noneOf, oneOf, space, - spaces, string) import Text.Parsec.Combinator (between, choice, eof, lookAhead, many1, manyTill, notFollowedBy, option, skipMany1) import Text.Parsec.Prim (getState, many, try, updateState, (<|>)) -readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readVimwiki :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc readVimwiki opts s = do - res <- readWithM parseVimwiki def{ stateOptions = opts } $ crFilter s + let sources = toSources s + res <- readWithM parseVimwiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right result -> return result -type VwParser = ParserT Text ParserState +type VwParser = ParserT Sources ParserState -- constants diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index e389c1727..920edca7b 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -298,6 +298,7 @@ tabFilter tabStop = T.unlines . map go . T.lines (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ") <> go (T.drop 1 s2) +{-# DEPRECATED crFilter "readers filter crs automatically" #-} -- | Strip out DOS line endings. crFilter :: T.Text -> T.Text crFilter = T.filter (/= '\r') diff --git a/src/Text/Pandoc/Sources.hs b/src/Text/Pandoc/Sources.hs new file mode 100644 index 000000000..5511ccfb8 --- /dev/null +++ b/src/Text/Pandoc/Sources.hs @@ -0,0 +1,195 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE OverloadedStrings #-} +{- | + Module : Text.Pandoc.Sources + Copyright : Copyright (C) 2021 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Defines Sources object to be used as input to pandoc parsers and redefines Char +parsers so they get source position information from it. +-} + +module Text.Pandoc.Sources + ( Sources(..) + , ToSources(..) + , UpdateSourcePos(..) + , sourcesToText + , initialSourceName + , addToSources + , ensureFinalNewlines + , addToInput + , satisfy + , oneOf + , noneOf + , anyChar + , char + , string + , newline + , space + , spaces + , letter + , digit + , hexDigit + , alphaNum + ) +where +import qualified Text.Parsec as P +import Text.Parsec (Stream(..), ParsecT) +import Text.Parsec.Pos as P +import Data.Text (Text) +import qualified Data.Text as T +import Data.Char (isSpace, isLetter, isAlphaNum, isDigit, isHexDigit) +import Data.String (IsString(..)) +import qualified Data.List.NonEmpty as NonEmpty + +-- | A list of inputs labeled with source positions. It is assumed +-- that the 'Text's have @\n@ line endings. +newtype Sources = Sources { unSources :: [(SourcePos, Text)] } + deriving (Show, Semigroup, Monoid) + +instance Monad m => Stream Sources m Char where + uncons (Sources []) = return Nothing + uncons (Sources ((pos,t):rest)) = + case T.uncons t of + Nothing -> uncons (Sources rest) + Just (c,t') -> return $ Just (c, Sources ((pos,t'):rest)) + +instance IsString Sources where + fromString s = Sources [(P.initialPos "", T.pack (filter (/='\r') s))] + +class ToSources a where + toSources :: a -> Sources + +instance ToSources Text where + toSources t = Sources [(P.initialPos "", T.filter (/='\r') t)] + +instance ToSources [(FilePath, Text)] where + toSources = Sources + . map (\(fp,t) -> + (P.initialPos fp, T.snoc (T.filter (/='\r') t) '\n')) + +instance ToSources Sources where + toSources = id + +sourcesToText :: Sources -> Text +sourcesToText (Sources xs) = mconcat $ map snd xs + +addToSources :: Monad m => SourcePos -> Text -> ParsecT Sources u m () +addToSources pos t = do + curpos <- P.getPosition + Sources xs <- P.getInput + let xs' = case xs of + [] -> [] + ((_,t'):rest) -> (curpos,t'):rest + P.setInput $ Sources ((pos, T.filter (/='\r') t):xs') + +ensureFinalNewlines :: Int -- ^ number of trailing newlines + -> Sources + -> Sources +ensureFinalNewlines n (Sources xs) = + case NonEmpty.nonEmpty xs of + Nothing -> Sources [(initialPos "", T.replicate n "\n")] + Just lst -> + case NonEmpty.last lst of + (spos, t) -> + case T.length (T.takeWhileEnd (=='\n') t) of + len | len >= n -> Sources xs + | otherwise -> Sources (NonEmpty.init lst ++ + [(spos, + t <> T.replicate (n - len) "\n")]) + +class UpdateSourcePos s c where + updateSourcePos :: SourcePos -> c -> s -> SourcePos + +instance UpdateSourcePos Text Char where + updateSourcePos pos c _ = updatePosChar pos c + +instance UpdateSourcePos Sources Char where + updateSourcePos pos c sources = + case sources of + Sources [] -> updatePosChar pos c + Sources ((_,t):(pos',_):_) + | T.null t -> pos' + Sources _ -> + case c of + '\n' -> incSourceLine (setSourceColumn pos 1) 1 + '\t' -> incSourceColumn pos (4 - ((sourceColumn pos - 1) `mod` 4)) + _ -> incSourceColumn pos 1 + +-- | Get name of first source in 'Sources'. +initialSourceName :: Sources -> FilePath +initialSourceName (Sources []) = "" +initialSourceName (Sources ((pos,_):_)) = sourceName pos + +-- | Add some text to the beginning of the input sources. +-- This simplifies code that expands macros. +addToInput :: Monad m => Text -> ParsecT Sources u m () +addToInput t = do + Sources xs <- P.getInput + case xs of + [] -> P.setInput $ Sources [(initialPos "",t)] + (pos,t'):rest -> P.setInput $ Sources ((pos, t <> t'):rest) + +-- We need to redefine the parsers in Text.Parsec.Char so that they +-- update source positions properly from the Sources stream. + +satisfy :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => (Char -> Bool) -> ParsecT s u m Char +satisfy f = P.tokenPrim show updateSourcePos matcher + where + matcher c = if f c then Just c else Nothing + +oneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => [Char] -> ParsecT s u m Char +oneOf cs = satisfy (`elem` cs) + +noneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => [Char] -> ParsecT s u m Char +noneOf cs = satisfy (`notElem` cs) + +anyChar :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +anyChar = satisfy (const True) + +char :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => Char -> ParsecT s u m Char +char c = satisfy (== c) + +string :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => [Char] -> ParsecT s u m [Char] +string = mapM char + +newline :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +newline = satisfy (== '\n') + +space :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +space = satisfy isSpace + +spaces :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m () +spaces = P.skipMany space P. "white space" + +letter :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +letter = satisfy isLetter + +alphaNum :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +alphaNum = satisfy isAlphaNum + +digit :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +digit = satisfy isDigit + +hexDigit :: (Monad m, Stream s m Char, UpdateSourcePos s Char) + => ParsecT s u m Char +hexDigit = satisfy isHexDigit diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index 6e38da21a..f055ab197 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -374,8 +374,8 @@ tests = [ testGroup "inline code" , testGroup "lhs" [ test (purely $ readMarkdown def{ readerExtensions = enableExtension Ext_literate_haskell pandocExtensions }) - "inverse bird tracks and html" $ - "> a\n\n< b\n\n
\n" + "inverse bird tracks and html" + $ ("> a\n\n< b\n\n
\n" :: Text) =?> codeBlockWith ("",["haskell","literate"],[]) "a" <> codeBlockWith ("",["haskell"],[]) "b" -- cgit v1.2.3 From 05ea507bd75e0bb4bbb8f25cad5fa2f02e4f6796 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 9 May 2021 15:26:11 -0600 Subject: T.P.Parsing: improve include file functions. Remove old `insertIncludedFileF`. [API change] Give `insertIncludedFile` a more general type, allowing it to be used where `insertIncludedFileF` was. --- src/Text/Pandoc/Parsing.hs | 61 +++++++++++++++++----------------- src/Text/Pandoc/Readers/Org/Blocks.hs | 3 +- src/Text/Pandoc/Readers/Org/Parsing.hs | 3 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 11c4c7a62..cbe9993c6 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -121,7 +121,6 @@ module Text.Pandoc.Parsing ( module Text.Pandoc.Sources, (<+?>), extractIdClass, insertIncludedFile, - insertIncludedFileF, -- * Re-exports from Text.Parsec Stream, runParser, @@ -1638,12 +1637,15 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs') cls' = maybe cls T.words $ lookup "class" kvs kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs -insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st) - => ParserT a st m (mf Blocks) - -> (Text -> a) - -> [FilePath] -> FilePath - -> ParserT a st m (mf Blocks) -insertIncludedFile' blocks totoks dirs f = do +insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) + => ParserT a st m b -- ^ parser to apply + -> (Text -> a) -- ^ convert Text to stream type + -> [FilePath] -- ^ search path (directories) + -> FilePath -- ^ path of file to include + -> Maybe Int -- ^ start line (negative counts from end) + -> Maybe Int -- ^ end line (negative counts from end) + -> ParserT a st m b +insertIncludedFile parser toStream dirs f mbstartline mbendline = do oldPos <- getPosition oldInput <- getInput containers <- getIncludeFiles <$> getState @@ -1652,33 +1654,32 @@ insertIncludedFile' blocks totoks dirs f = do updateState $ addIncludeFile $ T.pack f mbcontents <- readFileFromDirs dirs f contents <- case mbcontents of - Just s -> return s + Just s -> return $ exciseLines mbstartline mbendline s Nothing -> do report $ CouldNotLoadIncludeFile (T.pack f) oldPos return "" - setPosition $ newPos f 1 1 - setInput $ totoks contents - bs <- blocks + setInput $ toStream contents + setPosition $ newPos f (fromMaybe 1 mbstartline) 1 + result <- parser setInput oldInput setPosition oldPos updateState dropLatestIncludeFile - return bs + return result + +exciseLines :: Maybe Int -> Maybe Int -> Text -> Text +exciseLines Nothing Nothing t = t +exciseLines mbstartline mbendline t = + T.unlines $ take (endline' - (startline' - 1)) + $ drop (startline' - 1) contentLines + where + contentLines = T.lines t + numLines = length contentLines + startline' = case mbstartline of + Nothing -> 1 + Just x | x >= 0 -> x + | otherwise -> numLines + x -- negative from end + endline' = case mbendline of + Nothing -> numLines + Just x | x >= 0 -> x + | otherwise -> numLines + x -- negative from end --- | Parse content of include file as blocks. Circular includes result in an --- @PandocParseError@. -insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) - => ParserT [a] st m Blocks - -> (Text -> [a]) - -> [FilePath] -> FilePath - -> ParserT [a] st m Blocks -insertIncludedFile blocks totoks dirs f = - runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f - --- TODO: replace this with something using addToSources. --- | Parse content of include file as future blocks. Circular includes result in --- an @PandocParseError@. -insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st) - => ParserT Sources st m (Future st Blocks) - -> [FilePath] -> FilePath - -> ParserT Sources st m (Future st Blocks) -insertIncludedFileF p = insertIncludedFile' p (\t -> Sources [(initialPos "",t)]) diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 883434cdc..5e15c2c36 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -44,6 +44,7 @@ import Data.List.NonEmpty (nonEmpty) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Walk as Walk +import Text.Pandoc.Sources (ToSources(..)) -- -- parsing blocks @@ -527,7 +528,7 @@ include = try $ do _ -> nullAttr return $ pure . B.codeBlockWith attr <$> parseRaw _ -> return $ return . B.fromList . blockFilter params <$> blockList - insertIncludedFileF blocksParser ["."] filename + insertIncludedFile blocksParser toSources ["."] filename Nothing Nothing where includeTarget :: PandocMonad m => OrgParser m FilePath includeTarget = do diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index c7ea02815..701bf3398 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -63,8 +63,7 @@ module Text.Pandoc.Readers.Org.Parsing , ellipses , citeKey , gridTableWith - , insertIncludedFileF - -- * Re-exports from Text.Pandoc.Parsec + , insertIncludedFile , runParser , runParserT , getInput -- cgit v1.2.3 From 41a3ac9da99c2701fa7e6adbc85da91f191620f4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 9 May 2021 16:26:11 -0600 Subject: RST reader: use `insertIncludedFile` from T.P.Parsing... instead of reproducing much of its code. --- src/Text/Pandoc/Readers/RST.hs | 94 ++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index a3fcf028c..bb70b2620 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -27,8 +27,7 @@ import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, - readFileFromDirs, getTimestamp) +import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, getTimestamp) import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error @@ -453,59 +452,38 @@ encoding -} includeDirective :: PandocMonad m - => Text -> [(Text, Text)] -> Text + => Text + -> [(Text, Text)] + -> Text -> RSTParser m Blocks includeDirective top fields body = do - let f = trim top - guard $ not (T.null f) + let f = T.unpack $ trim top + guard $ not $ null f guard $ T.null (trim body) - -- options - let (startLine :: Maybe Int) = lookup "start-line" fields >>= safeRead - let (endLine :: Maybe Int) = lookup "end-line" fields >>= safeRead - oldPos <- getPosition - containers <- stateContainers <$> getState - when (f `elem` containers) $ - throwError $ PandocParseError $ "Include file loop at " <> tshow oldPos - updateState $ \s -> s{ stateContainers = f : stateContainers s } - mbContents <- readFileFromDirs ["."] $ T.unpack f - contentLines <- case mbContents of - Just s -> return $ T.lines s - Nothing -> do - logMessage $ CouldNotLoadIncludeFile f oldPos - return [] - let numLines = length contentLines - let startLine' = case startLine of - Nothing -> 1 - Just x | x >= 0 -> x - | otherwise -> numLines + x -- negative from end - let endLine' = case endLine of - Nothing -> numLines + 1 - Just x | x >= 0 -> x - | otherwise -> numLines + x -- negative from end - let contentLines' = drop (startLine' - 1) - $ take (endLine' - 1) contentLines - let contentLines'' = (case trim <$> lookup "end-before" fields of - Just patt -> takeWhile (not . (patt `T.isInfixOf`)) - Nothing -> id) . - (case trim <$> lookup "start-after" fields of - Just patt -> drop 1 . - dropWhile (not . (patt `T.isInfixOf`)) - Nothing -> id) $ contentLines' - let contents' = T.unlines contentLines'' - case lookup "code" fields of - Just lang -> do - let classes = maybe [] T.words (lookup "class" fields) - let ident = maybe "" trimr $ lookup "name" fields - codeblock ident classes fields (trimr lang) contents' False - Nothing -> case lookup "literal" fields of - Just _ -> return $ B.rawBlock "rst" contents' - Nothing -> do - addToSources (initialPos (T.unpack f)) - (contents' <> "\n") - updateState $ \s -> s{ stateContainers = - tail $ stateContainers s } - return mempty - + let startLine = lookup "start-line" fields >>= safeRead + let endLine = lookup "end-line" fields >>= safeRead + let classes = maybe [] T.words (lookup "class" fields) + let ident = maybe "" trimr $ lookup "name" fields + let parser = + case lookup "code" fields of + Just lang -> + (codeblock ident classes fields (trimr lang) False + . sourcesToText) <$> getInput + Nothing -> + case lookup "literal" fields of + Just _ -> B.rawBlock "rst" . sourcesToText <$> getInput + Nothing -> parseBlocks + let selectLines = + (case trim <$> lookup "end-before" fields of + Just patt -> takeWhile (not . (patt `T.isInfixOf`)) + Nothing -> id) . + (case trim <$> lookup "start-after" fields of + Just patt -> drop 1 . + dropWhile (not . (patt `T.isInfixOf`)) + Nothing -> id) + let toStream t = + toSources [(f, T.unlines . selectLines . T.lines $ t)] + insertIncludedFile parser toStream ["."] f startLine endLine -- -- list blocks @@ -734,8 +712,8 @@ directive' = do "" -> stateRstHighlight def lang -> Just lang }) x | x == "code" || x == "code-block" || x == "sourcecode" -> - codeblock name classes (map (second trimr) fields) - (trim top) body True + return $ codeblock name classes (map (second trimr) fields) + (trim top) True body "aafig" -> do let attribs = (name, ["aafig"], map (second trimr) fields) return $ B.codeBlockWith attribs $ stripTrailingNewlines body @@ -1021,10 +999,10 @@ toChunks = dropWhile T.null then "\\begin{aligned}\n" <> s <> "\n\\end{aligned}" else s -codeblock :: Text -> [Text] -> [(Text, Text)] -> Text -> Text -> Bool - -> RSTParser m Blocks -codeblock ident classes fields lang body rmTrailingNewlines = - return $ B.codeBlockWith attribs $ stripTrailingNewlines' body +codeblock :: Text -> [Text] -> [(Text, Text)] -> Text -> Bool -> Text + -> Blocks +codeblock ident classes fields lang rmTrailingNewlines body = + B.codeBlockWith attribs $ stripTrailingNewlines' body where stripTrailingNewlines' = if rmTrailingNewlines then stripTrailingNewlines else id -- cgit v1.2.3 From f69f1101e6689745cf5dea8276ba11d723b43ccf Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 9 May 2021 17:25:55 -0600 Subject: Update doc/using-the-pandoc-api.md for new reader types. --- doc/using-the-pandoc-api.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/using-the-pandoc-api.md b/doc/using-the-pandoc-api.md index 19ce8b274..d6eb9e15f 100644 --- a/doc/using-the-pandoc-api.md +++ b/doc/using-the-pandoc-api.md @@ -90,8 +90,14 @@ Some notes: Let's look at the types of `readMarkdown` and `writeRST`: ```haskell -readMarkdown :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text +readMarkdown :: (PandocMonad m, ToSources a) + => ReaderOptions + -> a + -> m Pandoc +writeRST :: PandocMonad m + => WriterOptions + -> Pandoc + -> m Text ``` The `PandocMonad m =>` part is a typeclass constraint. @@ -155,6 +161,13 @@ Note that `PandocIO` is an instance of `MonadIO`, so you can use `liftIO` to perform arbitrary IO operations inside a pandoc conversion chain. +`readMarkdown` is polymorphic in its second argument, which +can be any type that is an instance of the `ToSources` +typeclass. You can use `Text`, as in the example above. +But you can also use `[(FilePath, Text)]`, if the input comes +from multiple files and you want to track source positions +accurately. + # Options The first argument of each reader or writer is for -- cgit v1.2.3 From b2398cd74744ce34fa10cc34f4051555f21feb2c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 9 May 2021 18:42:42 -0600 Subject: Org reader: Resolve org includes relative to ... ...the directory containing the file containing the INCLUDE directive. Closes #5501. --- src/Text/Pandoc/Readers/Org/Blocks.hs | 6 ++++-- src/Text/Pandoc/Readers/Org/Parsing.hs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 5e15c2c36..f18d2f9a7 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -40,7 +40,7 @@ import Data.List (foldl', intersperse) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) import Data.List.NonEmpty (nonEmpty) - +import System.FilePath import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Walk as Walk @@ -528,7 +528,9 @@ include = try $ do _ -> nullAttr return $ pure . B.codeBlockWith attr <$> parseRaw _ -> return $ return . B.fromList . blockFilter params <$> blockList - insertIncludedFile blocksParser toSources ["."] filename Nothing Nothing + currentDir <- takeDirectory . sourceName <$> getPosition + insertIncludedFile blocksParser toSources + [currentDir] filename Nothing Nothing where includeTarget :: PandocMonad m => OrgParser m FilePath includeTarget = do diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 701bf3398..f0949e205 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -99,6 +99,7 @@ module Text.Pandoc.Readers.Org.Parsing , getState , updateState , SourcePos + , sourceName , getPosition ) where -- cgit v1.2.3 From 2a2e08d82335d45c982f4acf14b62130365f2c8e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 9 May 2021 18:50:41 -0600 Subject: RST reader: seek include files in the directory... ...of the file containing the include directive, as RST requires. Closes #6632. --- src/Text/Pandoc/Readers/RST.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index bb70b2620..35292d949 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -38,6 +38,7 @@ import Text.Pandoc.Parsing import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 import Data.Time.Format +import System.FilePath (takeDirectory) -- TODO: -- [ ] .. parsed-literal @@ -483,7 +484,8 @@ includeDirective top fields body = do Nothing -> id) let toStream t = toSources [(f, T.unlines . selectLines . T.lines $ t)] - insertIncludedFile parser toStream ["."] f startLine endLine + currentDir <- takeDirectory . sourceName <$> getPosition + insertIncludedFile parser toStream [currentDir] f startLine endLine -- -- list blocks -- cgit v1.2.3 From 83c83d4589ccd918cc0cbb11fb497367c66df1d3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 10 May 2021 08:45:33 -0600 Subject: CI: install numa library. See failure at https://github.com/jgm/pandoc/runs/2541449722 --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb5c743fa..d8083f44e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,10 @@ jobs: steps: - uses: actions/checkout@v2 + # needed by memory + - name: Install numa + run: sudo apt-get install libnuma-dev + # need to install older cabal/ghc versions from ppa repository - name: Install recent cabal/ghc -- cgit v1.2.3 From ba4ec8c5c0d83ee68dd9cb26cdc9553a3374b1ab Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 10 May 2021 09:21:27 -0600 Subject: latex template: use non-starred names for xcolor color names. This should make svgnames and x11names work properly. Closes #6109. --- data/templates/default.latex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index 81b2d91fe..f39e56c79 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -2,7 +2,7 @@ \PassOptionsToPackage{unicode$for(hyperrefoptions)$,$hyperrefoptions$$endfor$}{hyperref} \PassOptionsToPackage{hyphens}{url} $if(colorlinks)$ -\PassOptionsToPackage{dvipsnames,svgnames*,x11names*}{xcolor} +\PassOptionsToPackage{dvipsnames,svgnames,x11names}{xcolor} $endif$ $if(dir)$ $if(latex-dir-rtl)$ -- cgit v1.2.3 From ff7176de80726a5db4c4bc4c5a3296b2bcdab192 Mon Sep 17 00:00:00 2001 From: nuew Date: Mon, 10 May 2021 11:26:32 -0400 Subject: epub Writer: Fix belongs-to-collection XML id choice (#7267) The epub writer previously used the same XML id for both the book identifier and the epub collection. This causes an error on epubcheck. --- src/Text/Pandoc/Writers/EPUB.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 3f10cb437..3c092a2c1 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -991,12 +991,12 @@ metadataElement version md currentTime = showDateTimeISO8601 currentTime | version == EPUB3 ] belongsToCollectionNodes = maybe [] - (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-id-1")] $ belongsToCollection ) + (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-collection-1")] $ belongsToCollection ) : - [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: Text) ]) + [unode "meta" ! [("refines", "#epub-collection-1"), ("property", "collection-type")] $ ("series" :: Text) ]) (epubBelongsToCollection md)++ maybe [] - (\groupPosition -> [unode "meta" ! [("refines", "#epub-id-1"), ("property", "group-position")] $ groupPosition ]) + (\groupPosition -> [unode "meta" ! [("refines", "#epub-collection-1"), ("property", "group-position")] $ groupPosition ]) (epubGroupPosition md) dcTag n s = unode ("dc:" <> n) s dcTag' n s = [dcTag n s] -- cgit v1.2.3 From 2bd5d0cafbe67fa723f49fd2d35d913594fdb935 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 11 May 2021 07:52:05 -0600 Subject: LaTeX writer: better handling of line breaks in simple tables. Now we also handle the case where they're embedded in other elements, e.g. spans. Closes #7272. --- src/Text/Pandoc/Writers/LaTeX/Table.hs | 4 +--- test/command/7272.md | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 test/command/7272.md diff --git a/src/Text/Pandoc/Writers/LaTeX/Table.hs b/src/Text/Pandoc/Writers/LaTeX/Table.hs index 6f8386937..16f63314b 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Table.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Table.hs @@ -219,9 +219,7 @@ footRows (Ann.TableFoot _attr rows) = map headerRowCells rows -- we need to go to some lengths to get line breaks working: -- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}. fixLineBreaks :: Block -> Block -fixLineBreaks (Para ils) = Para $ fixLineBreaks' ils -fixLineBreaks (Plain ils) = Plain $ fixLineBreaks' ils -fixLineBreaks x = x +fixLineBreaks = walk fixLineBreaks' fixLineBreaks' :: [Inline] -> [Inline] fixLineBreaks' ils = case splitBy (== LineBreak) ils of diff --git a/test/command/7272.md b/test/command/7272.md new file mode 100644 index 000000000..d3a3b2137 --- /dev/null +++ b/test/command/7272.md @@ -0,0 +1,24 @@ +``` +% pandoc -t latex -f html + + + + + + +
+ + text +
+ text2 +
+
+^D +\begin{longtable}[]{@{} + >{\raggedright\arraybackslash}p{(\columnwidth - 0\tabcolsep) * \real{1.00}}@{}} +\toprule +\endhead +{\vtop{\hbox{\strut text}\hbox{\strut text2 }}} \\ +\bottomrule +\end{longtable} +``` -- cgit v1.2.3 From a66e50840bdc3c92e661257a4a0c5fec34aa25bc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 11 May 2021 09:01:36 -0600 Subject: T.P.XML.Light - add Eq, Ord instances... for Content, Element, Attr, CDataKind. [API change] --- src/Text/Pandoc/XML/Light/Types.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/XML/Light/Types.hs b/src/Text/Pandoc/XML/Light/Types.hs index 03fdc2e4d..ba602ac1f 100644 --- a/src/Text/Pandoc/XML/Light/Types.hs +++ b/src/Text/Pandoc/XML/Light/Types.hs @@ -53,7 +53,7 @@ type Line = Integer data Content = Elem Element | Text CData | CRef Text - deriving (Show, Typeable, Data) + deriving (Show, Typeable, Data, Ord, Eq) -- | XML elements data Element = Element { @@ -61,7 +61,7 @@ data Element = Element { elAttribs :: [Attr], elContent :: [Content], elLine :: Maybe Line - } deriving (Show, Typeable, Data) + } deriving (Show, Typeable, Data, Ord, Eq) -- | XML attributes data Attr = Attr { @@ -74,13 +74,13 @@ data CData = CData { cdVerbatim :: CDataKind, cdData :: Text, cdLine :: Maybe Line - } deriving (Show, Typeable, Data) + } deriving (Show, Typeable, Data, Ord, Eq) data CDataKind = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc. | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in Date: Tue, 11 May 2021 22:31:38 -0600 Subject: Improve integration of settings from reference.docx. The settings we can carry over from a reference.docx are autoHyphenation, consecutiveHyphenLimit, hyphenationZone, doNotHyphenateCap, evenAndOddHeaders, and proofState. Previously this was implemented in a buggy way, so that the reference doc's values AND the new values were included. This change allows users to create a reference.docx that sets w:proofState for spelling or grammar to "dirty," so that spell/grammar checking will be triggered on the generated docx. Closes #1209. --- src/Text/Pandoc/Writers/Docx.hs | 27 +++++++++++---------- test/docx/golden/block_quotes.docx | Bin 10067 -> 10061 bytes test/docx/golden/codeblock.docx | Bin 9916 -> 9910 bytes test/docx/golden/comments.docx | Bin 10254 -> 10248 bytes test/docx/golden/custom_style_no_reference.docx | Bin 10017 -> 10011 bytes test/docx/golden/custom_style_preserve.docx | Bin 10646 -> 10640 bytes test/docx/golden/custom_style_reference.docx | Bin 12400 -> 12397 bytes test/docx/golden/definition_list.docx | Bin 9916 -> 9910 bytes .../golden/document-properties-short-desc.docx | Bin 9922 -> 9916 bytes test/docx/golden/document-properties.docx | Bin 10399 -> 10393 bytes test/docx/golden/headers.docx | Bin 10055 -> 10049 bytes test/docx/golden/image.docx | Bin 26733 -> 26727 bytes test/docx/golden/inline_code.docx | Bin 9855 -> 9849 bytes test/docx/golden/inline_formatting.docx | Bin 10035 -> 10029 bytes test/docx/golden/inline_images.docx | Bin 26789 -> 26783 bytes test/docx/golden/link_in_notes.docx | Bin 10077 -> 10071 bytes test/docx/golden/links.docx | Bin 10248 -> 10242 bytes test/docx/golden/lists.docx | Bin 10314 -> 10308 bytes test/docx/golden/lists_continuing.docx | Bin 10110 -> 10104 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10192 -> 10186 bytes test/docx/golden/lists_restarting.docx | Bin 10108 -> 10102 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10212 -> 10206 bytes test/docx/golden/notes.docx | Bin 10024 -> 10018 bytes test/docx/golden/raw-blocks.docx | Bin 9956 -> 9950 bytes test/docx/golden/raw-bookmarks.docx | Bin 10090 -> 10084 bytes test/docx/golden/table_one_row.docx | Bin 9925 -> 9920 bytes test/docx/golden/table_with_list_cell.docx | Bin 10230 -> 10225 bytes test/docx/golden/tables.docx | Bin 10271 -> 10266 bytes test/docx/golden/track_changes_deletion.docx | Bin 9899 -> 9893 bytes test/docx/golden/track_changes_insertion.docx | Bin 9882 -> 9876 bytes test/docx/golden/track_changes_move.docx | Bin 9916 -> 9910 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 10028 -> 10022 bytes test/docx/golden/unicode.docx | Bin 9841 -> 9835 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9888 -> 9882 bytes 34 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index e11961bfd..75bed1595 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -469,12 +469,12 @@ writeDocx opts doc = do -- adds references to footnotes or endnotes we don't have... -- we do, however, copy some settings over from reference let settingsPath = "word/settings.xml" - settingsList = [ "w:autoHyphenation" - , "w:consecutiveHyphenLimit" - , "w:hyphenationZone" - , "w:doNotHyphenateCap" - , "w:evenAndOddHeaders" - , "w:proofState" + settingsList = [ "autoHyphenation" + , "consecutiveHyphenLimit" + , "hyphenationZone" + , "doNotHyphenateCap" + , "evenAndOddHeaders" + , "proofState" ] settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime settingsList @@ -577,16 +577,17 @@ copyChildren :: (PandocMonad m) copyChildren refArchive distArchive path timestamp elNames = do ref <- parseXml refArchive distArchive path dist <- parseXml distArchive distArchive path + let elsToCopy = + map cleanElem $ filterChildrenName (\e -> qName e `elem` elNames) ref + let elsToKeep = + [e | Elem e <- elContent dist, not (any (hasSameNameAs e) elsToCopy)] return $ toEntry path timestamp $ renderXml dist{ - elContent = elContent dist ++ copyContent ref + elContent = map Elem elsToKeep ++ map Elem elsToCopy } where - strName QName{qName=name, qPrefix=prefix} - | Just p <- prefix = p <> ":" <> name - | otherwise = name - shouldCopy = (`elem` elNames) . strName - cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}} - copyContent = map cleanElem . filterChildrenName shouldCopy + hasSameNameAs (Element {elName = n1}) (Element {elName = n2}) = + qName n1 == qName n2 + cleanElem el@Element{elName=name} = el{elName=name{qURI=Nothing}} -- this is the lowest number used for a list numId baseListId :: Int diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index af1b33ca2..b6973fcfd 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index f748f1f01..f0d35d3ad 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index ac9d56680..fb1c15dec 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index f27727edd..43d536b65 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 1da499d6a..8865010d1 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index 4d2fe245d..10f7bf661 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index f386fcea3..2a7d81a34 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index debe9a3f6..2fcd14908 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index cd17400bf..39533f42d 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index d3af8a3dd..c8d67c45b 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 1c4e738c0..8a7aeec10 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 35f43f19f..969237cec 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index 8de3f70f6..cda936a39 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index e76558be9..69991e791 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index 88bae8142..40e848195 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index 455adcfc7..28237a30d 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 081d9ddba..bf075805e 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index fc9213fc5..e7d308e13 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index b636fd3f8..9763e347e 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index 252623215..b717ca619 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index a8c3f5478..873e731d2 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index 43e650ebd..134cb2eaf 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index fe4f7845b..c49ed79c8 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index 45e90608f..1f2cbb214 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index e60bb303f..a0160cdb4 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index a4037cf32..6427d475c 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index bc1bc27f8..470eac2ae 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index 247725aaa..3542b8f9c 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index 3863afef2..b36b4485e 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index 5c848b63a..e30ab06ae 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index e0c843713..11597d578 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index 78a773bdd..c7bff82e5 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index c66a45b74..c70f6946e 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ -- cgit v1.2.3 From 46309319ef9eb66ea3598073df4174fb40b1b0f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 12 May 2021 06:01:13 -0600 Subject: Fix source position reporting for YAML bibliographies. Closes #7273. --- src/Text/Pandoc/Readers/Markdown.hs | 2 -- src/Text/Pandoc/Readers/Metadata.hs | 8 ++++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 69dd51bc4..2d20ff018 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -97,12 +97,10 @@ yamlToRefs :: PandocMonad m -> m [MetaValue] yamlToRefs idpred opts mbfp bstr = do let parser = do - oldPos <- getPosition case mbfp of Nothing -> return () Just fp -> setPosition $ initialPos fp refs <- yamlBsToRefs (fmap B.toMetaValue <$> parseBlocks) idpred bstr - setPosition oldPos return $ runF refs defaultParserState parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text) case parsed of diff --git a/src/Text/Pandoc/Readers/Metadata.hs b/src/Text/Pandoc/Readers/Metadata.hs index bbcfe62ea..45eddf25a 100644 --- a/src/Text/Pandoc/Readers/Metadata.hs +++ b/src/Text/Pandoc/Readers/Metadata.hs @@ -98,8 +98,12 @@ yamlBsToRefs pMetaValue idpred bstr = Right [YAML.Doc (YAML.Scalar _ YAML.SNull)] -> return . return $ mempty Right _ -> Prelude.fail "expecting YAML object" - Left (_pos, err') - -> Prelude.fail err' + Left (yamlpos, err') + -> do pos <- getPosition + setPosition $ incSourceLine + (setSourceColumn pos (YE.posColumn yamlpos)) + (YE.posLine yamlpos - 1) + Prelude.fail err' nodeToKey :: YAML.Node YE.Pos -> Maybe Text -- cgit v1.2.3 From 0217ae2a4ffe33bee7cc53ba44817cd540b5f01e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 12 May 2021 11:05:55 -0700 Subject: Hande 'annote' field in bibtex/biblatex writer. Closes #7266. --- src/Text/Pandoc/Citeproc/BibTeX.hs | 2 ++ test/command/7266.md | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/command/7266.md diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index f6833000c..b17240557 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -156,6 +156,7 @@ writeBibtexString opts variant mblang ref = , "langid" , "abstract" , "keywords" + , "annote" ] Bibtex -> [ "author" @@ -175,6 +176,7 @@ writeBibtexString opts variant mblang ref = , "address" , "type" , "note" + , "annote" ] valToInlines (TextVal t) = B.text t diff --git a/test/command/7266.md b/test/command/7266.md new file mode 100644 index 000000000..9db833636 --- /dev/null +++ b/test/command/7266.md @@ -0,0 +1,10 @@ +``` +% pandoc -f biblatex -t biblatex -s +@article{id, + annote = "annotation" +} +^D +@article{id, + annote = {annotation} +} +``` -- cgit v1.2.3 From edca1d1656a03d7de4901a1f6d356094ec9fb2df Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 12 May 2021 11:12:27 -0700 Subject: Plain writer: handle superscript unicode minus. Closes #7276. Note: currently we still get unwanted white space around the minus; this needs to be addressed with a change in texmath. --- src/Text/Pandoc/Writers/Shared.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index a09d18571..0b7c6bee0 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -394,6 +394,7 @@ toSuperscript '2' = Just '\x00B2' toSuperscript '3' = Just '\x00B3' toSuperscript '+' = Just '\x207A' toSuperscript '-' = Just '\x207B' +toSuperscript '\x2212' = Just '\x207B' -- unicode minus toSuperscript '=' = Just '\x207C' toSuperscript '(' = Just '\x207D' toSuperscript ')' = Just '\x207E' -- cgit v1.2.3 From 84fa901fd640cf279ac51b76820f7b57b04f07a6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 12 May 2021 12:52:04 -0700 Subject: Use texmath 0.12.3. --- pandoc.cabal | 2 +- stack.yaml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index de7951c54..705b574d5 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -486,7 +486,7 @@ library syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, temporary >= 1.1 && < 1.4, - texmath >= 0.12.2 && < 0.12.3, + texmath >= 0.12.3 && < 0.12.4, text >= 1.1.1.0 && < 1.3, text-conversions >= 0.3 && < 0.4, time >= 1.5 && < 1.10, diff --git a/stack.yaml b/stack.yaml index 32369895f..48821dfe8 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,9 +16,7 @@ extra-deps: - random-1.2.0 - xml-conduit-1.9.1.1 - unicode-collation-0.1.3 -# - texmath-0.12.2 -- git: https://github.com/jgm/texmath - commit: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d +- texmath-0.12.3 # - citeproc-0.3.0.9 - git: https://github.com/jgm/citeproc commit: f33b370bd1ab5ad5140720369c0586041e433a47 -- cgit v1.2.3 From 697d1497338f505325c4df430553a04bd71e1cc7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 12 May 2021 14:35:06 -0700 Subject: Use released citeproc 0.4. --- cabal.project | 9 --------- stack.yaml | 4 +--- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/cabal.project b/cabal.project index e06c375b9..26373c616 100644 --- a/cabal.project +++ b/cabal.project @@ -2,12 +2,3 @@ packages: pandoc.cabal tests: True flags: +embed_data_files -source-repository-package - type: git - location: https://github.com/jgm/citeproc - tag: f33b370bd1ab5ad5140720369c0586041e433a47 - -source-repository-package - type: git - location: https://github.com/jgm/texmath - tag: 0c34bb7d229389adc62bcbfe20cfbfdc0c8ccc8d diff --git a/stack.yaml b/stack.yaml index 48821dfe8..625543bf2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,9 +17,7 @@ extra-deps: - xml-conduit-1.9.1.1 - unicode-collation-0.1.3 - texmath-0.12.3 -# - citeproc-0.3.0.9 -- git: https://github.com/jgm/citeproc - commit: f33b370bd1ab5ad5140720369c0586041e433a47 +- citeproc-0.4 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.5 -- cgit v1.2.3 From b243bf0f44d97c93145fa6c68798b63340673cfa Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 13 May 2021 14:10:47 -0700 Subject: Bump to 2.14 and update changelog. --- changelog.md | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pandoc.cabal | 2 +- 2 files changed, 227 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 254a62315..265e7336b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,231 @@ # Revision history for pandoc +## pandoc 2.14 (PROVISIONAL) + + * Change reader types, allowing better tracking of source positions + [API change]. Previously, when multiple file arguments were provided, + pandoc simply concatenated them and passed the contents to the readers, + which took a Text argument. As a result, the readers had no way of knowing + which file was the source of any particular bit of text. This meant that + we couldn't report accurate source positions on errors or include accurate + source positions as attributes in the AST. More seriously, it meant that + we couldn't resolve resource paths relative to the files containing them + (see e.g. #5501, #6632, #6384, #3752). + + * Add Text.Pandoc.Sources (exported module), with a `Sources` type and a + `ToSources` class. A `Sources` wraps a list of `(SourcePos, Text)` pairs + [API change]. A parsec `Stream` instance is provided for `Sources`. The + module also exports versions of parsec's `satisfy` and other Char parsers + that track source positions accurately from a `Sources` stream (or any + instance of the new `UpdateSourcePos` class). + + * Text.Pandoc.Parsing + + + Export the modified Char parsers defined in Text.Pandoc.Sources + instead of the ones parsec provides. Modified parsers to use a + `Sources` as stream [API change]. + + Improve include file functions [API change]. Remove old + `insertIncludedFileF`. Give `insertIncludedFile` a more general type, + allowing it to be used where `insertIncludedFileF` was. + + * Text.Pandoc.Error: Modified the constructor `PandocParsecError` to take a + `Sources` rather than a `Text` as first argument, so parse error locations + can be accurately reported. + + * Fix source position reporting for YAML bibliographies (#7273). + + * Issue error message when reader or writer format is malformed + (#7231). Previously we exited with an error status but (due to a bug) + no message. + + * Smarter smart quotes (#7216, #2103). Treat a leading `"` with no + closing `"` as a left curly quote. This supports the practice, in + fiction, of continuing paragraphs quoting the same speaker without an + end quote. It also helps with quotes that break over lines in line blocks. + + * Markdown reader: Use MetaInlines not MetaBlocks for multimarkdown + metadata fields. This gives better results in converting to e.g. + pandoc markdown. + + * RST reader: + + + Seek include files in the directory of the file + containing the include directive, as RST requires (#6632). + + Use `insertIncludedFile` from Text.Pandoc.Parsing + instead of reproducing much of its code. + + * Org reader: Resolve org includes relative to the directory containing the + file containing the INCLUDE directive (#5501). + + * ODT reader: Treat tabs as spaces (#7185, niszet). + + * Docx reader: Add handling of vml image objects (#7257, mbrackeantidot). + + * DocBook/JATS readers: + + + Fix mathml regression caused by the switch in XML libraries (#7173). + + Fix "phrase" in DocBook: take classes from "role" not "class" (#7195). + + * Plain writer: handle superscript unicode minus (#7276). + + * LaTeX writer: better handling of line breaks in simple tables (#7272). + Now we also handle the case where they're embedded in other + elements, e.g. spans. + + * EPUB Writer: Fix belongs-to-collection XML id choice (#7267, nuew). + The epub writer previously used the same XML id for both the book + identifier and the epub collection. This causes an error on epubcheck. + + * BibTeX/BibLaTeX writer: Handle `annote` field (#7266). + + * ConTeXt writer: support blank lines in line blocks (#6564, + Albert Krewinkel, thanks to @denismaier). + + * Org writer: + + + Inline latex envs need newlines (#7252, tecosaur). + As specified in https://orgmode.org/manual/LaTeX-fragments.html, an + inline \begin{}...\end{} LaTeX block must start on a new line. + + Use LaTeX style maths deliminators (#7196, tecosaur). + + * JATS writer (Albert Krewinkel): + + + Use either styled-content or named-content for spans (#7211). + If the element has a content-type attribute, or at least one class, + then that value is used as `content-type` and the span is put inside + a `` element. Otherwise a `` element + is used instead. + + Reduce unnecessary use of

elements for wrapping (#7227). + The `

` element is used for wrapping in cases were the contents + would otherwise not be allowed in a certain context. Unnecessary + wrapping is avoided, especially around quotes (`` elements). + + Convert spans to `` elements (#7211). Spans with + attributes are converted to `` elements instead of + being wrapped with `` and `` + elements. Milestone elements are not allowed in documents using the + articleauthoring tag set, so this change ensures the creation of valid + documents. + + Add footnote number as label in backmatter (#7210). Footnotes in the + backmatter are given the footnote's number as a label. The + articleauthoring output is unaffected from this change, as footnotes + are placed inline there. + + Escape disallows chars in identifiers. XML identifiers must start + with an underscore or letter, and can contain only a limited set + of punctuation characters. Any IDs not adhering to these rules are + rewritten by writing the offending characters as `Uxxxx`, + where `xxxx` is the character's hex code. + + * Docx writer: + + + Autoset table width if no column has an explicit width (Albert + Krewinkel). + + Extract Table handling into separate module (Albert Krewinkel). + + Support colspans and rowspans in tables (Albert Krewinkel, #6315). + + Improve integration of settings from reference.docx (#1209). + This change allows users to create a reference.docx that + sets `w:proofState` for spelling or grammar to `dirty`, + so that spell/grammar checking will be triggered on the + generated docx. + + * Writers: Recognize custom syntax definitions (#7241, Jan Tojnar). + Languages defined using `--syntax-definition` were not recognized by + `languagesByExtension`. This patch corrects that, allowing the writers + to see all custom definitions. The LaTeX writer still uses the default + syntax map, but that's okay in that context, since + `--syntax-definition` won't create new listings styles. + + * Markdown writer: + + + Use cleaner braceless syntax for code blocks with a + single class (#7242, Jan Tojnar). + + Add quotes properly in markdown YAML metadata fields (#7245). + This fixes a bug, which caused the writer to look at the *last* + rather than the *first* character in determining whether quotes + were needed. So we got spurious quotes in some cases and + didn't get necessary quotes in others. + + * Commonmark writer: Use backslash escapes for `<` and `|`... + instead of entities (#7208). + + * Powerpoint writer: allow `monofont` to be specified in metadata + (#7187). + + * LaTeX template: + + + Use non-starred names for xcolor color names (#6109). + This should make svgnames and x11names work properly. + + Fix bad vertical spacing after bibliography (#7234, badumont). + + List of figures before list of tables (#7235, Julien Dutant). + + * ConTeXt template: List of figures before list of tables (#7235, + Julien Dutant). + + * reveal.js template: Support `toc-title` (#7171, Florian Kohrt). + + * Text.Pandoc.XML.Light: add Eq, Ord instances for Content, + Element, Attr, CDataKind [API change]. + + * Text.Pandoc.Asciify: simplify code and export `toAsciiText` [API change]. + Instead of encoding a giant (and incomplete) map, we now + just use unicode-transforms to normalize the text to + a canonical decomposition, and manipulate the result. + + * App: allow tabs expansion even if file-scope is used (Albert Krewinkel, + #6709). Tabs in plain-text inputs are now handled correctly, even if + the `--file-scope` flag is used. + + * Add new internal module Text.Pandoc.Writers.GridTable (Albert Krewinkel). + + * Text.Pandoc.Highlighting: Change type of `languagesByExtension`, adding + a parameter for a `SyntaxMap` [API change] (Jan Tojnar). + + * Use metadata's `lang` for the lang parameter of citeproc, overriding + `localeLanguage`. + + * Remove Text.Pandoc.BCP47 module [API change]. + Use types and functions from UnicodeCollation.Lang instead. + This is a richer implementation of BCP 47. + + * Text.Pandoc.Shared: + + + Fix regression in grid tables for wide characters (#7214). + In the translation from String to Text, a char-width-sensitive + `splitAt'` was dropped. This commit reinstates it and uses it to make + `splitTextByInstances` char-width sensitive. + + Add `getLang` (formerly in the now-removed BCP47) [API change]. + + * Text.Pandoc.SelfContained: use `application/octet-stream` + for unknown mime types instead of halting with an error (#7202). + + * Lua filters: respect Inlines/Blocks filter functions in `pandoc.walk_*` + (Albert Krewinkel). + + * Add text as build-depend for trypandoc (#7193, Roman Beránek). + + * Use citeproc 0.4. + + * Use texmath 0.12.3. + + * Allow attoparsec 0.14.x. + + * Require latest skylighting (fixes a bug in XML syntax highlighting). + + * Use latest xml-conduit. + + * MANUAL: Add information about `lang` and bibliography sorting. + + * Updated and fixed typos in documentation (Charanjit Singh, + Anti-Distinctlyminty, Tatiana Porras, obcat). + + * Add instructions for installing pandoc-types before compiling filter. + + * INSTALL: add note that parallel installations should be avoided + (#6865). + + * Remove `biblatex-nussbaum.md` test. It is basically the same + as `biblaetx-quotes.md`. + + ## pandoc 2.13 (2021-03-21) * Support `yaml_metadata_block` extension for `commonmark`, `gfm` (#6537). diff --git a/pandoc.cabal b/pandoc.cabal index 705b574d5..66873a301 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.13 +version: 2.14 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md -- cgit v1.2.3 From c46482bfc316a91802299ac8aa333500e97be16b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 13 May 2021 14:11:15 -0700 Subject: Update manual date and man page. --- MANUAL.txt | 2 +- man/pandoc.1 | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 4ddacb9a2..75fe599e2 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: March 20, 2021 +date: May 13, 2021 --- # Synopsis diff --git a/man/pandoc.1 b/man/pandoc.1 index bfe9872a4..795271b36 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t -.\" Automatically generated by Pandoc 2.12.1 +.\" Automatically generated by Pandoc 2.13 .\" -.TH "Pandoc User\[cq]s Guide" "" "March 20, 2021" "pandoc 2.13" "" +.TH "Pandoc User\[cq]s Guide" "" "May 13, 2021" "pandoc 2.14" "" .hy .SH NAME pandoc - general markup converter @@ -4880,6 +4880,21 @@ header-includes: \[ga]\[ga]\[ga] \f[R] .fi +.PP +Note: the \f[C]yaml_metadata_block\f[R] extension works with +\f[C]commonmark\f[R] as well as \f[C]markdown\f[R] (and it is enabled by +default in \f[C]gfm\f[R] and \f[C]commonmark_x\f[R]). +However, in these formats the following restrictions apply: +.IP \[bu] 2 +The YAML metadata block must occur at the beginning of the document (and +there can be only one). +If multiple files are given as arguments to pandoc, only the first can +be a YAML metadata block. +.IP \[bu] 2 +The leaf nodes of the YAML structure are parsed in isolation from each +other and from the rest of the document. +So, for example, you can\[cq]t use a reference link in these contexts if +the link definition is somewhere else in the document. .SS Backslash escapes .SS Extension: \f[C]all_symbols_escapable\f[R] .PP @@ -6468,9 +6483,29 @@ entries (for author-date and numerical styles only). .TP \f[B]\f[CB]lang\f[B]\f[R] The \f[C]lang\f[R] field will affect how the style is localized, for -example in the translation of labels and the use of quotation marks. +example in the translation of labels, the use of quotation marks, and +the way items are sorted. (For backwards compatibility, \f[C]locale\f[R] may be used instead of \f[C]lang\f[R], but this use is deprecated.) +.RS +.PP +A BCP 47 language tag is expected: for example, \f[C]en\f[R], +\f[C]de\f[R], \f[C]en-US\f[R], \f[C]fr-CA\f[R], \f[C]ug-Cyrl\f[R]. +The unicode extension syntax (after \f[C]-u-\f[R]) may be used to +specify options for collation (sorting) more precisely. +Here are some examples: +.IP \[bu] 2 +\f[C]zh-u-co-pinyin\f[R] \[en] Chinese with the Pinyin collation. +.IP \[bu] 2 +\f[C]es-u-co-trad\f[R] \[en] Spanish with the traditional collation +(with \f[C]Ch\f[R] sorting after \f[C]C\f[R]). +.IP \[bu] 2 +\f[C]fr-u-kb\f[R] \[en] French with \[lq]backwards\[rq] accent sorting +(with \f[C]cot\['e]\f[R] sorting after \f[C]c\[^o]te\f[R]). +.IP \[bu] 2 +\f[C]en-US-u-kf-upper\f[R] \[en] English with uppercase letters sorting +before lower (default is lower before upper). +.RE .TP \f[B]\f[CB]notes-after-punctuation\f[B]\f[R] If true (the default), pandoc will put footnote citations after -- cgit v1.2.3 From 3f09f53459b877f53072efbf57dec21fa37280b5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 13 May 2021 21:11:52 -0700 Subject: Implement curly-brace syntax for Markdown citation keys. The change provides a way to use citation keys that contain special characters not usable with the standard citation key syntax. Example: `@{foo_bar{x}'}` for the key `foo_bar{x}`. Closes #6026. The change requires adding a new parameter to the `citeKey` parser from Text.Pandoc.Parsing [API change]. Markdown reader: recognize @{..} syntax for citatinos. Markdown writer: use @{..} syntax for citations when needed. Update manual with curly-brace syntax for citations. Closes #6026. --- MANUAL.txt | 15 ++++++++++----- src/Text/Pandoc/Parsing.hs | 18 +++++++++++++----- src/Text/Pandoc/Readers/Markdown.hs | 6 +++--- src/Text/Pandoc/Readers/Org/Inlines.hs | 8 ++++---- src/Text/Pandoc/Writers/Markdown/Inline.hs | 9 +++++++-- test/command/6026.md | 19 +++++++++++++++++++ 6 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 test/command/6026.md diff --git a/MANUAL.txt b/MANUAL.txt index 75fe599e2..6f06d1e8a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4949,12 +4949,15 @@ Inline and regular footnotes may be mixed freely. #### Extension: `citations` #### Markdown citations go inside square brackets and are separated -by semicolons. Each citation must have a key, composed of '@' + -the citation identifier from the database, and may optionally -have a prefix, a locator, and a suffix. The citation key must -begin with a letter, digit, or `_`, and may contain +by semicolons. Each citation must have a key and may optionally +have a prefix, a locator, and a suffix. The citation key +consists of `@` plus the citation identifier, possibly +enclosed in curly braces. If the identifier starts +with a letter, digit, or `_`, followed by zero or more alphanumerics, `_`, and internal punctuation characters -(`:.#$%&-+?<>~/`). Here are some examples: +(`:.#$%&-+?<>~/`), then the curly braces may be omitted. +Identifiers may not contain whitespace characters or unbalanced +curly braces. Here are some examples: Blah blah [see @doe99, pp. 33-35; also @smith04, chap. 1]. @@ -4962,6 +4965,8 @@ alphanumerics, `_`, and internal punctuation characters Blah blah [@smith04; @doe99]. + Blah blah [@{https://example.com/bib?name=foobar&date=2000}, p. 33]. + `pandoc` detects locator terms in the [CSL locale files]. Either abbreviated or unabbreviated forms are accepted. In the `en-US` locale, locator terms can be written in either singular or plural forms, diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index cbe9993c6..0bb794ba1 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1605,19 +1605,27 @@ nested p = do return res citeKey :: (Stream s m Char, UpdateSourcePos s Char, HasLastStrPosition st) - => ParserT s st m (Bool, Text) -citeKey = try $ do + => Bool -- ^ If True, allow expanded @{..} syntax. + -> ParserT s st m (Bool, Text) +citeKey allowBraced = try $ do guard =<< notAfterString suppress_author <- option False (True <$ char '-') char '@' + key <- simpleCiteIdentifier + <|> if allowBraced + then charsInBalanced '{' '}' (satisfy (not . isSpace)) + else mzero + return (suppress_author, key) + +simpleCiteIdentifier :: (Stream s m Char, UpdateSourcePos s Char) + => ParserT s st m Text +simpleCiteIdentifier = do firstChar <- alphaNum <|> char '_' <|> char '*' -- @* for wildcard in nocite let regchar = satisfy (\c -> isAlphaNum c || c == '_') let internal p = try $ p <* lookAhead regchar rest <- many $ regchar <|> internal (oneOf ":.#$%&-+?<>~/") <|> try (oneOf ":/" <* lookAhead (char '/')) - let key = firstChar:rest - return (suppress_author, T.pack key) - + return $ T.pack $ firstChar:rest token :: (Stream s m t) => (t -> Text) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 2d20ff018..34f16ab4e 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2094,7 +2094,7 @@ cite = do textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do - (suppressAuthor, key) <- citeKey + (suppressAuthor, key) <- citeKey True -- If this is a reference to an earlier example list item, -- then don't parse it as a citation. If the example list -- item comes later, we'll parse it here and figure out in @@ -2174,7 +2174,7 @@ prefix = trimInlinesF . mconcat <$> manyTill inline (char ']' <|> lookAhead (try $ do optional (try (char ';' >> spnl)) - citeKey + citeKey True return ']')) citeList :: PandocMonad m => MarkdownParser m (F [Citation]) @@ -2183,7 +2183,7 @@ citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl) citation :: PandocMonad m => MarkdownParser m (F Citation) citation = try $ do pref <- prefix - (suppress_author, key) <- citeKey + (suppress_author, key) <- citeKey True suff <- suffix noteNum <- stateNoteNumber <$> getState return $ do diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 054f2611a..6862dd71e 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -263,7 +263,7 @@ berkeleyCitationList = try $ do where citationListPart :: PandocMonad m => OrgParser m (F Inlines) citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do - notFollowedBy' citeKey + notFollowedBy' $ citeKey False notFollowedBy (oneOf ";]") inline @@ -278,7 +278,7 @@ berkeleyBareTag' = try $ void (string "cite") berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation]) berkeleyTextualCite = try $ do - (suppressAuthor, key) <- citeKey + (suppressAuthor, key) <- citeKey False returnF . return $ Citation { citationId = key , citationPrefix = mempty @@ -351,7 +351,7 @@ citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces) citation :: PandocMonad m => OrgParser m (F Citation) citation = try $ do pref <- prefix - (suppress_author, key) <- citeKey + (suppress_author, key) <- citeKey False suff <- suffix return $ do x <- pref @@ -368,7 +368,7 @@ citation = try $ do } where prefix = trimInlinesF . mconcat <$> - manyTill inline (char ']' <|> (']' <$ lookAhead citeKey)) + manyTill inline (char ']' <|> (']' <$ lookAhead (citeKey False))) suffix = try $ do hasSpace <- option False (notFollowedBy nonspaceChar >> return True) skipSpaces diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index e35e1a0b9..2062050e4 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -491,11 +491,16 @@ inlineToMarkdown opts (Cite (c:cs) lst) rest <- mapM convertOne cs let inbr = suffs <+> joincits rest br = if isEmpty inbr then empty else char '[' <> inbr <> char ']' - return $ literal ("@" <> citationId c) <+> br + return $ literal ("@" <> maybeInBraces (citationId c)) <+> br else do cits <- mapM convertOne (c:cs) return $ literal "[" <> joincits cits <> literal "]" where + maybeInBraces key = + case readWith (citeKey False >> spaces >> eof) + defaultParserState ("@" <> key) of + Left _ -> "{" <> key <> "}" + Right _ -> key joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty) convertOne Citation { citationId = k , citationPrefix = pinlines @@ -504,7 +509,7 @@ inlineToMarkdown opts (Cite (c:cs) lst) = do pdoc <- inlineListToMarkdown opts pinlines sdoc <- inlineListToMarkdown opts sinlines - let k' = literal (modekey m <> "@" <> k) + let k' = literal (modekey m <> "@" <> maybeInBraces k) r = case sinlines of Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc _ -> k' <+> sdoc diff --git a/test/command/6026.md b/test/command/6026.md new file mode 100644 index 000000000..5e18a5f42 --- /dev/null +++ b/test/command/6026.md @@ -0,0 +1,19 @@ +``` +% pandoc -t native +@{https://openreview.net/forum?id=HkwoSDPgg} + +@https://openreview.net/forum?id=HkwoSDPgg +^D +[Para [Cite [Citation {citationId = "https://openreview.net/forum?id=HkwoSDPgg", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 1, citationHash = 0}] [Str "@https://openreview.net/forum?id=HkwoSDPgg"]] +,Para [Cite [Citation {citationId = "https://openreview.net/forum?id", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 2, citationHash = 0}] [Str "@https://openreview.net/forum?id"],Str "=HkwoSDPgg"]] +``` +``` +% pandoc -t markdown +@{https://openreview.net/forum?id=HkwoSDPgg} + +@https://openreview.net/forum?id=HkwoSDPgg +^D +@{https://openreview.net/forum?id=HkwoSDPgg} + +@https://openreview.net/forum?id=HkwoSDPgg +``` -- cgit v1.2.3 From 875f8f36545d1c21fa3d29c52c40517a667f2574 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 May 2021 11:58:58 +0200 Subject: HTML reader: don't fail on unmatched closing "script" tag. Prevent the reader from crashing if the HTML input contains an unmatched closing `` tag. Fixes: #7282 --- src/Text/Pandoc/Readers/HTML.hs | 16 +++++++++------- test/command/7282.md | 7 +++++++ 2 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 test/command/7282.md diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index f5c8a2277..0a9d67e35 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -942,13 +942,15 @@ getTagName (TagClose t) = Just t getTagName _ = Nothing isInlineTag :: Tag Text -> Bool -isInlineTag t = - isCommentTag t || - case getTagName t of - Nothing -> False - Just "script" -> "math/tex" `T.isPrefixOf` fromAttrib "type" t - Just x -> x `Set.notMember` blockTags || - T.take 1 x == "?" -- processing instr. +isInlineTag t = isCommentTag t || case t of + TagOpen "script" _ -> "math/tex" `T.isPrefixOf` fromAttrib "type" t + TagClose "script" -> True + TagOpen name _ -> isInlineTagName name + TagClose name -> isInlineTagName name + _ -> False + where isInlineTagName x = + x `Set.notMember` blockTags || + T.take 1 x == "?" -- processing instr. isBlockTag :: Tag Text -> Bool isBlockTag t = isBlockTagName || isTagComment t diff --git a/test/command/7282.md b/test/command/7282.md new file mode 100644 index 000000000..4ed6b5b3e --- /dev/null +++ b/test/command/7282.md @@ -0,0 +1,7 @@ +Don't crash on unmatched closing tag. +``` +% pandoc -f html -t native + +^D +[] +``` -- cgit v1.2.3 From 17d96404f5b6f5f080329e220a0784f10c364f2d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 May 2021 16:19:20 +0200 Subject: Docx writer: allow multirow table headers --- src/Text/Pandoc/Writers/Docx/Table.hs | 42 +++++++++++++++++------------ test/docx/golden/table_with_list_cell.docx | Bin 10225 -> 10238 bytes test/docx/golden/tables.docx | Bin 10266 -> 10279 bytes 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index bb931bf08..ccc325fbe 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -42,11 +42,12 @@ tableToOpenXML blocksToOpenXML gridTable = do then return [] else withParaPropM (pStyleM "Table Caption") $ blocksToOpenXML captionBlocks - head' <- cellGridToOpenXML blocksToOpenXML thead - bodies <- mapM (cellGridToOpenXML blocksToOpenXML) tbodies - foot' <- cellGridToOpenXML blocksToOpenXML tfoot + head' <- cellGridToOpenXML blocksToOpenXML HeadRow thead + bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow) tbodies + foot' <- cellGridToOpenXML blocksToOpenXML FootRow tfoot let hasHeader = not . null . indices . partRowAttrs $ thead + let hasFooter = not . null . indices . partRowAttrs $ tfoot -- for compatibility with Word <= 2007, we include a val with a bitmask -- 0×0020 Apply first row conditional formatting -- 0×0040 Apply last row conditional formatting @@ -61,7 +62,7 @@ tableToOpenXML blocksToOpenXML gridTable = do ( mknode "w:tblStyle" [("w:val","Table")] () : mknode "w:tblW" tblWattr () : mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") - ,("w:lastRow","0") + ,("w:lastRow",if hasFooter then "1" else "0") ,("w:firstColumn","0") ,("w:lastColumn","0") ,("w:noHBand","0") @@ -77,6 +78,9 @@ tableToOpenXML blocksToOpenXML gridTable = do modify $ \s -> s { stInTable = False } return $ captionXml ++ [Elem tbl] +-- | Parts of a table +data RowType = HeadRow | BodyRow | FootRow + alignmentToString :: Alignment -> Text alignmentToString = \case AlignLeft -> "left" @@ -104,22 +108,23 @@ tableLayout specs = , [ ("w:type", "pct"), ("w:w", tshow rowwidth) ]) cellGridToOpenXML :: PandocMonad m - => ([Block] -> WS m [Content]) - -> Part - -> WS m [Element] -cellGridToOpenXML blocksToOpenXML part@(Part _ _ rowAttrs) = + => ([Block] -> WS m [Content]) + -> RowType + -> Part + -> WS m [Element] +cellGridToOpenXML blocksToOpenXML rowType part@(Part _ _ rowAttrs) = if null (indices rowAttrs) then return mempty - else mapM (rowToOpenXML blocksToOpenXML) $ partToRows part + else mapM (rowToOpenXML blocksToOpenXML) $ partToRows rowType part data OOXMLCell = OOXMLCell Attr Alignment RowSpan ColSpan [Block] | OOXMLCellMerge ColSpan -data OOXMLRow = OOXMLRow Attr [OOXMLCell] +data OOXMLRow = OOXMLRow RowType Attr [OOXMLCell] -partToRows :: Part -> [OOXMLRow] -partToRows part = +partToRows :: RowType -> Part -> [OOXMLRow] +partToRows rowType part = let toOOXMLCell :: RowIndex -> ColIndex -> GridCell -> [OOXMLCell] toOOXMLCell ridx cidx = \case @@ -132,7 +137,7 @@ partToRows part = " at index " ++ show idx' _ -> mempty mkRow :: (RowIndex, Attr) -> OOXMLRow - mkRow (ridx, attr) = OOXMLRow attr + mkRow (ridx, attr) = OOXMLRow rowType attr . concatMap (uncurry $ toOOXMLCell ridx) . assocs . rowArray ridx @@ -143,12 +148,17 @@ rowToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) -> OOXMLRow -> WS m Element -rowToOpenXML blocksToOpenXML (OOXMLRow _attr cells) = do +rowToOpenXML blocksToOpenXML (OOXMLRow rowType _attr cells) = do xmlcells <- mapM (ooxmlCellToOpenXML blocksToOpenXML) cells + let addTrPr = case rowType of + HeadRow -> (mknode "w:trPr" [] + [mknode "w:tblHeader" [("w:val", "true")] ()] :) + BodyRow -> id + FootRow -> id -- let align' = case align of -- AlignDefault -> colAlign -- _ -> align - return $ mknode "w:tr" [] xmlcells + return $ mknode "w:tr" [] (addTrPr xmlcells) ooxmlCellToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) @@ -161,8 +171,6 @@ ooxmlCellToOpenXML blocksToOpenXML = \case , mknode "w:vMerge" [("w:val", "continue")] () ] , mknode "w:p" [] [mknode "w:pPr" [] ()]] OOXMLCell _attr align rowspan (ColSpan colspan) contents -> do - -- we handle rowspans via 'leftpad', so we can ignore those here - compactStyle <- pStyleM "Compact" es <- withParaProp (alignmentFor align) $ blocksToOpenXML contents -- Table cells require a element, even an empty one! diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index 6427d475c..e51910770 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 470eac2ae..8029774a9 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ -- cgit v1.2.3 From 3ec5726c9b7b9f7cc41ca5f26a80c347e20afc9a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 May 2021 16:49:19 +0200 Subject: Docx writer: fix alignment for cells. This fixes a regression introduced with the in the colspan/rowspan changes that caused column alignments to be ignored. The column alignment is used only if a default alignment is specified at the cell level; otherwise the cell-level alignment takes precedence. --- src/Text/Pandoc/Writers/Docx/Table.hs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index ccc325fbe..e7fc82a10 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -38,13 +38,14 @@ tableToOpenXML blocksToOpenXML gridTable = do gridTable let (Caption _maybeShortCaption captionBlocks) = caption let captionStr = stringify captionBlocks + let aligns = map fst $ elems colspecs captionXml <- if null captionBlocks then return [] else withParaPropM (pStyleM "Table Caption") $ blocksToOpenXML captionBlocks - head' <- cellGridToOpenXML blocksToOpenXML HeadRow thead - bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow) tbodies - foot' <- cellGridToOpenXML blocksToOpenXML FootRow tfoot + head' <- cellGridToOpenXML blocksToOpenXML HeadRow aligns thead + bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow aligns) tbodies + foot' <- cellGridToOpenXML blocksToOpenXML FootRow aligns tfoot let hasHeader = not . null . indices . partRowAttrs $ thead let hasFooter = not . null . indices . partRowAttrs $ tfoot @@ -110,12 +111,14 @@ tableLayout specs = cellGridToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) -> RowType + -> [Alignment] -> Part -> WS m [Element] -cellGridToOpenXML blocksToOpenXML rowType part@(Part _ _ rowAttrs) = +cellGridToOpenXML blocksToOpenXML rowType aligns part@(Part _ _ rowAttrs) = if null (indices rowAttrs) then return mempty - else mapM (rowToOpenXML blocksToOpenXML) $ partToRows rowType part + else mapM (rowToOpenXML blocksToOpenXML) $ + partToRows rowType aligns part data OOXMLCell = OOXMLCell Attr Alignment RowSpan ColSpan [Block] @@ -123,13 +126,17 @@ data OOXMLCell data OOXMLRow = OOXMLRow RowType Attr [OOXMLCell] -partToRows :: RowType -> Part -> [OOXMLRow] -partToRows rowType part = +partToRows :: RowType -> [Alignment] -> Part -> [OOXMLRow] +partToRows rowType aligns part = let - toOOXMLCell :: RowIndex -> ColIndex -> GridCell -> [OOXMLCell] - toOOXMLCell ridx cidx = \case + toOOXMLCell :: Alignment -> RowIndex -> ColIndex -> GridCell -> [OOXMLCell] + toOOXMLCell columnAlign ridx cidx = \case ContentCell attr align rowspan colspan blocks -> - [OOXMLCell attr align rowspan colspan blocks] + -- Respect non-default, cell specific alignment. + let align' = case align of + AlignDefault -> columnAlign + _ -> align + in [OOXMLCell attr align' rowspan colspan blocks] ContinuationCell idx'@(ridx',cidx') | ridx /= ridx', cidx == cidx' -> case (partCellArray part)!idx' of (ContentCell _ _ _ colspan _) -> [OOXMLCellMerge colspan] @@ -138,7 +145,9 @@ partToRows rowType part = _ -> mempty mkRow :: (RowIndex, Attr) -> OOXMLRow mkRow (ridx, attr) = OOXMLRow rowType attr - . concatMap (uncurry $ toOOXMLCell ridx) + . mconcat + . zipWith (\align -> uncurry $ toOOXMLCell align ridx) + aligns . assocs . rowArray ridx $ partCellArray part @@ -155,9 +164,6 @@ rowToOpenXML blocksToOpenXML (OOXMLRow rowType _attr cells) = do [mknode "w:tblHeader" [("w:val", "true")] ()] :) BodyRow -> id FootRow -> id - -- let align' = case align of - -- AlignDefault -> colAlign - -- _ -> align return $ mknode "w:tr" [] (addTrPr xmlcells) ooxmlCellToOpenXML :: PandocMonad m -- cgit v1.2.3 From 0e5b840c2a340f8b8c2e96148046af2dfd493bcb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 14 May 2021 08:48:59 -0700 Subject: LaTeX template: Move CSL macro defs before header-includes. This allows them to be overriden by header-includes. Closes #7286. --- data/templates/default.latex | 50 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index f39e56c79..142fe3e55 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -325,6 +325,31 @@ $endif$ $if(pagestyle)$ \pagestyle{$pagestyle$} $endif$ +$if(csl-refs)$ +\newlength{\cslhangindent} +\setlength{\cslhangindent}{1.5em} +\newlength{\csllabelwidth} +\setlength{\csllabelwidth}{3em} +\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing + {% don't indent paragraphs + \setlength{\parindent}{0pt} + % turn on hanging indent if param 1 is 1 + \ifodd #1 + \let\oldpar\par + \def\par{\hangindent=\cslhangindent\oldpar} + \fi + % set entry spacing + \ifnum #2 > 0 + \setlength{\parskip}{#2\baselineskip} + \fi + }% + {} +\usepackage{calc} +\newcommand{\CSLBlock}[1]{#1\hfill\break} +\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}} +\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break} +\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} +$endif$ $for(header-includes)$ $header-includes$ $endfor$ @@ -375,31 +400,6 @@ $endif$ $if(nocite-ids)$ \nocite{$for(nocite-ids)$$it$$sep$, $endfor$} $endif$ -$if(csl-refs)$ -\newlength{\cslhangindent} -\setlength{\cslhangindent}{1.5em} -\newlength{\csllabelwidth} -\setlength{\csllabelwidth}{3em} -\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing - {% don't indent paragraphs - \setlength{\parindent}{0pt} - % turn on hanging indent if param 1 is 1 - \ifodd #1 - \let\oldpar\par - \def\par{\hangindent=\cslhangindent\oldpar} - \fi - % set entry spacing - \ifnum #2 > 0 - \setlength{\parskip}{#2\baselineskip} - \fi - }% - {} -\usepackage{calc} -\newcommand{\CSLBlock}[1]{#1\hfill\break} -\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}} -\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break} -\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} -$endif$ $if(csquotes)$ \usepackage{csquotes} $endif$ -- cgit v1.2.3 From b9b1ec586a1e9e49af5dfef903678fbd029068eb Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 May 2021 17:51:29 +0200 Subject: changelog.md: fix Markdown, marking `

` as code. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 265e7336b..ccf012a7c 100644 --- a/changelog.md +++ b/changelog.md @@ -95,7 +95,7 @@ then that value is used as `content-type` and the span is put inside a `` element. Otherwise a `` element is used instead. - + Reduce unnecessary use of

elements for wrapping (#7227). + + Reduce unnecessary use of `

` elements for wrapping (#7227). The `

` element is used for wrapping in cases were the contents would otherwise not be allowed in a certain context. Unnecessary wrapping is avoided, especially around quotes (`` elements). -- cgit v1.2.3 From 76a4e7127be1116b67ae531f56aadb05963813b5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 14 May 2021 10:09:02 -0700 Subject: Beamer writer: support exampleblock and alertblock. A block will be rendered as an exampleblock if the heading has class `example` and alertblock if it has class `alert`. Closes #7278. --- MANUAL.txt | 6 +++++- src/Text/Pandoc/Writers/LaTeX.hs | 11 ++++++++--- test/command/7278.md | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 test/command/7278.md diff --git a/MANUAL.txt b/MANUAL.txt index 6f06d1e8a..cdfb0e332 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5666,7 +5666,11 @@ rules: * A heading at the slide level always starts a new slide. * Headings *below* the slide level in the hierarchy create - headings *within* a slide. + headings *within* a slide. (In beamer, a "block" will be + created. If the heading has the class `example`, an + `exampleblock` environment will be used; if it has the class + `alert`, an `alertblock` will be used; otherwise a regular + `block` will be used.) * Headings *above* the slide level in the hierarchy create "title slides," which just contain the section title diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 8b1f3df1d..d8722876e 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -290,7 +290,12 @@ blockToLaTeX :: PandocMonad m => Block -- ^ Block to convert -> LW m (Doc Text) blockToLaTeX Null = return empty -blockToLaTeX (Div attr@(identifier,"block":_,_) (Header _ _ ils : bs)) = do +blockToLaTeX (Div attr@(identifier,"block":dclasses,_) + (Header _ _ ils : bs)) = do + let blockname + | "example" `elem` dclasses = "exampleblock" + | "alert" `elem` dclasses = "alertblock" + | otherwise = "block" ref <- toLabel identifier let anchor = if T.null identifier then empty @@ -298,8 +303,8 @@ blockToLaTeX (Div attr@(identifier,"block":_,_) (Header _ _ ils : bs)) = do braces (literal ref) <> braces empty title' <- inlineListToLaTeX ils contents <- blockListToLaTeX bs - wrapDiv attr $ ("\\begin{block}" <> braces title' <> anchor) $$ - contents $$ "\\end{block}" + wrapDiv attr $ ("\\begin" <> braces blockname <> braces title' <> anchor) $$ + contents $$ "\\end" <> braces blockname blockToLaTeX (Div (identifier,"slide":dclasses,dkvs) (Header _ (_,hclasses,hkvs) ils : bs)) = do -- note: [fragile] is required or verbatim breaks diff --git a/test/command/7278.md b/test/command/7278.md new file mode 100644 index 000000000..dcf71c29f --- /dev/null +++ b/test/command/7278.md @@ -0,0 +1,38 @@ +``` +% pandoc -t beamer +# Slide + +Some blocks: + +## example block title {.example} + +text in block + +## alert block title {.alert} + +text in block + +## block title + +text in block +^D +\begin{frame}{Slide} +\protect\hypertarget{slide}{} +Some blocks: + +\begin{exampleblock}{example block title} +\protect\hypertarget{example-block-title}{} +text in block +\end{exampleblock} + +\begin{alertblock}{alert block title} +\protect\hypertarget{alert-block-title}{} +text in block +\end{alertblock} + +\begin{block}{block title} +\protect\hypertarget{block-title}{} +text in block +\end{block} +\end{frame} +``` -- cgit v1.2.3 From 247b1cd18bfbd06e4cb0175d9ac14dc888ccff66 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 14 May 2021 10:20:53 -0700 Subject: Update changelog. --- changelog.md | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index ccf012a7c..0deb346ce 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,9 @@ + Improve include file functions [API change]. Remove old `insertIncludedFileF`. Give `insertIncludedFile` a more general type, allowing it to be used where `insertIncludedFileF` was. + + Add parameter to the `citeKey` parser from + Text.Pandoc.Parsing, which controls whether the `@{..}` + syntax is allowed [API change]. * Text.Pandoc.Error: Modified the constructor `PandocParsecError` to take a `Sources` rather than a `Text` as first argument, so parse error locations @@ -43,9 +46,17 @@ fiction, of continuing paragraphs quoting the same speaker without an end quote. It also helps with quotes that break over lines in line blocks. - * Markdown reader: Use MetaInlines not MetaBlocks for multimarkdown - metadata fields. This gives better results in converting to e.g. - pandoc markdown. + * Markdown reader: + + + Use MetaInlines not MetaBlocks for multimarkdown + metadata fields. This gives better results in converting to e.g. + pandoc markdown. + + Implement curly-brace syntax for Markdown citation keys (#6026). + The change provides a way to use citation keys that contain + special characters not usable with the standard citation + key syntax. Example: `@{foo_bar{x}'}` for the key `foo_bar{x}`. + It also allows separating citation keys from immediately + following text, e.g. `@{foo}A`. * RST reader: @@ -61,6 +72,9 @@ * Docx reader: Add handling of vml image objects (#7257, mbrackeantidot). + * HTML reader: Don't fail on unmatched closing "script" tag + (Albert Krenkel, #7282). + * DocBook/JATS readers: + Fix mathml regression caused by the switch in XML libraries (#7173). @@ -68,9 +82,14 @@ * Plain writer: handle superscript unicode minus (#7276). - * LaTeX writer: better handling of line breaks in simple tables (#7272). - Now we also handle the case where they're embedded in other - elements, e.g. spans. + * LaTeX writer: + + + Better handling of line breaks in simple tables (#7272). + Now we also handle the case where they're embedded in other + elements, e.g. spans. + + For beamer output, support `exampleblock` and `alertblock` (#7278). + A block will be rendered as an `exampleblock` if the heading + has class `example` and an `alertblock` if it has class `alert`. * EPUB Writer: Fix belongs-to-collection XML id choice (#7267, nuew). The epub writer previously used the same XML id for both the book @@ -121,19 +140,13 @@ Krewinkel). + Extract Table handling into separate module (Albert Krewinkel). + Support colspans and rowspans in tables (Albert Krewinkel, #6315). + + Support multirow table headers (Albert Krewinkel). + Improve integration of settings from reference.docx (#1209). This change allows users to create a reference.docx that sets `w:proofState` for spelling or grammar to `dirty`, so that spell/grammar checking will be triggered on the generated docx. - * Writers: Recognize custom syntax definitions (#7241, Jan Tojnar). - Languages defined using `--syntax-definition` were not recognized by - `languagesByExtension`. This patch corrects that, allowing the writers - to see all custom definitions. The LaTeX writer still uses the default - syntax map, but that's okay in that context, since - `--syntax-definition` won't create new listings styles. - * Markdown writer: + Use cleaner braceless syntax for code blocks with a @@ -143,6 +156,7 @@ rather than the *first* character in determining whether quotes were needed. So we got spurious quotes in some cases and didn't get necessary quotes in others. + + Use `@{..}` syntax for citations when needed. * Commonmark writer: Use backslash escapes for `<` and `|`... instead of entities (#7208). @@ -156,6 +170,8 @@ This should make svgnames and x11names work properly. + Fix bad vertical spacing after bibliography (#7234, badumont). + List of figures before list of tables (#7235, Julien Dutant). + + Move CSL macro definitions before header-includes so they can be + overridden (#7286). * ConTeXt template: List of figures before list of tables (#7235, Julien Dutant). @@ -177,7 +193,12 @@ * Add new internal module Text.Pandoc.Writers.GridTable (Albert Krewinkel). * Text.Pandoc.Highlighting: Change type of `languagesByExtension`, adding - a parameter for a `SyntaxMap` [API change] (Jan Tojnar). + a parameter for a `SyntaxMap` [API change] (Jan Tojnar, #7241). + Languages defined using `--syntax-definition` were not recognized by + `languagesByExtension`. This patch corrects that, allowing the writers + to see all custom definitions. The LaTeX writer still uses the default + syntax map, but that's okay in that context, since + `--syntax-definition` won't create new listings styles. * Use metadata's `lang` for the lang parameter of citeproc, overriding `localeLanguage`. -- cgit v1.2.3 From 013e4a3164115bf84f3e1964f21b7cd7f020b86a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 May 2021 21:31:24 +0200 Subject: HTML reader: keep h1 tags as normal headers (#7274) The tags `` and `<h1 class="title">` often contain the same information, so the latter was dropped from the document. However, as this can lead to loss of information, the heading is now always retained. Use `--shift-heading-level-by=-1` to turn the `<h1>` into the document title, or a filter to restore the previous behavior. Closes: #2293 --- src/Text/Pandoc/Readers/HTML.hs | 6 +----- test/html-reader.native | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 0a9d67e35..b305de7b5 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -499,17 +499,13 @@ pHeader = try $ do tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"]) (const True) let attr = toStringAttr attr' - let bodyTitle = TagOpen tagtype attr' ~== TagOpen ("h1" :: Text) - [("class","title")] level <- headerLevel tagtype contents <- trimInlines . mconcat <$> manyTill inline (pCloses tagtype <|> eof) let ident = fromMaybe "" $ lookup "id" attr let classes = maybe [] T.words $ lookup "class" attr let keyvals = [(k,v) | (k,v) <- attr, k /= "class", k /= "id"] attr'' <- registerHeader (ident, classes, keyvals) contents - return $ if bodyTitle - then mempty -- skip a representation of the title in the body - else B.headerWith attr'' level contents + return $ B.headerWith attr'' level contents pHrule :: PandocMonad m => TagParser m Blocks pHrule = do diff --git a/test/html-reader.native b/test/html-reader.native index 04ec55d1e..880561a93 100644 --- a/test/html-reader.native +++ b/test/html-reader.native @@ -1,5 +1,6 @@ Pandoc (Meta {unMeta = fromList [("generator",MetaInlines [Str "pandoc"]),("title",MetaInlines [Str "Pandoc",Space,Str "Test",Space,Str "Suite"])]}) -[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc.",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber's",Space,Str "markdown",Space,Str "test",Space,Str "suite."] +[Header 1 ("pandoc-test-suite",["title"],[]) [Str "Pandoc",Space,Str "Test",Space,Str "Suite"] +,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc.",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber's",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,HorizontalRule ,Header 1 ("headers",[],[]) [Str "Headers"] ,Header 2 ("level-2-with-an-embedded-link",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link ("",[],[]) [Str "embedded",Space,Str "link"] ("/url","")] -- cgit v1.2.3 From 46baf6659dad59d1d633894b4db886a2e1311d43 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 May 2021 22:43:38 +0200 Subject: MANUAL: add info about YAML escape sequences, link to spec. Closes: #7152 --- MANUAL.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index cdfb0e332..7ab07264d 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4177,10 +4177,12 @@ option is used. All of the metadata will appear in a single block at the beginning of the document. Note that [YAML] escaping rules must be followed. Thus, for example, -if a title contains a colon, it must be quoted. The pipe character -(`|`) can be used to begin an indented block that will be interpreted -literally, without need for escaping. This form is necessary -when the field contains blank lines or block-level formatting: +if a title contains a colon, it must be quoted, and if it contains a +backslash escape, then it must be ensured that it is not treated as a +[YAML escape sequence]. The pipe character (`|`) can be used to begin +an indented block that will be interpreted literally, without need for +escaping. This form is necessary when the field contains blank lines +or block-level formatting: --- title: 'This is the title: it contains a colon' @@ -4255,6 +4257,7 @@ following restrictions apply: example, you can't use a reference link in these contexts if the link definition is somewhere else in the document. +[YAML escape sequence]: https://yaml.org/spec/1.2/spec.html#id2776092 ## Backslash escapes -- cgit v1.2.3 From 0794862aacdb5d28616dadc3d681a63c36251212 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 15 May 2021 16:36:13 +0200 Subject: HTML writer: parse `<header>` as a Div HTML5 `<header>` elements are treated like `<div>` elements. --- src/Text/Pandoc/Readers/HTML.hs | 2 ++ test/Tests/Readers/HTML.hs | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index b305de7b5..0a9e4addf 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -205,6 +205,7 @@ block = ((do | otherwise -> pDiv "section" -> pDiv + "header" -> pDiv "main" -> pDiv "figure" -> pFigure "iframe" -> pIframe @@ -404,6 +405,7 @@ pLineBlock = try $ do isDivLike :: Text -> Bool isDivLike "div" = True isDivLike "section" = True +isDivLike "header" = True isDivLike "main" = True isDivLike _ = False diff --git a/test/Tests/Readers/HTML.hs b/test/Tests/Readers/HTML.hs index f23af2cb1..7f5849991 100644 --- a/test/Tests/Readers/HTML.hs +++ b/test/Tests/Readers/HTML.hs @@ -99,11 +99,15 @@ tests = [ testGroup "base tag" plain (codeWith ("",["sample"],[]) "Answer is 42") ] , testGroup "var" - [ - test html "inline var block" $ - "<var>result</var>" =?> - plain (codeWith ("",["variable"],[]) "result") - ] + [ test html "inline var block" $ + "<var>result</var>" =?> + plain (codeWith ("",["variable"],[]) "result") + ] + , testGroup "header" + [ test htmlNativeDivs "<header> is parsed as a div" $ + "<header id=\"title\">Title</header>" =?> + divWith ("title", mempty, mempty) (plain "Title") + ] , askOption $ \(QuickCheckTests numtests) -> testProperty "Round trip" $ withMaxSuccess (if QuickCheckTests numtests == defaultValue -- cgit v1.2.3 From 97a0ee23bc2d7ec90d193f41fb89f91bc66f6528 Mon Sep 17 00:00:00 2001 From: Thomas Hodgson <hello@twshodgson.net> Date: Sat, 15 May 2021 18:56:48 +0200 Subject: HTML-based slide shows: add support for institute (#7289) Add institute variable (string or list) to HTML-based slide formats. --- data/templates/default.dzslides | 2 +- data/templates/default.revealjs | 3 +++ data/templates/default.s5 | 3 +++ data/templates/default.slideous | 7 +++++++ data/templates/default.slidy | 5 +++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/data/templates/default.dzslides b/data/templates/default.dzslides index 11103ab81..5e2749bbf 100644 --- a/data/templates/default.dzslides +++ b/data/templates/default.dzslides @@ -171,7 +171,7 @@ $if(subtitle)$ <h1 class="subtitle">$subtitle$</h1> $endif$ <footer> - $if(author)$<span class="author">$for(author)$$author$$sep$, $endfor$</span>$endif$ · $if(date)$<span class="date">$date$</span>$endif$ + $if(author)$<span class="author">$for(author)$$author$$sep$, $endfor$</span> · $endif$$if(institute)$<span class="institute">$for(institute)$$institute$$sep$, $endfor$</span> · $endif$$if(date)$<span class="date">$date$</span>$endif$ </footer> </section> $endif$ diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs index e90b32899..838d5465f 100644 --- a/data/templates/default.revealjs +++ b/data/templates/default.revealjs @@ -52,6 +52,9 @@ $endif$ $for(author)$ <p class="author">$author$</p> $endfor$ +$for(institute)$ + <p class="institute">$institute$</p> +$endfor$ $if(date)$ <p class="date">$date$</p> $endif$ diff --git a/data/templates/default.s5 b/data/templates/default.s5 index d76111284..27e681fe4 100644 --- a/data/templates/default.s5 +++ b/data/templates/default.s5 @@ -61,6 +61,9 @@ $endif$ $if(author)$ <h3 class="author">$for(author)$$author$$sep$<br/>$endfor$</h3> $endif$ +$if(institute)$ + <h3 class="institute">$for(institute)$$institute$$sep$<br/>$endfor$</h3> +$endif$ $if(date)$ <h4 class="date">$date$</h4> $endif$ diff --git a/data/templates/default.slideous b/data/templates/default.slideous index ebf582dca..70d144bc7 100644 --- a/data/templates/default.slideous +++ b/data/templates/default.slideous @@ -63,9 +63,16 @@ $if(title)$ $if(subtitle)$ <h1 class="subtitle">$subtitle$</h1> $endif$ +$if(author)$ <p class="author"> $for(author)$$author$$sep$<br/>$endfor$ </p> +$endif$ +$if(institute)$ + <p class="institute"> +$for(institute)$$institute$$sep$<br/>$endfor$ + </p> +$endif$ $if(date)$ <p class="date">$date$</p> $endif$ diff --git a/data/templates/default.slidy b/data/templates/default.slidy index 6b9776858..a4ac97f9d 100644 --- a/data/templates/default.slidy +++ b/data/templates/default.slidy @@ -52,6 +52,11 @@ $if(author)$ $for(author)$$author$$sep$<br/>$endfor$ </p> $endif$ +$if(institute)$ + <p class="institute"> +$for(institute)$$institute$$sep$<br/>$endfor$ + </p> +$endif$ $if(date)$ <p class="date">$date$</p> $endif$ -- cgit v1.2.3 From bf73a151974d9bca89901052255cb10628b72960 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 May 2021 09:58:43 -0700 Subject: MANUAL: note that `institute` variable works for HTML-based slides. --- MANUAL.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 7ab07264d..734df0d3f 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -2315,8 +2315,8 @@ math equations flush left using [YAML metadata](#layout) or with These affect HTML output when [producing slide shows with pandoc]. -All [reveal.js configuration options] are available as variables. -To turn off boolean flags that default to true in reveal.js, use `0`. +`institute` +: author affiliations: can be a list when there are multiple authors `revealjs-url` : base URL for reveal.js documents (defaults to @@ -2336,6 +2336,9 @@ To turn off boolean flags that default to true in reveal.js, use `0`. : additional attributes for the title slide of reveal.js slide shows. See [background in reveal.js and beamer] for an example. +All [reveal.js configuration options] are available as variables. +To turn off boolean flags that default to true in reveal.js, use `0`. + [reveal.js configuration options]: https://revealjs.com/config/ ### Variables for Beamer slides -- cgit v1.2.3 From 2cf971cf56cbcdfcd6ee245df0d6e9811dc3573b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 May 2021 10:54:05 -0700 Subject: docx writer: Remove rsids from settings.docx. Word will add these when revisions are made. But it's pointless to start out with a set of them. --- data/docx/word/settings.xml | 12 +----------- test/docx/golden/block_quotes.docx | Bin 10061 -> 9981 bytes test/docx/golden/codeblock.docx | Bin 9910 -> 9830 bytes test/docx/golden/comments.docx | Bin 10248 -> 10168 bytes test/docx/golden/custom_style_no_reference.docx | Bin 10011 -> 9931 bytes test/docx/golden/custom_style_preserve.docx | Bin 10640 -> 10560 bytes test/docx/golden/custom_style_reference.docx | Bin 12397 -> 12317 bytes test/docx/golden/definition_list.docx | Bin 9910 -> 9830 bytes .../docx/golden/document-properties-short-desc.docx | Bin 9916 -> 9835 bytes test/docx/golden/document-properties.docx | Bin 10393 -> 10314 bytes test/docx/golden/headers.docx | Bin 10049 -> 9969 bytes test/docx/golden/image.docx | Bin 26727 -> 26647 bytes test/docx/golden/inline_code.docx | Bin 9849 -> 9769 bytes test/docx/golden/inline_formatting.docx | Bin 10029 -> 9949 bytes test/docx/golden/inline_images.docx | Bin 26783 -> 26703 bytes test/docx/golden/link_in_notes.docx | Bin 10071 -> 9991 bytes test/docx/golden/links.docx | Bin 10242 -> 10162 bytes test/docx/golden/lists.docx | Bin 10308 -> 10228 bytes test/docx/golden/lists_continuing.docx | Bin 10104 -> 10024 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10186 -> 10106 bytes test/docx/golden/lists_restarting.docx | Bin 10102 -> 10022 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10206 -> 10126 bytes test/docx/golden/notes.docx | Bin 10018 -> 9938 bytes test/docx/golden/raw-blocks.docx | Bin 9950 -> 9870 bytes test/docx/golden/raw-bookmarks.docx | Bin 10084 -> 10004 bytes test/docx/golden/table_one_row.docx | Bin 9920 -> 9840 bytes test/docx/golden/table_with_list_cell.docx | Bin 10238 -> 10159 bytes test/docx/golden/tables.docx | Bin 10279 -> 10200 bytes test/docx/golden/track_changes_deletion.docx | Bin 9893 -> 9813 bytes test/docx/golden/track_changes_insertion.docx | Bin 9876 -> 9796 bytes test/docx/golden/track_changes_move.docx | Bin 9910 -> 9830 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 10022 -> 9942 bytes test/docx/golden/unicode.docx | Bin 9835 -> 9755 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9882 -> 9802 bytes 34 files changed, 1 insertion(+), 11 deletions(-) diff --git a/data/docx/word/settings.xml b/data/docx/word/settings.xml index afa0199c9..fca255b90 100644 --- a/data/docx/word/settings.xml +++ b/data/docx/word/settings.xml @@ -17,16 +17,6 @@ <w:characterSpacingControl w:val="doNotCompress" /> <w:savePreviewPicture /> <w:rsids> - <w:rsidRoot w:val="00590D07" /> - <w:rsid w:val="00011C8B" /> - <w:rsid w:val="004E29B3" /> - <w:rsid w:val="00590D07" /> - <w:rsid w:val="00784D58" /> - <w:rsid w:val="008D6863" /> - <w:rsid w:val="00B86B75" /> - <w:rsid w:val="00BC48D5" /> - <w:rsid w:val="00C36279" /> - <w:rsid w:val="00E315A3" /> </w:rsids> <m:mathPr> <m:mathFont m:val="Lucida Grande" /> @@ -44,4 +34,4 @@ <w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink" /> <w:decimalSymbol w:val="." /> <w:listSeparator w:val="," /> -</w:settings> \ No newline at end of file +</w:settings> diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index b6973fcfd..f5e4b6428 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index f0d35d3ad..0bbe42bd7 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index fb1c15dec..6b759b522 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index 43d536b65..74e30f651 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 8865010d1..7ee99c87b 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index 10f7bf661..dfaf16e2b 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index 2a7d81a34..02992c6bd 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index 2fcd14908..9bfe56dca 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index 39533f42d..2a37045f2 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index c8d67c45b..ca2c7a261 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 8a7aeec10..c4447d021 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 969237cec..1824c0aae 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index cda936a39..82565a5b1 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 69991e791..0416bcc15 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index 40e848195..b51ec5220 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index 28237a30d..ba0d100c8 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index bf075805e..9632c598b 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index e7d308e13..3269a0528 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 9763e347e..716129170 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index b717ca619..af6edfe86 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index 873e731d2..f141425f7 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index 134cb2eaf..93b4222f6 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index c49ed79c8..9d1aa9853 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index 1f2cbb214..b57289fdd 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index a0160cdb4..edb23cc72 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index e51910770..f9cbed156 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index 8029774a9..b585c803d 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index 3542b8f9c..313942750 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index b36b4485e..f18b3f85f 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index e30ab06ae..50bdab767 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index 11597d578..230b27006 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index c7bff82e5..627276e4b 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index c70f6946e..790278108 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ -- cgit v1.2.3 From 31b650b4ecb0901c1f8ae809165dcffa051855b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 May 2021 10:56:16 -0700 Subject: docx writer settings.xml: - Set zoom to 100% by default. - Align math options more with current Word defaults (e.g. Cambria Math font). --- data/docx/word/settings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/docx/word/settings.xml b/data/docx/word/settings.xml index fca255b90..5f5ef5bfa 100644 --- a/data/docx/word/settings.xml +++ b/data/docx/word/settings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main"> - <w:zoom w:percent="90" /> + <w:zoom w:percent="100" /> <w:embedSystemFonts /> <w:proofState w:spelling="clean" w:grammar="clean" /> <w:stylePaneFormatFilter w:val="0004" /> @@ -19,16 +19,16 @@ <w:rsids> </w:rsids> <m:mathPr> - <m:mathFont m:val="Lucida Grande" /> + <m:mathFont m:val="Cambria Math" /> <m:brkBin m:val="before" /> <m:brkBinSub m:val="--" /> - <m:smallFrac m:val="false" /> - <m:dispDef m:val="false" /> + <m:smallFrac m:val="0" /> + <m:dispDef /> <m:lMargin m:val="0" /> <m:rMargin m:val="0" /> <m:wrapRight /> <m:intLim m:val="subSup" /> - <m:naryLim m:val="subSup" /> + <m:naryLim m:val="undOvr" /> </m:mathPr> <w:themeFontLang w:val="en-US" /> <w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink" /> -- cgit v1.2.3 From 0a4c6925b6db433bdb8b9d57c94a7c36be3daea7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 May 2021 15:40:49 -0700 Subject: Docx writer: copy over more settings from referenc.odcx. From settings.xml in the reference-doc, we now include: `zoom`, `embedSystemFonts`, `doNotTrackMoves`, `defaultTabStop`, `drawingGridHorizontalSpacing`, `drawingGridVerticalSpacing`, `displayHorizontalDrawingGridEvery`, `displayVerticalDrawingGridEvery`, `characterSpacingControl`, `savePreviewPicture`, `mathPr`, `themeFontLang`, `decimalSymbol`, `listSeparator`, `autoHyphenation`, `compat`. Closes #7240. --- src/Text/Pandoc/Writers/Docx.hs | 17 ++++++++++++++++- test/docx/golden/block_quotes.docx | Bin 9981 -> 9981 bytes test/docx/golden/codeblock.docx | Bin 9830 -> 9830 bytes test/docx/golden/comments.docx | Bin 10168 -> 10168 bytes test/docx/golden/custom_style_no_reference.docx | Bin 9931 -> 9931 bytes test/docx/golden/custom_style_preserve.docx | Bin 10560 -> 10560 bytes test/docx/golden/custom_style_reference.docx | Bin 12317 -> 12368 bytes test/docx/golden/definition_list.docx | Bin 9830 -> 9830 bytes .../docx/golden/document-properties-short-desc.docx | Bin 9835 -> 9835 bytes test/docx/golden/document-properties.docx | Bin 10314 -> 10314 bytes test/docx/golden/headers.docx | Bin 9969 -> 9969 bytes test/docx/golden/image.docx | Bin 26647 -> 26647 bytes test/docx/golden/inline_code.docx | Bin 9769 -> 9769 bytes test/docx/golden/inline_formatting.docx | Bin 9949 -> 9949 bytes test/docx/golden/inline_images.docx | Bin 26703 -> 26703 bytes test/docx/golden/link_in_notes.docx | Bin 9991 -> 9991 bytes test/docx/golden/links.docx | Bin 10162 -> 10162 bytes test/docx/golden/lists.docx | Bin 10228 -> 10228 bytes test/docx/golden/lists_continuing.docx | Bin 10024 -> 10024 bytes test/docx/golden/lists_multiple_initial.docx | Bin 10106 -> 10106 bytes test/docx/golden/lists_restarting.docx | Bin 10022 -> 10022 bytes test/docx/golden/nested_anchors_in_header.docx | Bin 10126 -> 10126 bytes test/docx/golden/notes.docx | Bin 9938 -> 9938 bytes test/docx/golden/raw-blocks.docx | Bin 9870 -> 9870 bytes test/docx/golden/raw-bookmarks.docx | Bin 10004 -> 10004 bytes test/docx/golden/table_one_row.docx | Bin 9840 -> 9840 bytes test/docx/golden/table_with_list_cell.docx | Bin 10159 -> 10159 bytes test/docx/golden/tables.docx | Bin 10200 -> 10200 bytes test/docx/golden/track_changes_deletion.docx | Bin 9813 -> 9813 bytes test/docx/golden/track_changes_insertion.docx | Bin 9796 -> 9796 bytes test/docx/golden/track_changes_move.docx | Bin 9830 -> 9830 bytes .../golden/track_changes_scrubbed_metadata.docx | Bin 9942 -> 9942 bytes test/docx/golden/unicode.docx | Bin 9755 -> 9755 bytes test/docx/golden/verbatim_subsuper.docx | Bin 9802 -> 9802 bytes 34 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 75bed1595..e7a49ba02 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -469,12 +469,27 @@ writeDocx opts doc = do -- adds references to footnotes or endnotes we don't have... -- we do, however, copy some settings over from reference let settingsPath = "word/settings.xml" - settingsList = [ "autoHyphenation" + settingsList = [ "zoom" + , "embedSystemFonts" + , "doNotTrackMoves" + , "defaultTabStop" + , "drawingGridHorizontalSpacing" + , "drawingGridVerticalSpacing" + , "displayHorizontalDrawingGridEvery" + , "displayVerticalDrawingGridEvery" + , "characterSpacingControl" + , "savePreviewPicture" + , "mathPr" + , "themeFontLang" + , "decimalSymbol" + , "listSeparator" + , "autoHyphenation" , "consecutiveHyphenLimit" , "hyphenationZone" , "doNotHyphenateCap" , "evenAndOddHeaders" , "proofState" + , "compat" ] settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime settingsList diff --git a/test/docx/golden/block_quotes.docx b/test/docx/golden/block_quotes.docx index f5e4b6428..d05020f82 100644 Binary files a/test/docx/golden/block_quotes.docx and b/test/docx/golden/block_quotes.docx differ diff --git a/test/docx/golden/codeblock.docx b/test/docx/golden/codeblock.docx index 0bbe42bd7..616c9b1d0 100644 Binary files a/test/docx/golden/codeblock.docx and b/test/docx/golden/codeblock.docx differ diff --git a/test/docx/golden/comments.docx b/test/docx/golden/comments.docx index 6b759b522..aa200c8d6 100644 Binary files a/test/docx/golden/comments.docx and b/test/docx/golden/comments.docx differ diff --git a/test/docx/golden/custom_style_no_reference.docx b/test/docx/golden/custom_style_no_reference.docx index 74e30f651..49cf42f38 100644 Binary files a/test/docx/golden/custom_style_no_reference.docx and b/test/docx/golden/custom_style_no_reference.docx differ diff --git a/test/docx/golden/custom_style_preserve.docx b/test/docx/golden/custom_style_preserve.docx index 7ee99c87b..e24940478 100644 Binary files a/test/docx/golden/custom_style_preserve.docx and b/test/docx/golden/custom_style_preserve.docx differ diff --git a/test/docx/golden/custom_style_reference.docx b/test/docx/golden/custom_style_reference.docx index dfaf16e2b..e7da8f06d 100644 Binary files a/test/docx/golden/custom_style_reference.docx and b/test/docx/golden/custom_style_reference.docx differ diff --git a/test/docx/golden/definition_list.docx b/test/docx/golden/definition_list.docx index 02992c6bd..df148cfed 100644 Binary files a/test/docx/golden/definition_list.docx and b/test/docx/golden/definition_list.docx differ diff --git a/test/docx/golden/document-properties-short-desc.docx b/test/docx/golden/document-properties-short-desc.docx index 9bfe56dca..2d9e96b15 100644 Binary files a/test/docx/golden/document-properties-short-desc.docx and b/test/docx/golden/document-properties-short-desc.docx differ diff --git a/test/docx/golden/document-properties.docx b/test/docx/golden/document-properties.docx index 2a37045f2..d8f091956 100644 Binary files a/test/docx/golden/document-properties.docx and b/test/docx/golden/document-properties.docx differ diff --git a/test/docx/golden/headers.docx b/test/docx/golden/headers.docx index ca2c7a261..8c1b3e870 100644 Binary files a/test/docx/golden/headers.docx and b/test/docx/golden/headers.docx differ diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index c4447d021..48b72e283 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ diff --git a/test/docx/golden/inline_code.docx b/test/docx/golden/inline_code.docx index 1824c0aae..048ac8f15 100644 Binary files a/test/docx/golden/inline_code.docx and b/test/docx/golden/inline_code.docx differ diff --git a/test/docx/golden/inline_formatting.docx b/test/docx/golden/inline_formatting.docx index 82565a5b1..cf1301c4b 100644 Binary files a/test/docx/golden/inline_formatting.docx and b/test/docx/golden/inline_formatting.docx differ diff --git a/test/docx/golden/inline_images.docx b/test/docx/golden/inline_images.docx index 0416bcc15..662e70556 100644 Binary files a/test/docx/golden/inline_images.docx and b/test/docx/golden/inline_images.docx differ diff --git a/test/docx/golden/link_in_notes.docx b/test/docx/golden/link_in_notes.docx index b51ec5220..d800a5fb4 100644 Binary files a/test/docx/golden/link_in_notes.docx and b/test/docx/golden/link_in_notes.docx differ diff --git a/test/docx/golden/links.docx b/test/docx/golden/links.docx index ba0d100c8..bffdbbaf8 100644 Binary files a/test/docx/golden/links.docx and b/test/docx/golden/links.docx differ diff --git a/test/docx/golden/lists.docx b/test/docx/golden/lists.docx index 9632c598b..2b201df28 100644 Binary files a/test/docx/golden/lists.docx and b/test/docx/golden/lists.docx differ diff --git a/test/docx/golden/lists_continuing.docx b/test/docx/golden/lists_continuing.docx index 3269a0528..257053a78 100644 Binary files a/test/docx/golden/lists_continuing.docx and b/test/docx/golden/lists_continuing.docx differ diff --git a/test/docx/golden/lists_multiple_initial.docx b/test/docx/golden/lists_multiple_initial.docx index 716129170..0a3bf1016 100644 Binary files a/test/docx/golden/lists_multiple_initial.docx and b/test/docx/golden/lists_multiple_initial.docx differ diff --git a/test/docx/golden/lists_restarting.docx b/test/docx/golden/lists_restarting.docx index af6edfe86..0aa69805f 100644 Binary files a/test/docx/golden/lists_restarting.docx and b/test/docx/golden/lists_restarting.docx differ diff --git a/test/docx/golden/nested_anchors_in_header.docx b/test/docx/golden/nested_anchors_in_header.docx index f141425f7..88dd21abd 100644 Binary files a/test/docx/golden/nested_anchors_in_header.docx and b/test/docx/golden/nested_anchors_in_header.docx differ diff --git a/test/docx/golden/notes.docx b/test/docx/golden/notes.docx index 93b4222f6..f02d5951c 100644 Binary files a/test/docx/golden/notes.docx and b/test/docx/golden/notes.docx differ diff --git a/test/docx/golden/raw-blocks.docx b/test/docx/golden/raw-blocks.docx index 9d1aa9853..58a101b3f 100644 Binary files a/test/docx/golden/raw-blocks.docx and b/test/docx/golden/raw-blocks.docx differ diff --git a/test/docx/golden/raw-bookmarks.docx b/test/docx/golden/raw-bookmarks.docx index b57289fdd..484c363a2 100644 Binary files a/test/docx/golden/raw-bookmarks.docx and b/test/docx/golden/raw-bookmarks.docx differ diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index edb23cc72..f75e567ab 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index f9cbed156..a49f70643 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index b585c803d..f24e27516 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/golden/track_changes_deletion.docx b/test/docx/golden/track_changes_deletion.docx index 313942750..de7c44bf4 100644 Binary files a/test/docx/golden/track_changes_deletion.docx and b/test/docx/golden/track_changes_deletion.docx differ diff --git a/test/docx/golden/track_changes_insertion.docx b/test/docx/golden/track_changes_insertion.docx index f18b3f85f..958533459 100644 Binary files a/test/docx/golden/track_changes_insertion.docx and b/test/docx/golden/track_changes_insertion.docx differ diff --git a/test/docx/golden/track_changes_move.docx b/test/docx/golden/track_changes_move.docx index 50bdab767..04fa05062 100644 Binary files a/test/docx/golden/track_changes_move.docx and b/test/docx/golden/track_changes_move.docx differ diff --git a/test/docx/golden/track_changes_scrubbed_metadata.docx b/test/docx/golden/track_changes_scrubbed_metadata.docx index 230b27006..ef2dc96f8 100644 Binary files a/test/docx/golden/track_changes_scrubbed_metadata.docx and b/test/docx/golden/track_changes_scrubbed_metadata.docx differ diff --git a/test/docx/golden/unicode.docx b/test/docx/golden/unicode.docx index 627276e4b..a9de2b367 100644 Binary files a/test/docx/golden/unicode.docx and b/test/docx/golden/unicode.docx differ diff --git a/test/docx/golden/verbatim_subsuper.docx b/test/docx/golden/verbatim_subsuper.docx index 790278108..3ebadc59d 100644 Binary files a/test/docx/golden/verbatim_subsuper.docx and b/test/docx/golden/verbatim_subsuper.docx differ -- cgit v1.2.3 From 98a5a63b5228d3eaba55d9833f454f08b925b774 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 May 2021 19:47:11 -0700 Subject: Fix .editorconfig so Emacs can open zip archives and docx again. See https://github.com/hlissner/doom-emacs/issues/5057 --- .editorconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.editorconfig b/.editorconfig index 4eaaa8c44..c9b4e4e4f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,14 @@ indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true +[*.{zip,docx}] +charset = +end_of_line = +indent_style = +indent_size = +insert_final_newline = +trim_trailing_whitespace = + [*.{markdown,md}] trim_trailing_whitespace = false -- cgit v1.2.3 From 39a69c4f93d46e059c48a740b69dfff219a5d715 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 16 May 2021 11:53:19 -0700 Subject: Markdown writer: improve escaping of `@`. We need to escape literal `@` before `{` because of the new citation syntax. --- src/Text/Pandoc/Writers/Markdown/Inline.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index 2062050e4..ced5fbacb 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -56,7 +56,7 @@ escapeText opts = T.pack . go . T.unpack '@' | isEnabled Ext_citations opts -> case cs of (d:_) - | isAlphaNum d || d == '_' + | isAlphaNum d || d == '_' || d == '{' -> '\\':'@':go cs _ -> '@':go cs _ | c `elem` ['\\','`','*','_','[',']','#'] -> -- cgit v1.2.3 From 5a6399d9f62c4306fa073ae1311675158dd6a203 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 16 May 2021 12:23:34 -0700 Subject: Markdown writer: fewer unneeded escapes for `#`. See #6259. --- src/Text/Pandoc/Writers/Markdown/Inline.hs | 13 +++++++++++-- test/command/3792.md | 2 +- test/command/4164.md | 2 +- test/command/7208.md | 2 +- test/command/biblatex-sigfridsson.md | 2 +- test/writer.opml | 2 +- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index ced5fbacb..e6c6da5a9 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -43,7 +43,11 @@ import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..), -- | Escape special characters for Markdown. escapeText :: WriterOptions -> Text -> Text escapeText opts = T.pack . go . T.unpack - where + where + startsWithSpace (' ':_) = True + startsWithSpace ('\t':_) = True + startsWithSpace [] = True + startsWithSpace _ = False go [] = [] go (c:cs) = case c of @@ -59,7 +63,10 @@ escapeText opts = T.pack . go . T.unpack | isAlphaNum d || d == '_' || d == '{' -> '\\':'@':go cs _ -> '@':go cs - _ | c `elem` ['\\','`','*','_','[',']','#'] -> + '#' | isEnabled Ext_space_in_atx_header opts + , startsWithSpace cs + -> '\\':'#':go cs + _ | c `elem` ['\\','`','*','_','[',']'] -> '\\':c:go cs '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs @@ -81,6 +88,8 @@ escapeText opts = T.pack . go . T.unpack | isEnabled Ext_intraword_underscores opts , isAlphaNum c , isAlphaNum x -> c : '_' : x : go xs + '#':xs -> c : '#' : go xs + '>':xs -> c : '>' : go xs _ -> c : go cs attrsToMarkdown :: Attr -> Doc Text diff --git a/test/command/3792.md b/test/command/3792.md index eb109b9cc..eff26d517 100644 --- a/test/command/3792.md +++ b/test/command/3792.md @@ -6,7 +6,7 @@ and properly escaped. ok ^D --- -title: \<this\> \*that\* +title: \<this> \*that\* --- ok diff --git a/test/command/4164.md b/test/command/4164.md index 4e7b7e285..68cbd0584 100644 --- a/test/command/4164.md +++ b/test/command/4164.md @@ -26,6 +26,6 @@ Here is inline html: Here is inline html: -\<div\> \<balise\> bla bla \</div\> +\<div> \<balise> bla bla \</div> ``` diff --git a/test/command/7208.md b/test/command/7208.md index e65943ade..fe02ec32e 100644 --- a/test/command/7208.md +++ b/test/command/7208.md @@ -2,5 +2,5 @@ % pandoc -t gfm \<hi\> ^D -\<hi\> +\<hi> ``` diff --git a/test/command/biblatex-sigfridsson.md b/test/command/biblatex-sigfridsson.md index f83c35622..e042a8762 100644 --- a/test/command/biblatex-sigfridsson.md +++ b/test/command/biblatex-sigfridsson.md @@ -90,7 +90,7 @@ references: - family: Ryde given: Ulf container-title: Journal of Computational Chemistry - doi: "10.1002/(SICI)1096-987X(199803)19:4\\<377::AID-JCC1\\>3.0.CO;2-P" + doi: "10.1002/(SICI)1096-987X(199803)19:4\\<377::AID-JCC1>3.0.CO;2-P" id: sigfridsson issue: 4 issued: 1998 diff --git a/test/writer.opml b/test/writer.opml index 6bdcb882e..bfe1e5de6 100644 --- a/test/writer.opml +++ b/test/writer.opml @@ -52,7 +52,7 @@ </outline> <outline text="LaTeX" _note="- - 2 + 2 = 4 - *x* ∈ *y* - *α* ∧ *ω* - 223 - *p*-Tree - Here’s some display math: $$\\frac{d}{dx}f(x)=\\lim\_{h\\to 0}\\frac{f(x+h)-f(x)}{h}$$ - Here’s one that has a line break in it: *α* + *ω* × *x*². These shouldn’t be math: - To get the famous equation, write `$e = mc^2$`. - $22,000 is a *lot* of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped `$`: $73 *this should be emphasized* 23$. Here’s a LaTeX table: ------------------------------------------------------------------------"> </outline> -<outline text="Special Characters" _note="Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 &lt; 5. 6 &gt; 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: { Right brace: } Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: &gt; Hash: \# Period: . Bang: ! Plus: + Minus: - ------------------------------------------------------------------------"> +<outline text="Special Characters" _note="Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 &lt; 5. 6 &gt; 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: { Right brace: } Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: &gt; Hash: # Period: . Bang: ! Plus: + Minus: - ------------------------------------------------------------------------"> </outline> <outline text="Links"> <outline text="Explicit" _note="Just a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [URL and title](/url/ "title with "quotes" in it") [URL and title](/url/ "title with single quotes") [with\_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty]()."> -- cgit v1.2.3 From d92622ba3cae5ced69a256472d367a53fc5878a1 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 16 May 2021 21:33:32 +0200 Subject: LaTeX template: define commands for zero width non-joiner character Closes: #6639 The zero-width non-joiner character is used to avoid ligatures (e.g. in German). --- data/templates/default.latex | 19 +++++++++++++++++++ src/Text/Pandoc/Writers/LaTeX.hs | 3 +-- src/Text/Pandoc/Writers/LaTeX/Types.hs | 3 ++- src/Text/Pandoc/Writers/LaTeX/Util.hs | 7 ++++--- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index 142fe3e55..04784b971 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -156,6 +156,25 @@ $if(CJKmainfont)$ \fi $endif$ \fi +$if(zero-width-non-joiner)$ +%% Support for zero-width non-joiner characters. +\makeatletter +\def\zerowidthnonjoiner{% + % Prevent ligatures and adjust kerning, but still support hyphenating. + \texorpdfstring{% + \textormath{\nobreak\discretionary{-}{}{\kern.03em}% + \ifvmode\else\nobreak\hskip\z@skip\fi}{}% + }{}% +} +\makeatother +\ifPDFTeX + \DeclareUnicodeCharacter{200C}{\zerowidthnonjoiner} +\else + \catcode`^^^^200c=\active + \protected\def ^^^^200c{\zerowidthnonjoiner} +\fi +%% End of ZWNJ support +$endif$ $if(beamer)$ $if(theme)$ \usetheme[$for(themeoptions)$$themeoptions$$sep$,$endfor$]{$theme$} diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d8722876e..bf57937bd 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -173,6 +173,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "has-chapters" (stHasChapters st) $ defField "has-frontmatter" (documentClass `elem` frontmatterClasses) $ defField "listings" (writerListings options || stLHS st) $ + defField "zero-width-non-joiner" (stZwnj st) $ defField "beamer" beamer $ (if stHighlighting st then case writerHighlightStyle options of @@ -1048,5 +1049,3 @@ extractInline _ _ = [] -- Look up a key in an attribute and give a list of its values lookKey :: Text -> Attr -> [Text] lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs - - diff --git a/src/Text/Pandoc/Writers/LaTeX/Types.hs b/src/Text/Pandoc/Writers/LaTeX/Types.hs index d598794ad..c06b7e923 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Types.hs @@ -40,7 +40,7 @@ data WriterState = , stCsquotes :: Bool -- ^ true if document uses csquotes , stHighlighting :: Bool -- ^ true if document has highlighted code , stIncremental :: Bool -- ^ true if beamer lists should be - -- displayed bit by bit + , stZwnj :: Bool -- ^ true if document has a ZWNJ character , stInternalLinks :: [Text] -- ^ list of internal link targets , stBeamer :: Bool -- ^ produce beamer , stEmptyLine :: Bool -- ^ true if no content on line @@ -74,6 +74,7 @@ startingState options = , stCsquotes = False , stHighlighting = False , stIncremental = writerIncremental options + , stZwnj = False , stInternalLinks = [] , stBeamer = False , stEmptyLine = True diff --git a/src/Text/Pandoc/Writers/LaTeX/Util.hs b/src/Text/Pandoc/Writers/LaTeX/Util.hs index 56bb792ae..c34338121 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Util.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Util.hs @@ -22,6 +22,7 @@ module Text.Pandoc.Writers.LaTeX.Util ( where import Control.Applicative ((<|>)) +import Control.Monad (when) import Text.Pandoc.Class (PandocMonad, toLang) import Text.Pandoc.Options (WriterOptions(..), isEnabled) import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..)) @@ -30,7 +31,7 @@ import Text.Pandoc.Highlighting (toListingsLanguage) import Text.DocLayout import Text.Pandoc.Definition import Text.Pandoc.ImageSize (showFl) -import Control.Monad.State.Strict (gets) +import Control.Monad.State.Strict (gets, modify) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Extensions (Extension(Ext_smart)) @@ -49,6 +50,8 @@ data StringContext = TextString stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text stringToLaTeX context zs = do opts <- gets stOptions + when ('\x200c' `elemText` zs) $ + modify (\s -> s { stZwnj = True }) return $ T.pack $ foldr (go opts context) mempty $ T.unpack $ if writerPreferAscii opts @@ -270,5 +273,3 @@ mbBraced :: Text -> Text mbBraced x = if not (T.all isAlphaNum x) then "{" <> x <> "}" else x - - -- cgit v1.2.3 From cc088687b4013c2b8b744eb337ed04fc63f315f2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 16 May 2021 14:35:13 -0700 Subject: LaTeX template: move title, author, date up to top of preamble. This allows header-includes to use them, and puts them in a position where you can see them immediately. Closes #7295. --- data/templates/default.latex | 58 ++++++++++++++++++++--------------------- test/lhs-test.latex | 6 ++--- test/lhs-test.latex+lhs | 6 ++--- test/writer.latex | 8 +++--- test/writers-lang-and-dir.latex | 6 ++--- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index 04784b971..61402c605 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -36,6 +36,35 @@ $for(classoption)$ $classoption$$sep$, $endfor$ ]{$documentclass$} +$if(title)$ +\title{$title$$if(thanks)$\thanks{$thanks$}$endif$} +$endif$ +$if(subtitle)$ +$if(beamer)$ +$else$ +\usepackage{etoolbox} +\makeatletter +\providecommand{\subtitle}[1]{% add subtitle to \maketitle + \apptocmd{\@title}{\par {\large #1 \par}}{}{} +} +\makeatother +$endif$ +\subtitle{$subtitle$} +$endif$ +\author{$for(author)$$author$$sep$ \and $endfor$} +\date{$date$} +$if(beamer)$ +$if(institute)$ +\institute{$for(institute)$$institute$$sep$ \and $endfor$} +$endif$ +$if(titlegraphic)$ +\titlegraphic{\includegraphics{$titlegraphic$}} +$endif$ +$if(logo)$ +\logo{\includegraphics{$logo$}} +$endif$ +$endif$ + $if(beamer)$ $if(background-image)$ \usebackgroundtemplate{% @@ -423,35 +452,6 @@ $if(csquotes)$ \usepackage{csquotes} $endif$ -$if(title)$ -\title{$title$$if(thanks)$\thanks{$thanks$}$endif$} -$endif$ -$if(subtitle)$ -$if(beamer)$ -$else$ -\usepackage{etoolbox} -\makeatletter -\providecommand{\subtitle}[1]{% add subtitle to \maketitle - \apptocmd{\@title}{\par {\large #1 \par}}{}{} -} -\makeatother -$endif$ -\subtitle{$subtitle$} -$endif$ -\author{$for(author)$$author$$sep$ \and $endfor$} -\date{$date$} -$if(beamer)$ -$if(institute)$ -\institute{$for(institute)$$institute$$sep$ \and $endfor$} -$endif$ -$if(titlegraphic)$ -\titlegraphic{\includegraphics{$titlegraphic$}} -$endif$ -$if(logo)$ -\logo{\includegraphics{$logo$}} -$endif$ -$endif$ - \begin{document} $if(has-frontmatter)$ \frontmatter diff --git a/test/lhs-test.latex b/test/lhs-test.latex index fdc344335..20bc6ae19 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -4,6 +4,9 @@ % \documentclass[ ]{article} +\author{} +\date{} + \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -85,9 +88,6 @@ \usepackage{selnolig} % disable illegal ligatures \fi -\author{} -\date{} - \begin{document} \hypertarget{lhs-test}{% diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index f9149ba02..327dac4ef 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -4,6 +4,9 @@ % \documentclass[ ]{article} +\author{} +\date{} + \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -52,9 +55,6 @@ \usepackage{selnolig} % disable illegal ligatures \fi -\author{} -\date{} - \begin{document} \hypertarget{lhs-test}{% diff --git a/test/writer.latex b/test/writer.latex index abf772df4..468c271bc 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -4,6 +4,10 @@ % \documentclass[ ]{article} +\title{Pandoc Test Suite} +\author{John MacFarlane \and Anonymous} +\date{July 17, 2006} + \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -67,10 +71,6 @@ \usepackage{selnolig} % disable illegal ligatures \fi -\title{Pandoc Test Suite} -\author{John MacFarlane \and Anonymous} -\date{July 17, 2006} - \begin{document} \maketitle diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index d91f77325..e06fe05ed 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -5,6 +5,9 @@ \documentclass[ english, ]{article} +\author{} +\date{} + \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -84,9 +87,6 @@ \newenvironment{LTR}{\beginL}{\endL} \fi -\author{} -\date{} - \begin{document} \hypertarget{empty-divs-and-spans}{% -- cgit v1.2.3 From d3ca48656f036888fcf60acb7a0364a20f3554bf Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 May 2021 13:10:45 +0200 Subject: ConTeXt writer tests: keep code lines below 80 chars. --- test/Tests/Writers/ConTeXt.hs | 232 ++++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 113 deletions(-) diff --git a/test/Tests/Writers/ConTeXt.hs b/test/Tests/Writers/ConTeXt.hs index 5c1c98d4e..a4aa69dcd 100644 --- a/test/Tests/Writers/ConTeXt.hs +++ b/test/Tests/Writers/ConTeXt.hs @@ -39,116 +39,122 @@ infix 4 =: (=:) = test context tests :: [TestTree] -tests = [ testGroup "inline code" - [ "with '}'" =: code "}" =?> "\\mono{\\}}" - , "without '}'" =: code "]" =?> "\\type{]}" - , testProperty "code property" $ \s -> null s || '\n' `elem` s || - if '{' `elem` s || '}' `elem` s - then context' (code $ pack s) == "\\mono{" ++ - context' (str $ pack s) ++ "}" - else context' (code $ pack s) == "\\type{" ++ s ++ "}" - ] - , testGroup "headers" - [ "level 1" =: - headerWith ("my-header",[],[]) 1 "My header" =?> "\\section[title={My header},reference={my-header}]" - , test contextDiv "section-divs" $ - ( headerWith ("header1", [], []) 1 (text "Header1") - <> headerWith ("header2", [], []) 2 (text "Header2") - <> headerWith ("header3", [], []) 3 (text "Header3") - <> headerWith ("header4", [], []) 4 (text "Header4") - <> headerWith ("header5", [], []) 5 (text "Header5") - <> headerWith ("header6", [], []) 6 (text "Header6")) - =?> - unlines [ "\\startsection[title={Header1},reference={header1}]\n" - , "\\startsubsection[title={Header2},reference={header2}]\n" - , "\\startsubsubsection[title={Header3},reference={header3}]\n" - , "\\startsubsubsubsection[title={Header4},reference={header4}]\n" - , "\\startsubsubsubsubsection[title={Header5},reference={header5}]\n" - , "\\startsubsubsubsubsubsection[title={Header6},reference={header6}]\n" - , "\\stopsubsubsubsubsubsection\n" - , "\\stopsubsubsubsubsection\n" - , "\\stopsubsubsubsection\n" - , "\\stopsubsubsection\n" - , "\\stopsubsection\n" - , "\\stopsection" ] - ] - , testGroup "bullet lists" - [ "nested" =: - bulletList [ - plain (text "top") - <> bulletList [ - plain (text "next") - <> bulletList [plain (text "bot")] - ] - ] =?> unlines - [ "\\startitemize[packed]" - , "\\item" - , " top" - , " \\startitemize[packed]" - , " \\item" - , " next" - , " \\startitemize[packed]" - , " \\item" - , " bot" - , " \\stopitemize" - , " \\stopitemize" - , "\\stopitemize" ] - ] - , testGroup "natural tables" - [ test contextNtb "table with header and caption" $ - let capt = text "Table 1" - aligns = [(AlignRight, ColWidthDefault), (AlignLeft, ColWidthDefault), (AlignCenter, ColWidthDefault), (AlignDefault, ColWidthDefault)] - headers = [plain $ text "Right", - plain $ text "Left", - plain $ text "Center", - plain $ text "Default"] - rows = [[plain $ text "1.1", - plain $ text "1.2", - plain $ text "1.3", - plain $ text "1.4"] - ,[plain $ text "2.1", - plain $ text "2.2", - plain $ text "2.3", - plain $ text "2.4"] - ,[plain $ text "3.1", - plain $ text "3.2", - plain $ text "3.3", - plain $ text "3.4"]] - toRow = Row nullAttr . map simpleCell - in table (simpleCaption $ plain capt) - aligns - (TableHead nullAttr [toRow headers]) - [TableBody nullAttr 0 [] $ map toRow rows] - (TableFoot nullAttr []) - =?> unlines [ "\\startplacetable[title={Table 1}]" - , "\\startTABLE" - , "\\startTABLEhead" - , "\\NC[align=left] Right" - , "\\NC[align=right] Left" - , "\\NC[align=middle] Center" - , "\\NC Default" - , "\\NC\\NR" - , "\\stopTABLEhead" - , "\\startTABLEbody" - , "\\NC[align=left] 1.1" - , "\\NC[align=right] 1.2" - , "\\NC[align=middle] 1.3" - , "\\NC 1.4" - , "\\NC\\NR" - , "\\NC[align=left] 2.1" - , "\\NC[align=right] 2.2" - , "\\NC[align=middle] 2.3" - , "\\NC 2.4" - , "\\NC\\NR" - , "\\stopTABLEbody" - , "\\startTABLEfoot" - , "\\NC[align=left] 3.1" - , "\\NC[align=right] 3.2" - , "\\NC[align=middle] 3.3" - , "\\NC 3.4" - , "\\NC\\NR" - , "\\stopTABLEfoot" - , "\\stopTABLE" - , "\\stopplacetable" ] - ] - ] +tests = + [ testGroup "inline code" + [ "with '}'" =: code "}" =?> "\\mono{\\}}" + , "without '}'" =: code "]" =?> "\\type{]}" + , testProperty "code property" $ \s -> null s || '\n' `elem` s || + if '{' `elem` s || '}' `elem` s + then context' (code $ pack s) == "\\mono{" ++ + context' (str $ pack s) ++ "}" + else context' (code $ pack s) == "\\type{" ++ s ++ "}" + ] + , testGroup "headers" + [ "level 1" =: + headerWith ("my-header",[],[]) 1 "My header" =?> + "\\section[title={My header},reference={my-header}]" + , test contextDiv "section-divs" $ + ( headerWith ("header1", [], []) 1 (text "Header1") + <> headerWith ("header2", [], []) 2 (text "Header2") + <> headerWith ("header3", [], []) 3 (text "Header3") + <> headerWith ("header4", [], []) 4 (text "Header4") + <> headerWith ("header5", [], []) 5 (text "Header5") + <> headerWith ("header6", [], []) 6 (text "Header6")) + =?> + unlines + [ "\\startsection[title={Header1},reference={header1}]\n" + , "\\startsubsection[title={Header2},reference={header2}]\n" + , "\\startsubsubsection[title={Header3},reference={header3}]\n" + , "\\startsubsubsubsection[title={Header4},reference={header4}]\n" + , "\\startsubsubsubsubsection[title={Header5},reference={header5}]\n" + , "\\startsubsubsubsubsubsection[title={Header6},reference={header6}]\n" + , "\\stopsubsubsubsubsubsection\n" + , "\\stopsubsubsubsubsection\n" + , "\\stopsubsubsubsection\n" + , "\\stopsubsubsection\n" + , "\\stopsubsection\n" + , "\\stopsection" ] + ] + , testGroup "bullet lists" + [ "nested" =: + bulletList [ + plain (text "top") + <> bulletList [ + plain (text "next") + <> bulletList [plain (text "bot")] + ] + ] =?> unlines + [ "\\startitemize[packed]" + , "\\item" + , " top" + , " \\startitemize[packed]" + , " \\item" + , " next" + , " \\startitemize[packed]" + , " \\item" + , " bot" + , " \\stopitemize" + , " \\stopitemize" + , "\\stopitemize" ] + ] + , testGroup "natural tables" + [ test contextNtb "table with header and caption" $ + let capt = text "Table 1" + aligns = [ (AlignRight, ColWidthDefault) + , (AlignLeft, ColWidthDefault) + , (AlignCenter, ColWidthDefault) + , (AlignDefault, ColWidthDefault) ] + headers = [plain $ text "Right", + plain $ text "Left", + plain $ text "Center", + plain $ text "Default"] + rows = [[plain $ text "1.1", + plain $ text "1.2", + plain $ text "1.3", + plain $ text "1.4"] + ,[plain $ text "2.1", + plain $ text "2.2", + plain $ text "2.3", + plain $ text "2.4"] + ,[plain $ text "3.1", + plain $ text "3.2", + plain $ text "3.3", + plain $ text "3.4"]] + toRow = Row nullAttr . map simpleCell + in table (simpleCaption $ plain capt) + aligns + (TableHead nullAttr [toRow headers]) + [TableBody nullAttr 0 [] $ map toRow rows] + (TableFoot nullAttr []) + =?> unlines [ "\\startplacetable[title={Table 1}]" + , "\\startTABLE" + , "\\startTABLEhead" + , "\\NC[align=left] Right" + , "\\NC[align=right] Left" + , "\\NC[align=middle] Center" + , "\\NC Default" + , "\\NC\\NR" + , "\\stopTABLEhead" + , "\\startTABLEbody" + , "\\NC[align=left] 1.1" + , "\\NC[align=right] 1.2" + , "\\NC[align=middle] 1.3" + , "\\NC 1.4" + , "\\NC\\NR" + , "\\NC[align=left] 2.1" + , "\\NC[align=right] 2.2" + , "\\NC[align=middle] 2.3" + , "\\NC 2.4" + , "\\NC\\NR" + , "\\stopTABLEbody" + , "\\startTABLEfoot" + , "\\NC[align=left] 3.1" + , "\\NC[align=right] 3.2" + , "\\NC[align=middle] 3.3" + , "\\NC 3.4" + , "\\NC\\NR" + , "\\stopTABLEfoot" + , "\\stopTABLE" + , "\\stopplacetable" ] + ] + ] -- cgit v1.2.3 From 4417dacc440e269c5a4db1d67b52da9e0fe05561 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 May 2021 13:00:38 +0200 Subject: ConTeXt writer: use span identifiers as reference anchors. Closes: #7246 --- src/Text/Pandoc/Writers/ConTeXt.hs | 8 ++++++-- test/Tests/Writers/ConTeXt.hs | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index b694437d8..57d752a67 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -487,7 +487,7 @@ inlineToConTeXt (Note contents) = do then literal "\\footnote{" <> nest 2 (chomp contents') <> char '}' else literal "\\startbuffer " <> nest 2 (chomp contents') <> literal "\\stopbuffer\\footnote{\\getbuffer}" -inlineToConTeXt (Span (_,_,kvs) ils) = do +inlineToConTeXt (Span (ident,_,kvs) ils) = do mblang <- fromBCP47 (lookup "lang" kvs) let wrapDir txt = case lookup "dir" kvs of Just "rtl" -> braces $ "\\righttoleft " <> txt @@ -497,7 +497,11 @@ inlineToConTeXt (Span (_,_,kvs) ils) = do Just lng -> braces ("\\language" <> brackets (literal lng) <> txt) Nothing -> txt - wrapLang . wrapDir <$> inlineListToConTeXt ils + addReference = + if T.null ident + then id + else (("\\reference" <> brackets (literal ident) <> "{}") <>) + addReference . wrapLang . wrapDir <$> inlineListToConTeXt ils -- | Craft the section header, inserting the section reference, if supplied. sectionHeader :: PandocMonad m diff --git a/test/Tests/Writers/ConTeXt.hs b/test/Tests/Writers/ConTeXt.hs index a4aa69dcd..fbbf9b948 100644 --- a/test/Tests/Writers/ConTeXt.hs +++ b/test/Tests/Writers/ConTeXt.hs @@ -43,6 +43,9 @@ tests = [ testGroup "inline code" [ "with '}'" =: code "}" =?> "\\mono{\\}}" , "without '}'" =: code "]" =?> "\\type{]}" + , "span with ID" =: + spanWith ("city", [], []) "Berlin" =?> + "\\reference[city]{}Berlin" , testProperty "code property" $ \s -> null s || '\n' `elem` s || if '{' `elem` s || '}' `elem` s then context' (code $ pack s) == "\\mono{" ++ -- cgit v1.2.3 From 25f5b927773eb730c2d5ef834bd61e1d2d5f09df Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 May 2021 15:37:25 +0200 Subject: HTML writer: ensure headings only have valid attribs in HTML4 Fixes: #5944 --- src/Text/Pandoc/Writers/HTML.hs | 16 +++++- test/Tests/Writers/HTML.hs | 109 +++++++++++++++++++++------------------- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 332de1545..f7a387927 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -882,7 +882,7 @@ blockToHtml opts (BlockQuote blocks) = do else do contents <- blockListToHtml opts blocks return $ H.blockquote $ nl opts >> contents >> nl opts -blockToHtml opts (Header level attr@(_,classes,kvs) lst) = do +blockToHtml opts (Header level (ident,classes,kvs) lst) = do contents <- inlineListToHtml opts lst let secnum = fromMaybe mempty $ lookup "number" kvs let contents' = if writerNumberSections opts && not (T.null secnum) @@ -890,7 +890,13 @@ blockToHtml opts (Header level attr@(_,classes,kvs) lst) = do then (H.span ! A.class_ "header-section-number" $ toHtml secnum) >> strToHtml " " >> contents else contents - addAttrs opts attr + html5 <- gets stHtml5 + let kvs' = if html5 + then kvs + else [ (k, v) | (k, v) <- kvs + , k `elem` (["lang", "dir", "title", "style" + , "align"] ++ intrinsicEventsHTML4)] + addAttrs opts (ident,classes,kvs') $ case level of 1 -> H.h1 contents' 2 -> H.h2 contents' @@ -1526,6 +1532,12 @@ allowsMathEnvironments MathML = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False +-- | List of intrinsic event attributes allowed on all elements in HTML4. +intrinsicEventsHTML4 :: [Text] +intrinsicEventsHTML4 = + [ "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover" + , "onmouseout", "onmouseout", "onkeypress", "onkeydown", "onkeyup"] + isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool isRawHtml f = do html5 <- gets stHtml5 diff --git a/test/Tests/Writers/HTML.hs b/test/Tests/Writers/HTML.hs index 328801e31..404f6da98 100644 --- a/test/Tests/Writers/HTML.hs +++ b/test/Tests/Writers/HTML.hs @@ -34,55 +34,60 @@ infix 4 =: (=:) = test html tests :: [TestTree] -tests = [ testGroup "inline code" - [ "basic" =: code "@&" =?> "<code>@&</code>" - , "haskell" =: codeWith ("",["haskell"],[]) ">>=" - =?> "<code class=\"sourceCode haskell\"><span class=\"op\">>>=</span></code>" - , "nolanguage" =: codeWith ("",["nolanguage"],[]) ">>=" - =?> "<code class=\"nolanguage\">>>=</code>" - ] - , testGroup "images" - [ "alt with formatting" =: - image "/url" "title" ("my " <> emph "image") - =?> "<img src=\"/url\" title=\"title\" alt=\"my image\" />" - ] - , testGroup "blocks" - [ "definition list with empty <dt>" =: - definitionList [(mempty, [para $ text "foo bar"])] - =?> "<dl><dt></dt><dd><p>foo bar</p></dd></dl>" - ] - , testGroup "quotes" - [ "quote with cite attribute (without q-tags)" =: - doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) - =?> "“<span cite=\"http://example.org\">examples</span>”" - , tQ "quote with cite attribute (with q-tags)" $ - doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) - =?> "<q cite=\"http://example.org\">examples</q>" - ] - , testGroup "sample" - [ "sample should be rendered correctly" =: - plain (codeWith ("",["sample"],[]) "Answer is 42") =?> - "<samp>Answer is 42</samp>" - ] - , testGroup "variable" - [ "variable should be rendered correctly" =: - plain (codeWith ("",["variable"],[]) "result") =?> - "<var>result</var>" - ] - , testGroup "sample with style" - [ "samp should wrap highlighted code" =: - codeWith ("",["sample","haskell"],[]) ">>=" - =?> ("<samp><code class=\"sourceCode haskell\">" ++ - "<span class=\"op\">>>=</span></code></samp>") - ] - , testGroup "variable with style" - [ "var should wrap highlighted code" =: - codeWith ("",["haskell","variable"],[]) ">>=" - =?> ("<var><code class=\"sourceCode haskell\">" ++ - "<span class=\"op\">>>=</span></code></var>") - ] - ] - where - tQ :: (ToString a, ToPandoc a) - => String -> (a, String) -> TestTree - tQ = test htmlQTags +tests = + [ testGroup "inline code" + [ "basic" =: code "@&" =?> "<code>@&</code>" + , "haskell" =: codeWith ("",["haskell"],[]) ">>=" + =?> "<code class=\"sourceCode haskell\"><span class=\"op\">>>=</span></code>" + , "nolanguage" =: codeWith ("",["nolanguage"],[]) ">>=" + =?> "<code class=\"nolanguage\">>>=</code>" + ] + , testGroup "images" + [ "alt with formatting" =: + image "/url" "title" ("my " <> emph "image") + =?> "<img src=\"/url\" title=\"title\" alt=\"my image\" />" + ] + , testGroup "blocks" + [ "definition list with empty <dt>" =: + definitionList [(mempty, [para $ text "foo bar"])] + =?> "<dl><dt></dt><dd><p>foo bar</p></dd></dl>" + , "heading with disallowed attributes" =: + headerWith ("", [], [("invalid","1"), ("lang", "en")]) 1 "test" + =?> + "<h1 lang=\"en\">test</h1>" + ] + , testGroup "quotes" + [ "quote with cite attribute (without q-tags)" =: + doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) + =?> "“<span cite=\"http://example.org\">examples</span>”" + , tQ "quote with cite attribute (with q-tags)" $ + doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) + =?> "<q cite=\"http://example.org\">examples</q>" + ] + , testGroup "sample" + [ "sample should be rendered correctly" =: + plain (codeWith ("",["sample"],[]) "Answer is 42") =?> + "<samp>Answer is 42</samp>" + ] + , testGroup "variable" + [ "variable should be rendered correctly" =: + plain (codeWith ("",["variable"],[]) "result") =?> + "<var>result</var>" + ] + , testGroup "sample with style" + [ "samp should wrap highlighted code" =: + codeWith ("",["sample","haskell"],[]) ">>=" + =?> ("<samp><code class=\"sourceCode haskell\">" ++ + "<span class=\"op\">>>=</span></code></samp>") + ] + , testGroup "variable with style" + [ "var should wrap highlighted code" =: + codeWith ("",["haskell","variable"],[]) ">>=" + =?> ("<var><code class=\"sourceCode haskell\">" ++ + "<span class=\"op\">>>=</span></code></var>") + ] + ] + where + tQ :: (ToString a, ToPandoc a) + => String -> (a, String) -> TestTree + tQ = test htmlQTags -- cgit v1.2.3 From 1843a8793a9043a45c8c427b06f100461889d7ef Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 May 2021 18:08:02 +0200 Subject: HTML writer: keep attributes from code nested below pre tag. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a code block is defined with `<pre><code class="language-x">…</code></pre>`, where the `<pre>` element has no attributes, then the attributes from the `<code>` element are used instead. Any leading `language-` prefix is dropped in the code's *class* attribute are dropped to improve syntax highlighting. Closes: #7221 --- src/Text/Pandoc/Readers/HTML.hs | 13 ++++++++++++- test/Tests/Readers/HTML.hs | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 0a9e4addf..fc4575f2d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -557,7 +557,18 @@ pFigure = try $ do pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" []) - let attr = toAttr attr' + -- if the `pre` has no attributes, try if it is followed by a `code` + -- element and use those attributes if possible. + attr <- case attr' of + _:_ -> pure (toAttr attr') + [] -> option nullAttr $ do + TagOpen _ codeAttr <- pSatisfy (matchTagOpen "code" []) + pure $ toAttr + [ (k, v') | (k, v) <- codeAttr + -- strip language from class + , let v' = if k == "class" + then fromMaybe v (T.stripPrefix "language-" v) + else v ] contents <- manyTill pAny (pCloses "pre" <|> eof) let rawText = T.concat $ map tagToText contents -- drop leading newline if any diff --git a/test/Tests/Readers/HTML.hs b/test/Tests/Readers/HTML.hs index 7f5849991..9bf567194 100644 --- a/test/Tests/Readers/HTML.hs +++ b/test/Tests/Readers/HTML.hs @@ -108,6 +108,17 @@ tests = [ testGroup "base tag" "<header id=\"title\">Title</header>" =?> divWith ("title", mempty, mempty) (plain "Title") ] + , testGroup "code block" + [ test html "attributes in pre > code element" $ + "<pre><code id=\"a\" class=\"python\">\nprint('hi')\n</code></pre>" + =?> + codeBlockWith ("a", ["python"], []) "print('hi')" + + , test html "attributes in pre take precendence" $ + "<pre id=\"c\"><code id=\"d\">\nprint('hi mom!')\n</code></pre>" + =?> + codeBlockWith ("c", [], []) "print('hi mom!')" + ] , askOption $ \(QuickCheckTests numtests) -> testProperty "Round trip" $ withMaxSuccess (if QuickCheckTests numtests == defaultValue -- cgit v1.2.3 From 56fb4dae1ba2f6c28f561964249b89385d482f53 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 17 May 2021 20:42:43 -0700 Subject: Citeproc: ensure that CSL-related attributes are passed on... ...to a Div with id 'refs'. Previously we just left the attributes of such a Div alone, which meant that style options like entry-spacing had no effect there. --- src/Text/Pandoc/Citeproc.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index 2f4936fa6..de63aed1f 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -499,7 +499,7 @@ insertRefs refkvs refclasses meta refs bs = put True -- refHeader isn't used if you have an explicit references div let cs' = ordNub $ cs ++ refclasses - return $ Div ("refs",cs',kvs) (xs ++ refs) + return $ Div ("refs",cs' ++ refclasses,kvs ++ refkvs) (xs ++ refs) go x = return x refTitle :: Meta -> Maybe [Inline] -- cgit v1.2.3 From fa23effe2567183ec424feda55f507e27f50f7c8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 17 May 2021 20:43:41 -0700 Subject: LaTeX template: improve treatment of CSL entry-spacing. Previously with the default template settings (`indent` variable not set), we would get interparagraph spaces separating bib entries even with `entry-spacing="0"`. On the other hand, setting `entry-spacing="2"` gave ridiculously large spacing. This change makes the spacing caused by `entry-spacing` a multiple of `\parskip` by default, which gives aesthetically reasonable output. Those who want a larger or smaller unit (e.g. because they use `indent` which sets `\parskip` to 0) may `\setlength{\cslentryspacingunit}{10pt}` in header-includes to override the defaults. Closes #7296. --- data/templates/default.latex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index 61402c605..60f9972c2 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -378,6 +378,8 @@ $if(csl-refs)$ \setlength{\cslhangindent}{1.5em} \newlength{\csllabelwidth} \setlength{\csllabelwidth}{3em} +\newlength{\cslentryspacingunit} % times entry-spacing +\setlength{\cslentryspacingunit}{\parskip} \newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing {% don't indent paragraphs \setlength{\parindent}{0pt} @@ -387,9 +389,7 @@ $if(csl-refs)$ \def\par{\hangindent=\cslhangindent\oldpar} \fi % set entry spacing - \ifnum #2 > 0 - \setlength{\parskip}{#2\baselineskip} - \fi + \setlength{\parskip}{#2\cslentryspacingunit} }% {} \usepackage{calc} -- cgit v1.2.3 From eb3dff148e67e84362632e63848d40ba808940f4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 18 May 2021 22:55:47 +0200 Subject: LaTeX writer: separate successive quote chars with thin space Successive quote characters are separated with a thin space to improve readability and to prevent unwanted ligatures. Detection of these quotes sometimes had failed if the second quote was nested in a span element. Closes: #6958 --- src/Text/Pandoc/Writers/LaTeX.hs | 5 +++-- test/command/6958.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 test/command/6958.md diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index bf57937bd..978f94ea0 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -894,8 +894,9 @@ inlineToLaTeX (Quoted qt lst) = do then char '`' <> inner <> char '\'' else char '\x2018' <> inner <> char '\x2019' where - isQuoted (Quoted _ _) = True - isQuoted _ = False + isQuoted (Span _ (x:_)) = isQuoted x + isQuoted (Quoted _ _) = True + isQuoted _ = False inlineToLaTeX (Str str) = do setEmptyLine False liftM literal $ stringToLaTeX TextString str diff --git a/test/command/6958.md b/test/command/6958.md new file mode 100644 index 000000000..230371d7d --- /dev/null +++ b/test/command/6958.md @@ -0,0 +1,10 @@ +Add thin space between single and double quotes. +``` +% pandoc -t latex+smart +--- +lang: en-GB +--- +'["On the Outside"]{}: Constructing Cycling Citizenship.' +^D +`\,{``On the Outside''}: Constructing Cycling Citizenship.' +``` -- cgit v1.2.3 From ad2b4423ef350b7b8eaed67edcf500b58a487529 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 18 May 2021 22:14:19 -0700 Subject: Updaet changelog. --- changelog.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 0deb346ce..73d271a2c 100644 --- a/changelog.md +++ b/changelog.md @@ -72,8 +72,15 @@ * Docx reader: Add handling of vml image objects (#7257, mbrackeantidot). - * HTML reader: Don't fail on unmatched closing "script" tag - (Albert Krenkel, #7282). + * HTML reader: + + + Don't fail on unmatched closing "script" tag (Albert Krenkel, #7282). + + Keep h1 tags as normal headers (#2293, Albert Krewinkel). + The tags `<title>` and `<h1 class="title">` often contain the same + information, so the latter was dropped from the document. However, as + this can lead to loss of information, the heading is now always + retained. Use `--shift-heading-level-by=-1` to turn the `<h1>` + into the document title, or a filter to restore the previous behavior. * DocBook/JATS readers: @@ -90,6 +97,11 @@ + For beamer output, support `exampleblock` and `alertblock` (#7278). A block will be rendered as an `exampleblock` if the heading has class `example` and an `alertblock` if it has class `alert`. + + Separate successive quote chars with thin space (#6958, + Albert Krewinkel). Successive quote characters are separated with + a thin space to improve readability and to prevent unwanted ligatures. + Detection of these quotes sometimes had failed if the second quote + was nested in a span element. * EPUB Writer: Fix belongs-to-collection XML id choice (#7267, nuew). The epub writer previously used the same XML id for both the book @@ -97,8 +109,23 @@ * BibTeX/BibLaTeX writer: Handle `annote` field (#7266). - * ConTeXt writer: support blank lines in line blocks (#6564, - Albert Krewinkel, thanks to @denismaier). + * ConTeXt writer: + + + Support blank lines in line blocks (#6564, Albert Krewinkel, + thanks to @denismaier). + + Use span identifiers as reference anchors (#7246, Albert Krewinkel). + + * HTML writer: + + + Keep attributes from code nested below `pre` tag (#7221, + Albert Krewinkel). If a code block is defined with `<pre><code + class="language-x">…</code></pre>`, where the `<pre>` element has no + attributes, then the attributes from the `<code>` element are used + instead. Any leading `language-` prefix is dropped in the code's + `class` attribute are dropped to improve syntax highlighting. + + Ensure headings only have valid attribs in HTML4 (#5944, Albert + Krewinkel). + + Parse `<header>` as a Div (Albert Krewinkel). * Org writer: @@ -146,6 +173,19 @@ sets `w:proofState` for spelling or grammar to `dirty`, so that spell/grammar checking will be triggered on the generated docx. + + Copy over more settings from reference.docx (#7240). From settings.xml + in the reference-doc, we now include: `zoom`, `embedSystemFonts`, + `doNotTrackMoves`, `defaultTabStop`, `drawingGridHorizontalSpacing`, + `drawingGridVerticalSpacing`, `displayHorizontalDrawingGridEvery`, + `displayVerticalDrawingGridEvery`, `characterSpacingControl`, + `savePreviewPicture`, `mathPr`, `themeFontLang`, `decimalSymbol`, + `listSeparator`, `autoHyphenation`, `compat`. + + Set zoom to 100% by default in settings.xml. + + Align math options more with current Word defaults (e.g. + Cambria Math font). + + Remove `rsid`s from default settings.xml. Word will add these + when revisions are made. + * Markdown writer: @@ -157,6 +197,9 @@ were needed. So we got spurious quotes in some cases and didn't get necessary quotes in others. + Use `@{..}` syntax for citations when needed. + + Use fewer unneeded escapes for `#` (see #6259). + + Improve escaping of `@`. We need to escape literal `@` before + `{` because of the new citation syntax. * Commonmark writer: Use backslash escapes for `<` and `|`... instead of entities (#7208). @@ -172,12 +215,32 @@ + List of figures before list of tables (#7235, Julien Dutant). + Move CSL macro definitions before header-includes so they can be overridden (#7286). + + Improve treatment of CSL `entry-spacing` (#7296). + Previously with the default template settings (`indent` variable + not set), we would get interparagraph spaces separating bib + entries even with `entry-spacing="0"`. On the other hand, + setting `entry-spacing="2"` gave ridiculously large spacing. + This change makes the spacing caused by `entry-spacing` a multiple + of `\parskip` by default, which gives aesthetically reasonable + output. Those who want a larger or smaller unit (e.g. because + they use `indent` which sets `\parskip` to 0) may + `\setlength{\cslentryspacingunit}{10pt}` in header-includes + to override the defaults. + + Move title, author, date up to top of preamble (#7295). + This allows header-includes to use them, and puts them + in a position where you can see them immediately. + + Define commands for zero width non-joiner character + (#6639, Albert Krewinkel). The zero-width non-joiner character + is used to avoid ligatures (e.g. in German). * ConTeXt template: List of figures before list of tables (#7235, Julien Dutant). * reveal.js template: Support `toc-title` (#7171, Florian Kohrt). + * HTML-based slide shows: add support for `institute` (#7289, Thomas + Hodgson). + * Text.Pandoc.XML.Light: add Eq, Ord instances for Content, Element, Attr, CDataKind [API change]. @@ -200,8 +263,13 @@ syntax map, but that's okay in that context, since `--syntax-definition` won't create new listings styles. - * Use metadata's `lang` for the lang parameter of citeproc, overriding - `localeLanguage`. + * Text.Pandoc.Citeproc: + + + Ensure that CSL-related attributes are passed on to a Div + with id 'refs'. Otherwise things like `entry-spacing` + won't work when such Divs are used. + + Use metadata's `lang` for the lang parameter of citeproc, overriding + `localeLanguage`. * Remove Text.Pandoc.BCP47 module [API change]. Use types and functions from UnicodeCollation.Lang instead. @@ -233,7 +301,12 @@ * Use latest xml-conduit. - * MANUAL: Add information about `lang` and bibliography sorting. + * MANUAL: + + + Add information about `lang` and bibliography sorting. + + Add info about YAML escape sequences, link to spec (#7152, + Albert Krewinkel). + + Note that `institute` variable works for HTML-based slides. * Updated and fixed typos in documentation (Charanjit Singh, Anti-Distinctlyminty, Tatiana Porras, obcat). -- cgit v1.2.3 From ddbd984a0d8ea7e75f78ad6632fe3568e2390deb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 17 May 2021 09:31:52 -0700 Subject: Text.Pandoc.MediaBag: change type to use a Text key... instead of `[FilePath]`. We normalize the path and use `/` separators for consistency. --- src/Text/Pandoc/Class/PandocMonad.hs | 1 + src/Text/Pandoc/MediaBag.hs | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 7559cd7cd..226194503 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index 3249bcdeb..4a9b4efa1 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- | @@ -27,26 +28,31 @@ import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Typeable (Typeable) import System.FilePath -import qualified System.FilePath.Posix as Posix import Text.Pandoc.MIME (MimeType, getMimeTypeDef) +import Data.Text (Text) +import qualified Data.Text as T -- | A container for a collection of binary resources, with names and -- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty' -- can be used for an empty 'MediaBag', and '<>' can be used to append -- two 'MediaBag's. -newtype MediaBag = MediaBag (M.Map [FilePath] (MimeType, BL.ByteString)) +newtype MediaBag = MediaBag (M.Map Text (MimeType, BL.ByteString)) deriving (Semigroup, Monoid, Data, Typeable) instance Show MediaBag where show bag = "MediaBag " ++ show (mediaDirectory bag) +-- | We represent paths with /, in normalized form. +canonicalize :: FilePath -> Text +canonicalize = T.replace "\\" "/" . T.pack . normalise + -- | Delete a media item from a 'MediaBag', or do nothing if no item corresponds -- to the given path. deleteMedia :: FilePath -- ^ relative path and canonical name of resource -> MediaBag -> MediaBag deleteMedia fp (MediaBag mediamap) = - MediaBag $ M.delete (splitDirectories fp) mediamap + MediaBag $ M.delete (canonicalize fp) mediamap -- | Insert a media item into a 'MediaBag', replacing any existing -- value with the same name. @@ -56,7 +62,7 @@ insertMedia :: FilePath -- ^ relative path and canonical name of resource -> MediaBag -> MediaBag insertMedia fp mbMime contents (MediaBag mediamap) = - MediaBag (M.insert (splitDirectories fp) (mime, contents) mediamap) + MediaBag (M.insert (canonicalize fp) (mime, contents) mediamap) where mime = fromMaybe fallback mbMime fallback = case takeExtension fp of ".gz" -> getMimeTypeDef $ dropExtension fp @@ -66,16 +72,16 @@ insertMedia fp mbMime contents (MediaBag mediamap) = lookupMedia :: FilePath -> MediaBag -> Maybe (MimeType, BL.ByteString) -lookupMedia fp (MediaBag mediamap) = M.lookup (splitDirectories fp) mediamap +lookupMedia fp (MediaBag mediamap) = M.lookup (canonicalize fp) mediamap -- | Get a list of the file paths stored in a 'MediaBag', with -- their corresponding mime types and the lengths in bytes of the contents. mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)] mediaDirectory (MediaBag mediamap) = M.foldrWithKey (\fp (mime,contents) -> - ((Posix.joinPath fp, mime, fromIntegral $ BL.length contents):)) [] mediamap + ((T.unpack fp, mime, fromIntegral (BL.length contents)):)) [] mediamap mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)] mediaItems (MediaBag mediamap) = M.foldrWithKey (\fp (mime,contents) -> - ((Posix.joinPath fp, mime, contents):)) [] mediamap + ((T.unpack fp, mime, contents):)) [] mediamap -- cgit v1.2.3 From 9b5798bd9abe267f783dab7fe5295c9b61e2fdce Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 17 May 2021 21:22:46 -0700 Subject: Use fetchItem instead of downloadOrRead in fetchMediaResource. --- src/Text/Pandoc/Class/PandocMonad.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 226194503..b12850de5 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -635,7 +635,7 @@ withPaths (p:ps) action fp = fetchMediaResource :: PandocMonad m => T.Text -> m (FilePath, Maybe MimeType, BL.ByteString) fetchMediaResource src = do - (bs, mt) <- downloadOrRead src + (bs, mt) <- fetchItem src let ext = fromMaybe (T.pack $ takeExtension $ T.unpack src) (mt >>= extensionFromMimeType) let bs' = BL.fromChunks [bs] -- cgit v1.2.3 From 640dbf8b8f5e652661df42c631b4343570d7448e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 May 2021 09:51:50 -0700 Subject: Remove unused pragma. --- src/Text/Pandoc/Class/PandocMonad.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index b12850de5..dd6499a73 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -- cgit v1.2.3 From d452155bcf158b790b0087d6d5c02cc3212c921c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 May 2021 10:20:37 -0700 Subject: Update documentation on citation syntax. --- MANUAL.txt | 101 +++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 734df0d3f..8a36c1d35 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4954,39 +4954,63 @@ Inline and regular footnotes may be mixed freely. #### Extension: `citations` #### -Markdown citations go inside square brackets and are separated -by semicolons. Each citation must have a key and may optionally -have a prefix, a locator, and a suffix. The citation key -consists of `@` plus the citation identifier, possibly -enclosed in curly braces. If the identifier starts -with a letter, digit, or `_`, followed by zero or more -alphanumerics, `_`, and internal punctuation characters -(`:.#$%&-+?<>~/`), then the curly braces may be omitted. -Identifiers may not contain whitespace characters or unbalanced -curly braces. Here are some examples: - - Blah blah [see @doe99, pp. 33-35; also @smith04, chap. 1]. - - Blah blah [@doe99, pp. 33-35, 38-39 and *passim*]. - - Blah blah [@smith04; @doe99]. - - Blah blah [@{https://example.com/bib?name=foobar&date=2000}, p. 33]. - -`pandoc` detects locator terms in the [CSL locale files]. -Either abbreviated or unabbreviated forms are accepted. In the `en-US` -locale, locator terms can be written in either singular or plural forms, -as `book`, `bk.`/`bks.`; `chapter`, `chap.`/`chaps.`; `column`, -`col.`/`cols.`; `figure`, `fig.`/`figs.`; `folio`, `fol.`/`fols.`; -`number`, `no.`/`nos.`; `line`, `l.`/`ll.`; `note`, `n.`/`nn.`; `opus`, -`op.`/`opp.`; `page`, `p.`/`pp.`; `paragraph`, `para.`/`paras.`; `part`, -`pt.`/`pts.`; `section`, `sec.`/`secs.`; `sub verbo`, `s.v.`/`s.vv.`; -`verse`, `v.`/`vv.`; `volume`, `vol.`/`vols.`; `¶`/`¶¶`; `§`/`§§`. If no +To cite a bibliographic item with an identifier foo, use the +syntax `@foo`. Normal citations should be included in square +brackets, with semicolons separating distinct items: + + Blah blah [@doe99; @smith2000; @smith2004]. + +How this is rendered depends on the citation style. In an +author-date style, it might render as + + Blah blah (Doe 1999, Smith 2000, 2004). + +In a footnote style, it might render as + + Blah blah.[^1] + + [^1]: John Doe, "Frogs," *Journal of Amphibians* 44 (1999); + Susan Smith, "Flies," *Journal of Insects* (2000); + Susan Smith, "Bees," *Journal of Insects* (2004). + +See the [CSL user documentation] for more information about CSL +styles and how they affect rendering. + +If a citation key do not start with a letter, digit, or `_`, +and contain only alphanumerics and internal punctuation +characters (`:.#$%&-+?<>~/`), the key must be surrounded +by curly braces, which are not considered part of the key. +In `@Foo_bar.baz.`, the key is `Foo_bar.baz`. The final +period is not *internal* punctuation, so it is not included in +the key. In `@{Foo_bar.baz.}`, the key is `Foo_bar.baz.`, including +the final period. The curly braces are recommended if you use URLs as +keys: `[@{https://example.com/bib?name=foobar&date=2000}, p. 33]`. + +Citation items may optionally include a prefix, a locator, and +a suffix. In + + Blah blah [see @doe99, pp. 33-35 and *passim*; @smith04, chap. 1]. + +The first item (`doe99`) has prefix `see `, locator `pp. 33-35`, +and suffix `and *passim*`. The second item (`smith04`) has +locator `chap. 1` and no prefix or suffix. + +Pandoc uses some heuristics to separate the locator from the +rest of the subject. It is sensitive to the locator terms +defined in the [CSL locale files]. Either abbreviated or +unabbreviated forms are accepted. In the `en-US` locale, locator +terms can be written in either singular or plural forms, as +`book`, `bk.`/`bks.`; `chapter`, `chap.`/`chaps.`; `column`, +`col.`/`cols.`; `figure`, `fig.`/`figs.`; `folio`, +`fol.`/`fols.`; `number`, `no.`/`nos.`; `line`, `l.`/`ll.`; +`note`, `n.`/`nn.`; `opus`, `op.`/`opp.`; `page`, `p.`/`pp.`; +`paragraph`, `para.`/`paras.`; `part`, `pt.`/`pts.`; `section`, +`sec.`/`secs.`; `sub verbo`, `s.v.`/`s.vv.`; `verse`, +`v.`/`vv.`; `volume`, `vol.`/`vols.`; `¶`/`¶¶`; `§`/`§§`. If no locator term is used, "page" is assumed. -`pandoc` will use heuristics to distinguish the locator -from the suffix. In complex cases, the locator can be enclosed -in curly braces: +In complex cases, you can force something to be treated as +a locator by enclosing it in curly braces: [@smith{ii, A, D-Z}, with a suffix] [@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here] @@ -4997,13 +5021,26 @@ author is already mentioned in the text: Smith says blah [-@smith04]. -You can also write an in-text citation, as follows: +You can also write an author-in-text citation, by omitting the +brackets, as follows: @smith04 says blah. @smith04 [p. 33] says blah. +This will cause the author's name to be rendered, followed by +the bibliographical details. Use this form when you want to +make the citation the subject of a sentence. +When you are using a note style, it is usually better to let +citeproc create the footnotes from citations rather than writing +an explicit note. If you do write an explicit note that +contains a citation, note that normal citations will be put in +parentheses, while author-in-text citations will not. For +this reason, it is sometimes preferable to use the +author-in-text style inside notes, when using a note style. + +[CSL user documentation]: https://citationstyles.org/authors/ [CSL]: https://docs.citationstyles.org/en/stable/specification.html [CSL markup specs]: https://docs.citationstyles.org/en/1.0/release-notes.html#rich-text-markup-within-fields [Chicago Manual of Style]: https://chicagomanualofstyle.org -- cgit v1.2.3 From 7efb71f4f6feae5c57e685b588e725b1ab27c4b5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 May 2021 10:43:53 -0700 Subject: Revisions to citation syntax description update. --- MANUAL.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 8a36c1d35..bd2f01c32 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4976,9 +4976,9 @@ In a footnote style, it might render as See the [CSL user documentation] for more information about CSL styles and how they affect rendering. -If a citation key do not start with a letter, digit, or `_`, -and contain only alphanumerics and internal punctuation -characters (`:.#$%&-+?<>~/`), the key must be surrounded +Unless a citation key start with a letter, digit, or `_`, +and contains only alphanumerics and internal punctuation +characters (`:.#$%&-+?<>~/`), it must be surrounded by curly braces, which are not considered part of the key. In `@Foo_bar.baz.`, the key is `Foo_bar.baz`. The final period is not *internal* punctuation, so it is not included in @@ -5022,7 +5022,7 @@ author is already mentioned in the text: Smith says blah [-@smith04]. You can also write an author-in-text citation, by omitting the -brackets, as follows: +square brackets: @smith04 says blah. @@ -5038,7 +5038,7 @@ an explicit note. If you do write an explicit note that contains a citation, note that normal citations will be put in parentheses, while author-in-text citations will not. For this reason, it is sometimes preferable to use the -author-in-text style inside notes, when using a note style. +author-in-text style inside notes when using a note style. [CSL user documentation]: https://citationstyles.org/authors/ [CSL]: https://docs.citationstyles.org/en/stable/specification.html -- cgit v1.2.3 From 5736b331d8ecaa12cc3e2712211ada37c665a93a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 May 2021 16:14:49 -0700 Subject: LaTeX reader: better support for `\xspace`. Previously we only supported it in inline contexts; now we support it in all contexts, including math. Partially addresses #7299. --- src/Text/Pandoc/Readers/LaTeX.hs | 12 ------------ src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 21 +++++++++++++++++++-- test/command/4442.md | 2 +- test/command/7299.md | 23 +++++++++++++++++++++++ 4 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 test/command/7299.md diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f90d562ae..2ace18d1b 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -232,16 +232,6 @@ mkImage options (T.unpack -> src) = do _ -> return src return $ imageWith attr (T.pack src') "" alt -doxspace :: PandocMonad m => LP m Inlines -doxspace = - (space <$ lookAhead (satisfyTok startsWithLetter)) <|> return mempty - where startsWithLetter (Tok _ Word t) = - case T.uncons t of - Just (c, _) | isLetter c -> True - _ -> False - startsWithLetter _ = False - - removeDoubleQuotes :: Text -> Text removeDoubleQuotes t = Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" @@ -417,8 +407,6 @@ inlineCommands = M.unions -- LaTeX colors , ("textcolor", coloredInline "color") , ("colorbox", coloredInline "background-color") - -- xspace - , ("xspace", doxspace) -- etoolbox , ("ifstrequal", ifstrequal) , ("newtoggle", braced >>= newToggle) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 35ce3509d..b6804a825 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -464,7 +464,7 @@ satisfyTok f = do doMacros :: PandocMonad m => LP m () doMacros = do st <- getState - unless (sVerbatimMode st || M.null (sMacros st)) $ do + unless (sVerbatimMode st) $ getInput >>= doMacros' 1 >>= setInput doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] @@ -526,7 +526,7 @@ doMacros' n inp = $ throwError $ PandocMacroLoop name macros <- sMacros <$> getState case M.lookup name macros of - Nothing -> mzero + Nothing -> trySpecialMacro name ts Just (Macro expansionPoint argspecs optarg newtoks) -> do let getargs' = do args <- @@ -554,6 +554,23 @@ doMacros' n inp = ExpandWhenUsed -> doMacros' (n' + 1) result ExpandWhenDefined -> return result +-- | Certain macros do low-level tex manipulations that can't +-- be represented in our Macro type, so we handle them here. +trySpecialMacro :: PandocMonad m => Text -> [Tok] -> LP m [Tok] +trySpecialMacro "xspace" ts = do + ts' <- doMacros' 1 ts + case ts' of + Tok pos Word t : _ + | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts' + _ -> return ts' +trySpecialMacro _ _ = mzero + +startsWithAlphaNum :: Text -> Bool +startsWithAlphaNum t = + case T.uncons t of + Just (c, _) | isAlphaNum c -> True + _ -> False + setpos :: SourcePos -> Tok -> Tok setpos spos (Tok _ tt txt) = Tok spos tt txt diff --git a/test/command/4442.md b/test/command/4442.md index 8574fe759..447073406 100644 --- a/test/command/4442.md +++ b/test/command/4442.md @@ -5,5 +5,5 @@ ^D \newcommand{\myFruit}{Mango\xspace} -Mango\xspace is the king of fruits. +Mango is the king of fruits. ``` diff --git a/test/command/7299.md b/test/command/7299.md new file mode 100644 index 000000000..0847c40ce --- /dev/null +++ b/test/command/7299.md @@ -0,0 +1,23 @@ +``` +% pandoc -f latex -t plain +$1-{\ensuremath{r}\xspace}$ +^D +1 − r +``` + +``` +% pandoc -f latex -t plain +\newcommand{\foo}{Foo\xspace} + +$\text{\foo bar}$ +^D +Foo bar +``` + +``` +% pandoc -f latex -t plain +a\xspace b +^D +a b +``` + -- cgit v1.2.3 From b6239f41509c368b5befd316c290b5b6cc6f00e3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 20 May 2021 10:48:28 +0200 Subject: ZimWiki writer: allow links and emphasis in headers The latest version of ZimWiki supports this. Closes: #6605 --- src/Text/Pandoc/Writers/ZimWiki.hs | 2 +- test/writer.zimwiki | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index fcf9e000d..df914f590 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -116,7 +116,7 @@ blockToZimWiki opts b@(RawBlock f str) blockToZimWiki _ HorizontalRule = return "\n----\n" blockToZimWiki opts (Header level _ inlines) = do - contents <- inlineListToZimWiki opts $ removeFormatting inlines -- emphasis, links etc. not allowed in headers + contents <- inlineListToZimWiki opts inlines let eqs = T.replicate ( 7 - level ) "=" return $ eqs <> " " <> contents <> " " <> eqs <> "\n" diff --git a/test/writer.zimwiki b/test/writer.zimwiki index 4b384fb20..f793e5760 100644 --- a/test/writer.zimwiki +++ b/test/writer.zimwiki @@ -8,9 +8,9 @@ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s ====== Headers ====== -===== Level 2 with an embedded link ===== +===== Level 2 with an [[url|embedded link]] ===== -==== Level 3 with emphasis ==== +==== Level 3 with //emphasis// ==== === Level 4 === @@ -18,7 +18,7 @@ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s ====== Level 1 ====== -===== Level 2 with emphasis ===== +===== Level 2 with //emphasis// ===== ==== Level 3 ==== -- cgit v1.2.3 From 8437a4a002210a33ee721c58f5f95605898a8e1b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 08:15:48 -0700 Subject: LaTeX reader: support `\pm` in `SI{..}`. Closes #6620. --- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 4 +++- test/command/6620.md | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index 1952f4e1a..c4fb06700 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -45,7 +45,9 @@ doSI tok = do unit] doSInum :: PandocMonad m => LP m Inlines -doSInum = skipopts *> (tonum . untokenize <$> braced) +doSInum = skipopts *> (tonum . untokenize . map convertPM <$> braced) + where convertPM (Tok pos (CtrlSeq "pm") _) = Tok pos Word "\xb1\xa0" + convertPM t = t tonum :: Text -> Inlines tonum value = diff --git a/test/command/6620.md b/test/command/6620.md index 314200d30..00643e57c 100644 --- a/test/command/6620.md +++ b/test/command/6620.md @@ -7,9 +7,12 @@ \SI{0.135(21)}{\m} \SI{12.3(60)}{\m} + +\SI{10.0 \pm 3.3}{\ms} ^D <p>23 ± 2 m</p> <p>125 ± 12 m</p> <p>0.135 ± 0.021 m</p> <p>12.3 ± 6 m</p> +<p>10.0 ± 3.3 ms</p> ``` -- cgit v1.2.3 From a366bd6abccd18a49f0033de850ddd53830cc5ed Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 09:03:29 -0700 Subject: LaTeX reader: Fix parsing of `+-` in siunitx numbers. See #6658. --- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 11 +++++++---- test/command/6620.md | 5 ++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index c4fb06700..5e140ef7a 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -45,9 +45,7 @@ doSI tok = do unit] doSInum :: PandocMonad m => LP m Inlines -doSInum = skipopts *> (tonum . untokenize . map convertPM <$> braced) - where convertPM (Tok pos (CtrlSeq "pm") _) = Tok pos Word "\xb1\xa0" - convertPM t = t +doSInum = skipopts *> (tonum . untokenize <$> braced) tonum :: Text -> Inlines tonum value = @@ -74,12 +72,16 @@ parseNumPart = parseDecimalNum <|> parseComma <|> parsePlusMinus <|> + parsePM <|> parseI <|> parseExp <|> parseX <|> parseSpace where - parseDecimalNum = do + parseDecimalNum, parsePlusMinus, parsePM, + parseComma, parseI, parseX, + parseExp, parseSpace :: Parser Text () Inlines + parseDecimalNum = try $ do pref <- option mempty $ (mempty <$ char '+') <|> ("\x2212" <$ char '-') basenum <- (pref <>) . T.pack <$> many1 (satisfy (\c -> isDigit c || c == '.')) @@ -100,6 +102,7 @@ parseNumPart = | otherwise -> "." <> t parseComma = str "." <$ char ',' parsePlusMinus = str "\xa0\xb1\xa0" <$ try (string "+-") + parsePM = str "\xa0\xb1\xa0" <$ try (string "\\pm") parseParens = char '(' *> many1 (satisfy (\c -> isDigit c || c == '.')) <* char ')' parseI = str "i" <$ char 'i' diff --git a/test/command/6620.md b/test/command/6620.md index 00643e57c..b19cd1972 100644 --- a/test/command/6620.md +++ b/test/command/6620.md @@ -9,10 +9,13 @@ \SI{12.3(60)}{\m} \SI{10.0 \pm 3.3}{\ms} + +\SI{10.0 +- 3.3}{\ms} ^D <p>23 ± 2 m</p> <p>125 ± 12 m</p> <p>0.135 ± 0.021 m</p> <p>12.3 ± 6 m</p> -<p>10.0 ± 3.3 ms</p> +<p>10.0 ± 3.3 ms</p> +<p>10.0 ± 3.3 ms</p> ``` -- cgit v1.2.3 From 183ce584779b344ad6a6a3e085ddfdb00faf62aa Mon Sep 17 00:00:00 2001 From: Denis Maier <denis.maier@ub.unibe.ch> Date: Thu, 20 May 2021 18:59:53 +0200 Subject: ConTeXt reader: improve ordered lists (#7304) Closes #5016 - change ordered list from itemize to enumerate - adds new itemgroup for ordered lists - add fontfeature for table figures - remove width from itemize in context writer --- data/templates/default.context | 4 ++ src/Text/Pandoc/Writers/ConTeXt.hs | 14 ++----- test/writer.context | 80 ++++++++++++++++++++------------------ test/writers-lang-and-dir.context | 4 ++ 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/data/templates/default.context b/data/templates/default.context index 64de448ca..df39130b2 100644 --- a/data/templates/default.context +++ b/data/templates/default.context @@ -51,6 +51,7 @@ $endif$ % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] +\definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] @@ -97,6 +98,9 @@ $endif$ \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] +\defineitemgroup[enumerate] +\setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] + \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 57d752a67..3cafcefba 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -16,7 +16,6 @@ module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Control.Monad.State.Strict import Data.Char (ord, isDigit) import Data.List (intersperse) -import Data.List.NonEmpty (nonEmpty) import Data.Maybe (mapMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -233,14 +232,7 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do Period -> "stopper=." OneParen -> "stopper=)" TwoParens -> "left=(,stopper=)" - let width = maybe 0 maximum $ nonEmpty $ map T.length $ - take (length contents) - (orderedListMarkers (start, style', delim)) - let width' = (toEnum width + 1) / 2 - let width'' = if width' > (1.5 :: Double) - then "width=" <> tshow width' <> "em" - else "" - let specs2Items = filter (not . T.null) [start', delim', width''] + let specs2Items = filter (not . T.null) [start', delim'] let specs2 = if null specs2Items then "" else "[" <> T.intercalate "," specs2Items <> "]" @@ -254,8 +246,8 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do UpperAlpha -> 'A') : if isTightList lst then ",packed]" else "]" let specs = T.pack style'' <> specs2 - return $ "\\startitemize" <> literal specs $$ vcat contents $$ - "\\stopitemize" <> blankline + return $ "\\startenumerate" <> literal specs $$ vcat contents $$ + "\\stopenumerate" <> blankline blockToConTeXt (DefinitionList lst) = liftM vcat $ mapM defListItemToConTeXt lst blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline diff --git a/test/writer.context b/test/writer.context index 77570c249..78667813b 100644 --- a/test/writer.context +++ b/test/writer.context @@ -17,6 +17,7 @@ % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] +\definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] @@ -48,6 +49,9 @@ \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] +\defineitemgroup[enumerate] +\setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] + \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] @@ -133,12 +137,12 @@ sub status { A list: -\startitemize[n,packed][stopper=.] +\startenumerate[n,packed][stopper=.] \item item one \item item two -\stopitemize +\stopenumerate Nested block quotes: @@ -255,51 +259,51 @@ Minuses loose: Tight: -\startitemize[n,packed][stopper=.] +\startenumerate[n,packed][stopper=.] \item First \item Second \item Third -\stopitemize +\stopenumerate and: -\startitemize[n,packed][stopper=.] +\startenumerate[n,packed][stopper=.] \item One \item Two \item Three -\stopitemize +\stopenumerate Loose using tabs: -\startitemize[n][stopper=.] +\startenumerate[n][stopper=.] \item First \item Second \item Third -\stopitemize +\stopenumerate and using spaces: -\startitemize[n][stopper=.] +\startenumerate[n][stopper=.] \item One \item Two \item Three -\stopitemize +\stopenumerate Multiple paragraphs: -\startitemize[n][stopper=.] +\startenumerate[n][stopper=.] \item Item 1, graf one. @@ -308,7 +312,7 @@ Multiple paragraphs: Item 2. \item Item 3. -\stopitemize +\stopenumerate \subsection[title={Nested},reference={nested}] @@ -327,7 +331,7 @@ Multiple paragraphs: Here's another: -\startitemize[n,packed][stopper=.] +\startenumerate[n,packed][stopper=.] \item First \item @@ -342,11 +346,11 @@ Here's another: \stopitemize \item Third -\stopitemize +\stopenumerate Same thing but with paragraphs: -\startitemize[n][stopper=.] +\startenumerate[n][stopper=.] \item First \item @@ -362,7 +366,7 @@ Same thing but with paragraphs: \stopitemize \item Third -\stopitemize +\stopenumerate \subsection[title={Tabs and spaces},reference={tabs-and-spaces}] @@ -382,7 +386,7 @@ Same thing but with paragraphs: \subsection[title={Fancy list markers},reference={fancy-list-markers}] -\startitemize[n][start=2,left=(,stopper=),width=2.0em] +\startenumerate[n][start=2,left=(,stopper=)] \item begins with 2 \item @@ -390,51 +394,51 @@ Same thing but with paragraphs: with a continuation - \startitemize[r,packed][start=4,stopper=.,width=2.0em] + \startenumerate[r,packed][start=4,stopper=.] \item sublist with roman numerals, starting with 4 \item more items - \startitemize[A,packed][left=(,stopper=),width=2.0em] + \startenumerate[A,packed][left=(,stopper=)] \item a subsublist \item a subsublist - \stopitemize - \stopitemize -\stopitemize + \stopenumerate + \stopenumerate +\stopenumerate Nesting: -\startitemize[A,packed][stopper=.] +\startenumerate[A,packed][stopper=.] \item Upper Alpha - \startitemize[R,packed][stopper=.] + \startenumerate[R,packed][stopper=.] \item Upper Roman. - \startitemize[n,packed][start=6,left=(,stopper=),width=2.0em] + \startenumerate[n,packed][start=6,left=(,stopper=)] \item Decimal start with 6 - \startitemize[a,packed][start=3,stopper=)] + \startenumerate[a,packed][start=3,stopper=)] \item Lower alpha with paren - \stopitemize - \stopitemize - \stopitemize -\stopitemize + \stopenumerate + \stopenumerate + \stopenumerate +\stopenumerate Autonumbering: -\startitemize[n,packed] +\startenumerate[n,packed] \item Autonumber. \item More. - \startitemize[a,packed] + \startenumerate[a,packed] \item Nested. - \stopitemize -\stopitemize + \stopenumerate +\stopenumerate Should not be a list item: @@ -547,12 +551,12 @@ Blank line after term, indented marker, alternate markers: \startdescription{orange} orange fruit - \startitemize[n,packed][stopper=.] + \startenumerate[n,packed][stopper=.] \item sublist \item sublist - \stopitemize + \stopenumerate \stopdescription \section[title={HTML Blocks},reference={html-blocks}] @@ -897,10 +901,10 @@ note{]} Here is an inline note.\footnote{This is {\em easier} to type. Inline Notes can go in quotes.\footnote{In quote.} \stopblockquote -\startitemize[n,packed][stopper=.] +\startenumerate[n,packed][stopper=.] \item And in list items.\footnote{In list.} -\stopitemize +\stopenumerate This paragraph should not be part of the note, as it is not indented. diff --git a/test/writers-lang-and-dir.context b/test/writers-lang-and-dir.context index c4bff1a01..c01ecab58 100644 --- a/test/writers-lang-and-dir.context +++ b/test/writers-lang-and-dir.context @@ -15,6 +15,7 @@ % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] +\definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] @@ -46,6 +47,9 @@ \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] +\defineitemgroup[enumerate] +\setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] + \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] -- cgit v1.2.3 From 5dc917da3ed997c6e48e22bde242f0f8e1ae5333 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 09:11:26 -0700 Subject: LaTeX reader siunitx: add leading 0 to numbers starting with . --- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 7 +++++-- test/command/6620.md | 3 --- test/command/6658.md | 9 +++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 test/command/6658.md diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index 5e140ef7a..1474329d4 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -83,8 +83,11 @@ parseNumPart = parseExp, parseSpace :: Parser Text () Inlines parseDecimalNum = try $ do pref <- option mempty $ (mempty <$ char '+') <|> ("\x2212" <$ char '-') - basenum <- (pref <>) . T.pack - <$> many1 (satisfy (\c -> isDigit c || c == '.')) + basenum' <- many1 (satisfy (\c -> isDigit c || c == '.')) + let basenum = pref <> T.pack + (case basenum' of + '.':_ -> '0':basenum' + _ -> basenum') uncertainty <- option mempty $ T.pack <$> parseParens if T.null uncertainty then return $ str basenum diff --git a/test/command/6620.md b/test/command/6620.md index b19cd1972..e448ca6b5 100644 --- a/test/command/6620.md +++ b/test/command/6620.md @@ -9,13 +9,10 @@ \SI{12.3(60)}{\m} \SI{10.0 \pm 3.3}{\ms} - -\SI{10.0 +- 3.3}{\ms} ^D <p>23 ± 2 m</p> <p>125 ± 12 m</p> <p>0.135 ± 0.021 m</p> <p>12.3 ± 6 m</p> <p>10.0 ± 3.3 ms</p> -<p>10.0 ± 3.3 ms</p> ``` diff --git a/test/command/6658.md b/test/command/6658.md new file mode 100644 index 000000000..bcd174465 --- /dev/null +++ b/test/command/6658.md @@ -0,0 +1,9 @@ +``` +pandoc -f latex +\SI{10.0 +- 3.3}{\ms} + +\num{.3e45} +^D +<p>10.0 ± 3.3 ms</p> +<p>0.3 × 10<sup>45</sup></p> +``` -- cgit v1.2.3 From bc5058234feab7646f58dc01379b4eadf95bf411 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 09:18:23 -0700 Subject: LaTeX reader sinuitx: fix + sign on ang. --- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 9 ++++++--- test/command/6658.md | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index 1474329d4..72f81dcde 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -118,11 +118,14 @@ doSIang :: PandocMonad m => LP m Inlines doSIang = do skipopts ps <- T.splitOn ";" . untokenize <$> braced + let dropPlus t = case T.uncons t of + Just ('+',t') -> t' + _ -> t case ps ++ repeat "" of (d:m:s:_) -> return $ - (if T.null d then mempty else str d <> str "\xb0") <> - (if T.null m then mempty else str m <> str "\x2032") <> - (if T.null s then mempty else str s <> str "\x2033") + (if T.null d then mempty else str (dropPlus d) <> str "\xb0") <> + (if T.null m then mempty else str (dropPlus m) <> str "\x2032") <> + (if T.null s then mempty else str (dropPlus s) <> str "\x2033") _ -> return mempty -- converts e.g. \SIrange{100}{200}{\ms} to "100 ms--200 ms" diff --git a/test/command/6658.md b/test/command/6658.md index bcd174465..0a8512f85 100644 --- a/test/command/6658.md +++ b/test/command/6658.md @@ -3,7 +3,10 @@ pandoc -f latex \SI{10.0 +- 3.3}{\ms} \num{.3e45} + +\ang{+10;+3;} ^D <p>10.0 ± 3.3 ms</p> <p>0.3 × 10<sup>45</sup></p> +<p>10°3′</p> ``` -- cgit v1.2.3 From 4e990a8cf9207f2315d6a55a45c93c2857663316 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 10:12:44 -0700 Subject: LaTeX/siunitx: fix parsing of `\cubic` etc. See #6658. --- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 85 +++++++++++++++++++------------- test/command/6658.md | 3 ++ 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index 72f81dcde..63ab7267d 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.SIunitx ( siunitxCommands ) @@ -154,40 +155,55 @@ doSIrange includeUnits tok = do emptyOr160 :: Inlines -> Inlines emptyOr160 x = if x == mempty then x else str "\160" -siUnit :: PandocMonad m => LP m Inlines -> LP m Inlines -siUnit tok = try (do - Tok _ (CtrlSeq name) _ <- anyControlSeq - case name of - "square" -> do - unit <- siUnit tok - return $ unit <> superscript "2" - "cubic" -> do - unit <- siUnit tok - return $ unit <> superscript "3" - "raisetothe" -> do - n <- tok - unit <- siUnit tok - return $ unit <> superscript n - _ -> - case M.lookup name siUnitMap of - Just il -> - option il $ - choice - [ (il <> superscript "2") <$ controlSeq "squared" - , (il <> superscript "3") <$ controlSeq "cubed" - , (\n -> il <> superscript n) <$> (controlSeq "tothe" *> tok) - ] - Nothing -> fail "not an siunit unit command") - <|> (lookAhead anyControlSeq >> tok) - <|> (do Tok _ Word t <- satisfyTok isWordTok - return $ str t) - <|> (symbol '^' *> (superscript <$> tok)) - <|> (symbol '_' *> (subscript <$> tok)) - <|> ("\xa0" <$ symbol '.') - <|> ("\xa0" <$ symbol '~') - <|> tok - <|> (do Tok _ _ t <- anyTok - return (str t)) +siUnit :: forall m. PandocMonad m => LP m Inlines -> LP m Inlines +siUnit tok = mconcat <$> many1 siUnitPart + where + siUnitPart :: LP m Inlines + siUnitPart = + (siPrefix <*> siUnitPart) + <|> (do u <- siBase <|> tok + option u $ siSuffix <*> pure u) + siPrefix :: LP m (Inlines -> Inlines) + siPrefix = + (do _ <- controlSeq "per" + skipopts -- TODO handle option + return (str "/" <>)) + <|> (do _ <- controlSeq "square" + skipopts + return (<> superscript "2")) + <|> (do _ <- controlSeq "cubic" + skipopts + return (<> superscript "3")) + <|> (do _ <- controlSeq "raisetothe" + skipopts + n <- tok + return (<> superscript n)) + siSuffix :: LP m (Inlines -> Inlines) + siSuffix = + (do _ <- controlSeq "squared" + skipopts + return (<> superscript "2")) + <|> (do _ <- controlSeq "cubed" + skipopts + return (<> superscript "3")) + <|> (do _ <- controlSeq "tothe" + skipopts + n <- tok + return (<> superscript n)) + siBase :: LP m Inlines + siBase = mconcat <$> many1 + ((try + (do Tok _ (CtrlSeq name) _ <- anyControlSeq + case M.lookup name siUnitMap of + Just il -> pure il + Nothing -> fail "not a unit command")) + <|> (do Tok _ Word t <- satisfyTok isWordTok + return $ str t) + <|> (symbol '^' *> (superscript <$> tok)) + <|> (symbol '_' *> (subscript <$> tok)) + <|> (str "\xa0" <$ symbol '.') + <|> (str "\xa0" <$ symbol '~') + ) siUnitMap :: M.Map Text Inlines siUnitMap = M.fromList @@ -347,7 +363,6 @@ siUnitMap = M.fromList , ("Pa", str "Pa") , ("pascal", str "Pa") , ("percent", str "%") - , ("per", str "/") , ("peta", str "P") , ("pico", str "p") , ("planckbar", emph (str "\x210f")) diff --git a/test/command/6658.md b/test/command/6658.md index 0a8512f85..549610992 100644 --- a/test/command/6658.md +++ b/test/command/6658.md @@ -5,8 +5,11 @@ pandoc -f latex \num{.3e45} \ang{+10;+3;} + +\si{\gram\per\cubic\centi\metre} ^D <p>10.0 ± 3.3 ms</p> <p>0.3 × 10<sup>45</sup></p> <p>10°3′</p> +<p>g/cm<sup>3</sup></p> ``` -- cgit v1.2.3 From bb11f5fb86993559f9999d4795355b02ae78cc3d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 12:06:15 -0700 Subject: LaTeX reader: More siunitx improvements. Closes #6658. There's still one slight divergence from the siunitx behavior: we get 'kg m/A/s' instead of 'kg m/(A s)'. At the moment I'm not going to worry about that. --- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 3 +- src/Text/Pandoc/Readers/LaTeX/SIunitx.hs | 138 +++++++++++++++++++++---------- test/command/6658.md | 72 ++++++++++++++-- 3 files changed, 161 insertions(+), 52 deletions(-) diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index b6804a825..1c77eb299 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -806,7 +806,8 @@ withRaw parser = do keyval :: PandocMonad m => LP m (Text, Text) keyval = try $ do - Tok _ Word key <- satisfyTok isWordTok + key <- untokenize <$> many1 (notFollowedBy (symbol '=') >> + (symbol '-' <|> symbol '_' <|> satisfyTok isWordTok)) sp val <- option mempty $ do symbol '=' diff --git a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs index 63ab7267d..b8bf0ce7f 100644 --- a/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs +++ b/src/Text/Pandoc/Readers/LaTeX/SIunitx.hs @@ -10,27 +10,32 @@ import Text.Pandoc.Class import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) import Control.Applicative ((<|>)) +import Control.Monad (void) import qualified Data.Map as M import Data.Char (isDigit) import Data.Text (Text) import qualified Data.Text as T import Data.List (intersperse) - +import qualified Data.Sequence as Seq +import Text.Pandoc.Walk (walk) siunitxCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) siunitxCommands tok = M.fromList - [ ("si", skipopts *> dosi tok) + [ ("si", dosi tok) , ("SI", doSI tok) , ("SIrange", doSIrange True tok) , ("numrange", doSIrange False tok) , ("numlist", doSInumlist) + , ("SIlist", doSIlist tok) , ("num", doSInum) , ("ang", doSIang) ] dosi :: PandocMonad m => LP m Inlines -> LP m Inlines -dosi tok = grouped (siUnit tok) <|> siUnit tok +dosi tok = do + options <- option [] keyvals + grouped (siUnit options tok) <|> siUnit options tok -- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" doSI :: PandocMonad m => LP m Inlines -> LP m Inlines @@ -65,9 +70,29 @@ doSInumlist = do mconcat (intersperse (str "," <> space) (init xs)) <> text ", & " <> last xs +doSIlist :: PandocMonad m => LP m Inlines -> LP m Inlines +doSIlist tok = do + options <- option [] keyvals + nums <- map tonum . T.splitOn ";" . untokenize <$> braced + unit <- grouped (siUnit options tok) <|> siUnit options tok + let xs = map (<> (str "\xa0" <> unit)) nums + case xs of + [] -> return mempty + [x] -> return x + _ -> return $ + mconcat (intersperse (str "," <> space) (init xs)) <> + text ", & " <> last xs + parseNum :: Parser Text () Inlines parseNum = (mconcat <$> many parseNumPart) <* eof +minus :: Text +minus = "\x2212" + +hyphenToMinus :: Inline -> Inline +hyphenToMinus (Str t) = Str (T.replace "-" minus t) +hyphenToMinus x = x + parseNumPart :: Parser Text () Inlines parseNumPart = parseDecimalNum <|> @@ -83,7 +108,7 @@ parseNumPart = parseComma, parseI, parseX, parseExp, parseSpace :: Parser Text () Inlines parseDecimalNum = try $ do - pref <- option mempty $ (mempty <$ char '+') <|> ("\x2212" <$ char '-') + pref <- option mempty $ (mempty <$ char '+') <|> (minus <$ char '-') basenum' <- many1 (satisfy (\c -> isDigit c || c == '.')) let basenum = pref <> T.pack (case basenum' of @@ -155,20 +180,30 @@ doSIrange includeUnits tok = do emptyOr160 :: Inlines -> Inlines emptyOr160 x = if x == mempty then x else str "\160" -siUnit :: forall m. PandocMonad m => LP m Inlines -> LP m Inlines -siUnit tok = mconcat <$> many1 siUnitPart +siUnit :: forall m. PandocMonad m => [(Text,Text)] -> LP m Inlines -> LP m Inlines +siUnit options tok = mconcat . intersperse (str "\xa0") <$> many1 siUnitPart where siUnitPart :: LP m Inlines - siUnitPart = - (siPrefix <*> siUnitPart) - <|> (do u <- siBase <|> tok - option u $ siSuffix <*> pure u) + siUnitPart = try $ do + skipMany (void (symbol '.') <|> void (symbol '~') <|> spaces1) + x <- ((siPrefix <*> siBase) + <|> (do u <- siBase <|> tok + option u $ siSuffix <*> pure u)) + option x (siInfix x) + siInfix :: Inlines -> LP m Inlines + siInfix u1 = try $ + (do _ <- controlSeq "per" + u2 <- siUnitPart + let useSlash = lookup "per-mode" options == Just "symbol" + if useSlash + then return (u1 <> str "/" <> u2) + else return (u1 <> str "\xa0" <> negateExponent u2)) + <|> (do _ <- symbol '/' + u2 <- siUnitPart + return (u1 <> str "/" <> u2)) siPrefix :: LP m (Inlines -> Inlines) siPrefix = - (do _ <- controlSeq "per" - skipopts -- TODO handle option - return (str "/" <>)) - <|> (do _ <- controlSeq "square" + (do _ <- controlSeq "square" skipopts return (<> superscript "2")) <|> (do _ <- controlSeq "cubic" @@ -176,7 +211,7 @@ siUnit tok = mconcat <$> many1 siUnitPart return (<> superscript "3")) <|> (do _ <- controlSeq "raisetothe" skipopts - n <- tok + n <- walk hyphenToMinus <$> tok return (<> superscript n)) siSuffix :: LP m (Inlines -> Inlines) siSuffix = @@ -188,23 +223,57 @@ siUnit tok = mconcat <$> many1 siUnitPart return (<> superscript "3")) <|> (do _ <- controlSeq "tothe" skipopts - n <- tok + n <- walk hyphenToMinus <$> tok return (<> superscript n)) + <|> (symbol '^' *> (do n <- walk hyphenToMinus <$> tok + return (<> superscript n))) + <|> (symbol '_' *> (do n <- walk hyphenToMinus <$> tok + return (<> subscript n))) + negateExponent :: Inlines -> Inlines + negateExponent ils = + case Seq.viewr (unMany ils) of + xs Seq.:> Superscript ss -> (Many xs) <> + superscript (str minus <> fromList ss) + _ -> ils <> superscript (str (minus <> "1")) siBase :: LP m Inlines - siBase = mconcat <$> many1 + siBase = ((try (do Tok _ (CtrlSeq name) _ <- anyControlSeq - case M.lookup name siUnitMap of - Just il -> pure il - Nothing -> fail "not a unit command")) + case M.lookup name siUnitModifierMap of + Just il -> (il <>) <$> siBase + Nothing -> + case M.lookup name siUnitMap of + Just il -> pure il + Nothing -> fail "not a unit command")) <|> (do Tok _ Word t <- satisfyTok isWordTok return $ str t) - <|> (symbol '^' *> (superscript <$> tok)) - <|> (symbol '_' *> (subscript <$> tok)) - <|> (str "\xa0" <$ symbol '.') - <|> (str "\xa0" <$ symbol '~') ) +siUnitModifierMap :: M.Map Text Inlines +siUnitModifierMap = M.fromList + [ ("atto", str "a") + , ("centi", str "c") + , ("deca", str "d") + , ("deci", str "d") + , ("deka", str "d") + , ("exa", str "E") + , ("femto", str "f") + , ("giga", str "G") + , ("hecto", str "h") + , ("kilo", str "k") + , ("mega", str "M") + , ("micro", str "μ") + , ("milli", str "m") + , ("nano", str "n") + , ("peta", str "P") + , ("pico", str "p") + , ("tera", str "T") + , ("yocto", str "y") + , ("yotta", str "Y") + , ("zepto", str "z") + , ("zetta", str "Z") + ] + siUnitMap :: M.Map Text Inlines siUnitMap = M.fromList [ ("fg", str "fg") @@ -303,7 +372,6 @@ siUnitMap = M.fromList , ("arcsecond", str "″") , ("astronomicalunit", str "ua") , ("atomicmassunit", str "u") - , ("atto", str "a") , ("bar", str "bar") , ("barn", str "b") , ("becquerel", str "Bq") @@ -311,51 +379,38 @@ siUnitMap = M.fromList , ("bohr", emph (str "a") <> subscript (str "0")) , ("candela", str "cd") , ("celsius", str "°C") - , ("centi", str "c") , ("clight", emph (str "c") <> subscript (str "0")) , ("coulomb", str "C") , ("dalton", str "Da") , ("day", str "d") - , ("deca", str "d") - , ("deci", str "d") , ("decibel", str "db") , ("degreeCelsius",str "°C") , ("degree", str "°") - , ("deka", str "d") , ("electronmass", emph (str "m") <> subscript (str "e")) , ("electronvolt", str "eV") , ("elementarycharge", emph (str "e")) - , ("exa", str "E") , ("farad", str "F") - , ("femto", str "f") - , ("giga", str "G") , ("gram", str "g") , ("gray", str "Gy") , ("hartree", emph (str "E") <> subscript (str "h")) , ("hectare", str "ha") - , ("hecto", str "h") , ("henry", str "H") , ("hertz", str "Hz") , ("hour", str "h") , ("joule", str "J") , ("katal", str "kat") , ("kelvin", str "K") - , ("kilo", str "k") , ("kilogram", str "kg") , ("knot", str "kn") , ("liter", str "L") , ("litre", str "l") , ("lumen", str "lm") , ("lux", str "lx") - , ("mega", str "M") , ("meter", str "m") , ("metre", str "m") - , ("micro", str "μ") - , ("milli", str "m") , ("minute", str "min") , ("mmHg", str "mmHg") , ("mole", str "mol") - , ("nano", str "n") , ("nauticalmile", str "M") , ("neper", str "Np") , ("newton", str "N") @@ -363,24 +418,17 @@ siUnitMap = M.fromList , ("Pa", str "Pa") , ("pascal", str "Pa") , ("percent", str "%") - , ("peta", str "P") - , ("pico", str "p") , ("planckbar", emph (str "\x210f")) , ("radian", str "rad") , ("second", str "s") , ("siemens", str "S") , ("sievert", str "Sv") , ("steradian", str "sr") - , ("tera", str "T") , ("tesla", str "T") , ("tonne", str "t") , ("volt", str "V") , ("watt", str "W") , ("weber", str "Wb") - , ("yocto", str "y") - , ("yotta", str "Y") - , ("zepto", str "z") - , ("zetta", str "Z") ] diff --git a/test/command/6658.md b/test/command/6658.md index 549610992..96700c8fe 100644 --- a/test/command/6658.md +++ b/test/command/6658.md @@ -1,15 +1,75 @@ ``` -pandoc -f latex -\SI{10.0 +- 3.3}{\ms} +pandoc -f latex -t html +\num{12345,67890} + +\num{1+-2i} \num{.3e45} -\ang{+10;+3;} +\num{1.654 x 2.34 x 3.430} + +\si{kg.m.s^{-1}} + +\si{\kilogram\metre\per\second} + +\si[per-mode=symbol]{\kilogram\metre\per\second} + +\si[per-mode=symbol]{\kilogram\metre\per\ampere\per\second} + +\numlist{10;20;30} + +\SIlist{0.13;0.67;0.80}{\milli\metre} + +\numrange{10}{20} + +\SIrange{0.13}{0.67}{\milli\metre} + +\ang{10} + +\ang{1;2;3} + +\ang{;;1} + +\ang{+10;;} + +\ang{-0;1;} + +\si{kg.m/s^2} + +\si{g_{polymer}~mol_{cat}.s^{-1}} + +\si{\kilo\gram\metre\per\square\second} \si{\gram\per\cubic\centi\metre} + +\si{\square\volt\cubic\lumen\per\farad} + +\si{\metre\squared\per\gray\cubic\lux} + +\si{\henry\second} ^D -<p>10.0 ± 3.3 ms</p> +<p>12345.67890</p> +<p>1 ± 2i</p> <p>0.3 × 10<sup>45</sup></p> -<p>10°3′</p> -<p>g/cm<sup>3</sup></p> +<p>1.654 × 2.34 × 3.430</p> +<p>kg m s<sup>−1</sup></p> +<p>kg m s<sup>−1</sup></p> +<p>kg m/s</p> +<p>kg m/A/s</p> +<p>10, 20, & 30</p> +<p>0.13 mm, 0.67 mm, & 0.80 mm</p> +<p>10–20</p> +<p>0.13 mm–0.67 mm</p> +<p>10°</p> +<p>1°2′3″</p> +<p>1″</p> +<p>10°</p> +<p>-0°1′</p> +<p>kg m/s<sup>2</sup></p> +<p>g<sub>polymer</sub> mol<sub>cat</sub> s<sup>−1</sup></p> +<p>kg m s<sup>−2</sup></p> +<p>g cm<sup>−3</sup></p> +<p>V<sup>2</sup> lm<sup>3</sup> F<sup>−1</sup></p> +<p>m<sup>2</sup> Gy<sup>−1</sup> lx<sup>3</sup></p> +<p>H s</p> ``` -- cgit v1.2.3 From d7b5def287aefe91f881daeecc5f72121c843b66 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 17:12:00 -0700 Subject: Ms writer: handle tables with multiple paragraphs. Previously they overflowed the table cell width. We now set line lengths per-cell and restore them after the table has been written. Closes #7288. --- src/Text/Pandoc/Writers/Ms.hs | 28 ++++++++++++++++++++++------ test/command/7288.md | 40 ++++++++++++++++++++++++++++++++++++++++ test/tables.ms | 30 ++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 test/command/7288.md diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0ed7a8a64..97c23f24d 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -245,13 +245,17 @@ blockToMs opts (Table _ blkCapt specs thead tbody tfoot) = aligncode AlignDefault = "l" in do caption' <- inlineListToMs' opts caption - let iwidths = if all (== 0) widths - then repeat "" - else map (T.pack . printf "w(%0.1fn)" . (70 *)) widths + let isSimple = all (== 0) widths + let totalWidth = 70 -- 78n default width - 8n indent = 70n let coldescriptions = literal $ T.unwords - (zipWith (\align width -> aligncode align <> width) - alignments iwidths) <> "." + (zipWith (\align width -> aligncode align <> + if width == 0 + then "" + else T.pack $ + printf "w(%0.1fn)" + (totalWidth * width)) + alignments widths) <> "." colheadings <- mapM (blockListToMs opts) headers let makeRow cols = literal "T{" $$ vcat (intersperse (literal "T}\tT{") cols) $$ @@ -260,13 +264,25 @@ blockToMs opts (Table _ blkCapt specs thead tbody tfoot) = then empty else makeRow colheadings $$ char '_' body <- mapM (\row -> do - cols <- mapM (blockListToMs opts) row + cols <- mapM (\(cell, w) -> + (if isSimple + then id + else (literal (".nr LL " <> + T.pack (printf "%0.1fn" + (w * totalWidth))) $$)) <$> + blockListToMs opts cell) (zip row widths) return $ makeRow cols) rows setFirstPara return $ literal ".PP" $$ caption' $$ literal ".na" $$ -- we don't want justification in table cells + (if isSimple + then "" + else ".nr LLold \\n[LL]") $$ literal ".TS" $$ literal "delim(@@) tab(\t);" $$ coldescriptions $$ colheadings' $$ vcat body $$ literal ".TE" $$ + (if isSimple + then "" + else ".nr LL \\n[LLold]") $$ literal ".ad" blockToMs opts (BulletList items) = do diff --git a/test/command/7288.md b/test/command/7288.md new file mode 100644 index 000000000..e94aeeeb3 --- /dev/null +++ b/test/command/7288.md @@ -0,0 +1,40 @@ +``` +% pandoc -f rst -t ms +.. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Left + - Right + * - Long text that should be easy to break up into multiple lines + - Another long text that should be easy to break up into multiple lines + + Bar +^D +.PP +.na +.nr LLold \n[LL] +.TS +delim(@@) tab( ); +lw(35.0n) lw(35.0n). +T{ +Left +T} T{ +Right +T} +_ +T{ +.nr LL 35.0n +.LP +Long text that should be easy to break up into multiple lines +T} T{ +.nr LL 35.0n +.PP +Another long text that should be easy to break up into multiple lines +.PP +Bar +T} +.TE +.nr LL \n[LLold] +.ad +``` diff --git a/test/tables.ms b/test/tables.ms index 7337db25f..21183a5eb 100644 --- a/test/tables.ms +++ b/test/tables.ms @@ -143,6 +143,7 @@ Multiline table with caption: .PP Here\[cq]s the caption. It may span multiple lines. .na +.nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). @@ -157,30 +158,40 @@ Default aligned T} _ T{ +.nr LL 10.5n First T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 12.0 T} T{ +.nr LL 24.5n Example of a row that spans multiple lines. T} T{ +.nr LL 10.5n Second T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 5.0 T} T{ +.nr LL 24.5n Here\[cq]s another one. Note the blank line between rows. T} .TE +.nr LL \n[LLold] .ad .LP Multiline table without caption: .PP .na +.nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). @@ -195,25 +206,34 @@ Default aligned T} _ T{ +.nr LL 10.5n First T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 12.0 T} T{ +.nr LL 24.5n Example of a row that spans multiple lines. T} T{ +.nr LL 10.5n Second T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 5.0 T} T{ +.nr LL 24.5n Here\[cq]s another one. Note the blank line between rows. T} .TE +.nr LL \n[LLold] .ad .LP Table without column headers: @@ -255,27 +275,37 @@ T} Multiline table without column headers: .PP .na +.nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ +.nr LL 10.5n First T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 12.0 T} T{ +.nr LL 24.5n Example of a row that spans multiple lines. T} T{ +.nr LL 10.5n Second T} T{ +.nr LL 9.6n row T} T{ +.nr LL 11.4n 5.0 T} T{ +.nr LL 24.5n Here\[cq]s another one. Note the blank line between rows. T} .TE +.nr LL \n[LLold] .ad -- cgit v1.2.3 From 3a1988b6b2c08fa8a23ae5b6ce355b9b4e0f3dbe Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 17:17:15 -0700 Subject: reveal.js template: use `hash: true` by default rather than... `history: true`. Closes #6968. Setting `hash: true` is enough to get linkability to a particular page of the slide show. --- data/templates/default.revealjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs index 838d5465f..8a77674dd 100644 --- a/data/templates/default.revealjs +++ b/data/templates/default.revealjs @@ -124,17 +124,17 @@ $if(showSlideNumber)$ // 'all', 'print', or 'speaker' showSlideNumber: '$showSlideNumber$', $endif$ -$if(hash)$ // Add the current slide number to the URL hash so that reloading the // page/copying the URL will return you to the same slide - hash: $hash$, -$endif$ + hash: $if(hash)$$hash$$else$true$endif$, $if(hashOneBasedIndex)$ // Start with 1 for the hash rather than 0 hashOneBasedIndex: $hashOneBasedIndex$, $endif$ +$if(history)$ // Push each slide change to the browser history - history: $if(history)$$history$$else$true$endif$, + history: $history$, +$endif$ $if(keyboard)$ // Enable keyboard shortcuts for navigation keyboard: $keyboard$, -- cgit v1.2.3 From 07d299d353761a7c29aa7e7a51371ad7842ec767 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 20 May 2021 18:45:39 -0700 Subject: DocBook reader: ensure that first and last names are separated. Closes #6541. --- src/Text/Pandoc/Readers/DocBook.hs | 20 ++++++++++++++------ test/command/6541.md | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 test/command/6541.md diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 3db459cfd..b01ad3252 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -600,16 +600,24 @@ addMetadataFromElement e = do Nothing -> return () Just z -> addMetaField "author" z addMetaField "subtitle" e - addMetaField "author" e + addAuthor e addMetaField "date" e addMetaField "release" e addMetaField "releaseinfo" e return mempty - where addMetaField fieldname elt = - case filterChildren (named fieldname) elt of - [] -> return () - [z] -> getInlines z >>= addMeta fieldname - zs -> mapM getInlines zs >>= addMeta fieldname + where + addAuthor elt = + case filterChildren (named "author") elt of + [] -> return () + [z] -> fromAuthor z >>= addMeta "author" + zs -> mapM fromAuthor zs >>= addMeta "author" + fromAuthor elt = + mconcat . intersperse space <$> mapM getInlines (elChildren elt) + addMetaField fieldname elt = + case filterChildren (named fieldname) elt of + [] -> return () + [z] -> getInlines z >>= addMeta fieldname + zs -> mapM getInlines zs >>= addMeta fieldname addMeta :: PandocMonad m => ToMetaValue a => Text -> a -> DB m () addMeta field val = modify (setMeta field val) diff --git a/test/command/6541.md b/test/command/6541.md new file mode 100644 index 000000000..956340d4c --- /dev/null +++ b/test/command/6541.md @@ -0,0 +1,27 @@ +``` +% pandoc -f docbook -t markdown -s +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<book> + +<bookinfo> +<title>Title + +FirstnameLastname + +1.17 + + +Text. + + +^D +--- +author: Firstname Lastname +releaseinfo: 1.17 +title: Title +--- + +Text. +``` -- cgit v1.2.3 From bc7b909898e53271d454b07280692231b5297a12 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 20 May 2021 21:47:30 -0700 Subject: Update changelog. --- changelog.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 73d271a2c..d24c24381 100644 --- a/changelog.md +++ b/changelog.md @@ -72,6 +72,17 @@ * Docx reader: Add handling of vml image objects (#7257, mbrackeantidot). + * LaTeX reader: + + + Improved siunitx support (#6658, #6620). + + Better support for `\xspace` (#7299). + + * ConTeXt writer: improve ordered lists (#5016, Denis Maier). + Change ordered list from itemize to enumerate. Add new + itemgroup for ordered lists. Remove manual insertion of + width attributes. Use tabular figures in ordered list + enumerators. + * HTML reader: + Don't fail on unmatched closing "script" tag (Albert Krenkel, #7282). @@ -87,6 +98,8 @@ + Fix mathml regression caused by the switch in XML libraries (#7173). + Fix "phrase" in DocBook: take classes from "role" not "class" (#7195). + * DocBook reader: ensure that first and last names are separated (#6541). + * Plain writer: handle superscript unicode minus (#7276). * LaTeX writer: @@ -102,6 +115,8 @@ a thin space to improve readability and to prevent unwanted ligatures. Detection of these quotes sometimes had failed if the second quote was nested in a span element. + + Separate successive quote chars with thin space (#6958, Albert + Krewinkel). * EPUB Writer: Fix belongs-to-collection XML id choice (#7267, nuew). The epub writer previously used the same XML id for both the book @@ -109,6 +124,9 @@ * BibTeX/BibLaTeX writer: Handle `annote` field (#7266). + * ZimWiki writer: allow links and emphasis in headers (#6605, + Albert Krewinkel). + * ConTeXt writer: + Support blank lines in line blocks (#6564, Albert Krewinkel, @@ -186,6 +204,9 @@ + Remove `rsid`s from default settings.xml. Word will add these when revisions are made. + * Ms writer: Handle tables with multiple paragraphs (#7288). + Previously they overflowed the table cell width. We now set line lengths + per-cell and restore them after the table has been written. * Markdown writer: @@ -236,7 +257,10 @@ * ConTeXt template: List of figures before list of tables (#7235, Julien Dutant). - * reveal.js template: Support `toc-title` (#7171, Florian Kohrt). + * reveal.js template: + + + Support `toc-title` (#7171, Florian Kohrt). + + Use `hash: true` by default rather than `history: true` (#6968). * HTML-based slide shows: add support for `institute` (#7289, Thomas Hodgson). @@ -244,6 +268,13 @@ * Text.Pandoc.XML.Light: add Eq, Ord instances for Content, Element, Attr, CDataKind [API change]. + * Text.Pandoc.MediaBag: change type to use a Text key instead of + `[FilePath]`. We normalize the path and use `/` separators for + consistency. + + * Text.Pandoc.Class.PandocMonad: Use `fetchItem` instead of `downloadOrRead` + in `fetchMediaResource`. + * Text.Pandoc.Asciify: simplify code and export `toAsciiText` [API change]. Instead of encoding a giant (and incomplete) map, we now just use unicode-transforms to normalize the text to @@ -307,6 +338,7 @@ + Add info about YAML escape sequences, link to spec (#7152, Albert Krewinkel). + Note that `institute` variable works for HTML-based slides. + + Update documentation on citation syntax. * Updated and fixed typos in documentation (Charanjit Singh, Anti-Distinctlyminty, Tatiana Porras, obcat). -- cgit v1.2.3 From 8c9f0a62c10ad042aecd039ee134d6d2dbb7b599 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 21 May 2021 07:51:27 -0700 Subject: Fix link to KDE syntax highlighting docs. Closes jgm/pandoc-website#51. --- MANUAL.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index bd2f01c32..23d0895ed 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -6291,7 +6291,7 @@ Then edit `my.theme` and use it like this: If you are not satisfied with the built-in highlighting, or you want highlight a language that isn't supported, you can use the `--syntax-definition` option to load a [KDE-style XML syntax definition -file](https://docs.kde.org/stable5/en/applications/katepart/highlight.html). +file](https://docs.kde.org/stable5/en/kate/katepart/highlight.html). Before writing your own, have a look at KDE's [repository of syntax definitions](https://github.com/KDE/syntax-highlighting/tree/master/data/syntax). -- cgit v1.2.3 From 971a31b4d88c1853b3b6b8de12d48332b25c5a11 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 21 May 2021 10:51:35 -0700 Subject: Bump upper-bounds for network-uri, time. Change stack.yaml to use lts-17.12. --- pandoc.cabal | 6 +++--- stack.yaml | 8 +------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 66873a301..ec50d26f5 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -473,7 +473,7 @@ library jira-wiki-markup >= 1.3.4 && < 1.4, mtl >= 2.2 && < 2.3, network >= 2.6, - network-uri >= 2.6 && < 2.7, + network-uri >= 2.6 && < 2.8, pandoc-types >= 1.22 && < 1.23, parsec >= 3.1 && < 3.2, process >= 1.2.3 && < 1.7, @@ -489,7 +489,7 @@ library texmath >= 0.12.3 && < 0.12.4, text >= 1.1.1.0 && < 1.3, text-conversions >= 0.3 && < 0.4, - time >= 1.5 && < 1.10, + time >= 1.5 && < 1.12, unicode-transforms >= 0.3 && < 0.4, unordered-containers >= 0.2 && < 0.3, xml >= 1.3.12 && < 1.4, @@ -764,7 +764,7 @@ test-suite test-pandoc tasty-lua >= 0.2 && < 0.3, tasty-quickcheck >= 0.8 && < 0.11, text >= 1.1.1.0 && < 1.3, - time >= 1.5 && < 1.10, + time >= 1.5 && < 1.12, xml >= 1.3.12 && < 1.4, zip-archive >= 0.2.3.4 && < 0.5 other-modules: Tests.Old diff --git a/stack.yaml b/stack.yaml index 625543bf2..c67ff9986 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,17 +9,11 @@ packages: extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 -- jira-wiki-markup-1.3.4 -- skylighting-core-0.10.5.1 -- skylighting-0.10.5.1 -- doclayout-0.3.0.2 - random-1.2.0 -- xml-conduit-1.9.1.1 - unicode-collation-0.1.3 -- texmath-0.12.3 - citeproc-0.4 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules -resolver: lts-17.5 +resolver: lts-17.12 nix: packages: [zlib] -- cgit v1.2.3 From 4b229e59556e633e5ed5034c66a68490ca354ad2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 21 May 2021 11:41:37 -0700 Subject: Regenerate man page. --- MANUAL.txt | 2 +- man/pandoc.1 | 117 ++++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 23d0895ed..a0a5d43fe 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: May 13, 2021 +date: May 21, 2021 --- # Synopsis diff --git a/man/pandoc.1 b/man/pandoc.1 index 795271b36..ac5479414 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t -.\" Automatically generated by Pandoc 2.13 +.\" Automatically generated by Pandoc 2.14 .\" -.TH "Pandoc User\[cq]s Guide" "" "May 13, 2021" "pandoc 2.14" "" +.TH "Pandoc User\[cq]s Guide" "" "May 21, 2021" "pandoc 2.14" "" .hy .SH NAME pandoc - general markup converter @@ -2591,10 +2591,9 @@ YAML metadata or with \f[C]-M classoption=fleqn\f[R]. .SS Variables for HTML slides .PP These affect HTML output when [producing slide shows with pandoc]. -.PP -All reveal.js configuration options are available as variables. -To turn off boolean flags that default to true in reveal.js, use -\f[C]0\f[R]. +.TP +\f[B]\f[CB]institute\f[B]\f[R] +author affiliations: can be a list when there are multiple authors .TP \f[B]\f[CB]revealjs-url\f[B]\f[R] base URL for reveal.js documents (defaults to @@ -2613,6 +2612,10 @@ base URL for Slideous documents (defaults to \f[C]slideous\f[R]) \f[B]\f[CB]title-slide-attributes\f[B]\f[R] additional attributes for the title slide of reveal.js slide shows. See background in reveal.js and beamer for an example. +.PP +All reveal.js configuration options are available as variables. +To turn off boolean flags that default to true in reveal.js, use +\f[C]0\f[R]. .SS Variables for Beamer slides .PP These variables change the appearance of PDF slides using @@ -4793,7 +4796,9 @@ All of the metadata will appear in a single block at the beginning of the document. .PP Note that YAML escaping rules must be followed. -Thus, for example, if a title contains a colon, it must be quoted. +Thus, for example, if a title contains a colon, it must be quoted, and +if it contains a backslash escape, then it must be ensured that it is +not treated as a YAML escape sequence. The pipe character (\f[C]|\f[R]) can be used to begin an indented block that will be interpreted literally, without need for escaping. This form is necessary when the field contains blank lines or @@ -5775,27 +5780,70 @@ Inline and regular footnotes may be mixed freely. .SS Citation syntax .SS Extension: \f[C]citations\f[R] .PP -Markdown citations go inside square brackets and are separated by -semicolons. -Each citation must have a key, composed of `\[at]' + the citation -identifier from the database, and may optionally have a prefix, a -locator, and a suffix. -The citation key must begin with a letter, digit, or \f[C]_\f[R], and -may contain alphanumerics, \f[C]_\f[R], and internal punctuation -characters (\f[C]:.#$%&-+?<>\[ti]/\f[R]). -Here are some examples: +To cite a bibliographic item with an identifier foo, use the syntax +\f[C]\[at]foo\f[R]. +Normal citations should be included in square brackets, with semicolons +separating distinct items: .IP .nf \f[C] -Blah blah [see \[at]doe99, pp. 33-35; also \[at]smith04, chap. 1]. - -Blah blah [\[at]doe99, pp. 33-35, 38-39 and *passim*]. +Blah blah [\[at]doe99; \[at]smith2000; \[at]smith2004]. +\f[R] +.fi +.PP +How this is rendered depends on the citation style. +In an author-date style, it might render as +.IP +.nf +\f[C] +Blah blah (Doe 1999, Smith 2000, 2004). +\f[R] +.fi +.PP +In a footnote style, it might render as +.IP +.nf +\f[C] +Blah blah.[\[ha]1] -Blah blah [\[at]smith04; \[at]doe99]. +[\[ha]1]: John Doe, \[dq]Frogs,\[dq] *Journal of Amphibians* 44 (1999); +Susan Smith, \[dq]Flies,\[dq] *Journal of Insects* (2000); +Susan Smith, \[dq]Bees,\[dq] *Journal of Insects* (2004). +\f[R] +.fi +.PP +See the CSL user documentation for more information about CSL styles and +how they affect rendering. +.PP +Unless a citation key start with a letter, digit, or \f[C]_\f[R], and +contains only alphanumerics and internal punctuation characters +(\f[C]:.#$%&-+?<>\[ti]/\f[R]), it must be surrounded by curly braces, +which are not considered part of the key. +In \f[C]\[at]Foo_bar.baz.\f[R], the key is \f[C]Foo_bar.baz\f[R]. +The final period is not \f[I]internal\f[R] punctuation, so it is not +included in the key. +In \f[C]\[at]{Foo_bar.baz.}\f[R], the key is \f[C]Foo_bar.baz.\f[R], +including the final period. +The curly braces are recommended if you use URLs as keys: +\f[C][\[at]{https://example.com/bib?name=foobar&date=2000}, p. 33]\f[R]. +.PP +Citation items may optionally include a prefix, a locator, and a suffix. +In +.IP +.nf +\f[C] +Blah blah [see \[at]doe99, pp. 33-35 and *passim*; \[at]smith04, chap. 1]. \f[R] .fi .PP -\f[C]pandoc\f[R] detects locator terms in the CSL locale files. +The first item (\f[C]doe99\f[R]) has prefix \f[C]see\f[R], locator +\f[C]pp. 33-35\f[R], and suffix \f[C]and *passim*\f[R]. +The second item (\f[C]smith04\f[R]) has locator \f[C]chap. 1\f[R] and no +prefix or suffix. +.PP +Pandoc uses some heuristics to separate the locator from the rest of the +subject. +It is sensitive to the locator terms defined in the CSL locale files. Either abbreviated or unabbreviated forms are accepted. In the \f[C]en-US\f[R] locale, locator terms can be written in either singular or plural forms, as \f[C]book\f[R], @@ -5817,9 +5865,8 @@ singular or plural forms, as \f[C]book\f[R], \f[C]\[sc]\f[R]/\f[C]\[sc]\[sc]\f[R]. If no locator term is used, \[lq]page\[rq] is assumed. .PP -\f[C]pandoc\f[R] will use heuristics to distinguish the locator from the -suffix. -In complex cases, the locator can be enclosed in curly braces: +In complex cases, you can force something to be treated as a locator by +enclosing it in curly braces: .IP .nf \f[C] @@ -5838,7 +5885,8 @@ Smith says blah [-\[at]smith04]. \f[R] .fi .PP -You can also write an in-text citation, as follows: +You can also write an author-in-text citation, by omitting the square +brackets: .IP .nf \f[C] @@ -5847,6 +5895,20 @@ You can also write an in-text citation, as follows: \[at]smith04 [p. 33] says blah. \f[R] .fi +.PP +This will cause the author\[cq]s name to be rendered, followed by the +bibliographical details. +Use this form when you want to make the citation the subject of a +sentence. +.PP +When you are using a note style, it is usually better to let citeproc +create the footnotes from citations rather than writing an explicit +note. +If you do write an explicit note that contains a citation, note that +normal citations will be put in parentheses, while author-in-text +citations will not. +For this reason, it is sometimes preferable to use the author-in-text +style inside notes when using a note style. .SS Non-pandoc extensions .PP The following Markdown syntax extensions are not enabled by default in @@ -6626,6 +6688,11 @@ A heading at the slide level always starts a new slide. .IP \[bu] 2 Headings \f[I]below\f[R] the slide level in the hierarchy create headings \f[I]within\f[R] a slide. +(In beamer, a \[lq]block\[rq] will be created. +If the heading has the class \f[C]example\f[R], an +\f[C]exampleblock\f[R] environment will be used; if it has the class +\f[C]alert\f[R], an \f[C]alertblock\f[R] will be used; otherwise a +regular \f[C]block\f[R] will be used.) .IP \[bu] 2 Headings \f[I]above\f[R] the slide level in the hierarchy create \[lq]title slides,\[rq] which just contain the section title and help to -- cgit v1.2.3 From f76fe2ab56606528d4710cc6c40bceb5788c3906 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 22 May 2021 13:29:13 +0200 Subject: HTML reader: simplify col width parsing --- src/Text/Pandoc/Readers/HTML/Table.hs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index ad0b51253..6537bbce9 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -1,6 +1,5 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.HTML.Table Copyright : © 2006-2021 John MacFarlane, @@ -42,18 +41,15 @@ pCol = try $ do skipMany pBlank optional $ pSatisfy (matchTagClose "col") skipMany pBlank - let width = case lookup "width" attribs of - Nothing -> case lookup "style" attribs of - Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs -> - fromMaybe 0.0 $ safeRead (T.filter - (`notElem` (" \t\r\n%'\";" :: [Char])) xs) - _ -> 0.0 - Just (T.unsnoc -> Just (xs, '%')) -> - fromMaybe 0.0 $ safeRead xs - _ -> 0.0 - if width > 0.0 - then return $ ColWidth $ width / 100.0 - else return ColWidthDefault + let toColWidth = maybe ColWidthDefault (ColWidth . (/100.0)) . safeRead + return $ fromMaybe ColWidthDefault $ + (case lookup "width" attribs >>= T.unsnoc of + Just (xs, '%') -> Just (toColWidth xs) + _ -> Nothing) <|> + (case lookup "style" attribs >>= T.stripPrefix "width" of + Just xs | T.any (== '%') xs -> Just . toColWidth $ + T.filter (`notElem` (" \t\r\n%'\";" :: [Char])) xs + _ -> Nothing) pColgroup :: PandocMonad m => TagParser m [ColWidth] pColgroup = try $ do -- cgit v1.2.3 From 80b4b3fe82a19a4ea1e76fc4a81c9c88676c7ce0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 22 May 2021 22:03:51 -0700 Subject: Revert "HTML reader: simplify col width parsing" This reverts commit f76fe2ab56606528d4710cc6c40bceb5788c3906. --- src/Text/Pandoc/Readers/HTML/Table.hs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index 6537bbce9..ad0b51253 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -1,5 +1,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.HTML.Table Copyright : © 2006-2021 John MacFarlane, @@ -41,15 +42,18 @@ pCol = try $ do skipMany pBlank optional $ pSatisfy (matchTagClose "col") skipMany pBlank - let toColWidth = maybe ColWidthDefault (ColWidth . (/100.0)) . safeRead - return $ fromMaybe ColWidthDefault $ - (case lookup "width" attribs >>= T.unsnoc of - Just (xs, '%') -> Just (toColWidth xs) - _ -> Nothing) <|> - (case lookup "style" attribs >>= T.stripPrefix "width" of - Just xs | T.any (== '%') xs -> Just . toColWidth $ - T.filter (`notElem` (" \t\r\n%'\";" :: [Char])) xs - _ -> Nothing) + let width = case lookup "width" attribs of + Nothing -> case lookup "style" attribs of + Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs -> + fromMaybe 0.0 $ safeRead (T.filter + (`notElem` (" \t\r\n%'\";" :: [Char])) xs) + _ -> 0.0 + Just (T.unsnoc -> Just (xs, '%')) -> + fromMaybe 0.0 $ safeRead xs + _ -> 0.0 + if width > 0.0 + then return $ ColWidth $ width / 100.0 + else return ColWidthDefault pColgroup :: PandocMonad m => TagParser m [ColWidth] pColgroup = try $ do -- cgit v1.2.3 From 1af2cfb2873c5bb6ddd9fc00d076088b2e62af30 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 22 May 2021 21:56:10 -0700 Subject: Handle relative lengths (e.g. `2*`) in HTML column widths. See . "A relative length has the form "i*", where "i" is an integer. When allotting space among elements competing for that space, user agents allot pixel and percentage lengths first, then divide up remaining available space among relative lengths. Each relative length receives a portion of the available space that is proportional to the integer preceding the "*". The value "*" is equivalent to "1*". Thus, if 60 pixels of space are available after the user agent allots pixel and percentage space, and the competing relative lengths are 1*, 2*, and 3*, the 1* will be alloted 10 pixels, the 2* will be alloted 20 pixels, and the 3* will be alloted 30 pixels." Closes #4063. --- src/Text/Pandoc/Readers/HTML/Table.hs | 47 ++++++++++++++++++++++++----------- test/command/4063.md | 29 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 test/command/4063.md diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index ad0b51253..3a569dd0a 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -17,6 +17,7 @@ module Text.Pandoc.Readers.HTML.Table (pTable) where import Control.Applicative ((<|>)) import Data.Maybe (fromMaybe) +import Data.Either (lefts, rights) import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import Text.HTML.TagSoup @@ -33,34 +34,51 @@ import Text.Pandoc.Shared (onlySimpleTableCells, safeRead) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B --- | Parses a @@ element, returning the column's width. Defaults to --- @'ColWidthDefault'@ if the width is not set or cannot be determined. -pCol :: PandocMonad m => TagParser m ColWidth +-- | Parses a @@ element, returning the column's width. +-- An Either value is used: Left i means a "relative length" with +-- integral value i (see https://www.w3.org/TR/html4/types.html#h-6.6); +-- Right w means a regular width. Defaults to @'Right ColWidthDefault'@ +-- if the width is not set or cannot be determined. +pCol :: PandocMonad m => TagParser m (Either Int ColWidth) pCol = try $ do TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" []) let attribs = toStringAttr attribs' skipMany pBlank optional $ pSatisfy (matchTagClose "col") skipMany pBlank - let width = case lookup "width" attribs of + return $ case lookup "width" attribs of Nothing -> case lookup "style" attribs of Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs -> - fromMaybe 0.0 $ safeRead (T.filter - (`notElem` (" \t\r\n%'\";" :: [Char])) xs) - _ -> 0.0 + maybe (Right ColWidthDefault) (Right . ColWidth) + $ safeRead (T.filter + (`notElem` (" \t\r\n%'\";" :: [Char])) xs) + _ -> Right ColWidthDefault + Just (T.unsnoc -> Just (xs, '*')) -> + maybe (Left 1) Left $ safeRead xs Just (T.unsnoc -> Just (xs, '%')) -> - fromMaybe 0.0 $ safeRead xs - _ -> 0.0 - if width > 0.0 - then return $ ColWidth $ width / 100.0 - else return ColWidthDefault + maybe (Right ColWidthDefault) + (Right . ColWidth . (/ 100.0)) $ safeRead xs + _ -> Right ColWidthDefault -pColgroup :: PandocMonad m => TagParser m [ColWidth] +pColgroup :: PandocMonad m => TagParser m [Either Int ColWidth] pColgroup = try $ do pSatisfy (matchTagOpen "colgroup" []) skipMany pBlank manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank +resolveRelativeLengths :: [Either Int ColWidth] -> [ColWidth] +resolveRelativeLengths ws = + let remaining = 1 - sum (map getColWidth $ rights ws) + relatives = sum $ lefts ws + relUnit = remaining / fromIntegral relatives + toColWidth (Right x) = x + toColWidth (Left i) = ColWidth (fromIntegral i * relUnit) + in map toColWidth ws + +getColWidth :: ColWidth -> Double +getColWidth ColWidthDefault = 0 +getColWidth (ColWidth w) = w + data CellType = HeaderCell | BodyCell @@ -182,7 +200,8 @@ pTable :: PandocMonad m pTable block = try $ do TagOpen _ attribs <- pSatisfy (matchTagOpen "table" []) <* skipMany pBlank caption <- option mempty $ pInTags "caption" block <* skipMany pBlank - widths <- ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank + widths <- resolveRelativeLengths <$> + ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank thead <- pTableHead block <* skipMany pBlank topfoot <- optionMaybe (pTableFoot block) <* skipMany pBlank tbodies <- many (pTableBody block) <* skipMany pBlank diff --git a/test/command/4063.md b/test/command/4063.md new file mode 100644 index 000000000..838472b46 --- /dev/null +++ b/test/command/4063.md @@ -0,0 +1,29 @@ +``` +% pandoc -f html -t native + ++ + + + + + + +
12
+^D +[Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidth 0.3) + ,(AlignDefault,ColWidth 0.7)] + (TableHead ("",[],[]) + []) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]]]])] + (TableFoot ("",[],[]) + [])] +``` -- cgit v1.2.3 From c5f9446646a9add4a339a49ac8165e16474fa898 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 24 May 2021 08:45:59 +0200 Subject: Use jira-wiki-markup 1.3.5 * Allow spaces and most unicode characters in attachment links. * No longer require a newline character after `{noformat}`. * Only allow URI path segment characters in bare links. * The `file:` schema is no longer allowed in bare links; these rarely make sense. Closes: #7218 --- pandoc.cabal | 2 +- stack.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index ec50d26f5..4e10e01f3 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -470,7 +470,7 @@ library http-client-tls >= 0.2.4 && < 0.4, http-types >= 0.8 && < 0.13, ipynb >= 0.1 && < 0.2, - jira-wiki-markup >= 1.3.4 && < 1.4, + jira-wiki-markup >= 1.3.5 && < 1.4, mtl >= 2.2 && < 2.3, network >= 2.6, network-uri >= 2.6 && < 2.8, diff --git a/stack.yaml b/stack.yaml index c67ff9986..809157425 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,6 +9,7 @@ packages: extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 +- jira-wiki-markup-1.3.5 - random-1.2.0 - unicode-collation-0.1.3 - citeproc-0.4 -- cgit v1.2.3 From 58fbf56548bf985b40e4338befaf5b11a0665cbe Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 24 May 2021 09:56:02 +0200 Subject: Jira writer: use `{color}` when span has a color attribute Closes: tarleb/jira-wiki-markup#10 --- src/Text/Pandoc/Writers/Jira.hs | 10 +++++++--- test/Tests/Writers/Jira.hs | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/Jira.hs b/src/Text/Pandoc/Writers/Jira.hs index aa78d9419..cf4dadebc 100644 --- a/src/Text/Pandoc/Writers/Jira.hs +++ b/src/Text/Pandoc/Writers/Jira.hs @@ -309,9 +309,13 @@ quotedToJira qtype xs = do spanToJira :: PandocMonad m => Attr -> [Inline] -> JiraConverter m [Jira.Inline] -spanToJira (ident, _classes, _attribs) inls = case ident of - "" -> toJiraInlines inls - _ -> (Jira.Anchor ident :) <$> toJiraInlines inls +spanToJira (ident, _classes, attribs) inls = + let wrap = case lookup "color" attribs of + Nothing -> id + Just color -> singleton . Jira.ColorInline (Jira.ColorName color) + in wrap <$> case ident of + "" -> toJiraInlines inls + _ -> (Jira.Anchor ident :) <$> toJiraInlines inls registerNotes :: PandocMonad m => [Block] -> JiraConverter m [Jira.Inline] registerNotes contents = do diff --git a/test/Tests/Writers/Jira.hs b/test/Tests/Writers/Jira.hs index 0c6f48853..d8e856e34 100644 --- a/test/Tests/Writers/Jira.hs +++ b/test/Tests/Writers/Jira.hs @@ -67,6 +67,10 @@ tests = [ "id is used as anchor" =: spanWith ("unicorn", [], []) (str "Unicorn") =?> "{anchor:unicorn}Unicorn" + + , "use `color` attribute" =: + spanWith ("",[],[("color","red")]) "ruby" =?> + "{color:red}ruby{color}" ] , testGroup "code" -- cgit v1.2.3 From 8511f6fdf6c9fbc2cc926538bca4ae9f554b4ed9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 23 May 2021 22:57:02 -0700 Subject: MediaBag improvements. In the current dev version, we will sometimes add a version of an image with a hashed name, keeping the original version with the original name, which would leave to undesirable duplication. This change separates the media's filename from the media's canonical name (which is the path of the link in the document itself). Filenames are based on SHA1 hashes and assigned automatically. In Text.Pandoc.MediaBag: - Export MediaItem type [API change]. - Change MediaBag type to a map from Text to MediaItem [API change]. - `lookupMedia` now returns a `MediaItem` [API change]. - Change `insertMedia` so it sets the `mediaPath` to a filename based on the SHA1 hash of the contents. This will be used when contents are extracted. In Text.Pandoc.Class.PandocMonad: - Remove `fetchMediaResource` [API change]. Lua MediaBag module has been changed minimally. In the future it would be better, probably, to give Lua access to the full MediaItem type. --- src/Text/Pandoc/Class/IO.hs | 9 ++++--- src/Text/Pandoc/Class/PandocMonad.hs | 43 ++++++++++++++-------------------- src/Text/Pandoc/Lua/Module/MediaBag.hs | 6 ++--- src/Text/Pandoc/MediaBag.hs | 35 ++++++++++++++++++++------- test/Tests/Readers/Docx.hs | 10 ++++---- 5 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs index bb4e2b732..f12c0a938 100644 --- a/src/Text/Pandoc/Class/IO.hs +++ b/src/Text/Pandoc/Class/IO.hs @@ -62,7 +62,7 @@ import Text.Pandoc.Definition (Pandoc, Inline (Image)) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Logging (LogMessage (..), messageVerbosity, showLogMessage) import Text.Pandoc.MIME (MimeType) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) +import Text.Pandoc.MediaBag (MediaBag, MediaItem(..), lookupMedia, mediaDirectory) import Text.Pandoc.Walk (walk) import qualified Control.Exception as E import qualified Data.ByteString as B @@ -213,14 +213,13 @@ writeMedia :: (PandocMonad m, MonadIO m) writeMedia dir mediabag subpath = do -- we join and split to convert a/b/c to a\b\c on Windows; -- in zip containers all paths use / - let fullpath = dir unEscapeString (normalise subpath) let mbcontents = lookupMedia subpath mediabag case mbcontents of Nothing -> throwError $ PandocResourceNotFound $ pack subpath - Just (_, bs) -> do - report $ Extracting $ pack fullpath + Just item -> do + let fullpath = dir mediaPath item liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath) - logIOError $ BL.writeFile fullpath bs + logIOError $ BL.writeFile fullpath $ mediaContents item -- | If the given Inline element is an image with a @src@ path equal to -- one in the list of @paths@, then prepends @dir@ to the image source; diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index dd6499a73..ae6917e06 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -37,7 +37,6 @@ module Text.Pandoc.Class.PandocMonad , setUserDataDir , getUserDataDir , fetchItem - , fetchMediaResource , getInputFiles , setInputFiles , getOutputFile @@ -57,8 +56,6 @@ module Text.Pandoc.Class.PandocMonad import Codec.Archive.Zip import Control.Monad.Except (MonadError (catchError, throwError), MonadTrans, lift, when) -import Data.Digest.Pure.SHA (sha1, showDigest) -import Data.Maybe (fromMaybe) import Data.List (foldl') import Data.Time (UTCTime) import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds, @@ -67,7 +64,7 @@ import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime) import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) -import System.FilePath ((), (<.>), takeExtension, dropExtension, +import System.FilePath ((), takeExtension, dropExtension, isRelative, splitDirectories) import System.Random (StdGen) import Text.Collate.Lang (Lang(..), parseLang, renderLang) @@ -75,8 +72,8 @@ import Text.Pandoc.Class.CommonState (CommonState (..)) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Logging -import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) +import Text.Pandoc.MIME (MimeType, getMimeType) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia, MediaItem(..)) import Text.Pandoc.Shared (uriPathToPath, safeRead) import Text.Pandoc.Translations (Term(..), Translations, lookupTerm, readTranslations) @@ -376,7 +373,8 @@ fetchItem :: PandocMonad m fetchItem s = do mediabag <- getMediaBag case lookupMedia (T.unpack s) mediabag of - Just (mime, bs) -> return (BL.toStrict bs, Just mime) + Just item -> return (BL.toStrict (mediaContents item), + Just (mediaMimeType item)) Nothing -> downloadOrRead s -- | Returns the content and, if available, the MIME type of a resource. @@ -629,19 +627,6 @@ withPaths (p:ps) action fp = catchError (action (p fp)) (\_ -> withPaths ps action fp) --- | Fetch local or remote resource (like an image) and provide data suitable --- for adding it to the MediaBag. -fetchMediaResource :: PandocMonad m - => T.Text -> m (FilePath, Maybe MimeType, BL.ByteString) -fetchMediaResource src = do - (bs, mt) <- fetchItem src - let ext = fromMaybe (T.pack $ takeExtension $ T.unpack src) - (mt >>= extensionFromMimeType) - let bs' = BL.fromChunks [bs] - let basename = showDigest $ sha1 bs' - let fname = basename <.> T.unpack ext - return (fname, mt, bs') - -- | Traverse tree, filling media bag for any images that -- aren't already in the media bag. fillMediaBag :: PandocMonad m => Pandoc -> m Pandoc @@ -649,12 +634,18 @@ fillMediaBag d = walkM handleImage d where handleImage :: PandocMonad m => Inline -> m Inline handleImage (Image attr lab (src, tit)) = catchError (do mediabag <- getMediaBag - case lookupMedia (T.unpack src) mediabag of - Just (_, _) -> return $ Image attr lab (src, tit) - Nothing -> do - (fname, mt, bs) <- fetchMediaResource src - insertMedia fname mt bs - return $ Image attr lab (T.pack fname, tit)) + let fp = T.unpack src + src' <- T.pack <$> case lookupMedia fp mediabag of + Just item -> return $ mediaPath item + Nothing -> do + (bs, mt) <- fetchItem src + insertMedia fp mt (BL.fromStrict bs) + mediabag' <- getMediaBag + case lookupMedia fp mediabag' of + Just item -> return $ mediaPath item + Nothing -> throwError $ PandocSomeError $ + src <> " not successfully inserted into MediaBag" + return $ Image attr lab (src', tit)) (\e -> case e of PandocResourceNotFound _ -> do diff --git a/src/Text/Pandoc/Lua/Module/MediaBag.hs b/src/Text/Pandoc/Lua/Module/MediaBag.hs index 78b699176..3eed50fca 100644 --- a/src/Text/Pandoc/Lua/Module/MediaBag.hs +++ b/src/Text/Pandoc/Lua/Module/MediaBag.hs @@ -73,9 +73,9 @@ lookup fp = do res <- MB.lookupMedia fp <$> getMediaBag liftPandocLua $ case res of Nothing -> 1 <$ Lua.pushnil - Just (mimeType, contents) -> do - Lua.push mimeType - Lua.push contents + Just item -> do + Lua.push $ MB.mediaMimeType item + Lua.push $ MB.mediaContents item return 2 list :: PandocLua NumResults diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index 4a9b4efa1..a65f315fc 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -15,6 +15,7 @@ Definition of a MediaBag object to hold binary resources, and an interface for interacting with it. -} module Text.Pandoc.MediaBag ( + MediaItem(..), MediaBag, deleteMedia, lookupMedia, @@ -28,15 +29,23 @@ import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Typeable (Typeable) import System.FilePath -import Text.Pandoc.MIME (MimeType, getMimeTypeDef) +import Text.Pandoc.MIME (MimeType, getMimeTypeDef, extensionFromMimeType) import Data.Text (Text) import qualified Data.Text as T +import Data.Digest.Pure.SHA (sha1, showDigest) + +data MediaItem = + MediaItem + { mediaMimeType :: MimeType + , mediaPath :: FilePath + , mediaContents :: BL.ByteString + } deriving (Eq, Ord, Show, Data, Typeable) -- | A container for a collection of binary resources, with names and -- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty' -- can be used for an empty 'MediaBag', and '<>' can be used to append -- two 'MediaBag's. -newtype MediaBag = MediaBag (M.Map Text (MimeType, BL.ByteString)) +newtype MediaBag = MediaBag (M.Map Text MediaItem) deriving (Semigroup, Monoid, Data, Typeable) instance Show MediaBag where @@ -62,26 +71,34 @@ insertMedia :: FilePath -- ^ relative path and canonical name of resource -> MediaBag -> MediaBag insertMedia fp mbMime contents (MediaBag mediamap) = - MediaBag (M.insert (canonicalize fp) (mime, contents) mediamap) - where mime = fromMaybe fallback mbMime + MediaBag (M.insert (canonicalize fp) mediaItem mediamap) + where mediaItem = MediaItem{ mediaPath = showDigest (sha1 contents) <> + "." <> ext + , mediaContents = contents + , mediaMimeType = mt } fallback = case takeExtension fp of ".gz" -> getMimeTypeDef $ dropExtension fp _ -> getMimeTypeDef fp + mt = fromMaybe fallback mbMime + ext = maybe (takeExtension fp) T.unpack $ extensionFromMimeType mt + -- | Lookup a media item in a 'MediaBag', returning mime type and contents. lookupMedia :: FilePath -> MediaBag - -> Maybe (MimeType, BL.ByteString) + -> Maybe MediaItem lookupMedia fp (MediaBag mediamap) = M.lookup (canonicalize fp) mediamap -- | Get a list of the file paths stored in a 'MediaBag', with -- their corresponding mime types and the lengths in bytes of the contents. mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)] mediaDirectory (MediaBag mediamap) = - M.foldrWithKey (\fp (mime,contents) -> - ((T.unpack fp, mime, fromIntegral (BL.length contents)):)) [] mediamap + M.foldrWithKey (\fp item -> + ((T.unpack fp, mediaMimeType item, + fromIntegral (BL.length (mediaContents item))):)) [] mediamap mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)] mediaItems (MediaBag mediamap) = - M.foldrWithKey (\fp (mime,contents) -> - ((T.unpack fp, mime, contents):)) [] mediamap + M.foldrWithKey (\fp item -> + ((T.unpack fp, mediaMimeType item, mediaContents item):)) + [] mediamap diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 2cce70cc5..939ff9939 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -24,7 +24,7 @@ import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc import qualified Text.Pandoc.Class as P -import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) +import qualified Text.Pandoc.MediaBag as MB import Text.Pandoc.UTF8 as UTF8 -- We define a wrapper around pandoc that doesn't normalize in the @@ -91,11 +91,11 @@ getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString) getMedia archivePath mediaPath = fmap fromEntry . findEntryByPath ("word/" ++ mediaPath) . toArchive <$> B.readFile archivePath -compareMediaPathIO :: FilePath -> MediaBag -> FilePath -> IO Bool +compareMediaPathIO :: FilePath -> MB.MediaBag -> FilePath -> IO Bool compareMediaPathIO mediaPath mediaBag docxPath = do docxMedia <- getMedia docxPath mediaPath - let mbBS = case lookupMedia mediaPath mediaBag of - Just (_, bs) -> bs + let mbBS = case MB.lookupMedia mediaPath mediaBag of + Just item -> MB.mediaContents item Nothing -> error ("couldn't find " ++ mediaPath ++ " in media bag") @@ -110,7 +110,7 @@ compareMediaBagIO docxFile = do mb <- runIOorExplode $ readDocx defopts df >> P.getMediaBag bools <- mapM (\(fp, _, _) -> compareMediaPathIO fp mb docxFile) - (mediaDirectory mb) + (MB.mediaDirectory mb) return $ and bools testMediaBagIO :: String -> FilePath -> IO TestTree -- cgit v1.2.3 From d46ea7d7da3f842d265f09730c90cfc3691576ef Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 25 May 2021 16:54:42 +0200 Subject: Jira: add support for "smart" links Support has been added for the new `[alias|https://example.com|smart-card]` syntax. --- pandoc.cabal | 2 +- src/Text/Pandoc/Readers/Jira.hs | 2 ++ src/Text/Pandoc/Writers/Jira.hs | 2 ++ stack.yaml | 2 +- test/Tests/Readers/Jira.hs | 8 ++++++++ test/Tests/Writers/Jira.hs | 8 ++++++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 4e10e01f3..241196d7c 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -470,7 +470,7 @@ library http-client-tls >= 0.2.4 && < 0.4, http-types >= 0.8 && < 0.13, ipynb >= 0.1 && < 0.2, - jira-wiki-markup >= 1.3.5 && < 1.4, + jira-wiki-markup >= 1.4 && < 1.5, mtl >= 2.2 && < 2.3, network >= 2.6, network-uri >= 2.6 && < 2.8, diff --git a/src/Text/Pandoc/Readers/Jira.hs b/src/Text/Pandoc/Readers/Jira.hs index a3b415f09..cf111f173 100644 --- a/src/Text/Pandoc/Readers/Jira.hs +++ b/src/Text/Pandoc/Readers/Jira.hs @@ -172,6 +172,8 @@ jiraLinkToPandoc linkType alias url = Jira.Email -> link ("mailto:" <> url') "" alias' Jira.Attachment -> linkWith ("", ["attachment"], []) url' "" alias' Jira.User -> linkWith ("", ["user-account"], []) url' "" alias' + Jira.SmartCard -> linkWith ("", ["smart-card"], []) url' "" alias' + Jira.SmartLink -> linkWith ("", ["smart-link"], []) url' "" alias' -- | Get unicode representation of a Jira icon. iconUnicode :: Jira.Icon -> Text diff --git a/src/Text/Pandoc/Writers/Jira.hs b/src/Text/Pandoc/Writers/Jira.hs index cf4dadebc..1351814e9 100644 --- a/src/Text/Pandoc/Writers/Jira.hs +++ b/src/Text/Pandoc/Writers/Jira.hs @@ -280,6 +280,8 @@ toJiraLink (_, classes, _) (url, _) alias = do | Just email <- T.stripPrefix "mailto:" url' = (Jira.Email, email) | "user-account" `elem` classes = (Jira.User, dropTilde url) | "attachment" `elem` classes = (Jira.Attachment, url) + | "smart-card" `elem` classes = (Jira.SmartCard, url) + | "smart-link" `elem` classes = (Jira.SmartLink, url) | otherwise = (Jira.External, url) dropTilde txt = case T.uncons txt of Just ('~', username) -> username diff --git a/stack.yaml b/stack.yaml index 809157425..16e5ad2cf 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,7 +9,7 @@ packages: extra-deps: - hslua-1.3.0 - hslua-module-path-0.1.0 -- jira-wiki-markup-1.3.5 +- jira-wiki-markup-1.4.0 - random-1.2.0 - unicode-collation-0.1.3 - citeproc-0.4 diff --git a/test/Tests/Readers/Jira.hs b/test/Tests/Readers/Jira.hs index cb7dde4ea..b7194a3b9 100644 --- a/test/Tests/Readers/Jira.hs +++ b/test/Tests/Readers/Jira.hs @@ -167,6 +167,14 @@ tests = , "user with description" =: "[John Doe|~johndoe]" =?> para (linkWith ("", ["user-account"], []) "~johndoe" "" "John Doe") + + , "'smart' link" =: + "[x|http://example.com|smart-link]" =?> + para (linkWith ("", ["smart-link"], []) "http://example.com" "" "x") + + , "'smart' card" =: + "[x|http://example.com|smart-card]" =?> + para (linkWith ("", ["smart-card"], []) "http://example.com" "" "x") ] , "image" =: diff --git a/test/Tests/Writers/Jira.hs b/test/Tests/Writers/Jira.hs index d8e856e34..00a7ae931 100644 --- a/test/Tests/Writers/Jira.hs +++ b/test/Tests/Writers/Jira.hs @@ -61,6 +61,14 @@ tests = , "user link with user as description" =: linkWith ("", ["user-account"], []) "~johndoe" "" "~johndoe" =?> "[~johndoe]" + + , "'smart' link" =: + para (linkWith ("", ["smart-link"], []) "http://example.com" "" "x") =?> + "[x|http://example.com|smart-link]" + + , "'smart' card" =: + para (linkWith ("", ["smart-card"], []) "http://example.org" "" "x") =?> + "[x|http://example.org|smart-card]" ] , testGroup "spans" -- cgit v1.2.3 From fb40c8109dc969dce74c8153ad1c0d4b33d54a6c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 25 May 2021 10:07:24 -0700 Subject: Logging: add LoadedResource constructor to LogMessage. [API change] This is for INFO-level messages telling where image data has been loaded from. (This can vary because of the resource path.) --- src/Text/Pandoc/Logging.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 8c7292b69..2642d72ac 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -85,6 +85,7 @@ data LogMessage = | CouldNotParseCSS Text | Fetching Text | Extracting Text + | LoadedResource FilePath FilePath | NoTitleElement Text | NoLangSpecified | InvalidLang Text @@ -195,6 +196,9 @@ instance ToJSON LogMessage where ["path" .= fp] Extracting fp -> ["path" .= fp] + LoadedResource orig found -> + ["for" .= orig + ,"from" .= found] NoTitleElement fallback -> ["fallback" .= fallback] NoLangSpecified -> [] @@ -309,6 +313,8 @@ showLogMessage msg = "Fetching " <> fp <> "..." Extracting fp -> "Extracting " <> fp <> "..." + LoadedResource orig found -> + "Loaded " <> Text.pack orig <> " from " <> Text.pack found NoTitleElement fallback -> "This document format requires a nonempty element.\n" <> "Defaulting to '" <> fallback <> "' as the title.\n" <> @@ -389,6 +395,7 @@ messageVerbosity msg = CouldNotParseCSS{} -> WARNING Fetching{} -> INFO Extracting{} -> INFO + LoadedResource{} -> INFO NoTitleElement{} -> WARNING NoLangSpecified -> INFO InvalidLang{} -> WARNING -- cgit v1.2.3 From f2c1b5746912db945be780961b6503e38c3c7e1e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 May 2021 10:08:30 -0700 Subject: PandocMonad: add info message in `downloadOrRead`... indicating what path local resources have been loaded from. --- src/Text/Pandoc/Class/PandocMonad.hs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index ae6917e06..b5f401619 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} @@ -409,9 +410,10 @@ downloadOrRead s = do _ -> readLocalFile fp -- get from local file system where readLocalFile f = do resourcePath <- getResourcePath - cont <- if isRelative f - then withPaths resourcePath readFileStrict f - else readFileStrict f + (fp', cont) <- if isRelative f + then withPaths resourcePath readFileStrict f + else (f,) <$> readFileStrict f + report $ LoadedResource f fp' return (cont, mime) httpcolon = URI{ uriScheme = "http:", uriAuthority = Nothing, @@ -621,10 +623,11 @@ makeCanonical = Posix.joinPath . transformPathParts . splitDirectories -- that filepath. Returns the result of the first successful execution -- of the action, or throws a @PandocResourceNotFound@ exception if the -- action errors for all filepaths. -withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a +withPaths :: PandocMonad m + => [FilePath] -> (FilePath -> m a) -> FilePath -> m (FilePath, a) withPaths [] _ fp = throwError $ PandocResourceNotFound $ T.pack fp withPaths (p:ps) action fp = - catchError (action (p </> fp)) + catchError ((p </> fp,) <$> action (p </> fp)) (\_ -> withPaths ps action fp) -- | Traverse tree, filling media bag for any images that -- cgit v1.2.3 From bb2530caa414234f00e7c89ef18a538708b2297c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 25 May 2021 17:49:48 +0200 Subject: Use haddock-library-1.10.0 --- pandoc.cabal | 2 +- src/Text/Pandoc/Readers/Haddock.hs | 3 ++- stack.yaml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 241196d7c..c8e49c620 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -461,7 +461,7 @@ library exceptions >= 0.8 && < 0.11, file-embed >= 0.0 && < 0.1, filepath >= 1.1 && < 1.5, - haddock-library >= 1.8 && < 1.10, + haddock-library >= 1.10 && < 1.11, hslua >= 1.1 && < 1.4, hslua-module-path >= 0.1.0 && < 0.2.0, hslua-module-system >= 0.2 && < 0.3, diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 35eaac0a9..67b3af2d3 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -131,7 +131,8 @@ docHToInlines isCode d' = DocIdentifier s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s _ -> mempty DocIdentifierUnchecked s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s - DocModule s -> B.codeWith ("",["haskell","module"],[]) $ T.pack s + DocModule s -> B.codeWith ("",["haskell","module"],[]) $ + T.pack (modLinkName s) DocWarning _ -> mempty -- TODO DocEmphasis d -> B.emph (docHToInlines isCode d) DocMonospaced (DocString s) -> B.code $ T.pack s diff --git a/stack.yaml b/stack.yaml index 16e5ad2cf..ea48fbb88 100644 --- a/stack.yaml +++ b/stack.yaml @@ -7,6 +7,7 @@ flags: packages: - '.' extra-deps: +- haddock-library-1.10.0 - hslua-1.3.0 - hslua-module-path-0.1.0 - jira-wiki-markup-1.4.0 -- cgit v1.2.3 From 105a50569be6d2c1b37cc290abcbfbae1cd0fd1e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 25 May 2021 17:49:48 +0200 Subject: Allow compilation with base 4.15 --- src/Text/Pandoc/Options.hs | 34 +++++----- src/Text/Pandoc/Readers/Docx/Combine.hs | 6 +- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 11 ++-- src/Text/Pandoc/Writers/OpenDocument.hs | 98 +++++++++++++--------------- 4 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 92bda36b2..85d9aa103 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -315,6 +315,23 @@ defaultMathJaxURL = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.j defaultKaTeXURL :: Text defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.11.1/" +-- Update documentation in doc/filters.md if this is changed. +$(deriveJSON defaultOptions{ constructorTagModifier = + camelCaseStrToHyphenated + } ''TrackChanges) + +$(deriveJSON defaultOptions{ constructorTagModifier = + camelCaseStrToHyphenated + } ''WrapOption) + +$(deriveJSON defaultOptions{ constructorTagModifier = + camelCaseStrToHyphenated . drop 8 + } ''TopLevelDivision) + +$(deriveJSON defaultOptions{ constructorTagModifier = + camelCaseStrToHyphenated + } ''ReferenceLocation) + -- Update documentation in doc/filters.md if this is changed. $(deriveJSON defaultOptions ''ReaderOptions) @@ -338,20 +355,3 @@ $(deriveJSON defaultOptions{ constructorTagModifier = } ''ObfuscationMethod) $(deriveJSON defaultOptions ''HTMLSlideVariant) - --- Update documentation in doc/filters.md if this is changed. -$(deriveJSON defaultOptions{ constructorTagModifier = - camelCaseStrToHyphenated - } ''TrackChanges) - -$(deriveJSON defaultOptions{ constructorTagModifier = - camelCaseStrToHyphenated - } ''WrapOption) - -$(deriveJSON defaultOptions{ constructorTagModifier = - camelCaseStrToHyphenated . drop 8 - } ''TopLevelDivision) - -$(deriveJSON defaultOptions{ constructorTagModifier = - camelCaseStrToHyphenated - } ''ReferenceLocation) diff --git a/src/Text/Pandoc/Readers/Docx/Combine.hs b/src/Text/Pandoc/Readers/Docx/Combine.hs index 7c6d01769..6e4faa639 100644 --- a/src/Text/Pandoc/Readers/Docx/Combine.hs +++ b/src/Text/Pandoc/Readers/Docx/Combine.hs @@ -61,7 +61,7 @@ import Data.List import Data.Bifunctor import Data.Sequence ( ViewL (..), ViewR (..), viewl, viewr, spanr, spanl , (><), (|>) ) -import Text.Pandoc.Builder +import Text.Pandoc.Builder as B data Modifier a = Modifier (a -> a) | AttrModifier (Attr -> a -> a) Attr @@ -116,12 +116,12 @@ ilModifierAndInnards ils = case viewl $ unMany ils of inlinesL :: Inlines -> (Inlines, Inlines) inlinesL ils = case viewl $ unMany ils of - (s :< sq) -> (singleton s, Many sq) + (s :< sq) -> (B.singleton s, Many sq) _ -> (mempty, ils) inlinesR :: Inlines -> (Inlines, Inlines) inlinesR ils = case viewr $ unMany ils of - (sq :> s) -> (Many sq, singleton s) + (sq :> s) -> (Many sq, B.singleton s) _ -> (ils, mempty) combineInlines :: Inlines -> Inlines -> Inlines diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index c4220b0db..5520d039f 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -1,4 +1,5 @@ {-# LANGUAGE Arrows #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PatternGuards #-} @@ -33,7 +34,7 @@ import Data.List (find) import qualified Data.Map as M import qualified Data.Text as T import Data.Maybe -import Data.Semigroup (First(..), Option(..)) +import Data.Monoid (Alt (..)) import Text.TeXMath (readMathML, writeTeX) import qualified Text.Pandoc.XML.Light as XML @@ -505,13 +506,11 @@ type InlineMatcher = ElementMatcher Inlines type BlockMatcher = ElementMatcher Blocks - -newtype FirstMatch a = FirstMatch (Option (First a)) - deriving (Foldable, Monoid, Semigroup) +newtype FirstMatch a = FirstMatch (Alt Maybe a) + deriving (Foldable, Monoid, Semigroup) firstMatch :: a -> FirstMatch a -firstMatch = FirstMatch . Option . Just . First - +firstMatch = FirstMatch . Alt . Just -- matchingElement :: (Monoid e) diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 34a3a4aa5..5f3224c2f 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ViewPatterns #-} @@ -372,36 +373,32 @@ blocksToOpenDocument o b = vcat <$> mapM (blockToOpenDocument o) b -- | Convert a Pandoc block element to OpenDocument. blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m (Doc Text) -blockToOpenDocument o bs - | Plain b <- bs = if null b - then return empty - else inParagraphTags =<< inlinesToOpenDocument o b - | Para [Image attr c (s,T.stripPrefix "fig:" -> Just t)] <- bs - = figure attr c s t - | Para b <- bs = if null b && - not (isEnabled Ext_empty_paragraphs o) - then return empty - else inParagraphTags =<< inlinesToOpenDocument o b - | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b - | Div attr xs <- bs = mkDiv attr xs - | Header i (ident,_,_) b - <- bs = setFirstPara >> (inHeaderTags i ident - =<< inlinesToOpenDocument o b) - | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b - | DefinitionList b <- bs = setFirstPara >> defList b - | BulletList b <- bs = setFirstPara >> bulletListToOpenDocument o b - | OrderedList a b <- bs = setFirstPara >> orderedList a b - | CodeBlock _ s <- bs = setFirstPara >> preformatted s - | Table a bc s th tb tf <- bs = setFirstPara >> table (Ann.toTable a bc s th tb tf) - | HorizontalRule <- bs = setFirstPara >> return (selfClosingTag "text:p" - [ ("text:style-name", "Horizontal_20_Line") ]) - | RawBlock f s <- bs = if f == Format "opendocument" - then return $ text $ T.unpack s - else do - report $ BlockNotRendered bs - return empty - | Null <- bs = return empty - | otherwise = return empty +blockToOpenDocument o = \case + Plain b -> if null b + then return empty + else inParagraphTags =<< inlinesToOpenDocument o b + Para [Image attr c (s,T.stripPrefix "fig:" -> Just t)] -> figure attr c s t + Para b -> if null b && + not (isEnabled Ext_empty_paragraphs o) + then return empty + else inParagraphTags =<< inlinesToOpenDocument o b + LineBlock b -> blockToOpenDocument o $ linesToPara b + Div attr xs -> mkDiv attr xs + Header i (ident,_,_) b -> do + setFirstPara + inHeaderTags i ident =<< inlinesToOpenDocument o b + BlockQuote b -> setFirstPara >> mkBlockQuote b + DefinitionList b -> setFirstPara >> defList b + BulletList b -> setFirstPara >> bulletListToOpenDocument o b + OrderedList a b -> setFirstPara >> orderedList a b + CodeBlock _ s -> setFirstPara >> preformatted s + Table a bc s th tb tf -> setFirstPara >> table (Ann.toTable a bc s th tb tf) + HorizontalRule -> setFirstPara >> return (selfClosingTag "text:p" + [ ("text:style-name", "Horizontal_20_Line") ]) + b@(RawBlock f s) -> if f == Format "opendocument" + then return $ text $ T.unpack s + else empty <$ report (BlockNotRendered b) + Null -> return empty where defList b = do setInDefinitionList True r <- vcat <$> mapM (deflistItemToOpenDocument o) b @@ -874,27 +871,26 @@ data TextStyle = Italic textStyleAttr :: Map.Map Text Text -> TextStyle -> Map.Map Text Text -textStyleAttr m s - | Italic <- s = Map.insert "fo:font-style" "italic" . - Map.insert "style:font-style-asian" "italic" . - Map.insert "style:font-style-complex" "italic" $ m - | Bold <- s = Map.insert "fo:font-weight" "bold" . - Map.insert "style:font-weight-asian" "bold" . - Map.insert "style:font-weight-complex" "bold" $ m - | Under <- s = Map.insert "style:text-underline-style" "solid" . - Map.insert "style:text-underline-width" "auto" . - Map.insert "style:text-underline-color" "font-color" $ m - | Strike <- s = Map.insert "style:text-line-through-style" "solid" m - | Sub <- s = Map.insert "style:text-position" "sub 58%" m - | Sup <- s = Map.insert "style:text-position" "super 58%" m - | SmallC <- s = Map.insert "fo:font-variant" "small-caps" m - | Pre <- s = Map.insert "style:font-name" "Courier New" . - Map.insert "style:font-name-asian" "Courier New" . - Map.insert "style:font-name-complex" "Courier New" $ m - | Language lang <- s - = Map.insert "fo:language" (langLanguage lang) . - maybe id (Map.insert "fo:country") (langRegion lang) $ m - | otherwise = m +textStyleAttr m = \case + Italic -> Map.insert "fo:font-style" "italic" . + Map.insert "style:font-style-asian" "italic" . + Map.insert "style:font-style-complex" "italic" $ m + Bold -> Map.insert "fo:font-weight" "bold" . + Map.insert "style:font-weight-asian" "bold" . + Map.insert "style:font-weight-complex" "bold" $ m + Under -> Map.insert "style:text-underline-style" "solid" . + Map.insert "style:text-underline-width" "auto" . + Map.insert "style:text-underline-color" "font-color" $ m + Strike -> Map.insert "style:text-line-through-style" "solid" m + Sub -> Map.insert "style:text-position" "sub 58%" m + Sup -> Map.insert "style:text-position" "super 58%" m + SmallC -> Map.insert "fo:font-variant" "small-caps" m + Pre -> Map.insert "style:font-name" "Courier New" . + Map.insert "style:font-name-asian" "Courier New" . + Map.insert "style:font-name-complex" "Courier New" $ m + Language lang -> + Map.insert "fo:language" (langLanguage lang) . + maybe id (Map.insert "fo:country") (langRegion lang) $ m withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a withLangFromAttr (_,_,kvs) action = -- cgit v1.2.3 From 54ab7a0a3b4a3e0ddccde706bb75876070313210 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 25 May 2021 17:49:48 +0200 Subject: CI: test with GHC 9.0.1 --- .github/workflows/ci.yml | 7 ++++++- pandoc.cabal | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8083f44e..3b8a72efa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,11 +68,16 @@ jobs: prefix: '' cabalopts: '' testopts: '--test-option=--hide-successes' - - ghc: '8.10.2' + - ghc: '8.10.4' cabal: '3.2' prefix: '' cabalopts: '-ftrypandoc' testopts: '--test-option=--hide-successes' + - ghc: '9.0.1' + cabal: '3.4' + prefix: '' + cabalopts: '' + testopts: '--test-option=--hide-successes' steps: - uses: actions/checkout@v2 diff --git a/pandoc.cabal b/pandoc.cabal index c8e49c620..c74df3e69 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -12,7 +12,7 @@ stability: alpha homepage: https://pandoc.org category: Text tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, - GHC == 8.8.4, GHC == 8.10.2 + GHC == 8.8.4, GHC == 8.10.2, GHC == 9.0.1 synopsis: Conversion between markup formats description: Pandoc is a Haskell library for converting from one markup format to another, and a command-line tool that uses -- cgit v1.2.3 From 8d5014fdfc57b80be54a3d23358e92c3b45a7e7d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 May 2021 10:36:51 -0700 Subject: Logging: remove single quotes around paths in messages. We weren't doing it consistently and it seems unnecessary. --- src/Text/Pandoc/Logging.hs | 12 ++++++------ test/command/7099.md | 2 +- test/command/svg.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 2642d72ac..193b8b61c 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -282,7 +282,7 @@ showLogMessage msg = ParsingUnescaped s pos -> "Parsing unescaped '" <> s <> "' at " <> showPos pos CouldNotLoadIncludeFile fp pos -> - "Could not load include file '" <> fp <> "' at " <> showPos pos + "Could not load include file " <> fp <> " at " <> showPos pos MacroAlreadyDefined name pos -> "Macro '" <> name <> "' already defined, ignoring at " <> showPos pos InlineNotRendered il -> @@ -294,18 +294,18 @@ showLogMessage msg = IgnoredIOError s -> "IO Error (ignored): " <> s CouldNotFetchResource fp s -> - "Could not fetch resource '" <> fp <> "'" <> + "Could not fetch resource " <> fp <> if Text.null s then "" else ": " <> s CouldNotDetermineImageSize fp s -> - "Could not determine image size for '" <> fp <> "'" <> + "Could not determine image size for " <> fp <> if Text.null s then "" else ": " <> s CouldNotConvertImage fp s -> - "Could not convert image '" <> fp <> "'" <> + "Could not convert image " <> fp <> if Text.null s then "" else ": " <> s CouldNotDetermineMimeType fp -> - "Could not determine mime type for '" <> fp <> "'" + "Could not determine mime type for " <> fp CouldNotConvertTeXMath s m -> - "Could not convert TeX math '" <> s <> "', rendering as TeX" <> + "Could not convert TeX math " <> s <> ", rendering as TeX" <> if Text.null m then "" else ":\n" <> m CouldNotParseCSS m -> "Could not parse CSS" <> if Text.null m then "" else ":\n" <> m diff --git a/test/command/7099.md b/test/command/7099.md index 33ac8aea1..467b22a16 100644 --- a/test/command/7099.md +++ b/test/command/7099.md @@ -11,7 +11,7 @@ <iframe src="h:invalid@url"></iframe> ^D [INFO] Fetching h:invalid@url... -[WARNING] Could not fetch resource 'h:invalid@url': Could not fetch h:invalid@url +[WARNING] Could not fetch resource h:invalid@url: Could not fetch h:invalid@url InvalidUrlException "h:invalid@url" "Invalid scheme" [INFO] Skipped '<iframe src="h:invalid@url"></iframe>' at input line 1 column 29 [] diff --git a/test/command/svg.md b/test/command/svg.md index 4ba836b20..57c99db33 100644 --- a/test/command/svg.md +++ b/test/command/svg.md @@ -2,7 +2,7 @@ % pandoc -f latex -t icml \includegraphics{command/corrupt.svg} ^D -[WARNING] Could not determine image size for 'command/corrupt.svg': could not determine image type +[WARNING] Could not determine image size for command/corrupt.svg: could not determine image type <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 150 -100"> -- cgit v1.2.3 From 6804f47383a954fc1fb48e44ab0383f9f72d5dea Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 May 2021 21:36:40 -0700 Subject: Fix a command test so it writes to stdout not stderr. The error message to stderr was appearing in test output and confusing some users, who thought it indicated a failing test rather than expected output. --- test/command/defaults-inheritance-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/command/defaults-inheritance-2.md b/test/command/defaults-inheritance-2.md index 8b26a2613..c639655d3 100644 --- a/test/command/defaults-inheritance-2.md +++ b/test/command/defaults-inheritance-2.md @@ -1,5 +1,5 @@ ``` -% pandoc -d command/defaults6 +% pandoc -d command/defaults6 2>&1 ^D Error: Circular defaults file reference in 'command/defaults7.yaml' ``` -- cgit v1.2.3 From e0a1f7d2cfa1b18210ea13e0aa03747e6c76e5c5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 26 May 2021 09:52:23 -0700 Subject: Command tests: fail if a file contains no tests. And fix a test that failed in that way! --- test/Tests/Command.hs | 5 ++++- test/command/biblatex-basic.md | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index 3ec6d7230..539be1a1a 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -130,4 +130,7 @@ extractCommandTest testExePath fp = unsafePerformIO $ do def{ readerExtensions = pandocExtensions } contents) let codeblocks = map extractCode $ filter isCodeBlock blocks let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks - return $ testGroup fp cases + return $ testGroup fp + $ if null cases + then [testCase "!!" $ assertFailure "No command tests defined"] + else cases diff --git a/test/command/biblatex-basic.md b/test/command/biblatex-basic.md index 1110b0fb4..8a1176999 100644 --- a/test/command/biblatex-basic.md +++ b/test/command/biblatex-basic.md @@ -1,3 +1,5 @@ +``` +% pandoc -f biblatex -t markdown -s @Book{item1, author="John Doe", title="First Book", @@ -27,7 +29,7 @@ year="2007" ^D --- -nocite: '[@*]' +nocite: "[@*]" references: - author: - family: Doe @@ -44,9 +46,9 @@ references: container-title: Journal of Generic Studies id: item2 issued: 2006 - page: '33-34' + page: 33-34 title: Article - type: 'article-journal' + type: article-journal volume: 6 - author: - family: Doe -- cgit v1.2.3 From 81eadfd99ad3e905b806cc6c80ab0fea0185286f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 26 May 2021 22:50:35 -0700 Subject: LaTeX reader: improve `\def` and implement `\newif`. - Improve parsing of `\def` macros. We previously set "verbatim mode" even for parsing the initial `\def`; this caused problems for things like ``` \def\foo{\def\bar{BAR}} \foo \bar ``` - Implement `\newif`. - Add tests. --- src/Text/Pandoc/Readers/LaTeX/Macro.hs | 59 ++++++++++++++++++++++++-------- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 19 +++++++++- test/command/newif.md | 55 +++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 test/command/newif.md diff --git a/src/Text/Pandoc/Readers/LaTeX/Macro.hs b/src/Text/Pandoc/Readers/LaTeX/Macro.hs index 607f5438c..5495a8e74 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Macro.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Macro.hs @@ -14,6 +14,7 @@ import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline, import Control.Applicative ((<|>), optional) import qualified Data.Map as M import Data.Text (Text) +import qualified Data.Text as T macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a macroDef constructor = do @@ -22,9 +23,11 @@ macroDef constructor = do guardDisabled Ext_latex_macros) <|> return mempty where commandDef = do - (name, macro') <- newcommand <|> letmacro <|> defmacro + nameMacroPairs <- newcommand <|> letmacro <|> defmacro <|> newif guardDisabled Ext_latex_macros <|> - updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) }) + mapM_ (\(name, macro') -> + updateState (\s -> s{ sMacros = M.insert name macro' + (sMacros s) })) nameMacroPairs environmentDef = do mbenv <- newenvironment case mbenv of @@ -40,7 +43,7 @@ macroDef constructor = do -- @\newcommand{\envname}[n-args][default]{begin}@ -- @\newcommand{\endenvname}@ -letmacro :: PandocMonad m => LP m (Text, Macro) +letmacro :: PandocMonad m => LP m [(Text, Macro)] letmacro = do controlSeq "let" (name, contents) <- withVerbatimMode $ do @@ -53,18 +56,47 @@ letmacro = do contents <- bracedOrToken return (name, contents) contents' <- doMacros' 0 contents - return (name, Macro ExpandWhenDefined [] Nothing contents') + return [(name, Macro ExpandWhenDefined [] Nothing contents')] -defmacro :: PandocMonad m => LP m (Text, Macro) -defmacro = try $ +defmacro :: PandocMonad m => LP m [(Text, Macro)] +defmacro = do -- we use withVerbatimMode, because macros are to be expanded -- at point of use, not point of definition + controlSeq "def" withVerbatimMode $ do - controlSeq "def" Tok _ (CtrlSeq name) _ <- anyControlSeq argspecs <- many (argspecArg <|> argspecPattern) contents <- bracedOrToken - return (name, Macro ExpandWhenUsed argspecs Nothing contents) + return [(name, Macro ExpandWhenUsed argspecs Nothing contents)] + +-- \newif\iffoo' defines: +-- \iffoo to be \iffalse +-- \footrue to be a command that defines \iffoo to be \iftrue +-- \foofalse to be a command that defines \iffoo to be \iffalse +newif :: PandocMonad m => LP m [(Text, Macro)] +newif = do + controlSeq "newif" + withVerbatimMode $ do + Tok pos (CtrlSeq name) _ <- anyControlSeq + -- \def\iffoo\iffalse + -- \def\footrue{\def\iffoo\iftrue} + -- \def\foofalse{\def\iffoo\iffalse} + let base = T.drop 2 name + return [ (name, Macro ExpandWhenUsed [] Nothing + [Tok pos (CtrlSeq "iffalse") "\\iffalse"]) + , (base <> "true", + Macro ExpandWhenUsed [] Nothing + [ Tok pos (CtrlSeq "def") "\\def" + , Tok pos (CtrlSeq name) ("\\" <> name) + , Tok pos (CtrlSeq "iftrue") "\\iftrue" + ]) + , (base <> "false", + Macro ExpandWhenUsed [] Nothing + [ Tok pos (CtrlSeq "def") "\\def" + , Tok pos (CtrlSeq name) ("\\" <> name) + , Tok pos (CtrlSeq "iffalse") "\\iffalse" + ]) + ] argspecArg :: PandocMonad m => LP m ArgSpec argspecArg = do @@ -77,10 +109,9 @@ argspecPattern = (toktype' == Symbol || toktype' == Word) && (txt /= "{" && txt /= "\\" && txt /= "}"))) -newcommand :: PandocMonad m => LP m (Text, Macro) +newcommand :: PandocMonad m => LP m [(Text, Macro)] newcommand = do - pos <- getPosition - Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|> + Tok pos (CtrlSeq mtype) _ <- controlSeq "newcommand" <|> controlSeq "renewcommand" <|> controlSeq "providecommand" <|> controlSeq "DeclareMathOperator" <|> @@ -112,9 +143,9 @@ newcommand = do Just macro | mtype == "newcommand" -> do report $ MacroAlreadyDefined txt pos - return (name, macro) - | mtype == "providecommand" -> return (name, macro) - _ -> return (name, Macro ExpandWhenUsed argspecs optarg contents) + return [(name, macro)] + | mtype == "providecommand" -> return [(name, macro)] + _ -> return [(name, Macro ExpandWhenUsed argspecs optarg contents)] newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro)) newenvironment = do diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index 1c77eb299..a17b1f324 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -113,7 +113,6 @@ import Text.Pandoc.Readers.LaTeX.Types (ExpansionPoint (..), Macro (..), ArgSpec (..), Tok (..), TokType (..)) import Text.Pandoc.Shared import Text.Parsec.Pos --- import Debug.Trace newtype DottedNum = DottedNum [Int] deriving (Show, Eq) @@ -563,8 +562,26 @@ trySpecialMacro "xspace" ts = do Tok pos Word t : _ | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts' _ -> return ts' +trySpecialMacro "iftrue" ts = handleIf True ts +trySpecialMacro "iffalse" ts = handleIf False ts trySpecialMacro _ _ = mzero +handleIf :: PandocMonad m => Bool -> [Tok] -> LP m [Tok] +handleIf b ts = do + res' <- lift $ runParserT (ifParser b) defaultLaTeXState "tokens" ts + case res' of + Left _ -> Prelude.fail "Could not parse conditional" + Right ts' -> return ts' + +ifParser :: PandocMonad m => Bool -> LP m [Tok] +ifParser b = do + ifToks <- many (notFollowedBy (controlSeq "else" <|> controlSeq "fi") + *> anyTok) + elseToks <- (controlSeq "else" >> manyTill anyTok (controlSeq "fi")) + <|> ([] <$ controlSeq "fi") + rest <- getInput + return $ (if b then ifToks else elseToks) ++ rest + startsWithAlphaNum :: Text -> Bool startsWithAlphaNum t = case T.uncons t of diff --git a/test/command/newif.md b/test/command/newif.md new file mode 100644 index 000000000..f444f14c9 --- /dev/null +++ b/test/command/newif.md @@ -0,0 +1,55 @@ +``` +% pandoc -f latex -t plain +\iftrue +should print +\iftrue +should print +\else +should not print +\fi +\else +should not print +\fi + +\iffalse +should not print +\else +\iftrue +should print +\else +should not print +\fi +\fi + +\newif\ifepub + +\ifepub +should not print +\fi + +\epubtrue + +\ifepub +should print +\else +should not print +\fi + +\epubfalse + +\ifepub +should not print +\else +should print +\fi +^D +should print + +should print + +should print + +should print + +should print +``` -- cgit v1.2.3 From 0226d2320f5a57475ec260b9d0ad3ad4260ecf38 Mon Sep 17 00:00:00 2001 From: Tristan Stenner <ttstenner@gmail.com> Date: Thu, 27 May 2021 18:54:16 +0200 Subject: Add citation example for locators and suffixes --- MANUAL.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index a0a5d43fe..6dc783e8c 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5010,10 +5010,12 @@ terms can be written in either singular or plural forms, as locator term is used, "page" is assumed. In complex cases, you can force something to be treated as -a locator by enclosing it in curly braces: +a locator by enclosing it in curly braces or prevent parsing +the suffix as locator by prepending curly braces: [@smith{ii, A, D-Z}, with a suffix] [@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here] + [@smith{}, $a^2$ and following] A minus sign (`-`) before the `@` will suppress mention of the author in the citation. This can be useful when the -- cgit v1.2.3 From 834da53058069fe50da510fa86e0807a7ff7868f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 24 May 2021 10:17:37 -0700 Subject: Add `rebase_relative_paths` extension. - Add manual entry for (non-default) extension `rebase_relative_paths`. - Add constructor `Ext_rebase_relative_paths` to `Extensions` in Text.Pandoc.Extensions [API change]. When enabled, this extension rewrites relative image and link paths by prepending the (relative) directory of the containing file. - Make Markdown reader sensitive to the new extension. - Add tests for #3752. Closes #3752. NB. currently the extension applies to markdown and associated readers but not commonmark/gfm. --- MANUAL.txt | 28 ++++++++++++++++++++++++++-- pandoc.cabal | 4 ++++ src/Text/Pandoc/Extensions.hs | 3 +++ src/Text/Pandoc/Readers/Markdown.hs | 36 +++++++++++++++++++++++++++++------- test/command/3752.md | 35 +++++++++++++++++++++++++++++++++++ test/command/chap1/spider.png | Bin 0 -> 63531 bytes test/command/chap1/text.md | 11 +++++++++++ test/command/chap2/spider.png | Bin 0 -> 9861 bytes test/command/chap2/text.md | 3 +++ 9 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 test/command/3752.md create mode 100644 test/command/chap1/spider.png create mode 100644 test/command/chap1/text.md create mode 100644 test/command/chap2/spider.png create mode 100644 test/command/chap2/text.md diff --git a/MANUAL.txt b/MANUAL.txt index 6dc783e8c..48bf60d6e 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3755,7 +3755,7 @@ definition: Note that space between items in a definition list is required. (A variant that loosens this requirement, but disallows "lazy" hard wrapping, can be activated with `compact_definition_lists`: see -[Non-pandoc extensions], below.) +[Non-default extensions], below.) [^3]: I have been influenced by the suggestions of [David Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/). @@ -5051,13 +5051,37 @@ author-in-text style inside notes when using a note style. [finding and editing styles]: https://citationstyles.org/authors/ [CSL locale files]: https://github.com/citation-style-language/locales -## Non-pandoc extensions +## Non-default extensions The following Markdown syntax extensions are not enabled by default in pandoc, but may be enabled by adding `+EXTENSION` to the format name, where `EXTENSION` is the name of the extension. Thus, for example, `markdown+hard_line_breaks` is Markdown with hard line breaks. +#### Extension: `rebase_relative_paths` #### + +Rewrite relative paths for Markdown links and images, depending +on the path of the file containing the link or image link. For +each link or image, pandoc will compute the directory of the +containing file, relative to the working directory, and prepend +the resulting path to the link or image path. + +The use of this extension is best understood by example. +Suppose you have a a subdirectory for each chapter of a book, +`chap1`, `chap2`, `chap3`. Each contains a file `text.md` and a +number of images used in the chapter. You would like to have +`![image](spider.jpg)` in `chap1/text.md` refer to +`chap1/spider.jpg` and `![image](spider.jpg)` in `chap2/text.md` +refer to `chap2/spider.jpg`. To do this, use + + pandoc chap*/*.md -f markdown+rebase_relative_paths + +Without this extension, you would have to use +`![image](chap1/spider.jpg)` in `chap1/text.md` and +`![image](chap2/spider.jpg)` in `chap2/text.md`. Links with +relative paths will be rewritten in the same way as images. +*This option currently only affects Markdown input.* + #### Extension: `attributes` #### Allows attributes to be attached to any inline or block-level diff --git a/pandoc.cabal b/pandoc.cabal index c74df3e69..c8343d16e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -214,6 +214,10 @@ extra-source-files: test/command/C.txt test/command/D.txt test/command/01.csv + test/command/chap1/spider.png + test/command/chap2/spider.png + test/command/chap1/text.md + test/command/chap2/text.md test/command/defaults1.yaml test/command/defaults2.yaml test/command/defaults3.yaml diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 6423d5f56..c4d54c06e 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -136,6 +136,8 @@ data Extension = | Ext_raw_html -- ^ Allow raw HTML | Ext_raw_tex -- ^ Allow raw TeX (other than math) | Ext_raw_markdown -- ^ Parse markdown in ipynb as raw markdown + | Ext_rebase_relative_paths -- ^ Rebase relative image and link paths, + -- relative to directory of containing file | Ext_shortcut_reference_links -- ^ Shortcut reference links | Ext_simple_tables -- ^ Pandoc-style simple tables | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes @@ -462,6 +464,7 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_gutenberg , Ext_smart , Ext_literate_haskell + , Ext_rebase_relative_paths ] getAll "markdown_strict" = allMarkdownExtensions getAll "markdown_phpextra" = allMarkdownExtensions diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 34f16ab4e..968c6c165 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -29,7 +29,7 @@ import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import qualified Data.ByteString.Lazy as BL -import System.FilePath (addExtension, takeExtension) +import System.FilePath (addExtension, takeExtension, isAbsolute, takeDirectory) import Text.HTML.TagSoup hiding (Row) import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B @@ -1836,9 +1836,12 @@ regLink :: PandocMonad m -> MarkdownParser m (F Inlines) regLink constructor lab = try $ do (src, tit) <- source + rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) + pos <- getPosition + let src' = if rebase then rebasePath pos src else src attr <- option nullAttr $ guardEnabled Ext_link_attributes >> attributes - return $ constructor attr src tit <$> lab + return $ constructor attr src' tit <$> lab -- a link like [this][ref] or [this][] or [this] referenceLink :: PandocMonad m @@ -1854,6 +1857,8 @@ referenceLink constructor (lab, raw) = do return (mempty, ""))) <|> try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference) + rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) + pos <- getPosition when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' @@ -1878,7 +1883,9 @@ referenceLink constructor (lab, raw) = do Just ((src, tit), _) -> constructor nullAttr src tit <$> lab Nothing -> makeFallback else makeFallback - Just ((src,tit), attr) -> constructor attr src tit <$> lab + Just ((src,tit), attr) -> + let src' = if rebase then rebasePath pos src else src + in constructor attr src' tit <$> lab dropBrackets :: Text -> Text dropBrackets = dropRB . dropLB @@ -1911,15 +1918,30 @@ autoLink = try $ do return $ return $ B.linkWith attr (src <> escapeURI extra) "" (B.str $ orig <> extra) +-- | Rebase a relative path, by adding the (relative) directory +-- of the containing source position. Absolute links and URLs +-- are untouched. +rebasePath :: SourcePos -> Text -> Text +rebasePath pos path = do + let fp = sourceName pos + in if isAbsolute (T.unpack path) || isURI path + then path + else + case takeDirectory fp of + "" -> path + "." -> path + d -> T.pack d <> "/" <> path + image :: PandocMonad m => MarkdownParser m (F Inlines) image = try $ do char '!' (lab,raw) <- reference defaultExt <- getOption readerDefaultImageExtension - let constructor attr' src = case takeExtension (T.unpack src) of - "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src) - $ T.unpack defaultExt) - _ -> B.imageWith attr' src + let constructor attr' src = + case takeExtension (T.unpack src) of + "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src) + $ T.unpack defaultExt) + _ -> B.imageWith attr' src regLink constructor lab <|> referenceLink constructor (lab,raw) note :: PandocMonad m => MarkdownParser m (F Inlines) diff --git a/test/command/3752.md b/test/command/3752.md new file mode 100644 index 000000000..76d51989b --- /dev/null +++ b/test/command/3752.md @@ -0,0 +1,35 @@ +``` +% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx | pandoc -f docx -t plain +^D +[INFO] Loaded command/chap1/spider.png from ./command/chap1/spider.png +[INFO] Loaded command/chap1/../../lalune.jpg from ./command/chap1/../../lalune.jpg +[INFO] Loaded command/chap2/spider.png from ./command/chap2/spider.png +Chapter one + +A spider: [spider] + +The moon: [moon] + +Link to spider picture. + +URL left alone: manual. + +Absolute path left alone: absolute. + +Chapter two + +A spider: [spider] +``` + +``` +% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths -t html +^D +<h1 id="chapter-one">Chapter one</h1> +<p>A spider: <img src="command/chap1/spider.png" alt="spider" /></p> +<p>The moon: <img src="command/chap1/../../lalune.jpg" alt="moon" /></p> +<p>Link to <a href="command/chap1/spider.png">spider picture</a>.</p> +<p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p> +<p>Absolute path left alone: <a href="/foo/bar/baz.png">absolute</a>.</p> +<h1 id="chapter-two">Chapter two</h1> +<p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p> +``` diff --git a/test/command/chap1/spider.png b/test/command/chap1/spider.png new file mode 100644 index 000000000..7ee9fe339 Binary files /dev/null and b/test/command/chap1/spider.png differ diff --git a/test/command/chap1/text.md b/test/command/chap1/text.md new file mode 100644 index 000000000..88b30313d --- /dev/null +++ b/test/command/chap1/text.md @@ -0,0 +1,11 @@ +# Chapter one + +A spider: ![spider](spider.png) + +The moon: ![moon](../../lalune.jpg) + +Link to [spider picture](spider.png). + +URL left alone: [manual](https://pandoc.org/MANUAL.html). + +Absolute path left alone: [absolute](/foo/bar/baz.png). diff --git a/test/command/chap2/spider.png b/test/command/chap2/spider.png new file mode 100644 index 000000000..5377e940b Binary files /dev/null and b/test/command/chap2/spider.png differ diff --git a/test/command/chap2/text.md b/test/command/chap2/text.md new file mode 100644 index 000000000..435a266d7 --- /dev/null +++ b/test/command/chap2/text.md @@ -0,0 +1,3 @@ +# Chapter two + +A spider: ![spider](spider.png) -- cgit v1.2.3 From cbe16b2866abd8d0e4c15f027562b4b2bed3f01e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 27 May 2021 10:49:45 -0700 Subject: Citeproc: Don't detect math elements as locators. Closes #7321. --- MANUAL.txt | 2 +- src/Text/Pandoc/Citeproc/Locator.hs | 7 +++++++ test/command/7321.md | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/command/7321.md diff --git a/MANUAL.txt b/MANUAL.txt index 48bf60d6e..2ca50e44a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5015,7 +5015,7 @@ the suffix as locator by prepending curly braces: [@smith{ii, A, D-Z}, with a suffix] [@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here] - [@smith{}, $a^2$ and following] + [@smith{}, 99 years later] A minus sign (`-`) before the `@` will suppress mention of the author in the citation. This can be useful when the diff --git a/src/Text/Pandoc/Citeproc/Locator.hs b/src/Text/Pandoc/Citeproc/Locator.hs index 44416ca12..dbedc08d9 100644 --- a/src/Text/Pandoc/Citeproc/Locator.hs +++ b/src/Text/Pandoc/Citeproc/Locator.hs @@ -181,6 +181,7 @@ pPageUnit = roman <|> plainUnit plainUnit = do ts <- many1 (notFollowedBy pSpace >> notFollowedBy pLocatorPunct >> + notFollowedBy pMath >> anyToken) let s = stringify ts -- otherwise look for actual digits or -s @@ -211,6 +212,12 @@ pMatchChar msg f = satisfyTok f' <?> msg pSpace :: LocatorParser Inline pSpace = satisfyTok (\t -> isSpacey t || t == Str "\160") <?> "space" +pMath :: LocatorParser Inline +pMath = satisfyTok isMath + where + isMath (Math{}) = True + isMath _ = False + satisfyTok :: (Inline -> Bool) -> LocatorParser Inline satisfyTok f = tokenPrim show (\sp _ _ -> sp) (\tok -> if f tok then Just tok diff --git a/test/command/7321.md b/test/command/7321.md new file mode 100644 index 000000000..f5e644bee --- /dev/null +++ b/test/command/7321.md @@ -0,0 +1,24 @@ +``` +% pandoc -t plain --citeproc --wrap=none +--- +references: +- id: fenner2012a + title: One-click science marketing + author: + - family: Fenner + given: Martin + container-title: Nature Materials + volume: 11 + issue: 4 + publisher: Nature Publishing Group + type: article-journal + issued: + year: 2012 +--- + +[@fenner2012a, $a^2+b^2=c^2$] +^D +(Fenner 2012, a² + b² = c²) + +Fenner, Martin. 2012. “One-Click Science Marketing.” Nature Materials 11 (4). +``` -- cgit v1.2.3 From 6972a7dc9158c1d56b2dc9a06f596386f6b30860 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 27 May 2021 11:26:38 -0700 Subject: Modify rebase_reference_links treatment of reference links/images. The directory is based on the file containing the link reference, not the file containing the link, if these differ. --- MANUAL.txt | 6 +++++- src/Text/Pandoc/Readers/Markdown.hs | 9 ++++----- test/command/3752.md | 5 ++++- test/command/chap1/text.md | 2 ++ test/command/chap2/text.md | 2 ++ 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 2ca50e44a..d755c0c00 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5080,7 +5080,11 @@ Without this extension, you would have to use `![image](chap1/spider.jpg)` in `chap1/text.md` and `![image](chap2/spider.jpg)` in `chap2/text.md`. Links with relative paths will be rewritten in the same way as images. -*This option currently only affects Markdown input.* + +Note that relative paths in reference links and images will +be rewritten relative to the file containing the link +reference definition, not the file containing the reference link +or image itself, if these differ. #### Extension: `attributes` #### diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 968c6c165..e5cbadc94 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -336,7 +336,9 @@ referenceKey = try $ do notFollowedBy' (() <$ reference) many1Char $ notFollowedBy space >> litChar let betweenAngles = try $ char '<' >> manyTillChar litChar (char '>') - src <- try betweenAngles <|> sourceURL + rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) + src <- (if rebase then rebasePath pos else id) <$> + (try betweenAngles <|> sourceURL) tit <- option "" referenceTitle attr <- option nullAttr $ try $ do guardEnabled Ext_link_attributes @@ -1857,8 +1859,6 @@ referenceLink constructor (lab, raw) = do return (mempty, ""))) <|> try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference) - rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) - pos <- getPosition when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' @@ -1884,8 +1884,7 @@ referenceLink constructor (lab, raw) = do Nothing -> makeFallback else makeFallback Just ((src,tit), attr) -> - let src' = if rebase then rebasePath pos src else src - in constructor attr src' tit <$> lab + constructor attr src tit <$> lab dropBrackets :: Text -> Text dropBrackets = dropRB . dropLB diff --git a/test/command/3752.md b/test/command/3752.md index 76d51989b..6d31eb450 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -2,12 +2,14 @@ % pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx | pandoc -f docx -t plain ^D [INFO] Loaded command/chap1/spider.png from ./command/chap1/spider.png -[INFO] Loaded command/chap1/../../lalune.jpg from ./command/chap1/../../lalune.jpg [INFO] Loaded command/chap2/spider.png from ./command/chap2/spider.png +[INFO] Loaded command/chap1/../../lalune.jpg from ./command/chap1/../../lalune.jpg Chapter one A spider: [spider] +Another spider: [another spider] + The moon: [moon] Link to spider picture. @@ -26,6 +28,7 @@ A spider: [spider] ^D <h1 id="chapter-one">Chapter one</h1> <p>A spider: <img src="command/chap1/spider.png" alt="spider" /></p> +<p>Another spider: <img src="command/chap2/spider.png" alt="another spider" /></p> <p>The moon: <img src="command/chap1/../../lalune.jpg" alt="moon" /></p> <p>Link to <a href="command/chap1/spider.png">spider picture</a>.</p> <p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p> diff --git a/test/command/chap1/text.md b/test/command/chap1/text.md index 88b30313d..d25514241 100644 --- a/test/command/chap1/text.md +++ b/test/command/chap1/text.md @@ -2,6 +2,8 @@ A spider: ![spider](spider.png) +Another spider: ![another spider][refspider] + The moon: ![moon](../../lalune.jpg) Link to [spider picture](spider.png). diff --git a/test/command/chap2/text.md b/test/command/chap2/text.md index 435a266d7..082a1d79e 100644 --- a/test/command/chap2/text.md +++ b/test/command/chap2/text.md @@ -1,3 +1,5 @@ # Chapter two A spider: ![spider](spider.png) + +[refspider]: spider.png -- cgit v1.2.3 From 0661ce699fe83c64dd5a5874e8ab17241b19550c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 27 May 2021 13:53:26 -0700 Subject: rebase_relative_paths extension: don't change fragment paths. We don't want a pure fragment path to be rewritten, since these are used for cross-referencing. --- MANUAL.txt | 3 +++ src/Text/Pandoc/Readers/Markdown.hs | 3 ++- test/command/3752.md | 3 +++ test/command/chap1/text.md | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index d755c0c00..fb1a76e9a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5081,6 +5081,9 @@ Without this extension, you would have to use `![image](chap2/spider.jpg)` in `chap2/text.md`. Links with relative paths will be rewritten in the same way as images. +Absolute paths and URLs are not changed. Neither are paths +consisting entirely of a fragment, e.g. `[section one](#foo)`. + Note that relative paths in reference links and images will be rewritten relative to the file containing the link reference definition, not the file containing the reference link diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index e5cbadc94..74f2668e4 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1923,7 +1923,8 @@ autoLink = try $ do rebasePath :: SourcePos -> Text -> Text rebasePath pos path = do let fp = sourceName pos - in if isAbsolute (T.unpack path) || isURI path + isFragment = T.take 1 path == "#" + in if isFragment || isAbsolute (T.unpack path) || isURI path then path else case takeDirectory fp of diff --git a/test/command/3752.md b/test/command/3752.md index 6d31eb450..2765d79ac 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -18,6 +18,8 @@ URL left alone: manual. Absolute path left alone: absolute. +Link to fragment: chapter two. + Chapter two A spider: [spider] @@ -33,6 +35,7 @@ A spider: [spider] <p>Link to <a href="command/chap1/spider.png">spider picture</a>.</p> <p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p> <p>Absolute path left alone: <a href="/foo/bar/baz.png">absolute</a>.</p> +<p>Link to fragment: <a href="#chapter-two">chapter two</a>.</p> <h1 id="chapter-two">Chapter two</h1> <p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p> ``` diff --git a/test/command/chap1/text.md b/test/command/chap1/text.md index d25514241..68a317161 100644 --- a/test/command/chap1/text.md +++ b/test/command/chap1/text.md @@ -11,3 +11,5 @@ Link to [spider picture](spider.png). URL left alone: [manual](https://pandoc.org/MANUAL.html). Absolute path left alone: [absolute](/foo/bar/baz.png). + +Link to fragment: [chapter two](#chapter-two). -- cgit v1.2.3 From 4b16d181e7219ed161a0e03c0c5ee9dec4b526b4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 27 May 2021 14:16:37 -0700 Subject: rebase_relative_paths: leave empty paths unchanged. --- MANUAL.txt | 4 ++-- src/Text/Pandoc/Readers/Markdown.hs | 2 +- test/command/3752.md | 3 +++ test/command/chap1/text.md | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index fb1a76e9a..c234debc9 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -5081,8 +5081,8 @@ Without this extension, you would have to use `![image](chap2/spider.jpg)` in `chap2/text.md`. Links with relative paths will be rewritten in the same way as images. -Absolute paths and URLs are not changed. Neither are paths -consisting entirely of a fragment, e.g. `[section one](#foo)`. +Absolute paths and URLs are not changed. Neither are empty +paths or paths consisting entirely of a fragment, e.g., `#foo`. Note that relative paths in reference links and images will be rewritten relative to the file containing the link diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 74f2668e4..bc5e3e30f 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1924,7 +1924,7 @@ rebasePath :: SourcePos -> Text -> Text rebasePath pos path = do let fp = sourceName pos isFragment = T.take 1 path == "#" - in if isFragment || isAbsolute (T.unpack path) || isURI path + in if T.null path || isFragment || isAbsolute (T.unpack path) || isURI path then path else case takeDirectory fp of diff --git a/test/command/3752.md b/test/command/3752.md index 2765d79ac..df8af0ba1 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -20,6 +20,8 @@ Absolute path left alone: absolute. Link to fragment: chapter two. +Empty path: empty. + Chapter two A spider: [spider] @@ -36,6 +38,7 @@ A spider: [spider] <p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p> <p>Absolute path left alone: <a href="/foo/bar/baz.png">absolute</a>.</p> <p>Link to fragment: <a href="#chapter-two">chapter two</a>.</p> +<p>Empty path: <a href="">empty</a>.</p> <h1 id="chapter-two">Chapter two</h1> <p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p> ``` diff --git a/test/command/chap1/text.md b/test/command/chap1/text.md index 68a317161..54f0104de 100644 --- a/test/command/chap1/text.md +++ b/test/command/chap1/text.md @@ -13,3 +13,5 @@ URL left alone: [manual](https://pandoc.org/MANUAL.html). Absolute path left alone: [absolute](/foo/bar/baz.png). Link to fragment: [chapter two](#chapter-two). + +Empty path: [empty](). -- cgit v1.2.3 From 4842c5fb828c3c34d816fa7bccd4656857742a0b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 27 May 2021 18:28:52 -0700 Subject: Two citeproc locator/suffix improvements: - Recognize locators spelled with a capital letter. Closes #7323. - Add a comma and a space in front of the suffix if it doesn't start with space or punctuation. Closes #7324. --- src/Text/Pandoc/Citeproc/Locator.hs | 14 +++++++++++--- test/command/7323.md | 29 +++++++++++++++++++++++++++++ test/command/7324.md | 25 +++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/command/7323.md create mode 100644 test/command/7324.md diff --git a/src/Text/Pandoc/Citeproc/Locator.hs b/src/Text/Pandoc/Citeproc/Locator.hs index dbedc08d9..f8931d7b5 100644 --- a/src/Text/Pandoc/Citeproc/Locator.hs +++ b/src/Text/Pandoc/Citeproc/Locator.hs @@ -20,7 +20,7 @@ parseLocator :: Locale -> [Inline] -> (Maybe (Text, Text), [Inline]) parseLocator locale inp = case parse (pLocatorWords (toLocatorMap locale)) "suffix" $ splitInp inp of Right r -> r - Left _ -> (Nothing, inp) + Left _ -> (Nothing, maybeAddComma inp) splitInp :: [Inline] -> [Inline] splitInp = splitStrWhen (\c -> isSpace c || (isPunctuation c && c /= ':')) @@ -42,9 +42,17 @@ pLocatorWords locMap = do -- i.e. the first one will be " 9" return $ if T.null la && T.null lo - then (Nothing, s) + then (Nothing, maybeAddComma s) else (Just (la, T.strip lo), s) +maybeAddComma :: [Inline] -> [Inline] +maybeAddComma [] = [] +maybeAddComma ils@(Space : _) = ils +maybeAddComma ils@(Str t : _) + | Just (c, _) <- T.uncons t + , isPunctuation c = ils +maybeAddComma ils = Str "," : Space : ils + pLocatorDelimited :: LocatorMap -> LocatorParser (Text, Text) pLocatorDelimited locMap = try $ do _ <- pMatchChar "{" (== '{') @@ -97,7 +105,7 @@ pLocatorLabel' locMap lim = go "" t <- anyToken ts <- manyTill anyToken (try $ lookAhead lim) let s = acc <> stringify (t:ts) - case M.lookup (T.strip s) locMap of + case M.lookup (T.toCaseFold $ T.strip s) locMap of -- try to find a longer one, or return this one Just l -> go s <|> return (l, False) Nothing -> go s diff --git a/test/command/7323.md b/test/command/7323.md new file mode 100644 index 000000000..7de29cfe2 --- /dev/null +++ b/test/command/7323.md @@ -0,0 +1,29 @@ +``` +% pandoc --citeproc -t plain +--- +references: +- id: smith + author: John Smith + issued: 2019 + title: Insects + type: book +... + +@smith [chap. 6] + +@smith [chapter 6] + +@smith [Chap. 6] + +@smith [Chapter 6] +^D +John Smith (2019, chap. 6) + +John Smith (2019, chap. 6) + +John Smith (2019, chap. 6) + +John Smith (2019, chap. 6) + +John Smith. 2019. Insects. +``` diff --git a/test/command/7324.md b/test/command/7324.md new file mode 100644 index 000000000..fae1b9923 --- /dev/null +++ b/test/command/7324.md @@ -0,0 +1,25 @@ +``` +% pandoc --citeproc -t plain +--- +references: +- id: smith + author: John Smith + issued: 2019 + title: Insects + type: book +... + +@smith [, among others] + +@smith [ among others] + +@smith [among others] +^D +John Smith (2019, among others) + +John Smith (2019 among others) + +John Smith (2019, among others) + +John Smith. 2019. Insects. +``` -- cgit v1.2.3 From 44484d0dee1bd095240b9faf26f8d1dad8e560ea Mon Sep 17 00:00:00 2001 From: Emily Bourke <undergroundquizscene@gmail.com> Date: Sun, 11 Apr 2021 21:42:53 +0100 Subject: Docx reader: Read table column widths. --- src/Text/Pandoc/Readers/Docx.hs | 5 +- src/Text/Pandoc/Readers/Docx/Parse.hs | 2 +- test/Tests/Writers/Docx.hs | 5 ++ test/docx/0_level_headers.native | 4 +- test/docx/golden/table_one_row.docx | Bin 9840 -> 9840 bytes test/docx/golden/table_with_list_cell.docx | Bin 10159 -> 10162 bytes test/docx/golden/tables-default-widths.docx | Bin 0 -> 10200 bytes test/docx/golden/tables.docx | Bin 10200 -> 10202 bytes test/docx/sdt_elements.native | 6 +- test/docx/table_one_row.native | 8 +-- test/docx/table_variable_width.native | 12 ++-- test/docx/table_with_list_cell.native | 6 +- test/docx/tables-default-widths.native | 92 ++++++++++++++++++++++++++++ test/docx/tables.native | 18 +++--- 14 files changed, 128 insertions(+), 30 deletions(-) create mode 100644 test/docx/golden/tables-default-widths.docx create mode 100644 test/docx/tables-default-widths.native diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 22dd54193..375bb7338 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -639,7 +639,7 @@ bodyPartToBlocks (ListItem pPr _ _ _ parparts) = bodyPartToBlocks $ Paragraph pPr' parparts bodyPartToBlocks (Tbl _ _ _ []) = return $ para mempty -bodyPartToBlocks (Tbl cap _ look parts@(r:rs)) = do +bodyPartToBlocks (Tbl cap grid look parts@(r:rs)) = do let cap' = simpleCaption $ plain $ text cap (hdr, rows) = case firstRowFormatting look of True | null rs -> (Nothing, [r]) @@ -669,7 +669,8 @@ bodyPartToBlocks (Tbl cap _ look parts@(r:rs)) = do -- so should be possible. Alignment might be more difficult, -- since there doesn't seem to be a column entity in docx. let alignments = replicate width AlignDefault - widths = replicate width ColWidthDefault + totalWidth = sum grid + widths = (\w -> ColWidth (fromInteger w / fromInteger totalWidth)) <$> grid return $ table cap' (zip alignments widths) diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 7325ff300..978d6ff3a 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -563,7 +563,7 @@ elemToTblGrid :: NameSpaces -> Element -> D TblGrid elemToTblGrid ns element | isElem ns "w" "tblGrid" element = let cols = findChildrenByName ns "w" "gridCol" element in - mapD (\e -> maybeToD (findAttrByName ns "w" "val" e >>= stringToInteger)) + mapD (\e -> maybeToD (findAttrByName ns "w" "w" e >>= stringToInteger)) cols elemToTblGrid _ _ = throwError WrongElem diff --git a/test/Tests/Writers/Docx.hs b/test/Tests/Writers/Docx.hs index 2e0f1e3fb..da25b95e0 100644 --- a/test/Tests/Writers/Docx.hs +++ b/test/Tests/Writers/Docx.hs @@ -111,6 +111,11 @@ tests = [ testGroup "inlines" def "docx/tables.native" "docx/golden/tables.docx" + , docxTest + "tables without explicit column widths" + def + "docx/tables-default-widths.native" + "docx/golden/tables-default-widths.docx" , docxTest "tables with lists in cells" def diff --git a/test/docx/0_level_headers.native b/test/docx/0_level_headers.native index 7f875891e..ed589b029 100644 --- a/test/docx/0_level_headers.native +++ b/test/docx/0_level_headers.native @@ -1,6 +1,6 @@ [Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 1.0)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) @@ -49,4 +49,4 @@ ,Para [Strong [Str "Table",Space,Str "Page"]] ,Para [Strong [Str "No",Space,Str "table",Space,Str "of",Space,Str "figures",Space,Str "entries",Space,Str "found."]] ,Header 1 ("introduction",[],[]) [Str "Introduction"] -,Para [Str "Nothing",Space,Str "to",Space,Str "introduce,",Space,Str "yet."]] \ No newline at end of file +,Para [Str "Nothing",Space,Str "to",Space,Str "introduce,",Space,Str "yet."]] diff --git a/test/docx/golden/table_one_row.docx b/test/docx/golden/table_one_row.docx index f75e567ab..a7a8f2519 100644 Binary files a/test/docx/golden/table_one_row.docx and b/test/docx/golden/table_one_row.docx differ diff --git a/test/docx/golden/table_with_list_cell.docx b/test/docx/golden/table_with_list_cell.docx index a49f70643..1362d4609 100644 Binary files a/test/docx/golden/table_with_list_cell.docx and b/test/docx/golden/table_with_list_cell.docx differ diff --git a/test/docx/golden/tables-default-widths.docx b/test/docx/golden/tables-default-widths.docx new file mode 100644 index 000000000..f24e27516 Binary files /dev/null and b/test/docx/golden/tables-default-widths.docx differ diff --git a/test/docx/golden/tables.docx b/test/docx/golden/tables.docx index f24e27516..9dcbbc9d0 100644 Binary files a/test/docx/golden/tables.docx and b/test/docx/golden/tables.docx differ diff --git a/test/docx/sdt_elements.native b/test/docx/sdt_elements.native index dca82f0a0..a072c0d39 100644 --- a/test/docx/sdt_elements.native +++ b/test/docx/sdt_elements.native @@ -1,8 +1,8 @@ [Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.22069570301081556) + ,(AlignDefault,ColWidth 0.22069570301081556) + ,(AlignDefault,ColWidth 0.5586085939783689)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) diff --git a/test/docx/table_one_row.native b/test/docx/table_one_row.native index e9188b145..88d5e3af5 100644 --- a/test/docx/table_one_row.native +++ b/test/docx/table_one_row.native @@ -1,8 +1,8 @@ [Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.3333333333333333) + ,(AlignDefault,ColWidth 0.3333333333333333) + ,(AlignDefault,ColWidth 0.3333333333333333)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) @@ -15,4 +15,4 @@ ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Table"]]]])] (TableFoot ("",[],[]) - [])] \ No newline at end of file + [])] diff --git a/test/docx/table_variable_width.native b/test/docx/table_variable_width.native index 229cb83b1..43ac40cca 100644 --- a/test/docx/table_variable_width.native +++ b/test/docx/table_variable_width.native @@ -1,10 +1,10 @@ [Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 2.0096205237840725e-2) + ,(AlignDefault,ColWidth 1.9882415820416888e-2) + ,(AlignDefault,ColWidth 0.22202030999465527) + ,(AlignDefault,ColWidth 0.4761090326028862) + ,(AlignDefault,ColWidth 1.0689470871191876e-4)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) @@ -42,4 +42,4 @@ ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []]])] (TableFoot ("",[],[]) - [])] \ No newline at end of file + [])] diff --git a/test/docx/table_with_list_cell.native b/test/docx/table_with_list_cell.native index 06d8606da..51a35184b 100644 --- a/test/docx/table_with_list_cell.native +++ b/test/docx/table_with_list_cell.native @@ -1,7 +1,7 @@ [Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.5) + ,(AlignDefault,ColWidth 0.5)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) @@ -22,4 +22,4 @@ ,[Para [Str "A"]] ,[Para [Str "Numbered",Space,Str "list."]]]]]])] (TableFoot ("",[],[]) - [])] \ No newline at end of file + [])] diff --git a/test/docx/tables-default-widths.native b/test/docx/tables-default-widths.native new file mode 100644 index 000000000..e541e5a6e --- /dev/null +++ b/test/docx/tables-default-widths.native @@ -0,0 +1,92 @@ +[Header 2 ("a-table-with-and-without-a-header-row",[],[]) [Str "A",Space,Str "table,",Space,Str "with",Space,Str "and",Space,Str "without",Space,Str "a",Space,Str "header",Space,Str "row"] +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Name"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Game"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Fame"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Blame"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Lebron",Space,Str "James"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Basketball"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Very",Space,Str "High"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Leaving",Space,Str "Cleveland"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Ryan",Space,Str "Braun"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Baseball"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Moderate"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Steroids"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Russell",Space,Str "Wilson"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Football"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "High"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Tacky",Space,Str "uniform"]]]])] + (TableFoot ("",[],[]) + []) +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + []) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Sinple"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Table"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Without"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Header"]]]])] + (TableFoot ("",[],[]) + []) +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidthDefault) + ,(AlignDefault,ColWidthDefault)] + (TableHead ("",[],[]) + []) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Para [Str "Simple"] + ,Para [Str "Multiparagraph"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Para [Str "Table"] + ,Para [Str "Full"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Para [Str "Of"] + ,Para [Str "Paragraphs"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Para [Str "In",Space,Str "each"] + ,Para [Str "Cell."]]]])] + (TableFoot ("",[],[]) + [])] \ No newline at end of file diff --git a/test/docx/tables.native b/test/docx/tables.native index e541e5a6e..5a89496be 100644 --- a/test/docx/tables.native +++ b/test/docx/tables.native @@ -1,10 +1,10 @@ [Header 2 ("a-table-with-and-without-a-header-row",[],[]) [Str "A",Space,Str "table,",Space,Str "with",Space,Str "and",Space,Str "without",Space,Str "a",Space,Str "header",Space,Str "row"] ,Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.25) + ,(AlignDefault,ColWidth 0.25) + ,(AlignDefault,ColWidth 0.25) + ,(AlignDefault,ColWidth 0.25)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) @@ -48,8 +48,8 @@ []) ,Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.5) + ,(AlignDefault,ColWidth 0.5)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) @@ -68,8 +68,8 @@ []) ,Table ("",[],[]) (Caption Nothing []) - [(AlignDefault,ColWidthDefault) - ,(AlignDefault,ColWidthDefault)] + [(AlignDefault,ColWidth 0.5) + ,(AlignDefault,ColWidth 0.5)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) @@ -89,4 +89,4 @@ [Para [Str "In",Space,Str "each"] ,Para [Str "Cell."]]]])] (TableFoot ("",[],[]) - [])] \ No newline at end of file + [])] -- cgit v1.2.3 From 56b211120c62a01f8aba1c4512acfe4677d8c7d0 Mon Sep 17 00:00:00 2001 From: Emily Bourke <undergroundquizscene@protonmail.com> Date: Thu, 18 Jun 2020 09:53:32 +0100 Subject: Docx reader: Support new table features. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Column spans * Row spans - The spec says that if the `val` attribute is ommitted, its value should be assumed to be `continue`, and that its values are restricted to {`restart`, `continue`}. If the value has any other value, I think it seems reasonable to default it to `continue`. It might cause problems if the spec is extended in the future by adding a third possible value, in which case this would probably give incorrect behaviour, and wouldn't error. * Allow multiple header rows * Include table description in simple caption - The table description element is like alt text for a table (along with the table caption element). It seems like we should include this somewhere, but I’m not 100% sure how – I’m pairing it with the simple caption for the moment. (Should it maybe go in the block caption instead?) * Detect table captions - Check for caption paragraph style /and/ either the simple or complex table field. This means the caption detection fails for captions which don’t contain a field, as in an example doc I added as a test. However, I think it’s better to be too conservative: a missed table caption will still show up as a paragraph next to the table, whereas if I incorrectly classify something else as a table caption it could cause havoc by pairing it up with a table it’s not at all related to, or dropping it entirely. * Update tests and add new ones Partially fixes: #6316 --- src/Text/Pandoc/Readers/Docx.hs | 92 ++++++++------ src/Text/Pandoc/Readers/Docx/Parse.hs | 113 +++++++++++++++-- src/Text/Pandoc/Readers/Docx/Util.hs | 7 ++ test/Tests/Readers/Docx.hs | 16 +++ test/docx/sdt_elements.native | 13 +- test/docx/table_captions_no_field.docx | Bin 0 -> 40482 bytes test/docx/table_captions_no_field.native | 34 ++++++ test/docx/table_captions_with_field.docx | Bin 0 -> 41091 bytes test/docx/table_captions_with_field.native | 54 +++++++++ test/docx/table_header_rowspan.docx | Bin 0 -> 15826 bytes test/docx/table_header_rowspan.native | 189 +++++++++++++++++++++++++++++ test/docx/table_one_header_row.docx | Bin 0 -> 12185 bytes test/docx/table_one_header_row.native | 18 +++ test/docx/table_one_row.docx | Bin 25251 -> 12148 bytes test/docx/table_variable_width.native | 19 ++- 15 files changed, 487 insertions(+), 68 deletions(-) create mode 100644 test/docx/table_captions_no_field.docx create mode 100644 test/docx/table_captions_no_field.native create mode 100644 test/docx/table_captions_with_field.docx create mode 100644 test/docx/table_captions_with_field.native create mode 100644 test/docx/table_header_rowspan.docx create mode 100644 test/docx/table_header_rowspan.native create mode 100644 test/docx/table_one_header_row.docx create mode 100644 test/docx/table_one_header_row.native diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 375bb7338..c06adf7e3 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -61,13 +61,14 @@ module Text.Pandoc.Readers.Docx import Codec.Archive.Zip import Control.Monad.Reader import Control.Monad.State.Strict +import Data.Bifunctor (bimap, first) import qualified Data.ByteString.Lazy as B import Data.Default (Default) -import Data.List (delete, intersect) +import Data.List (delete, intersect, foldl') import Data.Char (isSpace) import qualified Data.Map as M import qualified Data.Text as T -import Data.Maybe (isJust, fromMaybe) +import Data.Maybe (catMaybes, isJust, fromMaybe) import Data.Sequence (ViewL (..), viewl) import qualified Data.Sequence as Seq import qualified Data.Set as Set @@ -113,6 +114,7 @@ data DState = DState { docxAnchorMap :: M.Map T.Text T.Text -- restarting , docxListState :: M.Map (T.Text, T.Text) Integer , docxPrevPara :: Inlines + , docxTableCaptions :: [Blocks] } instance Default DState where @@ -123,6 +125,7 @@ instance Default DState where , docxDropCap = mempty , docxListState = M.empty , docxPrevPara = mempty + , docxTableCaptions = [] } data DEnv = DEnv { docxOptions :: ReaderOptions @@ -491,15 +494,32 @@ singleParaToPlain blks singleton $ Plain ils singleParaToPlain blks = blks -cellToBlocks :: PandocMonad m => Docx.Cell -> DocxContext m Blocks -cellToBlocks (Docx.Cell bps) = do +cellToCell :: PandocMonad m => RowSpan -> Docx.Cell -> DocxContext m Pandoc.Cell +cellToCell rowSpan (Docx.Cell gridSpan _ bps) = do blks <- smushBlocks <$> mapM bodyPartToBlocks bps - return $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks + let blks' = singleParaToPlain $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks + return (cell AlignDefault rowSpan (ColSpan (fromIntegral gridSpan)) blks') + +rowsToRows :: PandocMonad m => [Docx.Row] -> DocxContext m [Pandoc.Row] +rowsToRows rows = do + let rowspans = (fmap . fmap) (first RowSpan) (Docx.rowsToRowspans rows) + cells <- traverse (traverse (uncurry cellToCell)) rowspans + return (fmap (Pandoc.Row nullAttr) cells) + +splitHeaderRows :: Bool -> [Docx.Row] -> ([Docx.Row], [Docx.Row]) +splitHeaderRows hasFirstRowFormatting rs = bimap reverse reverse $ fst + $ if hasFirstRowFormatting + then foldl' f ((take 1 rs, []), True) (drop 1 rs) + else foldl' f (([], []), False) rs + where + f ((headerRows, bodyRows), previousRowWasHeader) r@(Docx.Row h cs) + | h == HasTblHeader || (previousRowWasHeader && any isContinuationCell cs) + = ((r : headerRows, bodyRows), True) + | otherwise + = ((headerRows, r : bodyRows), False) + + isContinuationCell (Docx.Cell _ vm _) = vm == Docx.Continue -rowToBlocksList :: PandocMonad m => Docx.Row -> DocxContext m [Blocks] -rowToBlocksList (Docx.Row cells) = do - blksList <- mapM cellToBlocks cells - return $ map singleParaToPlain blksList -- like trimInlines, but also take out linebreaks trimSps :: Inlines -> Inlines @@ -546,6 +566,11 @@ normalizeToClassName = T.map go . fromStyleName where go c | isSpace c = '-' | otherwise = c +bodyPartToTableCaption :: PandocMonad m => BodyPart -> DocxContext m (Maybe Blocks) +bodyPartToTableCaption (TblCaption pPr parparts) = + Just <$> bodyPartToBlocks (Paragraph pPr parparts) +bodyPartToTableCaption _ = pure Nothing + bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks bodyPartToBlocks (Paragraph pPr parparts) | Just True <- pBidi pPr = do @@ -637,50 +662,43 @@ bodyPartToBlocks (ListItem pPr _ _ _ parparts) = let pPr' = pPr {pStyle = constructBogusParStyleData "list-paragraph": pStyle pPr} in bodyPartToBlocks $ Paragraph pPr' parparts +bodyPartToBlocks (TblCaption _ _) = + return $ para mempty -- collected separately bodyPartToBlocks (Tbl _ _ _ []) = return $ para mempty -bodyPartToBlocks (Tbl cap grid look parts@(r:rs)) = do - let cap' = simpleCaption $ plain $ text cap - (hdr, rows) = case firstRowFormatting look of - True | null rs -> (Nothing, [r]) - | otherwise -> (Just r, rs) - False -> (Nothing, r:rs) - - cells <- mapM rowToBlocksList rows +bodyPartToBlocks (Tbl cap grid look parts) = do + captions <- gets docxTableCaptions + fullCaption <- case captions of + c : cs -> do + modify (\s -> s { docxTableCaptions = cs }) + return c + [] -> return $ if T.null cap then mempty else plain (text cap) + let shortCaption = if T.null cap then Nothing else Just (toList (text cap)) + cap' = caption shortCaption fullCaption + (hdr, rows) = splitHeaderRows (firstRowFormatting look) parts let width = maybe 0 maximum $ nonEmpty $ map rowLength parts rowLength :: Docx.Row -> Int - rowLength (Docx.Row c) = length c + rowLength (Docx.Row _ c) = sum (fmap (\(Docx.Cell gridSpan _ _) -> fromIntegral gridSpan) c) - let toRow = Pandoc.Row nullAttr . map simpleCell - toHeaderRow l = [toRow l | not (null l)] + headerCells <- rowsToRows hdr + bodyCells <- rowsToRows rows - -- pad cells. New Text.Pandoc.Builder will do that for us, - -- so this is for compatibility while we switch over. - let cells' = map (\row -> toRow $ take width (row ++ repeat mempty)) cells - - hdrCells <- case hdr of - Just r' -> toHeaderRow <$> rowToBlocksList r' - Nothing -> return [] - - -- The two following variables (horizontal column alignment and - -- relative column widths) go to the default at the - -- moment. Width information is in the TblGrid field of the Tbl, - -- so should be possible. Alignment might be more difficult, - -- since there doesn't seem to be a column entity in docx. + -- Horizontal column alignment goes to the default at the moment. Getting + -- it might be difficult, since there doesn't seem to be a column entity + -- in docx. let alignments = replicate width AlignDefault totalWidth = sum grid widths = (\w -> ColWidth (fromInteger w / fromInteger totalWidth)) <$> grid return $ table cap' (zip alignments widths) - (TableHead nullAttr hdrCells) - [TableBody nullAttr 0 [] cells'] + (TableHead nullAttr headerCells) + [TableBody nullAttr 0 [] bodyCells] (TableFoot nullAttr []) bodyPartToBlocks (OMathPara e) = return $ para $ displayMath (writeTeX e) - -- replace targets with generated anchors. rewriteLink' :: PandocMonad m => Inline -> DocxContext m Inline rewriteLink' l@(Link attr ils (T.uncons -> Just ('#',target), title)) = do @@ -716,6 +734,8 @@ bodyToOutput :: PandocMonad m => Body -> DocxContext m (Meta, [Block]) bodyToOutput (Body bps) = do let (metabps, blkbps) = sepBodyParts bps meta <- bodyPartsToMeta metabps + captions <- catMaybes <$> mapM bodyPartToTableCaption blkbps + modify (\s -> s { docxTableCaptions = captions }) blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks blks'' <- removeOrphanAnchors blks' diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 978d6ff3a..aaa8f4ad0 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -33,7 +33,9 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..) , ParStyle , CharStyle(cStyleData) , Row(..) + , TblHeader(..) , Cell(..) + , VMerge(..) , TrackedChange(..) , ChangeType(..) , ChangeInfo(..) @@ -50,6 +52,7 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..) , pHeading , constructBogusParStyleData , leftBiasedMergeRunStyle + , rowsToRowspans ) where import Text.Pandoc.Readers.Docx.Parse.Styles import Codec.Archive.Zip @@ -225,6 +228,7 @@ defaultParagraphStyle = ParagraphStyle { pStyle = [] data BodyPart = Paragraph ParagraphStyle [ParPart] | ListItem ParagraphStyle T.Text T.Text (Maybe Level) [ParPart] | Tbl T.Text TblGrid TblLook [Row] + | TblCaption ParagraphStyle [ParPart] | OMathPara [Exp] deriving Show @@ -236,12 +240,61 @@ newtype TblLook = TblLook {firstRowFormatting::Bool} defaultTblLook :: TblLook defaultTblLook = TblLook{firstRowFormatting = False} -newtype Row = Row [Cell] - deriving Show +data Row = Row TblHeader [Cell] deriving Show + +data TblHeader = HasTblHeader | NoTblHeader deriving (Show, Eq) -newtype Cell = Cell [BodyPart] +data Cell = Cell GridSpan VMerge [BodyPart] deriving Show +type GridSpan = Integer + +data VMerge = Continue + -- ^ This cell should be merged with the one above it + | Restart + -- ^ This cell should not be merged with the one above it + deriving (Show, Eq) + +rowsToRowspans :: [Row] -> [[(Int, Cell)]] +rowsToRowspans rows = let + removeMergedCells = fmap (filter (\(_, Cell _ vmerge _) -> vmerge == Restart)) + in removeMergedCells (foldr f [] rows) + where + f :: Row -> [[(Int, Cell)]] -> [[(Int, Cell)]] + f (Row _ cells) acc = let + spans = g cells Nothing (listToMaybe acc) + in spans : acc + + g :: + -- | The current row + [Cell] -> + -- | Number of columns left below + Maybe Integer -> + -- | (rowspan so far, cell) for the row below this one + Maybe [(Int, Cell)] -> + -- | (rowspan so far, cell) for this row + [(Int, Cell)] + g cells _ Nothing = zip (repeat 1) cells + g cells columnsLeftBelow (Just rowBelow) = + case cells of + [] -> [] + thisCell@(Cell thisGridSpan _ _) : restOfRow -> case rowBelow of + [] -> zip (repeat 1) cells + (spanSoFarBelow, Cell gridSpanBelow vmerge _) : _ -> + let spanSoFar = case vmerge of + Restart -> 1 + Continue -> 1 + spanSoFarBelow + columnsToDrop = thisGridSpan + (gridSpanBelow - fromMaybe gridSpanBelow columnsLeftBelow) + (newColumnsLeftBelow, restOfRowBelow) = dropColumns columnsToDrop rowBelow + in (spanSoFar, thisCell) : g restOfRow (Just newColumnsLeftBelow) (Just restOfRowBelow) + + dropColumns :: Integer -> [(a, Cell)] -> (Integer, [(a, Cell)]) + dropColumns n [] = (n, []) + dropColumns n cells@((_, Cell gridSpan _ _) : otherCells) = + if n < gridSpan + then (gridSpan - n, cells) + else dropColumns (n - gridSpan) otherCells + leftBiasedMergeRunStyle :: RunStyle -> RunStyle -> RunStyle leftBiasedMergeRunStyle a b = RunStyle { isBold = isBold a <|> isBold b @@ -587,14 +640,31 @@ elemToRow ns element | isElem ns "w" "tr" element = do let cellElems = findChildrenByName ns "w" "tc" element cells <- mapD (elemToCell ns) cellElems - return $ Row cells + let hasTblHeader = maybe NoTblHeader (const HasTblHeader) + (findChildByName ns "w" "trPr" element + >>= findChildByName ns "w" "tblHeader") + return $ Row hasTblHeader cells elemToRow _ _ = throwError WrongElem elemToCell :: NameSpaces -> Element -> D Cell elemToCell ns element | isElem ns "w" "tc" element = do + let properties = findChildByName ns "w" "tcPr" element + let gridSpan = properties + >>= findChildByName ns "w" "gridSpan" + >>= findAttrByName ns "w" "val" + >>= stringToInteger + let vMerge = case properties >>= findChildByName ns "w" "vMerge" of + Nothing -> Restart + Just e -> + fromMaybe Continue $ do + s <- findAttrByName ns "w" "val" e + case s of + "continue" -> Just Continue + "restart" -> Just Restart + _ -> Nothing cellContents <- mapD (elemToBodyPart ns) (elChildren element) - return $ Cell cellContents + return $ Cell (fromMaybe 1 gridSpan) vMerge cellContents elemToCell _ _ = throwError WrongElem elemToParIndentation :: NameSpaces -> Element -> Maybe ParIndentation @@ -626,10 +696,9 @@ pNumInfo = getParStyleField numInfo . pStyle elemToBodyPart :: NameSpaces -> Element -> D BodyPart elemToBodyPart ns element | isElem ns "w" "p" element - , (c:_) <- findChildrenByName ns "m" "oMathPara" element = - do - expsLst <- eitherToD $ readOMML $ showElement c - return $ OMathPara expsLst + , (c:_) <- findChildrenByName ns "m" "oMathPara" element = do + expsLst <- eitherToD $ readOMML $ showElement c + return $ OMathPara expsLst elemToBodyPart ns element | isElem ns "w" "p" element , Just (numId, lvl) <- getNumInfo ns element = do @@ -647,13 +716,31 @@ elemToBodyPart ns element Nothing | Just (numId, lvl) <- pNumInfo parstyle -> do levelInfo <- lookupLevel numId lvl <$> asks envNumbering return $ ListItem parstyle numId lvl levelInfo parparts - _ -> return $ Paragraph parstyle parparts + _ -> let + hasCaptionStyle = elem "Caption" (pStyleId <$> pStyle parstyle) + + hasSimpleTableField = fromMaybe False $ do + fldSimple <- findChildByName ns "w" "fldSimple" element + instr <- findAttrByName ns "w" "instr" fldSimple + pure ("Table" `elem` T.words instr) + + hasComplexTableField = fromMaybe False $ do + instrText <- findElementByName ns "w" "instrText" element + pure ("Table" `elem` T.words (strContent instrText)) + + in if hasCaptionStyle && (hasSimpleTableField || hasComplexTableField) + then return $ TblCaption parstyle parparts + else return $ Paragraph parstyle parparts + elemToBodyPart ns element | isElem ns "w" "tbl" element = do - let caption' = findChildByName ns "w" "tblPr" element + let tblProperties = findChildByName ns "w" "tblPr" element + caption = fromMaybe "" $ tblProperties >>= findChildByName ns "w" "tblCaption" >>= findAttrByName ns "w" "val" - caption = fromMaybe "" caption' + description = fromMaybe "" $ tblProperties + >>= findChildByName ns "w" "tblDescription" + >>= findAttrByName ns "w" "val" grid' = case findChildByName ns "w" "tblGrid" element of Just g -> elemToTblGrid ns g Nothing -> return [] @@ -666,7 +753,7 @@ elemToBodyPart ns element grid <- grid' tblLook <- tblLook' rows <- mapD (elemToRow ns) (elChildren element) - return $ Tbl caption grid tblLook rows + return $ Tbl (caption <> description) grid tblLook rows elemToBodyPart _ _ = throwError WrongElem lookupRelationship :: DocumentLocation -> RelId -> [Relationship] -> Maybe Target diff --git a/src/Text/Pandoc/Readers/Docx/Util.hs b/src/Text/Pandoc/Readers/Docx/Util.hs index ac331cba6..970697a2d 100644 --- a/src/Text/Pandoc/Readers/Docx/Util.hs +++ b/src/Text/Pandoc/Readers/Docx/Util.hs @@ -19,6 +19,7 @@ module Text.Pandoc.Readers.Docx.Util ( , elemToNameSpaces , findChildByName , findChildrenByName + , findElementByName , findAttrByName ) where @@ -56,6 +57,12 @@ findChildrenByName ns pref name el = let ns' = ns <> elemToNameSpaces el in findChildren (elemName ns' pref name) el +-- | Like 'findChildrenByName', but searches descendants. +findElementByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element +findElementByName ns pref name el = + let ns' = ns <> elemToNameSpaces el + in findElement (elemName ns' pref name) el + findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text findAttrByName ns pref name el = let ns' = ns <> elemToNameSpaces el diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 939ff9939..220c7d9c5 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -317,14 +317,30 @@ tests = [ testGroup "document" "tables with lists in cells" "docx/table_with_list_cell.docx" "docx/table_with_list_cell.native" + , testCompare + "a table with a header which contains rowspans greater than 1" + "docx/table_header_rowspan.docx" + "docx/table_header_rowspan.native" , testCompare "tables with one row" "docx/table_one_row.docx" "docx/table_one_row.native" + , testCompare + "tables with just one row, which is a header" + "docx/table_one_header_row.docx" + "docx/table_one_header_row.native" , testCompare "tables with variable width" "docx/table_variable_width.docx" "docx/table_variable_width.native" + , testCompare + "tables with captions which contain a Table field" + "docx/table_captions_with_field.docx" + "docx/table_captions_with_field.native" + , testCompare + "tables with captions which don't contain a Table field" + "docx/table_captions_no_field.docx" + "docx/table_captions_no_field.native" , testCompare "code block" "docx/codeblock.docx" diff --git a/test/docx/sdt_elements.native b/test/docx/sdt_elements.native index a072c0d39..d2aa00994 100644 --- a/test/docx/sdt_elements.native +++ b/test/docx/sdt_elements.native @@ -4,17 +4,16 @@ ,(AlignDefault,ColWidth 0.22069570301081556) ,(AlignDefault,ColWidth 0.5586085939783689)] (TableHead ("",[],[]) - []) - [(TableBody ("",[],[]) (RowHeadColumns 0) - [] - [Row ("",[],[]) + [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Strong [Str "col1Header"]]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Strong [Str "col2Header"]]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [Plain [Strong [Str "col3Header"]]]] - ,Row ("",[],[]) + [Plain [Strong [Str "col3Header"]]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "col1",Space,Str "content"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) @@ -22,4 +21,4 @@ ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "col3",Space,Str "content"]]]])] (TableFoot ("",[],[]) - [])] \ No newline at end of file + [])] diff --git a/test/docx/table_captions_no_field.docx b/test/docx/table_captions_no_field.docx new file mode 100644 index 000000000..1687d32a2 Binary files /dev/null and b/test/docx/table_captions_no_field.docx differ diff --git a/test/docx/table_captions_no_field.native b/test/docx/table_captions_no_field.native new file mode 100644 index 000000000..b8f54d541 --- /dev/null +++ b/test/docx/table_captions_no_field.native @@ -0,0 +1,34 @@ +[Para [Str "See",Space,Str "Table",Space,Str "5.1."] +,Para [Str "Table",Space,Str "5.1"] +,Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidth 0.7605739372523825) + ,(AlignDefault,ColWidth 0.11971303137380876) + ,(AlignDefault,ColWidth 0.11971303137380876)] + (TableHead ("",[],[]) +[Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Count"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "%"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "First",Space,Str "option"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "242"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "45"]]] +,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Second",Space,Str "option"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "99"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "18"]]]])] + (TableFoot ("",[],[]) + []) +,Header 2 ("section", [], []) []] diff --git a/test/docx/table_captions_with_field.docx b/test/docx/table_captions_with_field.docx new file mode 100644 index 000000000..db6de3088 Binary files /dev/null and b/test/docx/table_captions_with_field.docx differ diff --git a/test/docx/table_captions_with_field.native b/test/docx/table_captions_with_field.native new file mode 100644 index 000000000..deb8afc6b --- /dev/null +++ b/test/docx/table_captions_with_field.native @@ -0,0 +1,54 @@ +[Para [Str "See",Space,Str "Table",Space,Str "1."] +,Para [] +,Table ("",[],[]) (Caption Nothing + [Para [Str "Table",Space,Str "1"]]) + [(AlignDefault,ColWidth 0.7605739372523825) + ,(AlignDefault,ColWidth 0.11971303137380876) + ,(AlignDefault,ColWidth 0.11971303137380876)] + (TableHead ("",[],[]) +[Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Count"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "%"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "First",Space,Str "option"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "242"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "45"]]] +,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Second",Space,Str "option"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "99"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "18"]]]])] + (TableFoot ("",[],[]) + []) +,Header 2 ("section", [], []) [] +,Table ("",[],[]) (Caption Nothing + [Para [Str "Table",Space,Str "2"]]) + [(AlignDefault,ColWidth 0.3332963620230701) + ,(AlignDefault,ColWidth 0.3332963620230701) + ,(AlignDefault,ColWidth 0.3334072759538598)] + (TableHead ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "One"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Two"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Three"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [])] + (TableFoot ("",[],[]) + []) +,Para [] +,Para [Str "See",Space,Str "Table",Space,Str "2."]] diff --git a/test/docx/table_header_rowspan.docx b/test/docx/table_header_rowspan.docx new file mode 100644 index 000000000..1cc32a105 Binary files /dev/null and b/test/docx/table_header_rowspan.docx differ diff --git a/test/docx/table_header_rowspan.native b/test/docx/table_header_rowspan.native new file mode 100644 index 000000000..d951f29e4 --- /dev/null +++ b/test/docx/table_header_rowspan.native @@ -0,0 +1,189 @@ +[Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidth 0.30701754385964913) + ,(AlignDefault,ColWidth 0.1364522417153996) + ,(AlignDefault,ColWidth 0.10009746588693957) + ,(AlignDefault,ColWidth 9.707602339181287e-2) + ,(AlignDefault,ColWidth 7.719298245614035e-2) + ,(AlignDefault,ColWidth 7.085769980506823e-2) + ,(AlignDefault,ColWidth 7.09551656920078e-2) + ,(AlignDefault,ColWidth 0.14035087719298245)] + (TableHead ("",[],[]) +[Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Str "A"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Strong [Str "B"]]] + ,Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Strong [Str "C"]]] + ,Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Strong [Str "D"]]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 3) + [Plain [Str "E"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) + [Plain [Str "F"]]] +,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Strong [Str "G"]]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Strong [Str "H"]]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Strong [Str "I"]]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ,Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "1"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "2"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "3"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "4"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "5"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "6"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "7"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "8"]]] + ])] + (TableFoot ("",[],[]) + [])] diff --git a/test/docx/table_one_header_row.docx b/test/docx/table_one_header_row.docx new file mode 100644 index 000000000..db715dda8 Binary files /dev/null and b/test/docx/table_one_header_row.docx differ diff --git a/test/docx/table_one_header_row.native b/test/docx/table_one_header_row.native new file mode 100644 index 000000000..4aae830ac --- /dev/null +++ b/test/docx/table_one_header_row.native @@ -0,0 +1,18 @@ +[Table ("",[],[]) (Caption Nothing + []) + [(AlignDefault,ColWidth 0.33302433371958284) + ,(AlignDefault,ColWidth 0.3332560834298957) + ,(AlignDefault,ColWidth 0.33371958285052145)] + (TableHead ("",[],[]) + [Row ("",[],[]) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "One"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Row"]] + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Plain [Str "Table"]]]]) + [(TableBody ("",[],[]) (RowHeadColumns 0) + [] + [])] + (TableFoot ("",[],[]) + [])] diff --git a/test/docx/table_one_row.docx b/test/docx/table_one_row.docx index f7e0ebe43..d05a856b5 100644 Binary files a/test/docx/table_one_row.docx and b/test/docx/table_one_row.docx differ diff --git a/test/docx/table_variable_width.native b/test/docx/table_variable_width.native index 43ac40cca..ff1cc0dc4 100644 --- a/test/docx/table_variable_width.native +++ b/test/docx/table_variable_width.native @@ -4,7 +4,8 @@ ,(AlignDefault,ColWidth 1.9882415820416888e-2) ,(AlignDefault,ColWidth 0.22202030999465527) ,(AlignDefault,ColWidth 0.4761090326028862) - ,(AlignDefault,ColWidth 1.0689470871191876e-4)] + ,(AlignDefault,ColWidth 1.0689470871191876e-4) + ,(AlignDefault,ColWidth 0.26178514163548905)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) @@ -13,33 +14,27 @@ [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "h3"]] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "h4"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "h5"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) - [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 3) [Plain [Str "c11"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) []] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "c22"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "c23"]] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) + ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) []]])] (TableFoot ("",[],[]) [])] -- cgit v1.2.3 From e72d2dd962f2c3073eb4b7a461c3c717f6a61e1a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 13:14:56 -0700 Subject: Use latest commonmark. --- pandoc.cabal | 6 +++--- stack.yaml | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index c8343d16e..fb70a9de4 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -451,9 +451,9 @@ library bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, citeproc >= 0.4 && < 0.4.1, - commonmark >= 0.1.1.3 && < 0.2, - commonmark-extensions >= 0.2.0.4 && < 0.3, - commonmark-pandoc >= 0.2 && < 0.3, + commonmark >= 0.2 && < 0.3, + commonmark-extensions >= 0.2.1 && < 0.3, + commonmark-pandoc >= 0.2.1 && < 0.3, connection >= 0.3.1, containers >= 0.4.2.1 && < 0.7, data-default >= 0.4 && < 0.8, diff --git a/stack.yaml b/stack.yaml index ea48fbb88..27c8c5f19 100644 --- a/stack.yaml +++ b/stack.yaml @@ -14,6 +14,9 @@ extra-deps: - random-1.2.0 - unicode-collation-0.1.3 - citeproc-0.4 +- commonmark-0.2 +- commonmark-extensions-0.2.1 +- commonmark-pandoc-0.2.1 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-17.12 -- cgit v1.2.3 From b6b2331fdcee37f1bfb3fcc21816d73d6d56cfae Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 13:58:44 -0700 Subject: Support `rebase_relative_paths` for commonmark based formats. (Including `gfm`.) --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Readers/CommonMark.hs | 4 +++- test/command/3752.md | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index c4d54c06e..9c55d0a7a 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -517,6 +517,7 @@ getAllExtensions f = universalExtensions <> getAll f , Ext_attributes , Ext_sourcepos , Ext_yaml_metadata_block + , Ext_rebase_relative_paths ] getAll "commonmark_x" = getAll "commonmark" getAll "org" = autoIdExtensions <> diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index b099a9b50..228e65312 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -112,5 +112,7 @@ specFor opts = foldr ($) defaultSyntaxSpec exts | isEnabled Ext_implicit_header_references opts ] ++ [ (footnoteSpec <>) | isEnabled Ext_footnotes opts ] ++ [ (definitionListSpec <>) | isEnabled Ext_definition_lists opts ] ++ - [ (taskListSpec <>) | isEnabled Ext_task_lists opts ] + [ (taskListSpec <>) | isEnabled Ext_task_lists opts ] ++ + [ (rebaseRelativePathsSpec <>) + | isEnabled Ext_rebase_relative_paths opts ] diff --git a/test/command/3752.md b/test/command/3752.md index df8af0ba1..2e96b531e 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -42,3 +42,19 @@ A spider: [spider] <h1 id="chapter-two">Chapter two</h1> <p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p> ``` + +``` +% pandoc command/chap1/text.md command/chap2/text.md -f commonmark+rebase_relative_paths -t html +^D +<h1>Chapter one</h1> +<p>A spider: <img src="command/chap1/spider.png" alt="spider" /></p> +<p>Another spider: <img src="command/chap2/spider.png" alt="another spider" /></p> +<p>The moon: <img src="command/chap1/../../lalune.jpg" alt="moon" /></p> +<p>Link to <a href="command/chap1/spider.png">spider picture</a>.</p> +<p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p> +<p>Absolute path left alone: <a href="/foo/bar/baz.png">absolute</a>.</p> +<p>Link to fragment: <a href="#chapter-two">chapter two</a>.</p> +<p>Empty path: <a href="">empty</a>.</p> +<h1>Chapter two</h1> +<p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p> +``` -- cgit v1.2.3 From 0bc18f10401ae6d309683f7e983dc5788956088f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 15:13:04 -0700 Subject: Update changelog. --- changelog.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index d24c24381..ec5962ef3 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,11 @@ we couldn't resolve resource paths relative to the files containing them (see e.g. #5501, #6632, #6384, #3752). + * Add `rebase_relative_paths` extension (#3752). When enabled, this + extension rewrites relative image and link paths by prepending + the (relative) directory of the containing file. This + extension can be enabled for all markdown and commonmark-based formats. + * Add Text.Pandoc.Sources (exported module), with a `Sources` type and a `ToSources` class. A `Sources` wraps a list of `(SourcePos, Text)` pairs [API change]. A parsec `Stream` instance is provided for `Sources`. The @@ -70,12 +75,21 @@ * ODT reader: Treat tabs as spaces (#7185, niszet). - * Docx reader: Add handling of vml image objects (#7257, mbrackeantidot). + * Docx reader: + + + Add handling of vml image objects (#7257, mbrackeantidot). + + Support new table features (Emily Bourke, #6316): column + spans, row spans, multiple header rows, table description + (parsed as a simple caption), captions, column widths. * LaTeX reader: + Improved siunitx support (#6658, #6620). + Better support for `\xspace` (#7299). + - Improve parsing of `\def` macros. We previously set "verbatim mode" + even for parsing the initial `\def`; this caused problems + for `\def` nested inside another `\def`. + - Implement `\newif`. * ConTeXt writer: improve ordered lists (#5016, Denis Maier). Change ordered list from itemize to enumerate. Add new @@ -92,6 +106,8 @@ this can lead to loss of information, the heading is now always retained. Use `--shift-heading-level-by=-1` to turn the `<h1>` into the document title, or a filter to restore the previous behavior. + + Handle relative lengths (e.g. `2*`) in HTML column widths (#4063). + See <https://www.w3.org/TR/html4/types.html#h-6.6>. * DocBook/JATS readers: @@ -100,6 +116,15 @@ * DocBook reader: ensure that first and last names are separated (#6541). + * Jira reader (Albert Krewinkel, #7218): + + + Support "smart" links: `[alias|https://example.com|smart-card]` syntax. + + Allow spaces and most unicode characters in attachment links. + + No longer require a newline character after `{noformat}`. + + Only allow URI path segment characters in bare links. + + The `file:` schema is no longer allowed in bare links; these + rarely make sense. + * Plain writer: handle superscript unicode minus (#7276). * LaTeX writer: @@ -179,6 +204,9 @@ rewritten by writing the offending characters as `Uxxxx`, where `xxxx` is the character's hex code. + * Jira writer: use `{color}` when span has a color attribute + (Albert Krewinkel, tarleb/jira-wiki-markup#10). + * Docx writer: + Autoset table width if no column has an explicit width (Albert @@ -265,15 +293,36 @@ * HTML-based slide shows: add support for `institute` (#7289, Thomas Hodgson). + * Text.Pandoc.Extensions: Add constructor `Ext_rebase_relative_paths` to + `Extensions` [API change]. + * Text.Pandoc.XML.Light: add Eq, Ord instances for Content, Element, Attr, CDataKind [API change]. - * Text.Pandoc.MediaBag: change type to use a Text key instead of - `[FilePath]`. We normalize the path and use `/` separators for - consistency. + * Text.Pandoc.MediaBag: + + + Change type to use a `Text` key instead of `[FilePath]`. + We normalize the path and use `/` separators for consistency. + + Export `MediaItem` type [API change]. + + Change `MediaBag` type to a map from Text to MediaItem [API change]. + + `lookupMedia` now returns a `MediaItem` [API change]. + + Change `insertMedia` so it sets the `mediaPath` to + a filename based on the SHA1 hash of the contents. + This will be used when contents are extracted. - * Text.Pandoc.Class.PandocMonad: Use `fetchItem` instead of `downloadOrRead` - in `fetchMediaResource`. + * Text.Pandoc.Class.PandocMonad: + + + Remove `fetchMediaResource` [API change]. Use `fetchItem` + to get resources in `fillMediaBag`. + + Add informational message in `downloadOrRead` indicating what path + local resources have been loaded from. + + * Text.Pandoc.Logging: + + + Remove single quotes around paths in messages. + + Add LoadedResource constructor to LogMessage [API change]. + This is for INFO-level messages telling where image data has been + loaded from. (This can vary because of the resource path.) * Text.Pandoc.Asciify: simplify code and export `toAsciiText` [API change]. Instead of encoding a giant (and incomplete) map, we now @@ -301,6 +350,10 @@ won't work when such Divs are used. + Use metadata's `lang` for the lang parameter of citeproc, overriding `localeLanguage`. + + Recognize locators spelled with a capital letter (#7323). + + Add a comma and a space in front of the suffix if it doesn't start + with space or punctuation (#7324). + + Don't detect math elements as locators (#7321). * Remove Text.Pandoc.BCP47 module [API change]. Use types and functions from UnicodeCollation.Lang instead. @@ -322,16 +375,24 @@ * Add text as build-depend for trypandoc (#7193, Roman Beránek). + * Bump upper-bounds for network-uri, time, attoparsec. + * Use citeproc 0.4. * Use texmath 0.12.3. - * Allow attoparsec 0.14.x. + * Use jira-wiki-markup 1.3.5 (Albert Krewinkel). * Require latest skylighting (fixes a bug in XML syntax highlighting). * Use latest xml-conduit. + * Use latest commonmark, commonmark-extensions, commonmark-pandoc. + + * Use haddock-library-1.10.0 (Albert Krewinkel). + + * Allow compilation with base 4.15 (Albert Krewinkel). + * MANUAL: + Add information about `lang` and bibliography sorting. @@ -339,6 +400,7 @@ Albert Krewinkel). + Note that `institute` variable works for HTML-based slides. + Update documentation on citation syntax. + + Add citation example for locators and suffixes (Tristan Stenner) * Updated and fixed typos in documentation (Charanjit Singh, Anti-Distinctlyminty, Tatiana Porras, obcat). @@ -351,6 +413,9 @@ * Remove `biblatex-nussbaum.md` test. It is basically the same as `biblaetx-quotes.md`. + * Command tests: fail if a file contains no tests---and fix a + test that failed in that way! + ## pandoc 2.13 (2021-03-21) -- cgit v1.2.3 From a7a603cc04a7fc0a5ea9694f7e3b391a98d4a9f7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 15:13:22 -0700 Subject: Update manual. --- MANUAL.txt | 2 +- man/pandoc.1 | 47 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index c234debc9..a076dcb6c 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: May 21, 2021 +date: May 28, 2021 --- # Synopsis diff --git a/man/pandoc.1 b/man/pandoc.1 index ac5479414..7eec066ab 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t .\" Automatically generated by Pandoc 2.14 .\" -.TH "Pandoc User\[cq]s Guide" "" "May 21, 2021" "pandoc 2.14" "" +.TH "Pandoc User\[cq]s Guide" "" "May 28, 2021" "pandoc 2.14" "" .hy .SH NAME pandoc - general markup converter @@ -4275,7 +4275,7 @@ Term 2 Note that space between items in a definition list is required. (A variant that loosens this requirement, but disallows \[lq]lazy\[rq] hard wrapping, can be activated with \f[C]compact_definition_lists\f[R]: -see Non-pandoc extensions, below.) +see Non-default extensions, below.) .SS Numbered example lists .SS Extension: \f[C]example_lists\f[R] .PP @@ -5866,12 +5866,14 @@ singular or plural forms, as \f[C]book\f[R], If no locator term is used, \[lq]page\[rq] is assumed. .PP In complex cases, you can force something to be treated as a locator by -enclosing it in curly braces: +enclosing it in curly braces or prevent parsing the suffix as locator by +prepending curly braces: .IP .nf \f[C] [\[at]smith{ii, A, D-Z}, with a suffix] [\[at]smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here] +[\[at]smith{}, 99 years later] \f[R] .fi .PP @@ -5909,13 +5911,50 @@ normal citations will be put in parentheses, while author-in-text citations will not. For this reason, it is sometimes preferable to use the author-in-text style inside notes when using a note style. -.SS Non-pandoc extensions +.SS Non-default extensions .PP The following Markdown syntax extensions are not enabled by default in pandoc, but may be enabled by adding \f[C]+EXTENSION\f[R] to the format name, where \f[C]EXTENSION\f[R] is the name of the extension. Thus, for example, \f[C]markdown+hard_line_breaks\f[R] is Markdown with hard line breaks. +.SS Extension: \f[C]rebase_relative_paths\f[R] +.PP +Rewrite relative paths for Markdown links and images, depending on the +path of the file containing the link or image link. +For each link or image, pandoc will compute the directory of the +containing file, relative to the working directory, and prepend the +resulting path to the link or image path. +.PP +The use of this extension is best understood by example. +Suppose you have a a subdirectory for each chapter of a book, +\f[C]chap1\f[R], \f[C]chap2\f[R], \f[C]chap3\f[R]. +Each contains a file \f[C]text.md\f[R] and a number of images used in +the chapter. +You would like to have \f[C]![image](spider.jpg)\f[R] in +\f[C]chap1/text.md\f[R] refer to \f[C]chap1/spider.jpg\f[R] and +\f[C]![image](spider.jpg)\f[R] in \f[C]chap2/text.md\f[R] refer to +\f[C]chap2/spider.jpg\f[R]. +To do this, use +.IP +.nf +\f[C] +pandoc chap*/*.md -f markdown+rebase_relative_paths +\f[R] +.fi +.PP +Without this extension, you would have to use +\f[C]![image](chap1/spider.jpg)\f[R] in \f[C]chap1/text.md\f[R] and +\f[C]![image](chap2/spider.jpg)\f[R] in \f[C]chap2/text.md\f[R]. +Links with relative paths will be rewritten in the same way as images. +.PP +Absolute paths and URLs are not changed. +Neither are empty paths or paths consisting entirely of a fragment, +e.g., \f[C]#foo\f[R]. +.PP +Note that relative paths in reference links and images will be rewritten +relative to the file containing the link reference definition, not the +file containing the reference link or image itself, if these differ. .SS Extension: \f[C]attributes\f[R] .PP Allows attributes to be attached to any inline or block-level element. -- cgit v1.2.3 From 962c7e5707551d71db323971c361e48676aa37d5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 15:20:35 -0700 Subject: Update AUTHORS and fixed some typos in changelog. --- AUTHORS.md | 10 ++++++++++ changelog.md | 51 ++++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index c196d7399..0f2216991 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -20,6 +20,7 @@ - Andreas Lööw - Andrew Dunning - Andy Morris +- Anti-Distinctlyminty - Antoine Latter - Antonio Terceiro - Arata Mizuki @@ -46,6 +47,7 @@ - Carlos Sosa - Cécile Chemin - Cédric Couralet +- Charanjit Singh - Chris Black - Christian Conkle - Christian Despres @@ -76,6 +78,7 @@ - Eigil Rischel - Emanuel Evans - Emerson Harkin +- Emily Bourke - Emily Eisenberg - Eric Kow - Erik Rask @@ -149,6 +152,7 @@ - Josef Svenningsson - Joseph C. Sible - Julien Cretel +- Julien Dutant - Juliette Fourcot - Juliusz Gonera - Justin Bogner @@ -231,6 +235,7 @@ - Raymond Ehlers - Recai Oktaş - Roland Hieber +- Roman Beránek - RyanGlScott - Salim B - Sascha Wilde @@ -248,6 +253,7 @@ - Stefan Dresselhaus - Sumit Sahrawat - Tarik Graba +- Tatiana Porras - Thenaesh Elango - Thomas Hodgson - Thomas Weißschuh @@ -281,6 +287,7 @@ - a-vrma - andrebauer - argent0 +- badumont - blmage - bucklereed - bumper314 @@ -296,8 +303,10 @@ - kaizshang91 - lux-lth - lwolfsonkin +- mbrackeantidot - nkalvi - niszet +- obcat - oltolm - oquechy - quasicomputational @@ -309,6 +318,7 @@ - shreevatsa.public - takahashim - taotieren +- tecosaur - TEC - tgkokk - the-solipsist diff --git a/changelog.md b/changelog.md index ec5962ef3..8a44052f5 100644 --- a/changelog.md +++ b/changelog.md @@ -14,7 +14,10 @@ * Add `rebase_relative_paths` extension (#3752). When enabled, this extension rewrites relative image and link paths by prepending - the (relative) directory of the containing file. This + the (relative) directory of the containing file. This + behavior is useful when your input sources are split + into multiple files, across several directories, with files + referring to images stored in the same directory. The extension can be enabled for all markdown and commonmark-based formats. * Add Text.Pandoc.Sources (exported module), with a `Sources` type and a @@ -53,15 +56,13 @@ * Markdown reader: - + Use MetaInlines not MetaBlocks for multimarkdown - metadata fields. This gives better results in converting to e.g. - pandoc markdown. + + Use MetaInlines not MetaBlocks for multimarkdown metadata fields. + This gives better results in converting to e.g. pandoc markdown. + Implement curly-brace syntax for Markdown citation keys (#6026). The change provides a way to use citation keys that contain - special characters not usable with the standard citation - key syntax. Example: `@{foo_bar{x}'}` for the key `foo_bar{x}`. - It also allows separating citation keys from immediately - following text, e.g. `@{foo}A`. + special characters not usable with the standard citation key syntax. + Example: `@{foo_bar{x}'}` for the key `foo_bar{x}`. It also allows + separating citation keys from immediately following text, e.g. `@{foo}A`. * RST reader: @@ -86,10 +87,10 @@ + Improved siunitx support (#6658, #6620). + Better support for `\xspace` (#7299). - - Improve parsing of `\def` macros. We previously set "verbatim mode" + + Improve parsing of `\def` macros. We previously set "verbatim mode" even for parsing the initial `\def`; this caused problems for `\def` nested inside another `\def`. - - Implement `\newif`. + + Implement `\newif`. * ConTeXt writer: improve ordered lists (#5016, Denis Maier). Change ordered list from itemize to enumerate. Add new @@ -130,8 +131,8 @@ * LaTeX writer: + Better handling of line breaks in simple tables (#7272). - Now we also handle the case where they're embedded in other - elements, e.g. spans. + Now we also handle the case where they're embedded in other elements, + e.g. spans. + For beamer output, support `exampleblock` and `alertblock` (#7278). A block will be rendered as an `exampleblock` if the heading has class `example` and an `alertblock` if it has class `alert`. @@ -227,8 +228,8 @@ `savePreviewPicture`, `mathPr`, `themeFontLang`, `decimalSymbol`, `listSeparator`, `autoHyphenation`, `compat`. + Set zoom to 100% by default in settings.xml. - + Align math options more with current Word defaults (e.g. - Cambria Math font). + + Align math options more with current Word defaults (e.g. Cambria Math + font). + Remove `rsid`s from default settings.xml. Word will add these when revisions are made. @@ -306,14 +307,14 @@ + Export `MediaItem` type [API change]. + Change `MediaBag` type to a map from Text to MediaItem [API change]. + `lookupMedia` now returns a `MediaItem` [API change]. - + Change `insertMedia` so it sets the `mediaPath` to - a filename based on the SHA1 hash of the contents. - This will be used when contents are extracted. + + Change `insertMedia` so it sets the `mediaPath` to a filename based on + the SHA1 hash of the contents. This will be used when contents + are extracted. * Text.Pandoc.Class.PandocMonad: - + Remove `fetchMediaResource` [API change]. Use `fetchItem` - to get resources in `fillMediaBag`. + + Remove `fetchMediaResource` [API change]. Use `fetchItem` to get + resources in `fillMediaBag`. + Add informational message in `downloadOrRead` indicating what path local resources have been loaded from. @@ -345,9 +346,9 @@ * Text.Pandoc.Citeproc: - + Ensure that CSL-related attributes are passed on to a Div - with id 'refs'. Otherwise things like `entry-spacing` - won't work when such Divs are used. + + Ensure that CSL-related attributes are passed on to a Div with id + 'refs'. Otherwise things like `entry-spacing` won't work when + such Divs are used. + Use metadata's `lang` for the lang parameter of citeproc, overriding `localeLanguage`. + Recognize locators spelled with a capital letter (#7323). @@ -355,9 +356,9 @@ with space or punctuation (#7324). + Don't detect math elements as locators (#7321). - * Remove Text.Pandoc.BCP47 module [API change]. - Use types and functions from UnicodeCollation.Lang instead. - This is a richer implementation of BCP 47. + * Remove Text.Pandoc.BCP47 module [API change]. Use types and functions + from UnicodeCollation.Lang instead. This is a richer implementation + of BCP 47. * Text.Pandoc.Shared: -- cgit v1.2.3 From c98fe1f02c76400e06847e414ad130849d23b503 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 16:34:45 -0700 Subject: Date on changelog. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 8a44052f5..b8c54dce8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Revision history for pandoc -## pandoc 2.14 (PROVISIONAL) +## pandoc 2.14 (2021-05-28) * Change reader types, allowing better tracking of source positions [API change]. Previously, when multiple file arguments were provided, -- cgit v1.2.3 From 1e6ede6611966deec4957ca15a2252ce36fef366 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 May 2021 22:48:45 -0700 Subject: Add nuew to AUTHORS. --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 0f2216991..0ed8e62a3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -306,6 +306,7 @@ - mbrackeantidot - nkalvi - niszet +- nuew - obcat - oltolm - oquechy -- cgit v1.2.3 From 8660f42f094b59cc0eebecb78731a52df744c12f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 10:59:14 -0700 Subject: Modify pptx tests to take a whole lot less space. - Replace a 300K image in the reference pptx with a 2K one. - Updated all the *_templated.pptx files based on the new reference pptx. - These changes should reduce the size of the tarball by roughly 7 MB! See haskell/hackage-server#935 --- test/pptx/code-custom_templated.pptx | Bin 395477 -> 41337 bytes test/pptx/code_templated.pptx | Bin 395477 -> 41337 bytes .../document-properties-short-desc_templated.pptx | Bin 394253 -> 40113 bytes test/pptx/document-properties_templated.pptx | Bin 394656 -> 40517 bytes test/pptx/endnotes_templated.pptx | Bin 394219 -> 40078 bytes test/pptx/endnotes_toc_templated.pptx | Bin 395041 -> 40899 bytes test/pptx/images_templated.pptx | Bin 411870 -> 57729 bytes test/pptx/inline_formatting_templated.pptx | Bin 393412 -> 39272 bytes test/pptx/lists_templated.pptx | Bin 394307 -> 40166 bytes test/pptx/raw_ooxml_templated.pptx | Bin 394198 -> 40059 bytes test/pptx/reference_depth.pptx | Bin 397502 -> 43743 bytes test/pptx/remove_empty_slides_templated.pptx | Bin 411311 -> 57172 bytes .../pptx/slide_breaks_slide_level_1_templated.pptx | Bin 395000 -> 40858 bytes test/pptx/slide_breaks_templated.pptx | Bin 395825 -> 41683 bytes test/pptx/slide_breaks_toc_templated.pptx | Bin 396776 -> 42634 bytes .../speaker_notes_after_metadata_templated.pptx | Bin 398915 -> 44775 bytes test/pptx/speaker_notes_afterheader_templated.pptx | Bin 397943 -> 43803 bytes test/pptx/speaker_notes_afterseps_templated.pptx | Bin 418834 -> 64695 bytes test/pptx/speaker_notes_templated.pptx | Bin 402650 -> 48507 bytes test/pptx/start_numbering_at_templated.pptx | Bin 394283 -> 40142 bytes test/pptx/tables_templated.pptx | Bin 394827 -> 40686 bytes test/pptx/two_column_templated.pptx | Bin 393327 -> 39189 bytes 22 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/pptx/code-custom_templated.pptx b/test/pptx/code-custom_templated.pptx index 15232c32d..0e640f373 100644 Binary files a/test/pptx/code-custom_templated.pptx and b/test/pptx/code-custom_templated.pptx differ diff --git a/test/pptx/code_templated.pptx b/test/pptx/code_templated.pptx index fe5b675f3..a03109217 100644 Binary files a/test/pptx/code_templated.pptx and b/test/pptx/code_templated.pptx differ diff --git a/test/pptx/document-properties-short-desc_templated.pptx b/test/pptx/document-properties-short-desc_templated.pptx index 89e6fbdf2..d39dc0c91 100644 Binary files a/test/pptx/document-properties-short-desc_templated.pptx and b/test/pptx/document-properties-short-desc_templated.pptx differ diff --git a/test/pptx/document-properties_templated.pptx b/test/pptx/document-properties_templated.pptx index 79d48560b..841ae8c42 100644 Binary files a/test/pptx/document-properties_templated.pptx and b/test/pptx/document-properties_templated.pptx differ diff --git a/test/pptx/endnotes_templated.pptx b/test/pptx/endnotes_templated.pptx index 54ec7f305..863cc29d4 100644 Binary files a/test/pptx/endnotes_templated.pptx and b/test/pptx/endnotes_templated.pptx differ diff --git a/test/pptx/endnotes_toc_templated.pptx b/test/pptx/endnotes_toc_templated.pptx index 1158c16fc..38a0c437d 100644 Binary files a/test/pptx/endnotes_toc_templated.pptx and b/test/pptx/endnotes_toc_templated.pptx differ diff --git a/test/pptx/images_templated.pptx b/test/pptx/images_templated.pptx index 6d297ef11..48ebf66d6 100644 Binary files a/test/pptx/images_templated.pptx and b/test/pptx/images_templated.pptx differ diff --git a/test/pptx/inline_formatting_templated.pptx b/test/pptx/inline_formatting_templated.pptx index 2cdf54474..dd778a102 100644 Binary files a/test/pptx/inline_formatting_templated.pptx and b/test/pptx/inline_formatting_templated.pptx differ diff --git a/test/pptx/lists_templated.pptx b/test/pptx/lists_templated.pptx index 676954cb8..2493e7890 100644 Binary files a/test/pptx/lists_templated.pptx and b/test/pptx/lists_templated.pptx differ diff --git a/test/pptx/raw_ooxml_templated.pptx b/test/pptx/raw_ooxml_templated.pptx index 1742b3296..0f20f9826 100644 Binary files a/test/pptx/raw_ooxml_templated.pptx and b/test/pptx/raw_ooxml_templated.pptx differ diff --git a/test/pptx/reference_depth.pptx b/test/pptx/reference_depth.pptx index 6906cf49d..97f444788 100644 Binary files a/test/pptx/reference_depth.pptx and b/test/pptx/reference_depth.pptx differ diff --git a/test/pptx/remove_empty_slides_templated.pptx b/test/pptx/remove_empty_slides_templated.pptx index cf6e52eef..1df48c5ad 100644 Binary files a/test/pptx/remove_empty_slides_templated.pptx and b/test/pptx/remove_empty_slides_templated.pptx differ diff --git a/test/pptx/slide_breaks_slide_level_1_templated.pptx b/test/pptx/slide_breaks_slide_level_1_templated.pptx index 258098082..e2815159a 100644 Binary files a/test/pptx/slide_breaks_slide_level_1_templated.pptx and b/test/pptx/slide_breaks_slide_level_1_templated.pptx differ diff --git a/test/pptx/slide_breaks_templated.pptx b/test/pptx/slide_breaks_templated.pptx index 2f0213919..71ba99701 100644 Binary files a/test/pptx/slide_breaks_templated.pptx and b/test/pptx/slide_breaks_templated.pptx differ diff --git a/test/pptx/slide_breaks_toc_templated.pptx b/test/pptx/slide_breaks_toc_templated.pptx index f288dde14..b83f3f596 100644 Binary files a/test/pptx/slide_breaks_toc_templated.pptx and b/test/pptx/slide_breaks_toc_templated.pptx differ diff --git a/test/pptx/speaker_notes_after_metadata_templated.pptx b/test/pptx/speaker_notes_after_metadata_templated.pptx index 5d4465f64..8d27c4c68 100644 Binary files a/test/pptx/speaker_notes_after_metadata_templated.pptx and b/test/pptx/speaker_notes_after_metadata_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterheader_templated.pptx b/test/pptx/speaker_notes_afterheader_templated.pptx index c922df3a8..d8d8481df 100644 Binary files a/test/pptx/speaker_notes_afterheader_templated.pptx and b/test/pptx/speaker_notes_afterheader_templated.pptx differ diff --git a/test/pptx/speaker_notes_afterseps_templated.pptx b/test/pptx/speaker_notes_afterseps_templated.pptx index f0b302738..9c22eaf38 100644 Binary files a/test/pptx/speaker_notes_afterseps_templated.pptx and b/test/pptx/speaker_notes_afterseps_templated.pptx differ diff --git a/test/pptx/speaker_notes_templated.pptx b/test/pptx/speaker_notes_templated.pptx index 2d8d00242..930377fbf 100644 Binary files a/test/pptx/speaker_notes_templated.pptx and b/test/pptx/speaker_notes_templated.pptx differ diff --git a/test/pptx/start_numbering_at_templated.pptx b/test/pptx/start_numbering_at_templated.pptx index 0a3f6e56d..efc56ac7a 100644 Binary files a/test/pptx/start_numbering_at_templated.pptx and b/test/pptx/start_numbering_at_templated.pptx differ diff --git a/test/pptx/tables_templated.pptx b/test/pptx/tables_templated.pptx index 82b8aa13d..0a8c3e8d9 100644 Binary files a/test/pptx/tables_templated.pptx and b/test/pptx/tables_templated.pptx differ diff --git a/test/pptx/two_column_templated.pptx b/test/pptx/two_column_templated.pptx index 44985d701..6d9470372 100644 Binary files a/test/pptx/two_column_templated.pptx and b/test/pptx/two_column_templated.pptx differ -- cgit v1.2.3 From 5cf887db20b723e3aee172f8ecbc31ca95f69542 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 11:48:52 -0700 Subject: Reduce size of cover image in test epub. --- test/Tests/Readers/EPUB.hs | 2 +- test/epub/wasteland.epub | Bin 101870 -> 35496 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Tests/Readers/EPUB.hs b/test/Tests/Readers/EPUB.hs index 13e239911..040fc96e7 100644 --- a/test/Tests/Readers/EPUB.hs +++ b/test/Tests/Readers/EPUB.hs @@ -47,7 +47,7 @@ featuresBag = [("img/check.gif","image/gif",1340) -- with additional meta tag for cover in EPUB2 format epub3CoverBag :: [(String, String, Int)] -epub3CoverBag = [("wasteland-cover.jpg","image/jpeg",103477)] +epub3CoverBag = [("wasteland-cover.jpg","image/jpeg", 16586)] epub3NoCoverBag :: [(String, String, Int)] epub3NoCoverBag = [("img/check.gif","image/gif",1340) diff --git a/test/epub/wasteland.epub b/test/epub/wasteland.epub index e4e52db7f..c958c5fc2 100644 Binary files a/test/epub/wasteland.epub and b/test/epub/wasteland.epub differ -- cgit v1.2.3 From 3ba9ef01ebaf2770f89526f2dc3e75c756996893 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 11:54:03 -0700 Subject: Reduce size of image in fb2 image test. --- test/fb2/test.jpg | Bin 153610 -> 23023 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/fb2/test.jpg b/test/fb2/test.jpg index 99d57db17..2b6c5e8de 100644 Binary files a/test/fb2/test.jpg and b/test/fb2/test.jpg differ -- cgit v1.2.3 From e86f6abc45b8192eb98ec6d0a3b65d2a3c6726ad Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 12:06:32 -0700 Subject: Further test image size reductions. --- test/command/chap1/spider.png | Bin 63531 -> 8758 bytes test/command/chap2/spider.png | Bin 9861 -> 2921 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/command/chap1/spider.png b/test/command/chap1/spider.png index 7ee9fe339..4c6bf13e9 100644 Binary files a/test/command/chap1/spider.png and b/test/command/chap1/spider.png differ diff --git a/test/command/chap2/spider.png b/test/command/chap2/spider.png index 5377e940b..5456a857a 100644 Binary files a/test/command/chap2/spider.png and b/test/command/chap2/spider.png differ -- cgit v1.2.3 From 7aade73dce70f75aa0fe04b11b118acc3ea7854f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 12:14:37 -0700 Subject: Replace biblatex-exmaples.bib with shorter averroes.bib in tests. --- pandoc.cabal | 2 +- test/command/averroes.bib | 95 ++ test/command/biblatex-examples.bib | 1674 ----------------------------------- test/command/pandoc-citeproc-119.md | 2 +- 4 files changed, 97 insertions(+), 1676 deletions(-) create mode 100644 test/command/averroes.bib delete mode 100644 test/command/biblatex-examples.bib diff --git a/pandoc.cabal b/pandoc.cabal index fb70a9de4..40b0db594 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -208,7 +208,7 @@ extra-source-files: test/command/*.md test/command/*.csl test/command/biblio.bib - test/command/biblatex-examples.bib + test/command/averroes.bib test/command/A.txt test/command/B.txt test/command/C.txt diff --git a/test/command/averroes.bib b/test/command/averroes.bib new file mode 100644 index 000000000..b95c8e354 --- /dev/null +++ b/test/command/averroes.bib @@ -0,0 +1,95 @@ +@string{anch-ie = {Angew.~Chem. Int.~Ed.}} +@string{cup = {Cambridge University Press}} +@string{dtv = {Deutscher Taschenbuch-Verlag}} +@string{hup = {Harvard University Press}} +@string{jams = {J.~Amer. Math. Soc.}} +@string{jchph = {J.~Chem. Phys.}} +@string{jomch = {J.~Organomet. Chem.}} +@string{pup = {Princeton University Press}} + +@book{averroes/bland, + author = {Averroes}, + title = {The Epistle on the Possibility of Conjunction with the Active + Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, + date = 1982, + editor = {Bland, Kalman P.}, + translator = {Bland, Kalman P.}, + series = {Moreshet: Studies in {Jewish} History, Literature and Thought}, + number = 7, + publisher = {Jewish Theological Seminary of America}, + location = {New York}, + keywords = {primary}, + langid = {english}, + langidopts = {variant=american}, + indextitle = {Epistle on the Possibility of Conjunction, The}, + shorttitle = {Possibility of Conjunction}, + annotation = {A \texttt{book} entry with a \texttt{series} and a + \texttt{number}. Note the concatenation of the \texttt{editor} + and \texttt{translator} fields as well as the + \texttt{indextitle} field}, +} + +@book{averroes/hannes, + author = {Averroes}, + title = {Des Averro{\"e}s Abhandlung: \mkbibquote{{\"U}ber die + M{\"o}glichkeit der Conjunktion} oder \mkbibquote{{\"U}ber den + materiellen Intellekt}}, + date = 1892, + editor = {Hannes, Ludwig}, + translator = {Hannes, Ludwig}, + annotator = {Hannes, Ludwig}, + publisher = {C.~A. Kaemmerer}, + location = {Halle an der Saale}, + keywords = {primary}, + langid = {german}, + sorttitle = {Uber die Moglichkeit der Conjunktion}, + indexsorttitle= {Uber die Moglichkeit der Conjunktion}, + indextitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, + shorttitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, + annotation = {An annotated edition. Note the concatenation of the + \texttt{editor}, \texttt{translator}, and \texttt{annotator} + fields. Also note the \texttt{shorttitle}, + \texttt{indextitle}, \texttt{sorttitle}, and + \texttt{indexsorttitle} fields}, +} + +@book{averroes/hercz, + author = {Averroes}, + title = {Drei Abhandlungen {\"u}ber die Conjunction des separaten + Intellects mit dem Menschen}, + date = 1869, + editor = {Hercz, J.}, + translator = {Hercz, J.}, + publisher = {S.~Hermann}, + location = {Berlin}, + keywords = {primary}, + langid = {german}, + indexsorttitle= {Drei Abhandlungen uber die Conjunction}, + indextitle = {Drei Abhandlungen {\"u}ber die Conjunction}, + subtitle = {Von Averroes (Vater und Sohn), aus dem Arabischen + {\"u}bersetzt von Samuel Ibn Tibbon}, + shorttitle = {Drei Abhandlungen}, + annotation = {A \texttt{book} entry. Note the concatenation of the + \texttt{editor} and \texttt{translator} fields as well as the + \texttt{indextitle} and \texttt{indexsorttitle} fields}, +} + +@book{cicero, + author = {Cicero, Marcus Tullius}, + title = {De natura deorum. {\"U}ber das Wesen der G{\"o}tter}, + date = 1995, + editor = {Blank-Sangmeister, Ursula}, + translator = {Blank-Sangmeister, Ursula}, + afterword = {Thraede, Klaus}, + language = {langlatin and langgerman}, + publisher = {Reclam}, + location = {Stuttgart}, + langid = {german}, + indextitle = {De natura deorum}, + shorttitle = {De natura deorum}, + annotation = {A bilingual edition of Cicero's \emph{De natura deorum}, with + a German translation. Note the format of the \texttt{language} + field in the database file, the concatenation of the + \texttt{editor} and \texttt{translator} fields, and the + \texttt{afterword} field}, +} diff --git a/test/command/biblatex-examples.bib b/test/command/biblatex-examples.bib deleted file mode 100644 index 36ac849dd..000000000 --- a/test/command/biblatex-examples.bib +++ /dev/null @@ -1,1674 +0,0 @@ -@string{anch-ie = {Angew.~Chem. Int.~Ed.}} -@string{cup = {Cambridge University Press}} -@string{dtv = {Deutscher Taschenbuch-Verlag}} -@string{hup = {Harvard University Press}} -@string{jams = {J.~Amer. Math. Soc.}} -@string{jchph = {J.~Chem. Phys.}} -@string{jomch = {J.~Organomet. Chem.}} -@string{pup = {Princeton University Press}} - -@incollection{westfahl:space, - author = {Westfahl, Gary}, - title = {The True Frontier}, - subtitle = {Confronting and Avoiding the Realities of Space in {American} - Science Fiction Films}, - pages = {55-65}, - crossref = {westfahl:frontier}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {True Frontier, The}, - annotation = {A cross-referenced article from a \texttt{collection}. This is - an \texttt{incollection} entry with a \texttt{crossref} - field. Note the \texttt{subtitle} and \texttt{indextitle} - fields}, -} - -@set{set, - entryset = {herrmann,aksin,yoon}, - annotation = {A \texttt{set} with three members. The \texttt{crossref} field - in the \texttt{@set} entry and the \texttt{entryset} field in - each set member entry is needed only when using BibTeX as the - backend}, -} - -@set{stdmodel, - entryset = {glashow,weinberg,salam}, - annotation = {A \texttt{set} with three members discussing the standard - model of particle physics. The \texttt{crossref} field - in the \texttt{@set} entry and the \texttt{entryset} field in - each set member entry is needed only when using BibTeX as the - backend}, -} - -@article{aksin, - author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent - and {\c{C}}etinkaya, Bekir and Ni, Chaoying and - B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan}, - title = {Effect of immobilization on catalytic characteristics of - saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck} - reactions}, - journaltitle = jomch, - date = 2006, - volume = 691, - number = 13, - pages = {3027-3036}, - indextitle = {Effect of immobilization on catalytic characteristics}, -} - -@article{angenendt, - author = {Angenendt, Arnold}, - title = {In Honore Salvatoris~-- Vom Sinn und Unsinn der - Patrozinienkunde}, - journaltitle = {Revue d'Histoire Eccl{\'e}siastique}, - date = 2002, - volume = 97, - pages = {431--456, 791--823}, - langid = {german}, - indextitle = {In Honore Salvatoris}, - shorttitle = {In Honore Salvatoris}, - annotation = {A German article in a French journal. Apart from that, a - typical \texttt{article} entry. Note the \texttt{indextitle} - field}, -} - -@article{baez/article, - author = {Baez, John C. and Lauda, Aaron D.}, - title = {Higher-Dimensional Algebra {V}: 2-Groups}, - journaltitle = {Theory and Applications of Categories}, - date = 2004, - volume = 12, - pages = {423-491}, - version = 3, - eprint = {math/0307200v3}, - eprinttype = {arxiv}, - langid = {english}, - langidopts = {variant=american}, - annotation = {An \texttt{article} with \texttt{eprint} and - \texttt{eprinttype} fields. Note that the arXiv reference is - transformed into a clickable link if \texttt{hyperref} support - has been enabled. Compare \texttt{baez\slash online}, which - is the same item given as an \texttt{online} entry}, -} - -@article{bertram, - author = {Bertram, Aaron and Wentworth, Richard}, - title = {Gromov invariants for holomorphic maps on {Riemann} surfaces}, - journaltitle = jams, - date = 1996, - volume = 9, - number = 2, - pages = {529-571}, - langid = {english}, - langidopts = {variant=american}, - shorttitle = {Gromov invariants}, - annotation = {An \texttt{article} entry with a \texttt{volume} and a - \texttt{number} field}, -} - -@article{doody, - author = {Doody, Terrence}, - title = {Hemingway's Style and {Jake's} Narration}, - year = 1974, - volume = 4, - number = 3, - pages = {212-225}, - langid = {english}, - langidopts = {variant=american}, - related = {matuz:doody}, - relatedstring= {\autocap{e}xcerpt in}, - journal = {The Journal of Narrative Technique}, - annotation = {An \texttt{article} entry cited as an excerpt from a - \texttt{collection} entry. Note the format of the - \texttt{related} and \texttt{relatedstring} fields}, -} - -@collection{matuz:doody, - editor = {Matuz, Roger}, - title = {Contemporary Literary Criticism}, - year = 1990, - volume = 61, - publisher = {Gale}, - location = {Detroit}, - pages = {204-208}, - langid = {english}, - langidopts = {variant=american}, - annotation = {A \texttt{collection} entry providing the excerpt information - for the \texttt{doody} entry. Note the format of the - \texttt{pages} field}, -} - -@article{gillies, - author = {Gillies, Alexander}, - title = {Herder and the Preparation of {Goethe's} Idea of World - Literature}, - journaltitle = {Publications of the English Goethe Society}, - date = 1933, - series = {newseries}, - volume = 9, - pages = {46-67}, - langid = {english}, - langidopts = {variant=british}, - annotation = {An \texttt{article} entry with a \texttt{series} and a - \texttt{volume} field. Note that format of the \texttt{series} - field in the database file}, -} - -@article{glashow, - author = {Glashow, Sheldon}, - title = {Partial Symmetries of Weak Interactions}, - journaltitle = {Nucl.~Phys.}, - date = 1961, - volume = 22, - pages = {579-588}, -} - -@article{herrmann, - author = {Herrmann, Wolfgang A. and {\"O}fele, Karl and Schneider, - Sabine K. and Herdtweck, Eberhardt and Hoffmann, Stephan D.}, - title = {A carbocyclic carbene as an efficient catalyst ligand for {C--C} - coupling reactions}, - journaltitle = anch-ie, - date = 2006, - volume = 45, - number = 23, - pages = {3859-3862}, - indextitle = {Carbocyclic carbene as an efficient catalyst, A}, -} - -@article{kastenholz, - author = {Kastenholz, M. A. and H{\"u}nenberger, Philippe H.}, - title = {Computation of methodology\hyphen independent ionic solvation - free energies from molecular simulations}, - journaltitle = jchph, - date = 2006, - subtitle = {{I}. {The} electrostatic potential in molecular liquids}, - volume = 124, - eid = 124106, - doi = {10.1063/1.2172593}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Computation of ionic solvation free energies}, - annotation = {An \texttt{article} entry with an \texttt{eid} and a - \texttt{doi} field. Note that the \textsc{doi} is transformed - into a clickable link if \texttt{hyperref} support has been - enabled}, - abstract = {The computation of ionic solvation free energies from - atomistic simulations is a surprisingly difficult problem that - has found no satisfactory solution for more than 15 years. The - reason is that the charging free energies evaluated from such - simulations are affected by very large errors. One of these is - related to the choice of a specific convention for summing up - the contributions of solvent charges to the electrostatic - potential in the ionic cavity, namely, on the basis of point - charges within entire solvent molecules (M scheme) or on the - basis of individual point charges (P scheme). The use of an - inappropriate convention may lead to a charge-independent - offset in the calculated potential, which depends on the - details of the summation scheme, on the quadrupole-moment - trace of the solvent molecule, and on the approximate form - used to represent electrostatic interactions in the - system. However, whether the M or P scheme (if any) represents - the appropriate convention is still a matter of on-going - debate. The goal of the present article is to settle this - long-standing controversy by carefully analyzing (both - analytically and numerically) the properties of the - electrostatic potential in molecular liquids (and inside - cavities within them).}, -} - -@article{murray, - author = {Hostetler, Michael J. and Wingate, Julia E. and Zhong, - Chuan-Jian and Harris, Jay E. and Vachet, Richard W. and - Clark, Michael R. and Londono, J. David and Green, Stephen - J. and Stokes, Jennifer J. and Wignall, George D. and Glish, - Gary L. and Porter, Marc D. and Evans, Neal D. and Murray, - Royce W.}, - title = {Alkanethiolate gold cluster molecules with core diameters from - 1.5 to 5.2~{nm}}, - journaltitle = {Langmuir}, - date = 1998, - subtitle = {Core and monolayer properties as a function of core size}, - volume = 14, - number = 1, - pages = {17-30}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Alkanethiolate gold cluster molecules}, - shorttitle = {Alkanethiolate gold cluster molecules}, - annotation = {An \texttt{article} entry with \arabic{author} authors. By - default, long author and editor lists are automatically - truncated. This is configurable}, -} - -@article{reese, - author = {Reese, Trevor R.}, - title = {Georgia in {Anglo-Spanish} Diplomacy, 1736--1739}, - journaltitle = {William and Mary Quarterly}, - date = 1958, - series = 3, - volume = 15, - pages = {168-190}, - langid = {english}, - langidopts = {variant=american}, - annotation = {An \texttt{article} entry with a \texttt{series} and a - \texttt{volume} field. Note the format of the series. If the - value of the \texttt{series} field is an integer, this number - is printed as an ordinal and the string \enquote*{series} is - appended automatically}, -} - -@article{sarfraz, - author = {M. Sarfraz and M. F. A. Razzak}, - title = {Technical section: {An} algorithm for automatic capturing of - the font outlines}, - year = 2002, - volume = 26, - number = 5, - pages = {795-804}, - issn = {0097-8493}, - journal = {Computers and Graphics}, - annotation = {An \texttt{article} entry with an \texttt{issn} field}, -} - -@article{shore, - author = {Shore, Bradd}, - title = {Twice-Born, Once Conceived}, - journaltitle = {American Anthropologist}, - date = {1991-03}, - subtitle = {Meaning Construction and Cultural Cognition}, - series = {newseries}, - volume = 93, - number = 1, - pages = {9-27}, - annotation = {An \texttt{article} entry with \texttt{series}, - \texttt{volume}, and \texttt{number} fields. Note the format - of the \texttt{series} which is a localization key}, -} - -@article{sigfridsson, - author = {Sigfridsson, Emma and Ryde, Ulf}, - title = {Comparison of methods for deriving atomic charges from the - electrostatic potential and moments}, - journaltitle = {Journal of Computational Chemistry}, - date = 1998, - volume = 19, - number = 4, - pages = {377-395}, - doi = {10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Methods for deriving atomic charges}, - annotation = {An \texttt{article} entry with \texttt{volume}, - \texttt{number}, and \texttt{doi} fields. Note that the - \textsc{doi} is transformed into a clickable link if - \texttt{hyperref} support has been enabled}, - abstract = {Four methods for deriving partial atomic charges from the - quantum chemical electrostatic potential (CHELP, CHELPG, - Merz-Kollman, and RESP) have been compared and critically - evaluated. It is shown that charges strongly depend on how and - where the potential points are selected. Two alternative - methods are suggested to avoid the arbitrariness in the - point-selection schemes and van der Waals exclusion radii: - CHELP-BOW, which also estimates the charges from the - electrostatic potential, but with potential points that are - Boltzmann-weighted after their occurrence in actual - simulations using the energy function of the program in which - the charges will be used, and CHELMO, which estimates the - charges directly from the electrostatic multipole - moments. Different criteria for the quality of the charges are - discussed.}, -} - -@article{spiegelberg, - author = {Spiegelberg, Herbert}, - title = {\mkbibquote{Intention} und \mkbibquote{Intentionalit{\"a}t} in - der Scholastik, bei Brentano und Husserl}, - journaltitle = {Studia Philosophica}, - date = 1969, - volume = 29, - pages = {189-216}, - langid = {german}, - sorttitle = {Intention und Intentionalitat in der Scholastik, bei Brentano - und Husserl}, - indexsorttitle= {Intention und Intentionalitat in der Scholastik, bei Brentano - und Husserl}, - shorttitle = {Intention und Intentionalit{\"a}t}, - annotation = {An \texttt{article} entry. Note the \texttt{sorttitle} and - \texttt{indexsorttitle} fields and the markup of the quotes in - the database file}, -} - -@article{springer, - author = {Springer, Otto}, - title = {Mediaeval Pilgrim Routes from {Scandinavia} to {Rome}}, - journaltitle = {Mediaeval Studies}, - date = 1950, - volume = 12, - pages = {92-122}, - langid = {english}, - langidopts = {variant=british}, - shorttitle = {Mediaeval Pilgrim Routes}, - annotation = {A plain \texttt{article} entry}, -} - -@article{weinberg, - author = {Weinberg, Steven}, - title = {A Model of Leptons}, - journaltitle = {Phys.~Rev.~Lett.}, - date = 1967, - volume = 19, - pages = {1264-1266}, -} - -@article{yoon, - author = {Yoon, Myeong S. and Ryu, Dowook and Kim, Jeongryul and Ahn, - Kyo Han}, - title = {Palladium pincer complexes with reduced bond angle strain: - efficient catalysts for the {Heck} reaction}, - journaltitle = {Organometallics}, - date = 2006, - volume = 25, - number = 10, - pages = {2409-2411}, - indextitle = {Palladium pincer complexes}, -} - -@book{aristotle:anima, - author = {Aristotle}, - title = {De Anima}, - date = 1907, - editor = {Hicks, Robert Drew}, - publisher = cup, - location = {Cambridge}, - keywords = {primary}, - langid = {english}, - langidopts = {variant=british}, - annotation = {A \texttt{book} entry with an \texttt{author} and an - \texttt{editor}}, -} - -@book{aristotle:physics, - author = {Aristotle}, - title = {Physics}, - date = 1929, - translator = {Wicksteed, P. H. and Cornford, F. M.}, - publisher = {G. P. Putnam}, - location = {New York}, - keywords = {primary}, - langid = {english}, - langidopts = {variant=american}, - shorttitle = {Physics}, - annotation = {A \texttt{book} entry with a \texttt{translator} field}, -} - -@book{aristotle:poetics, - author = {Aristotle}, - title = {Poetics}, - date = 1968, - editor = {Lucas, D. W.}, - series = {Clarendon {Aristotle}}, - publisher = {Clarendon Press}, - location = {Oxford}, - keywords = {primary}, - langid = {english}, - langidopts = {variant=british}, - shorttitle = {Poetics}, - annotation = {A \texttt{book} entry with an \texttt{author} and an - \texttt{editor} as well as a \texttt{series} field}, -} - -@book{aristotle:rhetoric, - author = {Aristotle}, - title = {The Rhetoric of {Aristotle} with a commentary by the late {Edward - Meredith Cope}}, - date = 1877, - editor = {Cope, Edward Meredith}, - commentator = {Cope, Edward Meredith}, - volumes = 3, - publisher = cup, - keywords = {primary}, - langid = {english}, - langidopts = {variant=british}, - sorttitle = {Rhetoric of Aristotle}, - indextitle = {Rhetoric of {Aristotle}, The}, - shorttitle = {Rhetoric}, - annotation = {A commented edition. Note the concatenation of the - \texttt{editor} and \texttt{commentator} fields as well as the - \texttt{volumes}, \texttt{sorttitle}, and \texttt{indextitle} - fields}, -} - -@book{augustine, - author = {Augustine, Robert L.}, - title = {Heterogeneous catalysis for the synthetic chemist}, - date = 1995, - publisher = {Marcel Dekker}, - location = {New York}, - langid = {english}, - langidopts = {variant=american}, - shorttitle = {Heterogeneous catalysis}, - annotation = {A plain \texttt{book} entry}, -} - -@book{averroes/bland, - author = {Averroes}, - title = {The Epistle on the Possibility of Conjunction with the Active - Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, - date = 1982, - editor = {Bland, Kalman P.}, - translator = {Bland, Kalman P.}, - series = {Moreshet: Studies in {Jewish} History, Literature and Thought}, - number = 7, - publisher = {Jewish Theological Seminary of America}, - location = {New York}, - keywords = {primary}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Epistle on the Possibility of Conjunction, The}, - shorttitle = {Possibility of Conjunction}, - annotation = {A \texttt{book} entry with a \texttt{series} and a - \texttt{number}. Note the concatenation of the \texttt{editor} - and \texttt{translator} fields as well as the - \texttt{indextitle} field}, -} - -@book{averroes/hannes, - author = {Averroes}, - title = {Des Averro{\"e}s Abhandlung: \mkbibquote{{\"U}ber die - M{\"o}glichkeit der Conjunktion} oder \mkbibquote{{\"U}ber den - materiellen Intellekt}}, - date = 1892, - editor = {Hannes, Ludwig}, - translator = {Hannes, Ludwig}, - annotator = {Hannes, Ludwig}, - publisher = {C.~A. Kaemmerer}, - location = {Halle an der Saale}, - keywords = {primary}, - langid = {german}, - sorttitle = {Uber die Moglichkeit der Conjunktion}, - indexsorttitle= {Uber die Moglichkeit der Conjunktion}, - indextitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, - shorttitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, - annotation = {An annotated edition. Note the concatenation of the - \texttt{editor}, \texttt{translator}, and \texttt{annotator} - fields. Also note the \texttt{shorttitle}, - \texttt{indextitle}, \texttt{sorttitle}, and - \texttt{indexsorttitle} fields}, -} - -@book{averroes/hercz, - author = {Averroes}, - title = {Drei Abhandlungen {\"u}ber die Conjunction des separaten - Intellects mit dem Menschen}, - date = 1869, - editor = {Hercz, J.}, - translator = {Hercz, J.}, - publisher = {S.~Hermann}, - location = {Berlin}, - keywords = {primary}, - langid = {german}, - indexsorttitle= {Drei Abhandlungen uber die Conjunction}, - indextitle = {Drei Abhandlungen {\"u}ber die Conjunction}, - subtitle = {Von Averroes (Vater und Sohn), aus dem Arabischen - {\"u}bersetzt von Samuel Ibn Tibbon}, - shorttitle = {Drei Abhandlungen}, - annotation = {A \texttt{book} entry. Note the concatenation of the - \texttt{editor} and \texttt{translator} fields as well as the - \texttt{indextitle} and \texttt{indexsorttitle} fields}, -} - -@book{cicero, - author = {Cicero, Marcus Tullius}, - title = {De natura deorum. {\"U}ber das Wesen der G{\"o}tter}, - date = 1995, - editor = {Blank-Sangmeister, Ursula}, - translator = {Blank-Sangmeister, Ursula}, - afterword = {Thraede, Klaus}, - language = {langlatin and langgerman}, - publisher = {Reclam}, - location = {Stuttgart}, - langid = {german}, - indextitle = {De natura deorum}, - shorttitle = {De natura deorum}, - annotation = {A bilingual edition of Cicero's \emph{De natura deorum}, with - a German translation. Note the format of the \texttt{language} - field in the database file, the concatenation of the - \texttt{editor} and \texttt{translator} fields, and the - \texttt{afterword} field}, -} - -@book{coleridge, - author = {Coleridge, Samuel Taylor}, - title = {Biographia literaria, or {Biographical} sketches of my literary - life and opinions}, - date = 1983, - editor = {Coburn, Kathleen and Engell, James and Bate, W. Jackson}, - maintitle = {The collected works of {Samuel Taylor Coleridge}}, - volume = 7, - part = 2, - series = {Bollingen Series}, - number = 75, - publisher = {Routledge {and} Kegan Paul}, - location = {London}, - langid = {english}, - langidopts = {variant=british}, - indextitle = {Biographia literaria}, - shorttitle = {Biographia literaria}, - annotation = {One (partial) volume of a multivolume book. This is a - \texttt{book} entry with a \texttt{volume} and a \texttt{part} - field which explicitly refers to the second (physical) part of - the seventh (logical) volume. Also note the \texttt{series} - and \texttt{number} fields}, -} - -@book{companion, - author = {Goossens, Michel and Mittelbach, Frank and Samarin, Alexander}, - title = {The {LaTeX} Companion}, - date = 1994, - edition = 1, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - pagetotal = 528, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {LaTeX Companion}, - indextitle = {LaTeX Companion, The}, - shorttitle = {LaTeX Companion}, - annotation = {A book with three authors. Note the formatting of the author - list. By default, only the first name is reversed in the - bibliography}, -} - -@book{cotton, - author = {Cotton, Frank Albert and Wilkinson, Geoffrey and Murillio, - Carlos A. and Bochmann, Manfred}, - title = {Advanced inorganic chemistry}, - date = 1999, - edition = 6, - publisher = {Wiley}, - location = {Chichester}, - langid = {english}, - langidopts = {variant=british}, - annotation = {A \texttt{book} entry with \arabic{author} authors and an - \texttt{edition} field. By default, long \texttt{author} and - \texttt{editor} lists are automatically truncated. This is - configurable}, -} - -@book{gerhardt, - author = {Gerhardt, Michael J.}, - title = {The Federal Appointments Process}, - date = 2000, - publisher = {Duke University Press}, - location = {Durham and London}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Federal Appointments Process}, - indextitle = {Federal Appointments Process, The}, - subtitle = {A Constitutional and Historical Analysis}, - shorttitle = {Federal Appointments Process}, - annotation = {This is a \texttt{book} entry. Note the format of the - \texttt{location} field as well as the \texttt{sorttitle} and - \texttt{indextitle} fields}, -} - -@book{gonzalez, - author = {Gonzalez, Ray}, - title = {The Ghost of {John Wayne} and Other Stories}, - date = 2001, - publisher = {The University of Arizona Press}, - location = {Tucson}, - isbn = {0-816-52066-6}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Ghost of John Wayne and Other Stories}, - indextitle = {Ghost of {John Wayne} and Other Stories, The}, - shorttitle = {Ghost of {John Wayne}}, - annotation = {A collection of short stories. This is a \texttt{book} entry. - Note the \texttt{sorttitle} and \texttt{indextitle} fields in - the database file. There's also an \texttt{isbn} field}, -} - -@book{hammond, - author = {Hammond, Christopher}, - title = {The basics of crystallography and diffraction}, - date = 1997, - publisher = {International Union of Crystallography and Oxford University - Press}, - location = {Oxford}, - langid = {english}, - langidopts = {variant=british}, - sorttitle = {Basics of crystallography and diffraction}, - indextitle = {Basics of crystallography and diffraction, The}, - shorttitle = {Crystallography and diffraction}, - annotation = {A \texttt{book} entry. Note the \texttt{sorttitle} and - \texttt{indextitle} fields as well as the format of the - \texttt{publisher} field}, -} - -@book{iliad, - author = {Homer}, - title = {Die Ilias}, - date = 2004, - translator = {Schadewaldt, Wolfgang}, - introduction = {Latacz, Joachim}, - edition = 3, - publisher = {Artemis \& Winkler}, - location = {D{\"u}sseldorf and Z{\"u}rich}, - langid = {german}, - sorttitle = {Ilias}, - indextitle = {Ilias, Die}, - shorttitle = {Ilias}, - annotation = {A German translation of the \emph{Iliad}. Note the - \texttt{translator} and \texttt{introduction} fields and the - format of the \texttt{location} field in the database - file. Also note the \texttt{sorttitle} and \texttt{indextitle} - fields}, -} - -@book{knuth:ct, - author = {Knuth, Donald E.}, - title = {Computers \& Typesetting}, - date = {1984/1986}, - volumes = 5, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1984-0}, - sorttitle = {Computers & Typesetting}, - indexsorttitle= {Computers & Typesetting}, - annotation = {A five-volume book cited as a whole. This is a \texttt{book} - entry, note the \texttt{volumes} field}, -} - -@book{knuth:ct:a, - author = {Knuth, Donald E.}, - title = {The {\TeX} book}, - date = 1984, - maintitle = {Computers \& Typesetting}, - volume = {A}, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1984-1}, - sorttitle = {Computers & Typesetting A}, - indexsorttitle= {The TeXbook}, - indextitle = {\protect\TeX book, The}, - shorttitle = {\TeX book}, - annotation = {The first volume of a five-volume book. Note the - \texttt{sorttitle} and \texttt{sortyear} fields. We want this - volume to be listed after the entry referring to the entire - five-volume set. Also note the \texttt{indextitle} and - \texttt{indexsorttitle} fields. Indexing packages that don't - generate robust index entries require some control sequences - to be protected from expansion}, -} - -@book{knuth:ct:b, - author = {Knuth, Donald E.}, - title = {\TeX: The Program}, - date = 1986, - maintitle = {Computers \& Typesetting}, - volume = {B}, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1986-1}, - sorttitle = {Computers & Typesetting B}, - indexsorttitle= {TeX: The Program}, - shorttitle = {\TeX}, - annotation = {The second volume of a five-volume book. Note the - \texttt{sorttitle} and \texttt{sortyear} fields. Also note the - \texttt{indexsorttitle} field}, -} - -@book{knuth:ct:c, - author = {Knuth, Donald E.}, - title = {The {METAFONTbook}}, - date = 1986, - maintitle = {Computers \& Typesetting}, - volume = {C}, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1986-2}, - sorttitle = {Computers & Typesetting C}, - indextitle = {METAFONTbook, The}, - shorttitle = {METAFONTbook}, - annotation = {The third volume of a five-volume book. Note the - \texttt{sorttitle} and \texttt{sortyear} fields as well as the - \texttt{indextitle} field}, -} - -@book{knuth:ct:d, - author = {Knuth, Donald E.}, - title = {METAFONT: The Program}, - date = 1986, - maintitle = {Computers \& Typesetting}, - volume = {D}, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1986-3}, - sorttitle = {Computers & Typesetting D}, - shorttitle = {METAFONT}, - annotation = {The fourth volume of a five-volume book. Note the - \texttt{sorttitle} and \texttt{sortyear} fields}, -} - -@book{knuth:ct:e, - author = {Knuth, Donald E.}, - title = {{Computer Modern} Typefaces}, - date = 1986, - maintitle = {Computers \& Typesetting}, - volume = {E}, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1986-4}, - sorttitle = {Computers & Typesetting E}, - annotation = {The fifth volume of a five-volume book. Note the - \texttt{sorttitle} and \texttt{sortyear} fields}, -} - -@book{knuth:ct:related, - author = {Knuth, Donald E.}, - title = {Computers \& Typesetting}, - date = {1984/1986}, - volumes = 5, - publisher = {Addison-Wesley}, - location = {Reading, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sortyear = {1984-0}, - sorttitle = {Computers & Typesetting}, - indexsorttitle= {Computers & Typesetting}, - related = {knuth:ct:a,knuth:ct:b,knuth:ct:c,knuth:ct:d,knuth:ct:e}, - relatedtype = {multivolume}, - annotation = {A five-volume book cited as a whole and related to its - individual volumes. Note the \texttt{related} and - \texttt{relatedtype} fields}, -} - -@book{kullback, - author = {Kullback, Solomon}, - title = {Information Theory and Statistics}, - year = 1959, - publisher = {John Wiley \& Sons}, - location = {New York}, - langid = {english}, - langidopts = {variant=american}, -} - -@book{kullback:reprint, - author = {Kullback, Solomon}, - title = {Information Theory and Statistics}, - year = 1997, - publisher = {Dover Publications}, - location = {New York}, - origyear = 1959, - origpublisher= {John Wiley \& Sons}, - langid = {english}, - langidopts = {variant=american}, - annotation = {A reprint of the \texttt{kullback} entry. Note the format of - \texttt{origyear} and \texttt{origpublisher}. These fields are - not used by the standard bibliography styles}, -} - -@book{kullback:related, - author = {Kullback, Solomon}, - title = {Information Theory and Statistics}, - year = 1997, - publisher = {Dover Publications}, - location = {New York}, - langid = {english}, - langidopts = {variant=american}, - related = {kullback}, - relatedtype = {origpubin}, - annotation = {A reprint of the \texttt{kullback} entry. Note the format of - the \texttt{related} and \texttt{relatedtype} fields}, -} - -@book{malinowski, - author = {Malinowski, Bronis{\l}aw}, - title = {Argonauts of the {Western Pacific}}, - date = 1972, - edition = 8, - publisher = {Routledge {and} Kegan Paul}, - location = {London}, - langid = {english}, - langidopts = {variant=british}, - subtitle = {An account of native enterprise and adventure in the - Archipelagoes of {Melanesian New Guinea}}, - shorttitle = {Argonauts}, - annotation = {This is a \texttt{book} entry. Note the format of the - \texttt{publisher} and \texttt{edition} fields as well as the - \texttt{subtitle} field}, -} - -@book{maron, - author = {Maron, Monika}, - title = {Animal Triste}, - date = 2000, - translator = {Brigitte Goldstein}, - origlanguage = {german}, - publisher = {University of Nebraska Press}, - location = {Lincoln}, - langid = {english}, - langidopts = {variant=american}, - shorttitle = {Animal Triste}, - annotation = {An English translation of a German novel with a French title. - In other words: a \texttt{book} entry with a - \texttt{translator} field. Note the \texttt{origlanguage} - field which is concatenated with the \texttt{translator}}, -} - -@book{massa, - author = {Werner Massa}, - title = {Crystal structure determination}, - date = 2004, - edition = 2, - publisher = {Spinger}, - location = {Berlin}, - langid = {english}, - langidopts = {variant=british}, - annotation = {A \texttt{book} entry with an \texttt{edition} field}, -} - -@article{moore, - author = {Moore, Gordon E.}, - title = {Cramming more components onto integrated circuits}, - journaltitle = {Electronics}, - year = 1965, - volume = 38, - number = 8, - pages = {114-117}, - langid = {english}, - langidopts = {variant=american}, -} - -@article{moore:related, - author = {Moore, Gordon E.}, - title = {Cramming more components onto integrated circuits}, - journaltitle = {Proceedings of the {IEEE}}, - year = 1998, - volume = 86, - number = 1, - pages = {82-85}, - langid = {english}, - langidopts = {variant=american}, - related = {moore}, - relatedtype = {reprintfrom}, - annotation = {A reprint of Moore's law. Note the \texttt{related} and - \texttt{relatedtype} fields}, -} - -@book{nietzsche:ksa, - author = {Nietzsche, Friedrich}, - title = {S{\"a}mtliche Werke}, - date = 1988, - editor = {Colli, Giorgio and Montinari, Mazzino}, - edition = 2, - volumes = 15, - publisher = dtv # { and Walter de Gruyter}, - location = {M{\"u}nchen and Berlin and New York}, - langid = {german}, - sortyear = {1988-0}, - sorttitle = {Werke-00-000}, - indexsorttitle= {Samtliche Werke}, - subtitle = {Kritische Studienausgabe}, - annotation = {The critical edition of Nietzsche's works. This is a - \texttt{book} entry referring to a 15-volume work as a - whole. Note the \texttt{volumes} field and the format of the - \texttt{publisher} and \texttt{location} fields in the - database file. Also note the \texttt{sorttitle} and - \texttt{sortyear} fields which are used to fine-tune the - sorting order of the bibliography. We want this item listed - first in the bibliography}, -} - -@book{nietzsche:ksa1, - author = {Nietzsche, Friedrich}, - title = {Die Geburt der Trag{\"o}die. Unzeitgem{\"a}{\ss}e - Betrachtungen I--IV. Nachgelassene Schriften 1870--1973}, - date = 1988, - editor = {Colli, Giorgio and Montinari, Mazzino}, - maintitle = {S{\"a}mtliche Werke}, - mainsubtitle = {Kritische Studienausgabe}, - volume = 1, - edition = 2, - publisher = dtv # { and Walter de Gruyter}, - location = {M{\"u}nchen and Berlin and New York}, - langid = {german}, - sortyear = {1988-1}, - sorttitle = {Werke-01-000}, - indexsorttitle= {Samtliche Werke I}, - bookauthor = {Nietzsche, Friedrich}, - indextitle = {S{\"a}mtliche Werke I}, - shorttitle = {S{\"a}mtliche Werke I}, - annotation = {A single volume from the critical edition of Nietzsche's - works. This \texttt{book} entry explicitly refers to the first - volume only. Note the \texttt{title} and \texttt{maintitle} - fields. Also note the \texttt{sorttitle} and \texttt{sortyear} - fields. We want this entry to be listed after the entry - referring to the entire edition}, -} - -@book{nussbaum, - author = {Nussbaum, Martha}, - title = {Aristotle's \mkbibquote{De Motu Animalium}}, - date = 1978, - publisher = pup, - location = {Princeton}, - keywords = {secondary}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Aristotle's De Motu Animalium}, - indexsorttitle= {Aristotle's De Motu Animalium}, - annotation = {A \texttt{book} entry. Note the \texttt{sorttitle} and - \texttt{indexsorttitle} fields and the markup of the quotes in - the database file}, -} - -@book{piccato, - author = {Piccato, Pablo}, - title = {City of Suspects}, - date = 2001, - publisher = {Duke University Press}, - location = {Durham and London}, - langid = {english}, - langidopts = {variant=american}, - subtitle = {Crime in {Mexico City}, 1900--1931}, - shorttitle = {City of Suspects}, - annotation = {This is a \texttt{book} entry. Note the format of the - \texttt{location} field in the database file}, -} - -@book{vangennep, - author = {van Gennep, Arnold}, - title = {Les rites de passage}, - date = 1909, - publisher = {Nourry}, - location = {Paris}, - options = {useprefix}, - langid = {french}, - sorttitle = {Rites de passage}, - indextitle = {Rites de passage, Les}, - shorttitle = {Rites de passage}, - annotation = {A \texttt{book} entry. Note the format of the printed name and - compare the \texttt{useprefix} option in the \texttt{options} - field as well as \texttt{brandt} and \texttt{geer}}, -} - -@book{vangennep:trans, - author = {van Gennep, Arnold}, - title = {The Rites of Passage}, - year = 1960, - translator = {Vizedom, Monika B. and Caffee, Gabrielle L.}, - language = {english}, - origlanguage = {french}, - publisher = {University of Chicago Press}, - options = {useprefix}, - indextitle = {Rites of Passage, The}, - sorttitle = {Rites of Passage}, - shorttitle = {Rites of Passage}, - langid = {english}, - langidopts = {variant=american}, - annotation = {A translation of the \texttt{vangennep} entry. Note the - \texttt{translator} and \texttt{origlanguage} fields. Compare - with the \texttt{vangennep:related} entry.}, -} - -@book{vangennep:related, - author = {van Gennep, Arnold}, - title = {Les rites de passage}, - date = 1909, - publisher = {Nourry}, - location = {Paris}, - options = {useprefix}, - langid = {french}, - related = {vizedom:related}, - relatedtype = {bytranslator}, - sorttitle = {Rites de passage}, - indextitle = {Rites de passage, Les}, - shorttitle = {Rites de passage}, - annotation = {A variant of the \texttt{vangennep} entry related to its - translation. Note the format of the \texttt{related} and - \texttt{relatedtype} fields}, -} - -@book{vizedom:related, - title = {The Rites of Passage}, - year = 1960, - translator = {Vizedom, Monika B. and Caffee, Gabrielle L.}, - language = {english}, - publisher = {University of Chicago Press}, - langid = {english}, - langidopts = {variant=american}, - options = {usetranslator}, - related = {vangennep}, - relatedtype = {translationof}, - indextitle = {Rites of Passage, The}, - sorttitle = {Rites of Passage}, - shorttitle = {Rites of Passage}, - annotation = {A translated work from \texttt{vangennep}. Note the format of - the \texttt{related} and \texttt{relatedtype} fields}, -} - -@book{vazques-de-parga, - author = {V{\'a}zques{ de }Parga, Luis and Lacarra, Jos{\'e} Mar{\'i}a - and Ur{\'i}a R{\'i}u, Juan}, - title = {Las Peregrinaciones a Santiago de Compostela}, - date = 1993, - volumes = 3, - note = {Ed. facs. de la realizada en 1948--49}, - publisher = {Iberdrola}, - location = {Pamplona}, - langid = {spanish}, - sorttitle = {Peregrinaciones a Santiago de Compostela}, - indextitle = {Peregrinaciones a Santiago de Compostela, Las}, - shorttitle = {Peregrinaciones}, - annotation = {A multivolume book cited as a whole. This is a \texttt{book} - entry with \texttt{volumes}, \texttt{note}, - \texttt{sorttitle}, and \texttt{indextitle} fields}, -} - -@book{wilde, - author = {Wilde, Oscar}, - title = {The Importance of Being Earnest: A Trivial Comedy for Serious - People}, - year = 1899, - series = {English and {American} drama of the Nineteenth Century}, - publisher = {Leonard Smithers {and} Company}, - eprint = {4HIWAAAAYAAJ}, - eprinttype = {googlebooks}, - annotation = {A \texttt{book} with \texttt{eprint} and \texttt{eprinttype} - fields.}, -} - -@book{worman, - author = {Worman, Nancy}, - title = {The Cast of Character}, - date = 2002, - publisher = {University of Texas Press}, - location = {Austin}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Cast of Character}, - indextitle = {Cast of Character, The}, - subtitle = {Style in {Greek} Literature}, - shorttitle = {Cast of Character}, - annotation = {A \texttt{book} entry. Note the \texttt{sorttitle} and - \texttt{indextitle} fields}, -} - -@collection{britannica, - editor = {Preece, Warren E.}, - title = {The {New Encyclop{\ae}dia Britannica}}, - date = 2003, - edition = 15, - volumes = 32, - publisher = {Encyclop{\ae}dia Britannica}, - location = {Chicago, Ill.}, - options = {useeditor=false}, - label = {EB}, - langid = {english}, - langidopts = {variant=british}, - sorttitle = {Encyclop{\ae}dia Britannica}, - indextitle = {{Encyclop{\ae}dia Britannica}, The {New}}, - shorttitle = {{Encyclop{\ae}dia Britannica}}, - annotation = {This is a \texttt{collection} entry for an encyclopedia. Note - the \texttt{useeditor} option in the \texttt{options} field as - well as the \texttt{sorttitle} field. We want this entry to be - cited and alphabetized by title even though there is an - editor. In addition to that, we want the title to be - alphabetized under \enquote*{E} rather than \enquote*{T}. Also - note the \texttt{label} field which is provided for - author-year citation styles}, -} - -@collection{gaonkar, - editor = {Gaonkar, Dilip Parameshwar}, - title = {Alternative Modernities}, - date = 2001, - publisher = {Duke University Press}, - location = {Durham and London}, - isbn = {0-822-32714-7}, - langid = {english}, - langidopts = {variant=american}, - annotation = {This is a \texttt{collection} entry. Note the format of the - \texttt{location} field in the database file as well as the - \texttt{isbn} field}, -} - -@InCollection{gaonkar:in, - author = {Gaonkar, Dilip Parameshwar}, - editor = {Gaonkar, Dilip Parameshwar}, - title = {On Alternative Modernities}, - date = 2001, - booktitle = {Alternative Modernities}, - publisher = {Duke University Press}, - location = {Durham and London}, - isbn = {0-822-32714-7}, - pages = {1-23}, -} - -@collection{jaffe, - editor = {Jaff{\'e}, Philipp}, - title = {Regesta Pontificum Romanorum ab condita ecclesia ad annum post - Christum natum \textsc{mcxcviii}}, - date = {1885/1888}, - editora = {Loewenfeld, Samuel and Kaltenbrunner, Ferdinand and Ewald, - Paul}, - edition = 2, - volumes = 2, - location = {Leipzig}, - langid = {latin}, - editoratype = {redactor}, - indextitle = {Regesta Pontificum Romanorum}, - shorttitle = {Regesta Pontificum Romanorum}, - annotation = {A \texttt{collection} entry with \texttt{edition} and - \texttt{volumes} fields. Note the \texttt{editora} and - \texttt{editoratype} fields}, -} - -@collection{westfahl:frontier, - editor = {Westfahl, Gary}, - title = {Space and Beyond}, - date = 2000, - subtitle = {The Frontier Theme in Science Fiction}, - publisher = {Greenwood}, - location = {Westport, Conn. and London}, - langid = {english}, - langidopts = {variant=american}, - booktitle = {Space and Beyond}, - booksubtitle = {The Frontier Theme in Science Fiction}, - annotation = {This is a \texttt{collection} entry. Note the format of the - \texttt{location} field as well as the \texttt{subtitle} and - \texttt{booksubtitle} fields}, -} - -@inbook{kant:kpv, - title = {Kritik der praktischen Vernunft}, - date = 1968, - author = {Kant, Immanuel}, - booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, - bookauthor = {Kant, Immanuel}, - maintitle = {Kants Werke. Akademie Textausgabe}, - volume = 5, - publisher = {Walter de Gruyter}, - location = {Berlin}, - pages = {1-163}, - shorthand = {KpV}, - langid = {german}, - shorttitle = {Kritik der praktischen Vernunft}, - annotation = {An edition of Kant's \emph{Collected Works}, volume five. This - is an \texttt{inbook} entry which explicitly refers to the - \emph{Critique of Practical Reason} only, not to the entire - fifth volume. Note the \texttt{author} and \texttt{bookauthor} - fields in the database file. By default, the - \texttt{bookauthor} is omitted if the values of the - \texttt{author} and \texttt{bookauthor} fields are identical}, -} - -@inbook{kant:ku, - title = {Kritik der Urtheilskraft}, - date = 1968, - author = {Kant, Immanuel}, - booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, - bookauthor = {Kant, Immanuel}, - maintitle = {Kants Werke. Akademie Textausgabe}, - volume = 5, - publisher = {Walter de Gruyter}, - location = {Berlin}, - pages = {165-485}, - shorthand = {KU}, - langid = {german}, - annotation = {An edition of Kant's \emph{Collected Works}, volume five. This - is an \texttt{inbook} entry which explicitly refers to the - \emph{Critique of Judgment} only, not to the entire fifth - volume}, -} - -@inbook{nietzsche:historie, - title = {Unzeitgem{\"a}sse Betrachtungen. Zweites St{\"u}ck}, - date = 1988, - author = {Nietzsche, Friedrich}, - booktitle = {Die Geburt der Trag{\"o}die. Unzeitgem{\"a}{\ss}e - Betrachtungen I--IV. Nachgelassene Schriften 1870--1973}, - bookauthor = {Nietzsche, Friedrich}, - editor = {Colli, Giorgio and Montinari, Mazzino}, - subtitle = {Vom Nutzen und Nachtheil der Historie f{\"u}r das Leben}, - maintitle = {S{\"a}mtliche Werke}, - mainsubtitle = {Kritische Studienausgabe}, - volume = 1, - publisher = dtv # { and Walter de Gruyter}, - location = {M{\"u}nchen and Berlin and New York}, - pages = {243-334}, - langid = {german}, - sortyear = {1988-2}, - sorttitle = {Werke-01-243}, - indexsorttitle= {Vom Nutzen und Nachtheil der Historie fur das Leben}, - indextitle = {Vom Nutzen und Nachtheil der Historie f{\"u}r das Leben}, - shorttitle = {Vom Nutzen und Nachtheil der Historie}, - annotation = {A single essay from the critical edition of Nietzsche's works. - This \texttt{inbook} entry explicitly refers to an essay found - in the first volume. Note the \texttt{title}, - \texttt{booktitle}, and \texttt{maintitle} fields. Also note - the \texttt{sorttitle} and \texttt{sortyear} fields. We want - this entry to be listed after the entry referring to the - entire first volume}, -} - -@incollection{brandt, - author = {von Brandt, Ahasver and Erich Hoffmann}, - editor = {Ferdinand Seibt}, - title = {Die nordischen L{\"a}nder von der Mitte des 11.~Jahrhunderts - bis 1448}, - date = 1987, - booktitle = {Europa im Hoch- und Sp{\"a}tmittelalter}, - series = {Handbuch der europ{\"a}ischen Geschichte}, - number = 2, - publisher = {Klett-Cotta}, - location = {Stuttgart}, - pages = {884-917}, - options = {useprefix=false}, - langid = {german}, - indexsorttitle= {Nordischen Lander von der Mitte des 11. Jahrhunderts bis - 1448}, - indextitle = {Nordischen L{\"a}nder von der Mitte des 11.~Jahrhunderts bis - 1448, Die}, - shorttitle = {Die nordischen L{\"a}nder}, - annotation = {An \texttt{incollection} entry with a \texttt{series} and a - \texttt{number}. Note the format of the printed name and - compare the \texttt{useprefix} option in the \texttt{options} - field as well as \texttt{vangennep}. Also note the - \texttt{indextitle, and \texttt{indexsorttitle} fields}}, -} - -@incollection{hyman, - author = {Arthur Hyman}, - editor = {O'Meara, Dominic J.}, - title = {Aristotle's Theory of the Intellect and its Interpretation by - {Averroes}}, - date = 1981, - booktitle = {Studies in {Aristotle}}, - series = {Studies in Philosophy and the History of Philosophy}, - number = 9, - publisher = {The Catholic University of America Press}, - location = {Washington, D.C.}, - pages = {161-191}, - keywords = {secondary}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Aristotle's Theory of the Intellect}, - shorttitle = {Aristotle's Theory of the Intellect}, - annotation = {An \texttt{incollection} entry with a \texttt{series} and - \texttt{number} field}, -} - -@incollection{pines, - author = {Pines, Shlomo}, - editor = {Twersky, Isadore}, - title = {The Limitations of Human Knowledge According to {Al-Farabi}, {ibn - Bajja}, and {Maimonides}}, - date = 1979, - booktitle = {Studies in Medieval {Jewish} History and Literature}, - publisher = hup, - location = {Cambridge, Mass.}, - pages = {82-109}, - keywords = {secondary}, - langid = {english}, - langidopts = {variant=american}, - indextitle = {Limitations of Human Knowledge According to {Al-Farabi}, {ibn - Bajja}, and {Maimonides}, The}, - shorttitle = {Limitations of Human Knowledge}, - annotation = {A typical \texttt{incollection} entry. Note the - \texttt{indextitle} field}, -} - -@inproceedings{moraux, - author = {Moraux, Paul}, - editor = {Lloyd, G. E. R. and Owen, G. E. L.}, - title = {Le \emph{De Anima} dans la tradition gr{\`e}cque}, - date = 1979, - booktitle = {Aristotle on Mind and the Senses}, - subtitle = {Quelques aspects de l'interpretation du trait{\'e}, de - Theophraste {\`a} Themistius}, - booktitleaddon= {Proceedings of the Seventh Symposium Aristotelicum}, - eventdate = 1975, - publisher = cup, - location = {Cambridge}, - pages = {281-324}, - keywords = {secondary}, - langid = {french}, - indexsorttitle= {De Anima dans la tradition grecque}, - indextitle = {\emph{De Anima} dans la tradition gr{\`e}cque, Le}, - shorttitle = {\emph{De Anima} dans la tradition gr{\`e}cque}, - annotation = {This is a typical \texttt{inproceedings} entry. Note the - \texttt{booksubtitle}, \texttt{shorttitle}, - \texttt{indextitle}, and \texttt{indexsorttitle} fields. Also - note the \texttt{eventdate} field.}, -} - -@inproceedings{salam, - author = {Salam, Abdus}, - editor = {Svartholm, Nils}, - title = {Weak and Electromagnetic Interactions}, - date = 1968, - booktitle = {Elementary particle theory}, - booksubtitle = {Relativistic groups and analyticity}, - booktitleaddon= {Proceedings of the {Eighth Nobel Symposium}}, - eventdate = {1968-05-19/1968-05-25}, - venue = {Aspen{\"a}sgarden, Lerum}, - publisher = {Almquist \& Wiksell}, - location = {Stockholm}, - pages = {367-377}, -} - -@manual{cms, - title = {The {Chicago} Manual of Style}, - date = 2003, - subtitle = {The Essential Guide for Writers, Editors, and Publishers}, - edition = 15, - publisher = {University of Chicago Press}, - location = {Chicago, Ill.}, - isbn = {0-226-10403-6}, - label = {CMS}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Chicago Manual of Style}, - indextitle = {Chicago Manual of Style, The}, - shorttitle = {Chicago Manual of Style}, - annotation = {This is a \texttt{manual} entry without an \texttt{author} or - \texttt{editor}. Note the \texttt{label} field in the database - file which is provided for author-year citation styles. Also - note the \texttt{sorttitle} and \texttt{indextitle} fields. By - default, all entries without an \texttt{author} or - \texttt{editor} are alphabetized by \texttt{title} but we want - this entry to be alphabetized under \enquote*{C} rather than - \enquote*{T}. There's also an \texttt{isbn} field}, -} - -@online{baez/online, - author = {Baez, John C. and Lauda, Aaron D.}, - title = {Higher-Dimensional Algebra {V}: 2-Groups}, - date = {2004-10-27}, - version = 3, - langid = {english}, - langidopts = {variant=american}, - eprinttype = {arxiv}, - eprint = {math/0307200v3}, - annotation = {An \texttt{online} reference from arXiv. Note the - \texttt{eprint} and \texttt{eprinttype} fields. Compare - \texttt{baez\slash article} which is the same item given as an - \texttt{article} entry with eprint information}, -} - -@online{ctan, - title = {CTAN}, - date = 2006, - url = {http://www.ctan.org}, - subtitle = {The {Comprehensive TeX Archive Network}}, - urldate = {2006-10-01}, - label = {CTAN}, - langid = {english}, - langidopts = {variant=american}, - annotation = {This is an \texttt{online} entry. The \textsc{url}, which is - given in the \texttt{url} field, is transformed into a - clickable link if \texttt{hyperref} support has been - enabled. Note the format of the \texttt{urldate} field - (\texttt{yyyy-mm-dd}) in the database file. Also note the - \texttt{label} field which may be used as a fallback by - citation styles which need an \texttt{author} and\slash or a - \texttt{year}}, -} - -@online{itzhaki, - author = {Itzhaki, Nissan}, - title = {Some remarks on {'t Hooft's} {S}-matrix for black holes}, - date = {1996-03-11}, - version = 1, - langid = {english}, - langidopts = {variant=american}, - eprinttype = {arxiv}, - eprint = {hep-th/9603067}, - annotation = {An \texttt{online} reference from arXiv. Note the - \texttt{eprint} and \texttt{eprinttype} fields. Also note that - the arXiv reference is transformed into a clickable link if - \texttt{hyperref} support has been enabled}, - abstract = {We discuss the limitations of 't Hooft's proposal for the - black hole S-matrix. We find that the validity of the S-matrix - implies violation of the semi-classical approximation at - scales large compared to the Planck scale. We also show that - the effect of the centrifugal barrier on the S-matrix is - crucial even for large transverse distances.}, -} - -@online{markey, - author = {Markey, Nicolas}, - title = {Tame the {BeaST}}, - date = {2005-10-16}, - url = {http://mirror.ctan.org/info/bibtex/tamethebeast/ttb_en.pdf}, - subtitle = {The {B} to {X} of {BibTeX}}, - version = {1.3}, - urldate = {2006-10-01}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Tame the Beast}, - annotation = {An \texttt{online} entry for a tutorial. Note the format of - the \texttt{date} field (\texttt{yyyy-mm-dd}) in the database - file.}, -} - -@online{wassenberg, - author = {Wassenberg, Jan and Sanders, Peter}, - title = {Faster Radix Sort via Virtual Memory and Write-Combining}, - date = {2010-08-17}, - version = 1, - langid = {english}, - langidopts = {variant=american}, - eprinttype = {arxiv}, - eprintclass = {cs.DS}, - eprint = {1008.2849v1}, - annotation = {A recent \texttt{online} reference from arXiv using the new - (April 2007 onward) identifier format. Note the - \texttt{eprint}, \texttt{eprinttype}, and \texttt{eprintclass} - fields. Also note that the arXiv reference is transformed into - a clickable link if \texttt{hyperref} support has been - enabled}, - abstract = {Sorting algorithms are the deciding factor for the performance - of common operations such as removal of duplicates or database - sort-merge joins. This work focuses on 32-bit integer keys, - optionally paired with a 32-bit value. We present a fast radix - sorting algorithm that builds upon a microarchitecture-aware - variant of counting sort}, -} - -@patent{almendro, - author = {Almendro, Jos{\'e} L. and Mart{\'i}n, Jacinto and S{\'a}nchez, - Alberto and Nozal, Fernando}, - title = {Elektromagnetisches Signalhorn}, - number = {EU-29702195U}, - date = 1998, - location = {countryfr and countryuk and countryde}, - langid = {german}, - annotation = {This is a \texttt{patent} entry with a \texttt{location} - field. The number is given in the \texttt{number} field. Note - the format of the \texttt{location} field in the database - file. Compare \texttt{laufenberg}, \texttt{sorace}, and - \texttt{kowalik}}, -} - -@patent{kowalik, - author = {Kowalik, F. and Isard, M.}, - title = {Estimateur d'un d{\'e}faut de fonctionnement d'un modulateur - en quadrature et {\'e}tage de modulation l'utilisant}, - number = 9500261, - date = {1995-01-11}, - type = {patreqfr}, - langid = {french}, - indextitle = {Estimateur d'un d{\'e}faut de fonctionnement}, - annotation = {This is a \texttt{patent} entry for a French patent request - with a full date. The number is given in the \texttt{number} - field. Note the format of the \texttt{type} and \texttt{date} - fields in the database file. Compare \texttt{almendro}, - \texttt{laufenberg}, and \texttt{sorace}}, -} - -@patent{laufenberg, - author = {Laufenberg, Xaver and Eynius, Dominique and Suelzle, Helmut - and Usbeck, Stephan and Spaeth, Matthias and Neuser-Hoffmann, - Miriam and Myrzik, Christian and Schmid, Manfred and Nietfeld, - Franz and Thiel, Alexander and Braun, Harald and Ebner, - Norbert}, - title = {Elektrische Einrichtung und Betriebsverfahren}, - number = 1700367, - date = {2006-09-13}, - holder = {{Robert Bosch GmbH} and {Daimler Chrysler AG} and {Bayerische - Motoren Werke AG}}, - type = {patenteu}, - langid = {german}, - annotation = {This is a \texttt{patent} entry with a \texttt{holder} field. - Note the format of the \texttt{type} and \texttt{location} - fields in the database file. Compare \texttt{almendro}, - \texttt{sorace}, and \texttt{kowalik}}, - abstract = {The invention relates to an electric device comprising a - generator, in particular for use in the vehicle electric - system of a motor vehicle and a controller for controlling the - generator voltage. The device is equipped with a control zone, - in which the voltage is controlled and zones, in which the - torque is controlled. The invention also relates to methods - for operating a device of this type.}, - file = {http://v3.espacenet.com/textdoc?IDX=EP1700367}, -} - -@patent{sorace, - author = {Sorace, Ronald E. and Reinhardt, Victor S. and Vaughn, Steven - A.}, - title = {High-Speed Digital-to-{RF} Converter}, - number = 5668842, - date = {1997-09-16}, - holder = {{Hughes Aircraft Company}}, - type = {patentus}, - langid = {english}, - langidopts = {variant=american}, - annotation = {This is a \texttt{patent} entry with a \texttt{holder} field. - Note the format of the \texttt{type} and \texttt{date} fields - in the database file. Compare \texttt{almendro}, - \texttt{laufenberg}, and \texttt{kowalik}}, -} - -@periodical{jcg, - title = {Computers and Graphics}, - year = 2011, - issuetitle = {Semantic {3D} Media and Content}, - volume = 35, - number = 4, - issn = {0097-8493}, - annotation = {This is a \texttt{periodical} entry with an \texttt{issn} - field.}, -} - -@report{chiu, - author = {Chiu, Willy W. and Chow, We Min}, - title = {A Hybrid Hierarchical Model of a {Multiple Virtual Storage} - ({MVS}) Operating System}, - type = {resreport}, - institution = {IBM}, - date = 1978, - number = {RC-6947}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {Hybrid Hierarchical Model of a Multiple Virtual Storage (MVS) - Operating System}, - indextitle = {Hybrid Hierarchical Model, A}, - annotation = {This is a \texttt{report} entry for a research report. Note - the format of the \texttt{type} field in the database file - which uses a localization key. The number of the report is - given in the \texttt{number} field. Also note the - \texttt{sorttitle} and \texttt{indextitle} fields}, -} - -@report{padhye, - author = {Padhye, Jitendra and Firoiu, Victor and Towsley, Don}, - title = {A Stochastic Model of {TCP Reno} Congestion Avoidance and - Control}, - type = {techreport}, - institution = {University of Massachusetts}, - date = 1999, - number = {99-02}, - location = {Amherst, Mass.}, - langid = {english}, - langidopts = {variant=american}, - sorttitle = {A Stochastic Model of TCP Reno Congestion Avoidance and - Control}, - indextitle = {Stochastic Model of {TCP Reno} Congestion Avoidance and Control, - A}, - annotation = {This is a \texttt{report} entry for a technical report. Note - the format of the \texttt{type} field in the database file - which uses a localization key. The number of the report is - given in the \texttt{number} field. Also note the - \texttt{sorttitle} and \texttt{indextitle} fields}, - abstract = {The steady state performance of a bulk transfer TCP flow - (i.e. a flow with a large amount of data to send, such as FTP - transfers) may be characterized by three quantities. The first - is the send rate, which is the amount of data sent by the - sender in unit time. The second is the throughput, which is - the amount of data received by the receiver in unit time. Note - that the throughput will always be less than or equal to the - send rate due to losses. Finally, the number of non-duplicate - packets received by the receiver in unit time gives us the - goodput of the connection. The goodput is always less than or - equal to the throughput, since the receiver may receive two - copies of the same packet due to retransmissions by the - sender. In a previous paper, we presented a simple model for - predicting the steady state send rate of a bulk transfer TCP - flow as a function of loss rate and round trip time. In this - paper, we extend that work in two ways. First, we analyze the - performance of bulk transfer TCP flows using more precise, - stochastic analysis. Second, we build upon the previous - analysis to provide both an approximate formula as well as a - more accurate stochastic model for the steady state throughput - of a bulk transfer TCP flow.}, - file = {ftp://gaia.cs.umass.edu/pub/Padhey99-markov.ps}, -} - -@thesis{geer, - author = {de Geer, Ingrid}, - title = {Earl, Saint, Bishop, Skald~-- and Music}, - type = {phdthesis}, - institution = {Uppsala Universitet}, - date = 1985, - subtitle = {The {Orkney Earldom} of the Twelfth Century. {A} Musicological - Study}, - location = {Uppsala}, - options = {useprefix=false}, - langid = {english}, - langidopts = {variant=british}, - annotation = {This is a typical \texttt{thesis} entry for a PhD thesis. Note - the \texttt{type} field in the database file which uses a - localization key. Also note the format of the printed name and - compare the \texttt{useprefix} option in the \texttt{options} - field as well as \texttt{vangennep}}, -} - -@thesis{loh, - author = {Loh, Nin C.}, - title = {High-Resolution Micromachined Interferometric Accelerometer}, - type = {mathesis}, - institution = {Massachusetts Institute of Technology}, - date = 1992, - location = {Cambridge, Mass.}, - langid = {english}, - langidopts = {variant=american}, - annotation = {This is a typical \texttt{thesis} entry for an MA thesis. Note - the \texttt{type} field in the database file which uses a - localization key}, -} diff --git a/test/command/pandoc-citeproc-119.md b/test/command/pandoc-citeproc-119.md index 34ffc93af..cbae7c8fe 100644 --- a/test/command/pandoc-citeproc-119.md +++ b/test/command/pandoc-citeproc-119.md @@ -1,7 +1,7 @@ ``` % pandoc --citeproc -t markdown-citations --- -bibliography: 'command/biblatex-examples.bib' +bibliography: 'command/averroes.bib' csl: command/apa.csl --- -- cgit v1.2.3 From 5772f7f943f377bdc34fb8413ed524c15aaef5f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 May 2021 12:27:59 -0700 Subject: Further test image size reductions. --- test/fb2/images.fb2 | 2 +- test/fb2/test.jpg | Bin 23023 -> 3959 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fb2/images.fb2 b/test/fb2/images.fb2 index 6de9720a0..e666a2ae2 100644 --- a/test/fb2/images.fb2 +++ b/test/fb2/images.fb2 @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="UTF-8"?> -<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info><genre>unrecognised</genre></title-info><document-info><program-used>pandoc</program-used></document-info></description><body><title><p />

This example test if Pandoc correctly embeds images into FictionBook.

Small inline image: alt text a small PNG image.

Paragraph image:

alt text of a big JPEG image

alt text of a big missing image

A missing image inline: alt text of missing image.

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 +unrecognisedpandoc<p />

This example test if Pandoc correctly embeds images into FictionBook.

Small inline image: alt text a small PNG image.

Paragraph image:

alt text of a big JPEG image

alt text of a big missing image

A missing image inline: alt text of missing image.

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
diff --git a/test/fb2/test.jpg b/test/fb2/test.jpg index 2b6c5e8de..05f74b88d 100644 Binary files a/test/fb2/test.jpg and b/test/fb2/test.jpg differ -- cgit v1.2.3 From c210b983662c26c0b827c9199f6f1a2b34c1559e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 29 May 2021 14:36:49 -0700 Subject: Fix test #3752 (1) for Windows. --- test/command/3752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/command/3752.md b/test/command/3752.md index 2e96b531e..6ac025ebe 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -1,5 +1,5 @@ ``` -% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx | pandoc -f docx -t plain +% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx -o - | pandoc -f docx -t plain ^D [INFO] Loaded command/chap1/spider.png from ./command/chap1/spider.png [INFO] Loaded command/chap2/spider.png from ./command/chap2/spider.png -- cgit v1.2.3 From 0d7103de7e893002eedcc50fea7f81aae9535106 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 29 May 2021 14:41:28 -0700 Subject: In rebasePath, check for absolute paths two ways. isAbsolute from FilePath doesn't return True on Windows for paths beginning with `/`, so we check that separately. --- src/Text/Pandoc/Readers/Markdown.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index bc5e3e30f..9a5e0889e 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1924,7 +1924,10 @@ rebasePath :: SourcePos -> Text -> Text rebasePath pos path = do let fp = sourceName pos isFragment = T.take 1 path == "#" - in if T.null path || isFragment || isAbsolute (T.unpack path) || isURI path + -- check for leading / because on Windows this won't be + -- recognized as absolute by isAbsolute + isAbsolutePath = isAbsolute (T.unpack path) || T.take 1 path == "/" + in if T.null path || isFragment || isAbsolutePath || isURI path then path else case takeDirectory fp of -- cgit v1.2.3 From cc6dcf03928404c08cfb3075a90d814b25fd7c69 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 29 May 2021 17:36:30 -0700 Subject: Markdown reader: in rebasePaths, check for both Windows and Posix absolute paths. Previously Windows pandoc was treating `/foo/bar.jpg` as non-absolute. --- src/Text/Pandoc/Readers/Markdown.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 9a5e0889e..1761ee2c1 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -29,7 +29,9 @@ import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import qualified Data.ByteString.Lazy as BL -import System.FilePath (addExtension, takeExtension, isAbsolute, takeDirectory) +import System.FilePath (addExtension, takeExtension, takeDirectory) +import qualified System.FilePath.Windows as Windows +import qualified System.FilePath.Posix as Posix import Text.HTML.TagSoup hiding (Row) import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B @@ -1924,9 +1926,8 @@ rebasePath :: SourcePos -> Text -> Text rebasePath pos path = do let fp = sourceName pos isFragment = T.take 1 path == "#" - -- check for leading / because on Windows this won't be - -- recognized as absolute by isAbsolute - isAbsolutePath = isAbsolute (T.unpack path) || T.take 1 path == "/" + path' = T.unpack path + isAbsolutePath = Posix.isAbsolute path' || Windows.isAbsolute path' in if T.null path || isFragment || isAbsolutePath || isURI path then path else -- cgit v1.2.3 From f363f00db8829b2aba774a588443720098d28ed8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 29 May 2021 19:20:03 -0700 Subject: Use commonmark-extensions 0.2.1.2 --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index 40b0db594..38ca54746 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -452,7 +452,7 @@ library case-insensitive >= 1.2 && < 1.3, citeproc >= 0.4 && < 0.4.1, commonmark >= 0.2 && < 0.3, - commonmark-extensions >= 0.2.1 && < 0.3, + commonmark-extensions >= 0.2.1.2 && < 0.3, commonmark-pandoc >= 0.2.1 && < 0.3, connection >= 0.3.1, containers >= 0.4.2.1 && < 0.7, diff --git a/stack.yaml b/stack.yaml index 27c8c5f19..0c69306ea 100644 --- a/stack.yaml +++ b/stack.yaml @@ -15,7 +15,7 @@ extra-deps: - unicode-collation-0.1.3 - citeproc-0.4 - commonmark-0.2 -- commonmark-extensions-0.2.1 +- commonmark-extensions-0.2.1.2 - commonmark-pandoc-0.2.1 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules -- cgit v1.2.3 From c2f46e6df4a80a313ab4329b740770dbdfbe1578 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 30 May 2021 10:07:28 -0700 Subject: Docx writer: fix regression on captions. The "Table Caption" style was no longer getting applied. (It was overwritten by "Compact.") Closes #7328. --- src/Text/Pandoc/Writers/Docx/Table.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index e7fc82a10..13cb31040 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -33,7 +33,6 @@ tableToOpenXML :: PandocMonad m -> WS m [Content] tableToOpenXML blocksToOpenXML gridTable = do setFirstPara - modify $ \s -> s { stInTable = True } let (Grid.Table _attr caption colspecs _rowheads thead tbodies tfoot) = gridTable let (Caption _maybeShortCaption captionBlocks) = caption @@ -43,6 +42,9 @@ tableToOpenXML blocksToOpenXML gridTable = do then return [] else withParaPropM (pStyleM "Table Caption") $ blocksToOpenXML captionBlocks + -- We set "in table" after processing the caption, because we don't + -- want the "Table Caption" style to be overwritten with "Compact". + modify $ \s -> s { stInTable = True } head' <- cellGridToOpenXML blocksToOpenXML HeadRow aligns thead bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow aligns) tbodies foot' <- cellGridToOpenXML blocksToOpenXML FootRow aligns tfoot -- cgit v1.2.3 From cc206af392a40dd7b01b714ae7f33b2fbf4925cc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 30 May 2021 10:22:02 -0700 Subject: Have LoadedResource use relative paths. The immediate reason for this is to allow the test output of #3752 to work on both windows and linux. --- src/Text/Pandoc/Class/PandocMonad.hs | 4 ++-- test/command/3752.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index b5f401619..4eb80df29 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -66,7 +66,7 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) import System.FilePath ((), takeExtension, dropExtension, - isRelative, splitDirectories) + isRelative, splitDirectories, makeRelative) import System.Random (StdGen) import Text.Collate.Lang (Lang(..), parseLang, renderLang) import Text.Pandoc.Class.CommonState (CommonState (..)) @@ -413,7 +413,7 @@ downloadOrRead s = do (fp', cont) <- if isRelative f then withPaths resourcePath readFileStrict f else (f,) <$> readFileStrict f - report $ LoadedResource f fp' + report $ LoadedResource f (makeRelative "." fp') return (cont, mime) httpcolon = URI{ uriScheme = "http:", uriAuthority = Nothing, diff --git a/test/command/3752.md b/test/command/3752.md index 6ac025ebe..863e3f2d4 100644 --- a/test/command/3752.md +++ b/test/command/3752.md @@ -1,9 +1,9 @@ ``` % pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx -o - | pandoc -f docx -t plain ^D -[INFO] Loaded command/chap1/spider.png from ./command/chap1/spider.png -[INFO] Loaded command/chap2/spider.png from ./command/chap2/spider.png -[INFO] Loaded command/chap1/../../lalune.jpg from ./command/chap1/../../lalune.jpg +[INFO] Loaded command/chap1/spider.png from command/chap1/spider.png +[INFO] Loaded command/chap2/spider.png from command/chap2/spider.png +[INFO] Loaded command/chap1/../../lalune.jpg from command/chap1/../../lalune.jpg Chapter one A spider: [spider] -- cgit v1.2.3 From fc70f44ee2814914c0d32f6dff30fb36cc51bf11 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 30 May 2021 17:15:14 -0700 Subject: HTML reader: fix column width regression. Column widths specified with a style attribute were off by a factor of 100 in 2.14. Closes #7334. --- src/Text/Pandoc/Readers/HTML/Table.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs index 3a569dd0a..6e62e12f5 100644 --- a/src/Text/Pandoc/Readers/HTML/Table.hs +++ b/src/Text/Pandoc/Readers/HTML/Table.hs @@ -49,7 +49,7 @@ pCol = try $ do return $ case lookup "width" attribs of Nothing -> case lookup "style" attribs of Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs -> - maybe (Right ColWidthDefault) (Right . ColWidth) + maybe (Right ColWidthDefault) (Right . ColWidth . (/ 100.0)) $ safeRead (T.filter (`notElem` (" \t\r\n%'\";" :: [Char])) xs) _ -> Right ColWidthDefault -- cgit v1.2.3 From be144dd7de081d10bcc7a4d77b98c1b879f7995d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 31 May 2021 14:46:55 -0600 Subject: Small tweak to 2.14 changelog. --- changelog.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index b8c54dce8..e8761320d 100644 --- a/changelog.md +++ b/changelog.md @@ -283,8 +283,10 @@ (#6639, Albert Krewinkel). The zero-width non-joiner character is used to avoid ligatures (e.g. in German). - * ConTeXt template: List of figures before list of tables (#7235, - Julien Dutant). + * ConTeXt template: + + + Define `enumerate` itemgroup (#5016, Denis Maier). + + List of figures before list of tables (#7235, Julien Dutant). * reveal.js template: -- cgit v1.2.3 From 62f46b3995425c9a3ec87cba0eb8a4d736adec07 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 31 May 2021 21:34:51 -0600 Subject: Fix regression with commonmark/gfm yaml metdata block parsing. A regression in 2.14 led to the document body being omitted after YAML metadata in some cases. This is now fixed. Closes #7339. --- src/Text/Pandoc/Readers/CommonMark.hs | 10 +++++----- test/command/7339.md | 11 +++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 test/command/7339.md diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 228e65312..411d64278 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -30,7 +30,7 @@ import Text.Pandoc.Readers.Metadata (yamlMetaBlock) import Control.Monad.Except import Data.Functor.Identity (runIdentity) import Data.Typeable -import Text.Pandoc.Parsing (runParserT, getPosition, +import Text.Pandoc.Parsing (runParserT, getInput, runF, defaultParserState, option, many1, anyChar, Sources(..), ToSources(..), ParserT, Future, sourceName) @@ -44,14 +44,14 @@ readCommonMark opts s let sources = toSources s let toks = concatMap sourceToToks (unSources sources) res <- runParserT (do meta <- yamlMetaBlock (metaValueParser opts) - pos <- getPosition - return (meta, pos)) + rest <- getInput + return (meta, rest)) defaultParserState "YAML metadata" (toSources s) case res of Left _ -> readCommonMarkBody opts sources toks - Right (meta, pos) -> do + Right (meta, rest) -> do -- strip off metadata section and parse body - let body = dropWhile (\t -> tokPos t < pos) toks + let body = concatMap sourceToToks (unSources rest) Pandoc _ bs <- readCommonMarkBody opts sources body return $ Pandoc (runF meta defaultParserState) bs | otherwise = do diff --git a/test/command/7339.md b/test/command/7339.md new file mode 100644 index 000000000..9697c1c32 --- /dev/null +++ b/test/command/7339.md @@ -0,0 +1,11 @@ +``` +% pandoc -f gfm -s -t native +--- +title: Test +--- + +Hi +^D +Pandoc (Meta {unMeta = fromList [("title",MetaInlines [Str "Test"])]}) +[Para [Str "Hi"]] +``` -- cgit v1.2.3 From 677ccb7138d495e93183f990fe73e4d95535c3ed Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 31 May 2021 22:07:12 -0600 Subject: Bump to 2.14.0.1; update changelog and man page. --- MANUAL.txt | 2 +- changelog.md | 21 +++++++++++++++++++++ man/pandoc.1 | 2 +- pandoc.cabal | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index a076dcb6c..b0c5962c4 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: May 28, 2021 +date: May 31, 2021 --- # Synopsis diff --git a/changelog.md b/changelog.md index e8761320d..c23f2b665 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,26 @@ # Revision history for pandoc +## pandoc 2.14.0.1 (2021-06-01) + + * Commonmark reader: Fix regression in 2.14 with YAML metdata block parsing, + which could cause the document body to be omitted after metadata (#7339). + + * HTML reader: fix column width regression in 2.14 (#7334). + Column widths specified with a style attribute were off by a factor of 100. + + * Markdown reader: in `rebasePaths`, check for both Windows and Posix + absolute paths. Previously Windows pandoc was treating + `/foo/bar.jpg` as non-absolute. + + * Text.Pandoc.Logging: In rendering `LoadedResource`, use relative paths. + + * Docx writer: fix regression on captions (#7328). The "Table Caption" + style was no longer getting applied. (It was overwritten by "Compact.") + + * Use commonmark-extensions 0.2.1.2 + + * Use smaller images in tests, reducing the size of the source tarball by 8 MB. + ## pandoc 2.14 (2021-05-28) * Change reader types, allowing better tracking of source positions diff --git a/man/pandoc.1 b/man/pandoc.1 index 7eec066ab..ae3aba19c 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t .\" Automatically generated by Pandoc 2.14 .\" -.TH "Pandoc User\[cq]s Guide" "" "May 28, 2021" "pandoc 2.14" "" +.TH "Pandoc User\[cq]s Guide" "" "May 31, 2021" "pandoc 2.14.0.1" "" .hy .SH NAME pandoc - general markup converter diff --git a/pandoc.cabal b/pandoc.cabal index 38ca54746..da3244647 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.14 +version: 2.14.0.1 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md -- cgit v1.2.3 From 7225d4a612cd79e0027c5b0ea721577ab4df3f49 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 31 May 2021 22:53:12 -0600 Subject: Move tarball diet item from 2.14.0.1 to 2.14 in changelog. This was actually part of the 2.14 release. --- changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index c23f2b665..880bfc26f 100644 --- a/changelog.md +++ b/changelog.md @@ -19,8 +19,6 @@ * Use commonmark-extensions 0.2.1.2 - * Use smaller images in tests, reducing the size of the source tarball by 8 MB. - ## pandoc 2.14 (2021-05-28) * Change reader types, allowing better tracking of source positions @@ -440,6 +438,8 @@ * Command tests: fail if a file contains no tests---and fix a test that failed in that way! + * Use smaller images in tests, reducing the size of the source tarball by 8 MB. + ## pandoc 2.13 (2021-03-21) -- cgit v1.2.3 From abb59bd58222c67fd36a8e447c01de3404a7ed1d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 1 Jun 2021 13:54:51 -0600 Subject: LaTeX reader: don't allow optional * on symbol control sequences. Generally we allow optional starred variants of LaTeX commands (since many allow them, and if we don't accept these explicitly, ignoring the star usually gives acceptable results). But we don't want to do this for `\(*\)` and similar cases. Closes #7340. --- src/Text/Pandoc/Readers/LaTeX.hs | 6 ++++-- test/command/7340.md | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/command/7340.md diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 2ace18d1b..9e14c159a 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -24,7 +24,7 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX, import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) -import Data.Char (isDigit, isLetter, toUpper, chr) +import Data.Char (isDigit, isLetter, isAlphaNum, toUpper, chr) import Data.Default import Data.List (intercalate) import qualified Data.Map as M @@ -300,7 +300,9 @@ inlineCommand' :: PandocMonad m => LP m Inlines inlineCommand' = try $ do Tok _ (CtrlSeq name) cmd <- anyControlSeq guard $ name /= "begin" && name /= "end" && name /= "and" - star <- option "" ("*" <$ symbol '*' <* sp) + star <- if T.all isAlphaNum name + then option "" ("*" <$ symbol '*' <* sp) + else pure "" overlay <- option "" overlaySpecification let name' = name <> star <> overlay let names = ordNub [name', name] -- check non-starred as fallback diff --git a/test/command/7340.md b/test/command/7340.md new file mode 100644 index 000000000..25decd732 --- /dev/null +++ b/test/command/7340.md @@ -0,0 +1,6 @@ +``` +% pandoc -f latex -t native +\(*\) +^D +[Para [Math InlineMath "*"]] +``` -- cgit v1.2.3 From 2e4ef14d9112f230e336a1f3767f87293bdfb73e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 1 Jun 2021 21:44:55 -0600 Subject: Markdown reader: fix pipe table regression in 2.11.4. Previously pipe tables with empty headers (that is, a header line with all empty cells) would be rendered as headerless tables. This broke in 2.11.4. The fix here is to produce an AST with an empty table head when a pipe table has all empty header cells. Closes #7343. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- test/pipe-tables.native | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 1761ee2c1..1e9867d07 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2254,4 +2254,4 @@ toRow :: [Blocks] -> Row toRow = Row nullAttr . map B.simpleCell toHeaderRow :: [Blocks] -> [Row] -toHeaderRow l = [toRow l | not (null l)] +toHeaderRow l = [toRow l | not (null l) && not (all null l)] diff --git a/test/pipe-tables.native b/test/pipe-tables.native index 557cd0642..249eec17e 100644 --- a/test/pipe-tables.native +++ b/test/pipe-tables.native @@ -131,13 +131,7 @@ ,(AlignLeft,ColWidthDefault) ,(AlignCenter,ColWidthDefault)] (TableHead ("",[],[]) - [Row ("",[],[]) - [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - [] - ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - []]]) + []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) @@ -213,9 +207,7 @@ []) [(AlignCenter,ColWidthDefault)] (TableHead ("",[],[]) - [Row ("",[],[]) - [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) - []]]) + []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) -- cgit v1.2.3 From 3b628f7664a95e6f3b3d7da177c83333ec2bc0fa Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 1 Jun 2021 21:57:49 -0600 Subject: HTML writer: Don't omit width attribute on div. Closes #7342. --- src/Text/Pandoc/Writers/HTML.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index f7a387927..b1dd9a659 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -770,9 +770,10 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do lookup "entry-spacing" kvs' >>= safeRead } let isCslBibEntry = "csl-entry" `elem` classes - let kvs = [(k,v) | (k,v) <- kvs', k /= "width"] ++ - [("style", "width:" <> w <> ";") | "column" `elem` classes, - ("width", w) <- kvs'] ++ + let kvs = [(k,v) | (k,v) <- kvs' + , k /= "width" || "column" `notElem` classes] ++ + [("style", "width:" <> w <> ";") | "column" `elem` classes + , ("width", w) <- kvs'] ++ [("role", "doc-bibliography") | isCslBibBody && html5] ++ [("role", "doc-biblioentry") | isCslBibEntry && html5] let speakerNotes = "notes" `elem` classes -- cgit v1.2.3 From 2b5dad9912de659424246657c91f70417590c3fe Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 2 Jun 2021 10:42:22 -0600 Subject: Fix regression in 2.14 for generation of PDFs with SVGs. Closes #7344. --- src/Text/Pandoc/PDF.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 6f462aad5..7fce17cea 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -202,7 +202,7 @@ convertImage opts tmpdir fname = do Just "image/svg+xml" -> E.catch (do (exit, _) <- pipeProcess Nothing "rsvg-convert" ["-f","pdf","-a","--dpi-x",dpi,"--dpi-y",dpi, - "-o",pdfOut,fname] BL.empty + "-o",pdfOut,svgIn] BL.empty if exit == ExitSuccess then return $ Right pdfOut else return $ Left "conversion from SVG failed") @@ -217,6 +217,7 @@ convertImage opts tmpdir fname = do where pngOut = replaceDirectory (replaceExtension fname ".png") tmpdir pdfOut = replaceDirectory (replaceExtension fname ".pdf") tmpdir + svgIn = tmpdir fname mime = getMimeType fname doNothing = return (Right fname) -- cgit v1.2.3 From 311736fb0ae21e5b5c78d1ad6fa4b3f4840941f9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 2 Jun 2021 15:21:13 -0600 Subject: Text.Pandoc.PDF: only print relevant part of environment on `--verbose`. --- src/Text/Pandoc/PDF.hs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 7fce17cea..f85ef5b1f 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -507,8 +507,20 @@ showVerboseInfo mbTmpDir program programArgs env source = do UTF8.hPutStrLn stderr $ T.pack program <> " " <> T.pack (unwords (map show programArgs)) UTF8.hPutStr stderr "\n" - UTF8.hPutStrLn stderr "[makePDF] Environment:" - mapM_ (UTF8.hPutStrLn stderr . tshow) env + UTF8.hPutStrLn stderr "[makePDF] Relevant environment variables:" + -- we filter out irrelevant stuff to avoid leaking passwords and keys! + let isRelevant ("PATH",_) = True + isRelevant ("TMPDIR",_) = True + isRelevant ("PWD",_) = True + isRelevant ("LANG",_) = True + isRelevant ("HOME",_) = True + isRelevant ("LUA_PATH",_) = True + isRelevant ("LUA_CPATH",_) = True + isRelevant ("SHELL",_) = True + isRelevant ("TEXINPUTS",_) = True + isRelevant ("TEXMFOUTPUT",_) = True + isRelevant _ = False + mapM_ (UTF8.hPutStrLn stderr . tshow) (filter isRelevant env) UTF8.hPutStr stderr "\n" UTF8.hPutStrLn stderr "[makePDF] Source:" UTF8.hPutStrLn stderr source -- cgit v1.2.3 From a54a19cca5d00378f6103d0bb5a6e3ca2dcfef3c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 2 Jun 2021 22:48:56 -0600 Subject: MANUAL: more details and a useful link for YAML syntax. --- MANUAL.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MANUAL.txt b/MANUAL.txt index b0c5962c4..b4f74b8c4 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -4199,6 +4199,12 @@ or block-level formatting: It consists of two paragraphs. ... +The literal block after the `|` must be indented relative to the +line containing the `|`. If it is not, the YAML will be invalid +and pandoc will not interpret it as metadata. For an overview +of the complex rules governing YAML, see the [Wikipedia entry on +YAML syntax]. + Template variables will be set automatically from the metadata. Thus, for example, in writing HTML, the variable `abstract` will be set to the HTML equivalent of the Markdown in the `abstract` field: @@ -4261,6 +4267,7 @@ following restrictions apply: if the link definition is somewhere else in the document. [YAML escape sequence]: https://yaml.org/spec/1.2/spec.html#id2776092 +[Wikipedia entry on YAML syntax]: https://en.m.wikipedia.org/wiki/YAML#Syntax ## Backslash escapes -- cgit v1.2.3 From b6c04383e403b0962db09e6748760d3ec376f2ed Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 3 Jun 2021 18:34:38 -0600 Subject: T.P.Class.IO: normalise path in writeMedia. This ensures that we get `\` separators on Windows. --- src/Text/Pandoc/Class/IO.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs index f12c0a938..6df39d4d0 100644 --- a/src/Text/Pandoc/Class/IO.hs +++ b/src/Text/Pandoc/Class/IO.hs @@ -211,13 +211,12 @@ writeMedia :: (PandocMonad m, MonadIO m) => FilePath -> MediaBag -> FilePath -> m () writeMedia dir mediabag subpath = do - -- we join and split to convert a/b/c to a\b\c on Windows; - -- in zip containers all paths use / let mbcontents = lookupMedia subpath mediabag case mbcontents of Nothing -> throwError $ PandocResourceNotFound $ pack subpath Just item -> do - let fullpath = dir mediaPath item + -- we normalize to get proper path separators for the platform + let fullpath = dir normalise (mediaPath item) liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath) logIOError $ BL.writeFile fullpath $ mediaContents item -- cgit v1.2.3 From af9de925de4a441f4c45a977363f9a56589f57bc Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Jun 2021 14:16:44 +0200 Subject: DocBook writer: Remove non-existent admonitions attention, error and hint are actually just reStructuredText specific. danger was too until introduced in DocBook 5.2: https://github.com/docbook/docbook/issues/55 --- src/Text/Pandoc/Writers/Docbook.hs | 3 +-- test/Tests/Writers/Docbook.hs | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 25bd308bf..33a6f5f0c 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -198,8 +198,7 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl (_,_,attrs) ils : xs)) blockToDocbook opts (Div (ident,classes,_) bs) = do version <- ask let identAttribs = [(idName version, ident) | not (T.null ident)] - admonitions = ["attention","caution","danger","error","hint", - "important","note","tip","warning"] + admonitions = ["caution","danger","important","note","tip","warning"] case classes of (l:_) | l `elem` admonitions -> do let (mTitleBs, bodyBs) = diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index 46203eeae..f517f803a 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -83,32 +83,32 @@ tests = [ testGroup "line blocks" , "" ] , "admonition-with-title" =: - divWith ("foo", ["attention"], []) ( + divWith ("foo", ["note"], []) ( divWith ("foo", ["title"], []) (plain (text "This is title")) <> para "This is a test" ) =?> unlines - [ "" + [ "" , " This is title" , " " , " This is a test" , " " - , "" + , "" ] , "admonition-with-title-in-para" =: - divWith ("foo", ["attention"], []) ( + divWith ("foo", ["note"], []) ( divWith ("foo", ["title"], []) (para "This is title") <> para "This is a test" ) =?> unlines - [ "" + [ "" , " This is title" , " " , " This is a test" , " " - , "" + , "" ] , "single-child" =: divWith ("foo", [], []) (para "This is a test") -- cgit v1.2.3 From c8ab8bccf25ad01b9a98b2e59c84c151c5b0c371 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Jun 2021 14:26:55 +0200 Subject: DocBook reader: Add support for danger element Added in DocBook 5.2: - https://github.com/docbook/docbook/pull/64 - https://tdg.docbook.org/tdg/5.2/danger.html --- src/Text/Pandoc/Readers/DocBook.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index b01ad3252..6ac1c99f9 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -134,6 +134,7 @@ List of all DocBook tags, with [x] indicating implemented, [ ] corpcredit - A corporation or organization credited in a document [ ] corpname - The name of a corporation [ ] country - The name of a country +[x] danger - An admonition set off from the text indicating hazardous situation [ ] database - The name of a database, or part of a database [x] date - The date of publication or revision of a document [ ] dedication - A wrapper for the dedication section of a book @@ -718,7 +719,7 @@ blockTags = ] ++ admonitionTags admonitionTags :: [Text] -admonitionTags = ["important","caution","note","tip","warning"] +admonitionTags = ["caution","danger","important","note","tip","warning"] -- Trim leading and trailing newline characters trimNl :: Text -> Text -- cgit v1.2.3 From 17a2f4c49dab7d4e6ec9cc010ad2cc02a147e2d4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 5 Jun 2021 00:16:59 -0600 Subject: Require citeproc 0.4.0.1. This fixes a bug which led to doubled "et al." in some (rare) circumstances. --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index da3244647..590bb445e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -450,7 +450,7 @@ library blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, - citeproc >= 0.4 && < 0.4.1, + citeproc >= 0.4.0.1 && < 0.4.1, commonmark >= 0.2 && < 0.3, commonmark-extensions >= 0.2.1.2 && < 0.3, commonmark-pandoc >= 0.2.1 && < 0.3, diff --git a/stack.yaml b/stack.yaml index 0c69306ea..23d031681 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,7 +13,7 @@ extra-deps: - jira-wiki-markup-1.4.0 - random-1.2.0 - unicode-collation-0.1.3 -- citeproc-0.4 +- citeproc-0.4.0.1 - commonmark-0.2 - commonmark-extensions-0.2.1.2 - commonmark-pandoc-0.2.1 -- cgit v1.2.3 From c6f8c38c49e9579bf0bc0c91131bb206f0617e6b Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Jun 2021 16:31:12 +0200 Subject: Markdown writer: re-use functions from Inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of duplicating linkAttributes and attrsToMarkdown, let’s just use those from the Inline module. --- src/Text/Pandoc/Writers/Markdown.hs | 29 +---------------------------- src/Text/Pandoc/Writers/Markdown/Inline.hs | 4 +++- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 2ad9eabd9..38227dfa8 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -43,7 +43,7 @@ import Text.Pandoc.Templates (renderTemplate) import Text.DocTemplates (Val(..), Context(..), FromContext(..)) import Text.Pandoc.Walk import Text.Pandoc.Writers.HTML (writeHtml5String) -import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown) +import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown, linkAttributes, attrsToMarkdown) import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..), WriterState(..), WriterEnv(..), @@ -257,39 +257,12 @@ noteToMarkdown opts num blocks = do then hang (writerTabStop opts) (marker <> spacer) contents else marker <> spacer <> contents -attrsToMarkdown :: Attr -> Doc Text -attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] - where attribId = case attribs of - ("",_,_) -> empty - (i,_,_) -> "#" <> escAttr i - attribClasses = case attribs of - (_,[],_) -> empty - (_,cs,_) -> hsep $ - map (escAttr . ("."<>)) - cs - attribKeys = case attribs of - (_,_,[]) -> empty - (_,_,ks) -> hsep $ - map (\(k,v) -> escAttr k - <> "=\"" <> - escAttr v <> "\"") ks - escAttr = mconcat . map escAttrChar . T.unpack - escAttrChar '"' = literal "\\\"" - escAttrChar '\\' = literal "\\\\" - escAttrChar c = literal $ T.singleton c - -- | (Code) blocks with a single class can just use it standalone, -- no need to bother with curly braces. classOrAttrsToMarkdown :: Attr -> Doc Text classOrAttrsToMarkdown ("",[cls],_) = literal cls classOrAttrsToMarkdown attrs = attrsToMarkdown attrs -linkAttributes :: WriterOptions -> Attr -> Doc Text -linkAttributes opts attr = - if isEnabled Ext_link_attributes opts && attr /= nullAttr - then attrsToMarkdown attr - else empty - -- | Ordered list start parser for use in Para below. olMarker :: Parser Text ParserState () olMarker = do (start, style', delim) <- anyOrderedListMarker diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index e6c6da5a9..e66258220 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -11,7 +11,9 @@ Portability : portable -} module Text.Pandoc.Writers.Markdown.Inline ( - inlineListToMarkdown + inlineListToMarkdown, + linkAttributes, + attrsToMarkdown ) where import Control.Monad.Reader import Control.Monad.State.Strict -- cgit v1.2.3 From 7a3ee9d3d83b73cb53de80e01a9968ebf8f7cf12 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Jun 2021 15:53:24 +0200 Subject: CommonMark writer: do not throw away attributes when Ext_attributes is enabled Ext_attributes covers at least the following: - Ext_fenced_code_attributes - Ext_header_attributes - Ext_inline_code_attributes - Ext_link_attributes --- src/Text/Pandoc/Writers/Markdown.hs | 8 +++++--- src/Text/Pandoc/Writers/Markdown/Inline.hs | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 38227dfa8..6316d9419 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -366,7 +366,7 @@ blockToMarkdown' opts (Plain inlines) = do -- title beginning with fig: indicates figure blockToMarkdown' opts (Para [Image attr alt (src,tgt@(T.stripPrefix "fig:" -> Just tit))]) | isEnabled Ext_raw_html opts && - not (isEnabled Ext_link_attributes opts) && + not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr = -- use raw HTML (<> blankline) . literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } @@ -431,7 +431,8 @@ blockToMarkdown' opts (Header level attr inlines) = do && id' == autoId -> empty (id',_,_) | isEnabled Ext_mmd_header_identifiers opts -> space <> brackets (literal id') - _ | isEnabled Ext_header_attributes opts -> + _ | isEnabled Ext_header_attributes opts || + isEnabled Ext_attributes opts -> space <> attrsToMarkdown attr | otherwise -> empty contents <- inlineListToMarkdown opts $ @@ -490,7 +491,8 @@ blockToMarkdown' opts (CodeBlock attribs str) = do endline c = literal $ T.replicate (endlineLen c) $ T.singleton c backticks = endline '`' tildes = endline '~' - attrs = if isEnabled Ext_fenced_code_attributes opts + attrs = if isEnabled Ext_fenced_code_attributes opts || + isEnabled Ext_attributes opts then nowrap $ " " <> classOrAttrsToMarkdown attribs else case attribs of (_,cls:_,_) -> " " <> literal cls diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs index e66258220..cd5f5b896 100644 --- a/src/Text/Pandoc/Writers/Markdown/Inline.hs +++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs @@ -117,7 +117,7 @@ attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] linkAttributes :: WriterOptions -> Attr -> Doc Text linkAttributes opts attr = - if isEnabled Ext_link_attributes opts && attr /= nullAttr + if (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr then attrsToMarkdown attr else empty @@ -394,13 +394,15 @@ inlineToMarkdown opts (Quoted DoubleQuote lst) = do then "“" <> contents <> "”" else "“" <> contents <> "”" inlineToMarkdown opts (Code attr str) = do - let tickGroups = filter (T.any (== '`')) $ T.group str - let longest = maybe 0 maximum $ nonEmpty $ map T.length tickGroups - let marker = T.replicate (longest + 1) "`" - let spacer = if longest == 0 then "" else " " - let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr - then attrsToMarkdown attr - else empty + let tickGroups = filter (T.any (== '`')) $ T.group str + let longest = maybe 0 maximum $ nonEmpty $ map T.length tickGroups + let marker = T.replicate (longest + 1) "`" + let spacer = if longest == 0 then "" else " " + let attrsEnabled = isEnabled Ext_inline_code_attributes opts || + isEnabled Ext_attributes opts + let attrs = if attrsEnabled && attr /= nullAttr + then attrsToMarkdown attr + else empty variant <- asks envVariant case variant of PlainText -> return $ literal str @@ -559,7 +561,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do else "[" <> reftext <> "]" in return $ first <> second | isEnabled Ext_raw_html opts - , not (isEnabled Ext_link_attributes opts) + , not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) , attr /= nullAttr -> -- use raw HTML to render attributes literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } @@ -569,7 +571,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do linkAttributes opts attr inlineToMarkdown opts img@(Image attr alternate (source, tit)) | isEnabled Ext_raw_html opts && - not (isEnabled Ext_link_attributes opts) && + not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr = -- use raw HTML literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]]) -- cgit v1.2.3 From c550bf8482310dfbcb20694e7bc969d19acc5f7d Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 5 Jun 2021 16:34:27 +0200 Subject: CommonMark writer: do not use simple class for fenced-divs In https://github.com/jgm/pandoc/pull/7242, we introduced a simple attribute style for for code blocks and fenced divs with a single class but turns out the CommonMark extension does not support it for fenced divs. https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/fenced_divs.md --- src/Text/Pandoc/Writers/Markdown.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 6316d9419..b16c71358 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -322,9 +322,12 @@ blockToMarkdown' opts (Div attrs ils) = do case () of _ | isEnabled Ext_fenced_divs opts && attrs /= nullAttr -> - nowrap (literal ":::" <+> classOrAttrsToMarkdown attrs) $$ - chomp contents $$ - literal ":::" <> blankline + let attrsToMd = if variant == Commonmark + then attrsToMarkdown + else classOrAttrsToMarkdown + in nowrap (literal ":::" <+> attrsToMd attrs) $$ + chomp contents $$ + literal ":::" <> blankline | isEnabled Ext_native_divs opts || (isEnabled Ext_raw_html opts && (variant == Commonmark || -- cgit v1.2.3 From 21cc52abe33997ea2f2c539f10d26684b7633bc0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 5 Jun 2021 14:13:58 -0600 Subject: LaTeX writer: Fix regression in table header position. In recent versions the table headers were no longer bottom-aligned (if more than one line). This patch fixes that by using minipages for table headers in non-simple tables. Closes #7347. --- src/Text/Pandoc/Writers/LaTeX/Table.hs | 13 ++++++++++--- test/command/5367.md | 8 ++++++-- test/tables.latex | 30 +++++++++++++++++++++++++++--- test/tables/nordics.latex | 26 ++++++++++++++++++++++---- test/tables/planets.latex | 8 ++++---- test/tables/students.latex | 12 ++++++++++-- 6 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX/Table.hs b/src/Text/Pandoc/Writers/LaTeX/Table.hs index 16f63314b..8dc7d1162 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Table.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Table.hs @@ -16,6 +16,7 @@ module Text.Pandoc.Writers.LaTeX.Table ) where import Control.Monad.State.Strict import Data.List (intersperse) +import qualified Data.List.NonEmpty as NonEmpty import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Text (Text) import qualified Data.Text as T @@ -243,8 +244,13 @@ cellToLaTeX :: PandocMonad m -> Ann.Cell -> LW m (Doc Text) cellToLaTeX blockListToLaTeX celltype annotatedCell = do - let (Ann.Cell _specs _colnum cell) = annotatedCell - let (Cell _attr align rowspan colspan blocks) = cell + let (Ann.Cell specs _colnum cell) = annotatedCell + let hasWidths = snd (NonEmpty.head specs) /= ColWidthDefault + let specAlign = fst (NonEmpty.head specs) + let (Cell _attr align' rowspan colspan blocks) = cell + let align = case align' of + AlignDefault -> specAlign + _ -> align' beamer <- gets stBeamer externalNotes <- gets stExternalNotes inMinipage <- gets stInMinipage @@ -256,7 +262,7 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do Plain{} -> True _ -> False result <- - if all isPlainOrPara blocks + if not hasWidths || (celltype /= HeaderCell && all isPlainOrPara blocks) then blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks else do @@ -290,3 +296,4 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do data CellType = HeaderCell | BodyCell + deriving Eq diff --git a/test/command/5367.md b/test/command/5367.md index 2d3a5e52e..a67011c2f 100644 --- a/test/command/5367.md +++ b/test/command/5367.md @@ -24,11 +24,15 @@ hello\footnote{doc footnote} >{\centering\arraybackslash}p{(\columnwidth - 0\tabcolsep) * \real{0.17}}@{}} \caption[Sample table.]{Sample table.\footnote{caption footnote}}\tabularnewline \toprule -Fruit\footnote{header footnote} \\ +\begin{minipage}[b]{\linewidth}\centering +Fruit\footnote{header footnote} +\end{minipage} \\ \midrule \endfirsthead \toprule -Fruit{} \\ +\begin{minipage}[b]{\linewidth}\centering +Fruit{} +\end{minipage} \\ \midrule \endhead Bans\footnote{table cell footnote} \\ diff --git a/test/tables.latex b/test/tables.latex index afa14d845..9d111fa7a 100644 --- a/test/tables.latex +++ b/test/tables.latex @@ -56,11 +56,27 @@ Multiline table with caption: >{\raggedright\arraybackslash}p{(\columnwidth - 6\tabcolsep) * \real{0.35}}@{}} \caption{Here's the caption. It may span multiple lines.}\tabularnewline \toprule -Centered Header & Left Aligned & Right Aligned & Default aligned \\ +\begin{minipage}[b]{\linewidth}\centering +Centered Header +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Left Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft +Right Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Default aligned +\end{minipage} \\ \midrule \endfirsthead \toprule -Centered Header & Left Aligned & Right Aligned & Default aligned \\ +\begin{minipage}[b]{\linewidth}\centering +Centered Header +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Left Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft +Right Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Default aligned +\end{minipage} \\ \midrule \endhead First & row & 12.0 & Example of a row that spans multiple lines. \\ @@ -76,7 +92,15 @@ Multiline table without caption: >{\raggedleft\arraybackslash}p{(\columnwidth - 6\tabcolsep) * \real{0.16}} >{\raggedright\arraybackslash}p{(\columnwidth - 6\tabcolsep) * \real{0.35}}@{}} \toprule -Centered Header & Left Aligned & Right Aligned & Default aligned \\ +\begin{minipage}[b]{\linewidth}\centering +Centered Header +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Left Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft +Right Aligned +\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright +Default aligned +\end{minipage} \\ \midrule \endhead First & row & 12.0 & Example of a row that spans multiple lines. \\ diff --git a/test/tables/nordics.latex b/test/tables/nordics.latex index 1b5929bad..1dcac7319 100644 --- a/test/tables/nordics.latex +++ b/test/tables/nordics.latex @@ -5,13 +5,31 @@ >{\raggedright\arraybackslash}p{(\columnwidth - 6\tabcolsep) * \real{0.20}}@{}} \caption{States belonging to the \emph{Nordics.}}\tabularnewline \toprule -Name & Capital & \vtop{\hbox{\strut Population}\hbox{\strut (in 2018)}} & -\vtop{\hbox{\strut Area}\hbox{\strut (in km\textsuperscript{2})}} \\ +\begin{minipage}[b]{\linewidth}\centering +Name +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Capital +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Population\\ +(in 2018) +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Area\\ +(in km\textsuperscript{2}) +\end{minipage} \\ \midrule \endfirsthead \toprule -Name & Capital & \vtop{\hbox{\strut Population}\hbox{\strut (in 2018)}} & -\vtop{\hbox{\strut Area}\hbox{\strut (in km\textsuperscript{2})}} \\ +\begin{minipage}[b]{\linewidth}\centering +Name +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Capital +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Population\\ +(in 2018) +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Area\\ +(in km\textsuperscript{2}) +\end{minipage} \\ \midrule \endhead Denmark & Copenhagen & 5,809,502 & 43,094 \\ diff --git a/test/tables/planets.latex b/test/tables/planets.latex index 8238c43f3..b22c3adeb 100644 --- a/test/tables/planets.latex +++ b/test/tables/planets.latex @@ -1,18 +1,18 @@ \begin{longtable}[]{@{}cclrrrrrrrrl@{}} \caption{Data about the planets of our solar system.}\tabularnewline \toprule -\multicolumn{2}{l}{} & Name & Mass (10\^{}24kg) & Diameter (km) & Density +\multicolumn{2}{c}{} & Name & Mass (10\^{}24kg) & Diameter (km) & Density (kg/m\^{}3) & Gravity (m/s\^{}2) & Length of day (hours) & Distance from Sun (10\^{}6km) & Mean temperature (C) & Number of moons & Notes \\ \midrule \endfirsthead \toprule -\multicolumn{2}{l}{} & Name & Mass (10\^{}24kg) & Diameter (km) & Density +\multicolumn{2}{c}{} & Name & Mass (10\^{}24kg) & Diameter (km) & Density (kg/m\^{}3) & Gravity (m/s\^{}2) & Length of day (hours) & Distance from Sun (10\^{}6km) & Mean temperature (C) & Number of moons & Notes \\ \midrule \endhead -\multicolumn{2}{l}{\multirow{4}{*}{Terrestrial planets}} & Mercury & 0.330 & +\multicolumn{2}{c}{\multirow{4}{*}{Terrestrial planets}} & Mercury & 0.330 & 4,879 & 5427 & 3.7 & 4222.6 & 57.9 & 167 & 0 & Closest to the Sun \\ & & Venus & 4.87 & 12,104 & 5243 & 8.9 & 2802.0 & 108.2 & 464 & 0 & \\ & & Earth & 5.97 & 12,756 & 5514 & 9.8 & 24.0 & 149.6 & 15 & 1 & Our world \\ @@ -24,7 +24,7 @@ planet \\ & \multirow{2}{*}{Ice giants} & Uranus & 86.8 & 51,118 & 1271 & 8.7 & 17.2 & 2872.5 & -195 & 27 & \\ & & Neptune & 102 & 49,528 & 1638 & 11.0 & 16.1 & 4495.1 & -200 & 14 & \\ -\multicolumn{2}{l}{Dwarf planets} & Pluto & 0.0146 & 2,370 & 2095 & 0.7 & +\multicolumn{2}{c}{Dwarf planets} & Pluto & 0.0146 & 2,370 & 2095 & 0.7 & 153.3 & 5906.4 & -225 & 5 & Declassified as a planet in 2006. \\ \bottomrule \end{longtable} diff --git a/test/tables/students.latex b/test/tables/students.latex index 87efb0851..3d4d287d9 100644 --- a/test/tables/students.latex +++ b/test/tables/students.latex @@ -3,11 +3,19 @@ >{\raggedright\arraybackslash}p{(\columnwidth - 2\tabcolsep) * \real{0.50}}@{}} \caption{List of Students}\tabularnewline \toprule -Student ID & Name \\ +\begin{minipage}[b]{\linewidth}\centering +Student ID +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Name +\end{minipage} \\ \midrule \endfirsthead \toprule -Student ID & Name \\ +\begin{minipage}[b]{\linewidth}\centering +Student ID +\end{minipage} & \begin{minipage}[b]{\linewidth}\centering +Name +\end{minipage} \\ \midrule \endhead \multicolumn{2}{l}{Computer Science} \\ -- cgit v1.2.3 From 94e8a650fb200b55af7cd1ea7f331fa642b3102e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 7 Jun 2021 20:10:56 +0200 Subject: CONTRIBUTING.md: update modules overview --- CONTRIBUTING.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf8d3aa03..17df42b87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -406,11 +406,11 @@ The library is structured as follows: tree automatically. - `Text.Pandoc.Readers.*` are the readers, and `Text.Pandoc.Writers.*` are the writers. - - `Text.Pandoc.Biblio` is a utility module for formatting citations - using citeproc-hs. + - `Text.Pandoc.Citeproc.*` contain the code for citation handling, + including an interface to the [citeproc] library. - `Text.Pandoc.Data` is used to embed data files when the `embed_data_files` - cabal flag is used. It is generated from `src/Text/Pandoc/Data.hsb` using - the preprocessor [hsb2hs]. + cabal flag is used. + - `Text.Pandoc.Emoji` is a thin wrapper around [emojis]. - `Text.Pandoc.Highlighting` contains the interface to the skylighting library, which is used for code syntax highlighting. - `Text.Pandoc.ImageSize` is a utility module containing functions for @@ -421,7 +421,6 @@ The library is structured as follows: - `Text.Pandoc.PDF` contains functions for producing a PDF from a LaTeX source. - `Text.Pandoc.Parsing` contains parsing functions used in multiple readers. - - `Text.Pandoc.Pretty` is a pretty-printing library specialized to the needs of pandoc. - `Text.Pandoc.SelfContained` contains functions for making an HTML file "self-contained," by importing remotely linked images, CSS, @@ -456,6 +455,8 @@ you may want to consider submitting a pull request to the [EditorConfig]: https://editorconfig.org/ [Haskell platform]: https://www.haskell.org/platform/ [hlint]: https://hackage.haskell.org/package/hlint +[citeproc]: https://hackage.haskell.org/package/citeproc +[emojis]: https://hackage.haskell.org/package/emojis [hsb2hs]: https://hackage.haskell.org/package/hsb2hs [pre-commit hook]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks [GitHub labels]: https://github.com/jgm/pandoc/labels -- cgit v1.2.3 From 39afd4297ff9d03b3c70eec0d99a43ed5c05ab34 Mon Sep 17 00:00:00 2001 From: Sebastian Humenda Date: Sun, 30 May 2021 19:03:04 +0200 Subject: Mention GladTeX for EPUB export This updates the manual and the web site about the GladTeX usage. --- MANUAL.txt | 9 ++++----- doc/epub.md | 11 +++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index b4f74b8c4..b3a1f95e2 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1415,13 +1415,12 @@ results only for basic math, usually you will want to use `--gladtex` : Enclose TeX math in `` tags in HTML output. The resulting HTML - can then be processed by [GladTeX] to produce images of the typeset - formulas and an HTML file with links to these images. - So, the procedure is: + can then be processed by [GladTeX] to produce SVG images of the typeset + formulas and an HTML file with these images embedded. pandoc -s --gladtex input.md -o myfile.htex - gladtex -d myfile-images myfile.htex - # produces myfile.html and images in myfile-images + gladtex -d image_dir myfile.htex + # produces myfile.html and images in image_dir [MathML]: https://www.w3.org/Math/ [MathJax]: https://www.mathjax.org diff --git a/doc/epub.md b/doc/epub.md index 82c26dcef..c05faf80c 100644 --- a/doc/epub.md +++ b/doc/epub.md @@ -141,8 +141,15 @@ Pandoc has an EPUB3 writer. It renders LaTeX math into MathML, which EPUB3 readers are supposed to support (but unfortunately few do). Of course, this isn't much help if you want EPUB2 output (`pandoc -t epub2`) -or target readers that don't support MathML. Then you should try using the -`--webtex` option, which will use a web service to convert the TeX to an image. +or target readers that don't support MathML. Then you have two options: + +1. Use the [`--webtex`](https://pandoc.org/MANUAL.html#option--webtex) option, + which will use a web service to convert the TeX to an image. +2. Use the [`--gladtex`](https://pandoc.org/MANUAL.html#option--gladtex) option + to convert maths into SVG images on your local machine. + +Both GladTeX and WebTeX add the LaTeX source of the formula as alternative text +of the image, increasing accessibility for blind users. [KindleGen]: https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211 [EPUB]: https://en.wikipedia.org/wiki/EPUB -- cgit v1.2.3 From e66960fe4f3e49d33eefe56ff05ac4007a800cc5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 8 Jun 2021 12:35:55 +0200 Subject: using-the-pandoc-api.md: switch from String to Text Fixed examples that would no longer compile with current library versions, as the API now uses Text instead of String in most places. --- doc/using-the-pandoc-api.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/using-the-pandoc-api.md b/doc/using-the-pandoc-api.md index d6eb9e15f..d44e82e9b 100644 --- a/doc/using-the-pandoc-api.md +++ b/doc/using-the-pandoc-api.md @@ -139,7 +139,7 @@ report :: PandocMonad m => LogMessage -> m () -- | Fetch an image or other item from the local filesystem or the net. -- Returns raw content and maybe mime type. fetchItem :: PandocMonad m - => String + => Text -> m (B.ByteString, Maybe MimeType) -- Set the resource path searched by 'fetchItem'. @@ -213,8 +213,8 @@ of the Monoid typeclass and can easily be concatenated: import Text.Pandoc.Builder mydoc :: Pandoc -mydoc = doc $ header 1 (text "Hello!") - <> para (emph (text "hello world") <> text ".") +mydoc = doc $ header 1 (text (T.pack "Hello!")) + <> para (emph (text (T.pack "hello world")) <> text (T.pack ".")) main :: IO () main = print mydoc @@ -261,16 +261,16 @@ import qualified Data.Text as T import Data.List (intersperse) data Station = Station{ - address :: String - , name :: String - , cardsAccepted :: [String] + address :: T.Text + , name :: T.Text + , cardsAccepted :: [T.Text] } deriving Show instance FromJSON Station where parseJSON (Object v) = Station <$> v .: "street_address" <*> v .: "station_name" <*> - (words <$> (v .:? "cards_accepted" .!= "")) + (T.words <$> (v .:? "cards_accepted" .!= "")) parseJSON _ = mzero createLetter :: [Station] -> Pandoc @@ -328,7 +328,7 @@ users to override the system defaults. If you want to disable this behavior, use `setUserDataDir Nothing`. To render a template, use `renderTemplate'`, which takes two -arguments, a template (String) and a context (any instance +arguments, a template (Text) and a context (any instance of ToJSON). If you want to create a context from the metadata part of a Pandoc document, use `metaToJSON'` from [Text.Pandoc.Writers.Shared]. If you also want to incorporate @@ -426,7 +426,7 @@ concatenated together. Here's an example that returns a list of the URLs linked to in a document: ```haskell -listURLs :: Pandoc -> [String] +listURLs :: Pandoc -> [Text] listURLs = query urls where urls (Link _ _ (src, _)) = [src] urls _ = [] -- cgit v1.2.3 From 76e5f047b0ef4c293687f9ddce62d601b23058a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 8 Jun 2021 08:20:22 -0600 Subject: Citeproc: avoid duplicate classes and attributes on refs div. --- src/Text/Pandoc/Citeproc.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index de63aed1f..ad3b26c0f 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -49,7 +49,6 @@ import qualified Data.Text as T import System.FilePath (takeExtension) import Safe (lastMay, initSafe) - processCitations :: PandocMonad m => Pandoc -> m Pandoc processCitations (Pandoc meta bs) = do style <- getStyle (Pandoc meta bs) @@ -499,7 +498,8 @@ insertRefs refkvs refclasses meta refs bs = put True -- refHeader isn't used if you have an explicit references div let cs' = ordNub $ cs ++ refclasses - return $ Div ("refs",cs' ++ refclasses,kvs ++ refkvs) (xs ++ refs) + let kvs' = ordNub $ kvs ++ refkvs + return $ Div ("refs",cs',kvs') (xs ++ refs) go x = return x refTitle :: Meta -> Maybe [Inline] -- cgit v1.2.3 From 55bcd4b4fb1dced6c6e316db6cd117b52bbadee5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 10 Jun 2021 18:26:53 +0200 Subject: Lua utils: fix handling of table headers in `from_simple_table` Passing an empty list of header cells now results in an empty table header. Fixes: #7369 --- src/Text/Pandoc/Lua/Module/Utils.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 1b04021a7..3ec3afc26 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -146,7 +146,7 @@ from_simple_table (SimpleTable capt aligns widths head' body) = do nullAttr (Caption Nothing [Plain capt]) (zipWith (\a w -> (a, toColWidth w)) aligns widths) - (TableHead nullAttr [blockListToRow head']) + (TableHead nullAttr [blockListToRow head' | not (null head') ]) [TableBody nullAttr 0 [] $ map blockListToRow body] (TableFoot nullAttr []) return (NumResults 1) -- cgit v1.2.3 From c7dd33d5aaa30f4c95e141b10e6d8fe0212edf1e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 10 Jun 2021 18:34:20 +0200 Subject: Docx writer: fix handling of empty table headers A table header which does not contain any cells is now treated as an empty header. Fixes: #7369 --- src/Text/Pandoc/Writers/Docx/Table.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index 13cb31040..49917e315 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -116,8 +116,8 @@ cellGridToOpenXML :: PandocMonad m -> [Alignment] -> Part -> WS m [Element] -cellGridToOpenXML blocksToOpenXML rowType aligns part@(Part _ _ rowAttrs) = - if null (indices rowAttrs) +cellGridToOpenXML blocksToOpenXML rowType aligns part@(Part _ cellArray _) = + if null (elems cellArray) then return mempty else mapM (rowToOpenXML blocksToOpenXML) $ partToRows rowType aligns part -- cgit v1.2.3 From aa79b3035c3343adf1bb41b37266049a65ab5da7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Jun 2021 16:36:54 -0700 Subject: T.P.MIME, extensionFromMimeType: add a few special cases. When we do a reverse lookup in the MIME table, we just get the last match, so when the same mime type is associated with several different extensions, we sometimes got weird results, e.g. `.vs` for `text/plain`. These special cases help us get the most standard extensions for mime types like `text/plain`. --- src/Text/Pandoc/MIME.hs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index 3d06e1579..77c7069e9 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -43,6 +43,16 @@ getMimeTypeDef :: FilePath -> MimeType getMimeTypeDef = fromMaybe "application/octet-stream" . getMimeType extensionFromMimeType :: MimeType -> Maybe T.Text +-- few special cases, where there are multiple options: +extensionFromMimeType "text/plain" = Just "txt" +extensionFromMimeType "video/quicktime" = Just "mov" +extensionFromMimeType "video/mpeg" = Just "mpeg" +extensionFromMimeType "video/dv" = Just "dv" +extensionFromMimeType "image/vnd.djvu" = Just "djvu" +extensionFromMimeType "image/tiff" = Just "tiff" +extensionFromMimeType "image/jpeg" = Just "jpg" +extensionFromMimeType "application/xml" = Just "xml" +extensionFromMimeType "application/ogg" = Just "ogg" extensionFromMimeType mimetype = M.lookup (T.takeWhile (/=';') mimetype) reverseMimeTypes -- note: we just look up the basic mime type, dropping the content-encoding etc. -- cgit v1.2.3 From 3776e828a83048697e5c64d9fb4bedc0145197dc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Jun 2021 16:47:02 -0700 Subject: Fix MediaBag regressions. With the 2.14 release `--extract-media` stopped working as before; there could be mismatches between the paths in the rendered document and the extracted media. This patch makes several changes (while keeping the same API). The `mediaPath` in 2.14 was always constructed from the SHA1 hash of the media contents. Now, we preserve the original path unless it's an absolute path or contains `..` segments (in that case we use a path based on the SHA1 hash of the contents). When constructing a path from the SHA1 hash, we always use the original extension, if there is one. Otherwise we look up an appropriate extension for the mime type. `mediaDirectory` and `mediaItems` now use the `mediaPath`, rather than the mediabag key, for the first component of the tuple. This makes more sense, I think, and fits with the documentation of these functions; eventually, though, we should rework the API so that `mediaItems` returns both the keys and the MediaItems. Rewriting of source paths in `extractMedia` has been fixed. `fillMediaBag` has been modified so that it doesn't modify image paths (that was part of the problem in #7345). We now do path normalization (e.g. `\` separators on Windows) only in writing the media; the paths are left unchanged in the image links (sensibly, since they might be URLs and not file paths). These changes should restore the original behavior from before 2.14. Closes #7345. --- MANUAL.txt | 12 +++++------ src/Text/Pandoc/Class/IO.hs | 41 ++++++++++++++++++------------------ src/Text/Pandoc/Class/PandocMonad.hs | 17 ++++++--------- src/Text/Pandoc/MediaBag.hs | 25 ++++++++++++---------- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index b3a1f95e2..ef569433a 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -675,12 +675,12 @@ header when requesting a document from a URL: : Extract images and other media contained in or linked from the source document to the path *DIR*, creating it if necessary, and adjust the images references in the document - so they point to the extracted files. If the source format is - a binary container (docx, epub, or odt), the media is - extracted from the container and the original - filenames are used. Otherwise the media is read from the - file system or downloaded, and new filenames are constructed - based on SHA1 hashes of the contents. + so they point to the extracted files. Media are downloaded, + read from the file system, or extracted from a binary + container (e.g. docx), as needed. The original file paths + are used if they are relative paths not containing `..`. + Otherwise filenames are constructed from the SHA1 hash of + the contents. `--abbreviations=`*FILE* diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs index 6df39d4d0..169074860 100644 --- a/src/Text/Pandoc/Class/IO.hs +++ b/src/Text/Pandoc/Class/IO.hs @@ -62,7 +62,7 @@ import Text.Pandoc.Definition (Pandoc, Inline (Image)) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Logging (LogMessage (..), messageVerbosity, showLogMessage) import Text.Pandoc.MIME (MimeType) -import Text.Pandoc.MediaBag (MediaBag, MediaItem(..), lookupMedia, mediaDirectory) +import Text.Pandoc.MediaBag (MediaBag, MediaItem(..), lookupMedia, mediaItems) import Text.Pandoc.Walk (walk) import qualified Control.Exception as E import qualified Data.ByteString as B @@ -200,31 +200,32 @@ alertIndent (l:ls) = do extractMedia :: (PandocMonad m, MonadIO m) => FilePath -> Pandoc -> m Pandoc extractMedia dir d = do media <- getMediaBag - case [fp | (fp, _, _) <- mediaDirectory media] of - [] -> return d - fps -> do - mapM_ (writeMedia dir media) fps - return $ walk (adjustImagePath dir fps) d + let items = mediaItems media + if null items + then return d + else do + mapM_ (writeMedia dir) items + return $ walk (adjustImagePath dir media) d -- | Write the contents of a media bag to a path. writeMedia :: (PandocMonad m, MonadIO m) - => FilePath -> MediaBag -> FilePath + => FilePath + -> (FilePath, MimeType, BL.ByteString) -> m () -writeMedia dir mediabag subpath = do - let mbcontents = lookupMedia subpath mediabag - case mbcontents of - Nothing -> throwError $ PandocResourceNotFound $ pack subpath - Just item -> do - -- we normalize to get proper path separators for the platform - let fullpath = dir normalise (mediaPath item) - liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath) - logIOError $ BL.writeFile fullpath $ mediaContents item +writeMedia dir (fp, _mt, bs) = do + -- we normalize to get proper path separators for the platform + let fullpath = normalise $ dir fp + liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath) + logIOError $ BL.writeFile fullpath bs -- | If the given Inline element is an image with a @src@ path equal to -- one in the list of @paths@, then prepends @dir@ to the image source; -- returns the element unchanged otherwise. -adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline -adjustImagePath dir paths (Image attr lab (src, tit)) - | unpack src `elem` paths - = Image attr lab (pack (normalise $ dir unpack src), tit) +adjustImagePath :: FilePath -> MediaBag -> Inline -> Inline +adjustImagePath dir mediabag (Image attr lab (src, tit)) = + case lookupMedia (T.unpack src) mediabag of + Nothing -> Image attr lab (src, tit) + Just item -> + let fullpath = dir mediaPath item + in Image attr lab (T.pack fullpath, tit) adjustImagePath _ _ x = x diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs index 4eb80df29..439aec071 100644 --- a/src/Text/Pandoc/Class/PandocMonad.hs +++ b/src/Text/Pandoc/Class/PandocMonad.hs @@ -638,17 +638,12 @@ fillMediaBag d = walkM handleImage d handleImage (Image attr lab (src, tit)) = catchError (do mediabag <- getMediaBag let fp = T.unpack src - src' <- T.pack <$> case lookupMedia fp mediabag of - Just item -> return $ mediaPath item - Nothing -> do - (bs, mt) <- fetchItem src - insertMedia fp mt (BL.fromStrict bs) - mediabag' <- getMediaBag - case lookupMedia fp mediabag' of - Just item -> return $ mediaPath item - Nothing -> throwError $ PandocSomeError $ - src <> " not successfully inserted into MediaBag" - return $ Image attr lab (src', tit)) + case lookupMedia fp mediabag of + Just _ -> return () + Nothing -> do + (bs, mt) <- fetchItem src + insertMedia fp mt (BL.fromStrict bs) + return $ Image attr lab (src, tit)) (\e -> case e of PandocResourceNotFound _ -> do diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index a65f315fc..06fba5632 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -71,16 +71,21 @@ insertMedia :: FilePath -- ^ relative path and canonical name of resource -> MediaBag -> MediaBag insertMedia fp mbMime contents (MediaBag mediamap) = - MediaBag (M.insert (canonicalize fp) mediaItem mediamap) - where mediaItem = MediaItem{ mediaPath = showDigest (sha1 contents) <> - "." <> ext + MediaBag (M.insert fp' mediaItem mediamap) + where mediaItem = MediaItem{ mediaPath = newpath , mediaContents = contents , mediaMimeType = mt } + fp' = canonicalize fp + newpath = if isRelative fp && ".." `notElem` splitPath fp + then T.unpack fp' + else showDigest (sha1 contents) <> "." <> ext fallback = case takeExtension fp of ".gz" -> getMimeTypeDef $ dropExtension fp _ -> getMimeTypeDef fp mt = fromMaybe fallback mbMime - ext = maybe (takeExtension fp) T.unpack $ extensionFromMimeType mt + ext = case takeExtension fp of + '.':e -> e + _ -> maybe "" T.unpack $ extensionFromMimeType mt -- | Lookup a media item in a 'MediaBag', returning mime type and contents. @@ -92,13 +97,11 @@ lookupMedia fp (MediaBag mediamap) = M.lookup (canonicalize fp) mediamap -- | Get a list of the file paths stored in a 'MediaBag', with -- their corresponding mime types and the lengths in bytes of the contents. mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)] -mediaDirectory (MediaBag mediamap) = - M.foldrWithKey (\fp item -> - ((T.unpack fp, mediaMimeType item, - fromIntegral (BL.length (mediaContents item))):)) [] mediamap +mediaDirectory mediabag = + map (\(fp, mt, bs) -> (fp, mt, fromIntegral (BL.length bs))) + (mediaItems mediabag) mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)] mediaItems (MediaBag mediamap) = - M.foldrWithKey (\fp item -> - ((T.unpack fp, mediaMimeType item, mediaContents item):)) - [] mediamap + map (\item -> (mediaPath item, mediaMimeType item, mediaContents item)) + (M.elems mediamap) -- cgit v1.2.3 From 67b3c36a9338364a0a7aa95b20dffafd16f6c36a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 10 Jun 2021 22:47:53 -0700 Subject: Bump to 2.14.0.2, update chaneglog and manual. --- MANUAL.txt | 2 +- changelog.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ man/pandoc.1 | 31 +++++++++++------- pandoc.cabal | 2 +- 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index ef569433a..815ed0fdf 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: May 31, 2021 +date: June 11, 2021 --- # Synopsis diff --git a/changelog.md b/changelog.md index 880bfc26f..822f2fb16 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,106 @@ # Revision history for pandoc +## pandoc 2.14.0.2 (2021-06-11) + + * Fix MediaBag regressions (#7345). iIn the 2.14 release `--extract-media` + stopped working as before; there could be mismatches between the + paths in the rendered document and the extracted media. + This patch makes several changes that restore the earlier behavior + (while keeping the same API). The `mediaPath` in 2.14 was always + constructed from the SHA1 hash of the media contents. Now, we + preserve the original path unless it's an absolute path or contains + `..` segments (in that case we use a path based on the SHA1 hash of + the contents). + + In Text.Pandoc.MediaBag, `mediaDirectory` and `mediaItems` now use the + `mediaPath`, rather than the mediabag key, for the first component of the + tuple. This makes more sense, I think, and fits with the documentation of + these functions; eventually, though, we should rework the API so that + `mediaItems` returns both the keys and the MediaItems. + + In Text.Pandoc.Class.IO, rewriting of source paths in `extractMedia` has + been fixed. + + In Text.Pandoc.Class.PandocMonad, `fillMediaBag` has been modified so that + it doesn't modify image paths (that was part of the problem in #7345). + + We now do path normalization (e.g. `\` separators on Windows) in + writing the media. + + * Text.Pandoc.PDF: + + + Text.Pandoc.PDF: Fix regression in 2.14 for generation of PDFs with + SVGs (#7344). + + Only print relevant part of environment on `--verbose`. Since + `--verbose` output might be put in an issue, we want to avoid + spilling out secrets in environment variables. + + * Markdown reader: fix pipe table regression in 2.11.4 (#7343). + Previously pipe tables with empty headers (that is, a header + line with all empty cells) would be rendered as headerless + tables. This broke in 2.11.4. The fix here is to produce an + AST with an empty table head when a pipe table has all empty + header cells. + + * LaTeX reader: don't allow optional `*` on symbol control sequences + (#7340). Generally we allow optional starred variants of LaTeX commands + (since many allow them, and if we don't accept these explicitly, + ignoring the star usually gives acceptable results). But we + don't want to do this for `\(*\)` and similar cases. + + * Docx writer: fix handling of empty table headers (Albert Krewinkel, + #7369). A table header which does not contain any cells is now treated as + an empty header. + + * LaTeX writer: Fix regression in table header position (#7347). + In recent versions the table headers were no longer bottom-aligned + (if more than one line). This patch fixes that by using minipages + for table headers in non-simple tables. + + * CommonMark writer: + + + Do not use simple class for fenced-divs (Jan Tojnar, amends #7242.) + + Do not throw away attributes when `Ext_attributes` is enabled. + `Ext_attributes` covers at least the following: + `Ext_fenced_code_attributes`, `Ext_header_attributes`, + `Ext_inline_code_attributes`, `Ext_link_attributes`. + + * Markdown writer: re-use functions from Text.Pandoc.Markdown.Inline (Jan + Tojnar). + + * DocBook writer: Remove non-existent admonitions (Jan Tojnar). + `attention`, `error` and `hint` are reStructuredText specific. + + * HTML writer: Don't omit width attribute on div (#7342). + + * Text.Pandoc.MIME, `extensionFromMimeType`: add a few special cases. + When we do a reverse lookup in the MIME table, we just get the + last match, so when the same mime type is associated with several + different extensions, we sometimes got weird results, e.g. `.vs` + for `text/plain`. These special cases help us get the most standard + extensions for mime types like `text/plain`. + + * Lua utils: fix handling of table headers in `from_simple_table` (Albert + Krewinkel, #7369). Passing an empty list of header cells now results + in an empty table header. + + * Text.Pandoc.Citeproc: avoid duplicate classes and attributes on + references div. + + * Require citeproc 0.4.0.1. This fixes a bug which led to doubled + "et al." in some (rare) circumstances. + + * MANUAL.txt: + + + Mention GladTeX for EPUB export (Sebastian Humenda). + This updates the manual and the web site about the GladTeX usage. + + More details and a useful link for YAML syntax. + + * CONTRIBUTING.md: update modules overview (Albert Krewinkel). + + * using-the-pandoc-api.md: switch from String to Text (Albert Krewinkel). + + ## pandoc 2.14.0.1 (2021-06-01) * Commonmark reader: Fix regression in 2.14 with YAML metdata block parsing, diff --git a/man/pandoc.1 b/man/pandoc.1 index ae3aba19c..de2133cf2 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t -.\" Automatically generated by Pandoc 2.14 +.\" Automatically generated by Pandoc 2.14.0.1 .\" -.TH "Pandoc User\[cq]s Guide" "" "May 31, 2021" "pandoc 2.14.0.1" "" +.TH "Pandoc User\[cq]s Guide" "" "June 11, 2021" "pandoc 2.14.0.2" "" .hy .SH NAME pandoc - general markup converter @@ -716,11 +716,11 @@ Extract images and other media contained in or linked from the source document to the path \f[I]DIR\f[R], creating it if necessary, and adjust the images references in the document so they point to the extracted files. -If the source format is a binary container (docx, epub, or odt), the -media is extracted from the container and the original filenames are -used. -Otherwise the media is read from the file system or downloaded, and new -filenames are constructed based on SHA1 hashes of the contents. +Media are downloaded, read from the file system, or extracted from a +binary container (e.g.\ docx), as needed. +The original file paths are used if they are relative paths not +containing \f[C]..\f[R]. +Otherwise filenames are constructed from the SHA1 hash of the contents. .TP \f[B]\f[CB]--abbreviations=\f[B]\f[R]\f[I]FILE\f[R] Specifies a custom abbreviations file, with abbreviations one to a line. @@ -1504,16 +1504,16 @@ inserted. .TP \f[B]\f[CB]--gladtex\f[B]\f[R] Enclose TeX math in \f[C]\f[R] tags in HTML output. -The resulting HTML can then be processed by GladTeX to produce images of -the typeset formulas and an HTML file with links to these images. -So, the procedure is: +The resulting HTML can then be processed by GladTeX to produce SVG +images of the typeset formulas and an HTML file with these images +embedded. .RS .IP .nf \f[C] pandoc -s --gladtex input.md -o myfile.htex -gladtex -d myfile-images myfile.htex -# produces myfile.html and images in myfile-images +gladtex -d image_dir myfile.htex +# produces myfile.html and images in image_dir \f[R] .fi .RE @@ -4820,6 +4820,13 @@ abstract: | \f[R] .fi .PP +The literal block after the \f[C]|\f[R] must be indented relative to the +line containing the \f[C]|\f[R]. +If it is not, the YAML will be invalid and pandoc will not interpret it +as metadata. +For an overview of the complex rules governing YAML, see the Wikipedia +entry on YAML syntax. +.PP Template variables will be set automatically from the metadata. Thus, for example, in writing HTML, the variable \f[C]abstract\f[R] will be set to the HTML equivalent of the Markdown in the \f[C]abstract\f[R] diff --git a/pandoc.cabal b/pandoc.cabal index 590bb445e..a0acbe654 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.14.0.1 +version: 2.14.0.2 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md -- cgit v1.2.3 From 8d1853bd3637816c1a74e1dd12d1aecdd0829d1f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:06:47 -0700 Subject: Fancier issue templates. Based on Doom Emacs's templates. --- .github/ISSUE_TEMPLATE | 11 -------- .github/ISSUE_TEMPLATE/bug_report.yml | 44 ++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 12 ++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 20 ++++++++++++++ 4 files changed, 76 insertions(+), 11 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index 83336e684..000000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,11 +0,0 @@ -Asking a question? -Please use the pandoc-discuss mailing list instead: -https://groups.google.com/forum/#!forum/pandoc-discuss -This tracker is for bug reports and enhancement requests. - -Reporting a bug? -Please include your pandoc version and an example that allows us -to reproduce the problem (complete input, exact command line -used, expected and actual output). The smaller the example, the -better. - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..b3ebc257a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,44 @@ +--- +name: 📝 Bug Report +description: Report a bug, error, or misbehavior +title: "Briefly summarize your issue here" +labels: ["bug"] +body: +- type: markdown + attributes: + value: | + **Thank you for reporting an issue to our issue tracker!** + Before you proceed, please consult our available resources and collect as much information about your issue as possible: + +- type: checkboxes + attributes: + options: + - label: I have searched [the manual](https://pandoc.org/MANUAL.html) for solutions. + required: true + - label: I have searched [the FAQs](https://pandoc.org/faqs.html) for solutions. + required: true + - label: I have searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too). + required: true + - label: I have searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for solutions. + required: true + - label: I can reproduce my issue on [the latest release](https://github.com/jgm/pandoc/releases) doom-emacs/commit) of pandoc, or on [try pandoc](https://pandoc.org/try). + required: true + - label: If reporting a behavior change, I have searched the [changelog](https://github.com/jgm/pandoc/changelog.md) to see if it was intentional. + required: true +- type: textarea + attributes: + label: Explain how to reproduce the problem. + description: | + + Please give as minimal an example as you can to reproduce the issue. + + Include full command line and all files necessary to reproduce. + validations: + required: true +- type: textarea + attributes: + label: What happened, and what did you expect to happen? +- type: textarea + attributes: + label: What pandoc version did you use? (`pandoc --version`) + validations: + required: true + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..48021f86d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +blank_issues_enabled: false +contact_links: + - name: Pandoc User's Guide + url: https://pandoc.org/MANUAL.html + about: "Official manual for pandoc" + - name: pandoc-discuss mailing list + url: https://groups.google.com/forum/#!forum/pandoc-discuss + about: "Discussion forum for pandoc (may be used as an email list)" + - name: Frequently Asked Questions + url: https://pandoc.org/faqs.html + about: "Some frequently asked qusetions (and answers)" + diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..76edc0a5c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +--- +name: 📝 Feature Request +description: Propose a new idea or feature +title: "Briefly summarize your request here" +labels: ["enhancement"] +body: +- type: checkboxes + attributes: + options: + - label: I have read the [contributing guide](https://github.com/jgm/CONTRIBUTING.md). + required: true + - label: I have searched [the issue tracker](https://github.com/jgm/pandoc/issues) (including closed issues) for similar requests. + required: true + - label: I have searched [the pandoc-discuss mailing list](https://groups.google.com/forum/#!forum/pandoc-discuss) for relevant discussions. + required: true +- type: textarea + attributes: + label: Describe your request + required: true + -- cgit v1.2.3 From 1fe07e638713d09f93001066316c1d04c1936ff3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:13:49 -0700 Subject: Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. -- cgit v1.2.3 From 61c0e0bbfbbaa53a31ba302237704001ca22e791 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:15:22 -0700 Subject: Remove md versions of issue templates. --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ------------------------------- .github/ISSUE_TEMPLATE/feature_request.md | 20 ---------------- 2 files changed, 58 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index dd84ea782..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7d6..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. -- cgit v1.2.3 From d46e0551fe988e4d3bc18a2364e1c53e0289bbe6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:17:59 -0700 Subject: Revert "Remove md versions of issue templates." This reverts commit 61c0e0bbfbbaa53a31ba302237704001ca22e791. --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. -- cgit v1.2.3 From 6d471a4a2803de1831ad09ae0df55e197e026ea3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:37:27 -0700 Subject: Improve issue templates. --- .github/ISSUE_TEMPLATE/bug_report.md | 35 ++++++------------------ .github/ISSUE_TEMPLATE/bug_report.yml | 44 ------------------------------ .github/ISSUE_TEMPLATE/feature_request.md | 16 +++++------ .github/ISSUE_TEMPLATE/feature_request.yml | 20 -------------- 4 files changed, 17 insertions(+), 98 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..8b2f40bcb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,37 +2,20 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: 'bug' assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +Thank you for reporting an issue! Before you continue, please make sure that you have -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too) +- [ ] searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for solutions +- [ ] reproduced your issue on [the latest release](https://github.com/jgm/pandoc/releases) doom-emacs/commit) of pandoc, or on [try pandoc](https://pandoc.org/try) -**Expected behavior** -A clear and concise description of what you expected to happen. +**Explain the problem.** +Include the exact command line you used and all inputs necessary to reproduce the issue. Please create as minimal an example as possible, to help the maintainers isolate the problem. Explain the output you received and how it differs from what you expected. -**Screenshots** -If applicable, add screenshots to help explain your problem. +**Pandoc version?** +What version of pandoc are you using (`pandoc --version`), on what OS? -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index b3ebc257a..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: 📝 Bug Report -description: Report a bug, error, or misbehavior -title: "Briefly summarize your issue here" -labels: ["bug"] -body: -- type: markdown - attributes: - value: | - **Thank you for reporting an issue to our issue tracker!** - Before you proceed, please consult our available resources and collect as much information about your issue as possible: - -- type: checkboxes - attributes: - options: - - label: I have searched [the manual](https://pandoc.org/MANUAL.html) for solutions. - required: true - - label: I have searched [the FAQs](https://pandoc.org/faqs.html) for solutions. - required: true - - label: I have searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too). - required: true - - label: I have searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for solutions. - required: true - - label: I can reproduce my issue on [the latest release](https://github.com/jgm/pandoc/releases) doom-emacs/commit) of pandoc, or on [try pandoc](https://pandoc.org/try). - required: true - - label: If reporting a behavior change, I have searched the [changelog](https://github.com/jgm/pandoc/changelog.md) to see if it was intentional. - required: true -- type: textarea - attributes: - label: Explain how to reproduce the problem. - description: | - + Please give as minimal an example as you can to reproduce the issue. - + Include full command line and all files necessary to reproduce. - validations: - required: true -- type: textarea - attributes: - label: What happened, and what did you expect to happen? -- type: textarea - attributes: - label: What pandoc version did you use? (`pandoc --version`) - validations: - required: true - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..f64c097c7 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,19 +2,19 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: 'enhancement' assignees: '' --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +Thank you for suggesting a fetaure! Before you continue, please make sure that you have -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +- [ ] read the [contributing guidelines](https://github.com/jgm/pandoc/CONTRIBUTING.md) +- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too) +- [ ] searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for relevant discussions +- [ ] searched the [changelog](https://github.com/jgm/pandoc/changelog.md) to make sure the feature has not already been implemented. + +**Describe your proposed improvement and the problem it solves.** **Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 76edc0a5c..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: 📝 Feature Request -description: Propose a new idea or feature -title: "Briefly summarize your request here" -labels: ["enhancement"] -body: -- type: checkboxes - attributes: - options: - - label: I have read the [contributing guide](https://github.com/jgm/CONTRIBUTING.md). - required: true - - label: I have searched [the issue tracker](https://github.com/jgm/pandoc/issues) (including closed issues) for similar requests. - required: true - - label: I have searched [the pandoc-discuss mailing list](https://groups.google.com/forum/#!forum/pandoc-discuss) for relevant discussions. - required: true -- type: textarea - attributes: - label: Describe your request - required: true - -- cgit v1.2.3 From c9b354e15f2653e58303c6689043ce50fb86355e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:37:37 -0700 Subject: Add pull request template. --- .github/PULL_REQUEST_TEMPLATE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..2716efbac --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +--- +name: Pull request +about: Improve pandoc's code +title: '' +labels: '' +assignees: '' + +--- + +Before you continue, please make sure that you have + +- [ ] read the [contributing guidelines](https://github.com/jgm/pandoc/CONTRIBUTING.md) +- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too). + +**What issue does this pull request address?** +Fixes #0000 + +**Describe the changes.** + -- cgit v1.2.3 From 85f1e1f3069d1ddf55fdd69881e759fdb35d1ee3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:39:27 -0700 Subject: Remove pull requset template. --- .github/PULL_REQUEST_TEMPLATE.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 2716efbac..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Pull request -about: Improve pandoc's code -title: '' -labels: '' -assignees: '' - ---- - -Before you continue, please make sure that you have - -- [ ] read the [contributing guidelines](https://github.com/jgm/pandoc/CONTRIBUTING.md) -- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too). - -**What issue does this pull request address?** -Fixes #0000 - -**Describe the changes.** - -- cgit v1.2.3 From 1abac223c2038a671d7508ce1f4dd585ddffa58a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:45:13 -0700 Subject: More honing of issue templates. --- .github/ISSUE_TEMPLATE/bug_report.md | 9 +++++---- .github/ISSUE_TEMPLATE/feature_request.md | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8b2f40bcb..3c6a1f32e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,13 +9,14 @@ assignees: '' Thank you for reporting an issue! Before you continue, please make sure that you have -- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too) -- [ ] searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for solutions -- [ ] reproduced your issue on [the latest release](https://github.com/jgm/pandoc/releases) doom-emacs/commit) of pandoc, or on [try pandoc](https://pandoc.org/try) +- searched the issue tracker for similar issues (including + closed issues): https://github.com/jgm/pandoc/issues +- searched the pandoc-discuss mailing list for solutions: https://groups.google.com/forum/#!forum/pandoc-discuss +- reproduced your issue on the latest release of pandoc, or on https://pandoc.org/try **Explain the problem.** Include the exact command line you used and all inputs necessary to reproduce the issue. Please create as minimal an example as possible, to help the maintainers isolate the problem. Explain the output you received and how it differs from what you expected. **Pandoc version?** -What version of pandoc are you using (`pandoc --version`), on what OS? +What version of pandoc are you using, on what OS? diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index f64c097c7..d95b1daad 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,14 +7,14 @@ assignees: '' --- -Thank you for suggesting a fetaure! Before you continue, please make sure that you have +Thank you for suggesting a feature! Before you continue, please make sure that you have -- [ ] read the [contributing guidelines](https://github.com/jgm/pandoc/CONTRIBUTING.md) -- [ ] searched [the issue tracker](https://github.com/jgm/pandoc/issues) for similar issues (the closed issues too) -- [ ] searched [pandoc-discuss](https://groups.google.com/forum/#!forum/pandoc-discuss) for relevant discussions -- [ ] searched the [changelog](https://github.com/jgm/pandoc/changelog.md) to make sure the feature has not already been implemented. +- read the contributing guidelines: https://github.com/jgm/pandoc/CONTRIBUTING.md +- searched the issue tracker for similar issues (including + closed issues): https://github.com/jgm/pandoc/issues +- searched the pandoc-discuss mailing list for relevant discussions: https://groups.google.com/forum/#!forum/pandoc-discuss **Describe your proposed improvement and the problem it solves.** -**Describe alternatives you've considered** +**Describe alternatives you've considered.** -- cgit v1.2.3 From 01bae106bff928a7cb944c6c89f7846243bf4913 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:47:17 -0700 Subject: Add contributing guidelines to issue menu. --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 48021f86d..b5267bc74 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -9,4 +9,7 @@ contact_links: - name: Frequently Asked Questions url: https://pandoc.org/faqs.html about: "Some frequently asked qusetions (and answers)" + - name: Contributing Guidelines + url: https://github.com/jgm/pandoc/CONTRIBUTING.md + about: "Guidelines for submitting bug reports and code improvements" -- cgit v1.2.3 From 2390f58a07024ab29d4e85952d99da4571959371 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:49:13 -0700 Subject: Fix link to contributing guidelines. --- .github/ISSUE_TEMPLATE/config.yml | 4 ++-- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b5267bc74..3d55cf67a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -8,8 +8,8 @@ contact_links: about: "Discussion forum for pandoc (may be used as an email list)" - name: Frequently Asked Questions url: https://pandoc.org/faqs.html - about: "Some frequently asked qusetions (and answers)" + about: "Some frequently asked questions (and answers)" - name: Contributing Guidelines - url: https://github.com/jgm/pandoc/CONTRIBUTING.md + url: https://pandoc.org/CONTRIBUTING.html about: "Guidelines for submitting bug reports and code improvements" diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index d95b1daad..9183d4823 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -9,7 +9,7 @@ assignees: '' Thank you for suggesting a feature! Before you continue, please make sure that you have -- read the contributing guidelines: https://github.com/jgm/pandoc/CONTRIBUTING.md +- read the contributing guidelines: https://pandoc.org/CONTRIBUTING.html - searched the issue tracker for similar issues (including closed issues): https://github.com/jgm/pandoc/issues - searched the pandoc-discuss mailing list for relevant discussions: https://groups.google.com/forum/#!forum/pandoc-discuss -- cgit v1.2.3 From 55e9ca7c78909fd13497d01a1e31f5944c2dace0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:51:19 -0700 Subject: Fix line breaks in issue templates. --- .github/ISSUE_TEMPLATE/bug_report.md | 3 +-- .github/ISSUE_TEMPLATE/feature_request.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3c6a1f32e..5e3c8f02f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,8 +9,7 @@ assignees: '' Thank you for reporting an issue! Before you continue, please make sure that you have -- searched the issue tracker for similar issues (including - closed issues): https://github.com/jgm/pandoc/issues +- searched the issue tracker for similar issues (including closed issues): https://github.com/jgm/pandoc/issues - searched the pandoc-discuss mailing list for solutions: https://groups.google.com/forum/#!forum/pandoc-discuss - reproduced your issue on the latest release of pandoc, or on https://pandoc.org/try diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 9183d4823..9b5db76c3 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -10,8 +10,7 @@ assignees: '' Thank you for suggesting a feature! Before you continue, please make sure that you have - read the contributing guidelines: https://pandoc.org/CONTRIBUTING.html -- searched the issue tracker for similar issues (including - closed issues): https://github.com/jgm/pandoc/issues +- searched the issue tracker for similar issues (including closed issues): https://github.com/jgm/pandoc/issues - searched the pandoc-discuss mailing list for relevant discussions: https://groups.google.com/forum/#!forum/pandoc-discuss **Describe your proposed improvement and the problem it solves.** -- cgit v1.2.3 From b01a7aa66cb95808185654cff392702c94d54810 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 09:53:07 -0700 Subject: Add note about asking questions to bug report template. --- .github/ISSUE_TEMPLATE/bug_report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5e3c8f02f..020283aca 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,6 +13,8 @@ Thank you for reporting an issue! Before you continue, please make sure that you - searched the pandoc-discuss mailing list for solutions: https://groups.google.com/forum/#!forum/pandoc-discuss - reproduced your issue on the latest release of pandoc, or on https://pandoc.org/try +Note that this bug tracker is for reporting bugs, not asking questions. For questions, use the pandoc-discuss mailing list. + **Explain the problem.** Include the exact command line you used and all inputs necessary to reproduce the issue. Please create as minimal an example as possible, to help the maintainers isolate the problem. Explain the output you received and how it differs from what you expected. -- cgit v1.2.3 From a916c4bce4cfc31dc8050e01aecd618ed4ca12c8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 13:15:22 -0700 Subject: Revise issue report "dashboard." --- .github/ISSUE_TEMPLATE/config.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3d55cf67a..e4d479ce6 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,8 @@ blank_issues_enabled: false contact_links: - - name: Pandoc User's Guide - url: https://pandoc.org/MANUAL.html - about: "Official manual for pandoc" - - name: pandoc-discuss mailing list + - name: Ask a question url: https://groups.google.com/forum/#!forum/pandoc-discuss about: "Discussion forum for pandoc (may be used as an email list)" - - name: Frequently Asked Questions - url: https://pandoc.org/faqs.html - about: "Some frequently asked questions (and answers)" - name: Contributing Guidelines url: https://pandoc.org/CONTRIBUTING.html about: "Guidelines for submitting bug reports and code improvements" -- cgit v1.2.3 From ea88979aa2ea4b41463216f00d9769d7c11f90fc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 11 Jun 2021 13:29:21 -0700 Subject: Make parts of the bug report templates HTML comments. --- .github/ISSUE_TEMPLATE/bug_report.md | 2 ++ .github/ISSUE_TEMPLATE/feature_request.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 020283aca..6162c2e57 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,7 @@ assignees: '' --- + **Explain the problem.** Include the exact command line you used and all inputs necessary to reproduce the issue. Please create as minimal an example as possible, to help the maintainers isolate the problem. Explain the output you received and how it differs from what you expected. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 9b5db76c3..cff52c1d4 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,11 +7,13 @@ assignees: '' --- + **Describe your proposed improvement and the problem it solves.** -- cgit v1.2.3 From b0cd6c622494666add6bdd7674ec5b7791bc83d0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 12 Jun 2021 10:16:44 -0700 Subject: Fix regression in citeproc processing. If inline references are used (in the metadata `references` field), we should still only include in the bibliography items that are actually cited -- unless `nocite` is used. Closes #7376. --- src/Text/Pandoc/Citeproc.hs | 4 +++- test/command/7376.md | 16 ++++++++++++++++ test/command/pandoc-citeproc-356.md | 9 ++------- 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 test/command/7376.md diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index ad3b26c0f..a5b26c9b4 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -194,7 +194,9 @@ getReferences mblocale (Pandoc meta bs) = do then const True else (`Set.member` citeIds) let inlineRefs = case lookupMeta "references" meta of - Just (MetaList rs) -> mapMaybe metaValueToReference rs + Just (MetaList rs) -> + filter (idpred . unItemId . referenceId) + $ mapMaybe metaValueToReference rs _ -> [] externalRefs <- case lookupMeta "bibliography" meta of Just (MetaList xs) -> diff --git a/test/command/7376.md b/test/command/7376.md new file mode 100644 index 000000000..229c61cfb --- /dev/null +++ b/test/command/7376.md @@ -0,0 +1,16 @@ +``` +% pandoc --citeproc -t plain +--- +references: +- id: item1 + type: book + author: + - family: Doe + given: Jane + issued: 2020 + title: The title +... +^D + + +``` diff --git a/test/command/pandoc-citeproc-356.md b/test/command/pandoc-citeproc-356.md index 4463ef63f..b4f998dae 100644 --- a/test/command/pandoc-citeproc-356.md +++ b/test/command/pandoc-citeproc-356.md @@ -15,11 +15,6 @@ references: [@bar] ^D -(Alice 2042) - -::: {#refs .references .csl-bib-body .hanging-indent} -::: {#ref-foo .csl-entry} -Alice. 2042. -::: -::: +[WARNING] Citeproc: citation bar not found +(**bar?**) ``` -- cgit v1.2.3 From ea53a1dc5c68e6a1a2e450422147a66fd2aa9efd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 12 Jun 2021 10:20:19 -0700 Subject: Markdown writer: allow `pipe_tables` to be disabled for commonmark... (commonmark_x, gfm). Closes #7375. --- src/Text/Pandoc/Writers/Markdown.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index b16c71358..425ea07ca 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -75,7 +75,6 @@ writeCommonMark opts document = -- we set them here so that escapeText will behave -- properly. enableExtension Ext_all_symbols_escapable $ - enableExtension Ext_pipe_tables $ enableExtension Ext_intraword_underscores $ writerExtensions opts } -- cgit v1.2.3 From cfa26e3ca0346397f41af9aed5b4cd1d86be1220 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 12 Jun 2021 13:56:09 -0700 Subject: Docx reader: handle absolute URIs in Relationship Target. Closes #7374. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index aaa8f4ad0..dbb16a821 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -479,20 +479,26 @@ filePathToRelType path docXmlPath = then Just InDocument else Nothing -relElemToRelationship :: DocumentLocation -> Element -> Maybe Relationship -relElemToRelationship relType element | qName (elName element) == "Relationship" = +relElemToRelationship :: FilePath -> DocumentLocation -> Element + -> Maybe Relationship +relElemToRelationship fp relType element | qName (elName element) == "Relationship" = do relId <- findAttr (QName "Id" Nothing Nothing) element target <- findAttr (QName "Target" Nothing Nothing) element - return $ Relationship relType relId target -relElemToRelationship _ _ = Nothing + -- target may be relative (media/image1.jpeg) or absolute + -- (/word/media/image1.jpeg); we need to relativize it (see #7374) + let frontOfFp = T.pack $ takeWhile (/= '_') fp + let target' = fromMaybe target $ + T.stripPrefix frontOfFp $ T.dropWhile (== '/') target + return $ Relationship relType relId target' +relElemToRelationship _ _ _ = Nothing filePathToRelationships :: Archive -> FilePath -> FilePath -> [Relationship] filePathToRelationships ar docXmlPath fp | Just relType <- filePathToRelType fp docXmlPath , Just entry <- findEntryByPath fp ar , Just relElems <- parseXMLFromEntry entry = - mapMaybe (relElemToRelationship relType) $ elChildren relElems + mapMaybe (relElemToRelationship fp relType) $ elChildren relElems filePathToRelationships _ _ _ = [] archiveToRelationships :: Archive -> FilePath -> [Relationship] -- cgit v1.2.3 From 7ab5c2057bdda26028b71749e688f3a65a93d453 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 12 Jun 2021 14:02:00 -0700 Subject: Update changelog. --- changelog.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 822f2fb16..3b45e85ca 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Revision history for pandoc -## pandoc 2.14.0.2 (2021-06-11) +## pandoc 2.14.0.2 (2021-06-12) * Fix MediaBag regressions (#7345). iIn the 2.14 release `--extract-media` stopped working as before; there could be mismatches between the @@ -48,6 +48,8 @@ ignoring the star usually gives acceptable results). But we don't want to do this for `\(*\)` and similar cases. + * Docx reader: handle absolute URIs in Relationship Target (#7374). + * Docx writer: fix handling of empty table headers (Albert Krewinkel, #7369). A table header which does not contain any cells is now treated as an empty header. @@ -65,8 +67,11 @@ `Ext_fenced_code_attributes`, `Ext_header_attributes`, `Ext_inline_code_attributes`, `Ext_link_attributes`. - * Markdown writer: re-use functions from Text.Pandoc.Markdown.Inline (Jan - Tojnar). + * Markdown writer: + + + Allow `pipe_tables` to be disabled for commonmark formats + (`commonmark_x`, `gfm`) (#7375). + + Re-use functions from Text.Pandoc.Markdown.Inline (Jan Tojnar). * DocBook writer: Remove non-existent admonitions (Jan Tojnar). `attention`, `error` and `hint` are reStructuredText specific. @@ -84,8 +89,13 @@ Krewinkel, #7369). Passing an empty list of header cells now results in an empty table header. - * Text.Pandoc.Citeproc: avoid duplicate classes and attributes on - references div. + * Text.Pandoc.Citeproc: + + + Avoid duplicate classes and attributes on references div. + + Fix regression in citeproc processing (#7376). If inline + references are used (in the metadata `references` field), we + should still only include in the bibliography items that are + actually cited (unless `nocite` is used). * Require citeproc 0.4.0.1. This fixes a bug which led to doubled "et al." in some (rare) circumstances. -- cgit v1.2.3 From 2d966c4c2d8f043f8722ab4427056afab01d0bf3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 13 Jun 2021 09:53:01 -0700 Subject: Update changelog date. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 3b45e85ca..34615b948 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Revision history for pandoc -## pandoc 2.14.0.2 (2021-06-12) +## pandoc 2.14.0.2 (2021-06-13) * Fix MediaBag regressions (#7345). iIn the 2.14 release `--extract-media` stopped working as before; there could be mismatches between the -- cgit v1.2.3 From 115962079e099ef8dcb363a2fa1fd2962cccc89e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 14 Jun 2021 09:15:23 -0700 Subject: Create SECURITY.md --- SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..a8d8fa11f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Supported Versions + +Only the most recent version of pandoc is supported with security updates. + +## Reporting a Vulnerability + +To report a vulnerability, email the maintainer, jgm@berkeley.edu. -- cgit v1.2.3 From 62533053da158d00256ef3808e15eeb17401dd62 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 14 Jun 2021 10:35:29 -0700 Subject: Add link to the manual's note on security to SECURITY.md. --- SECURITY.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index a8d8fa11f..ec035df45 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,3 +7,6 @@ Only the most recent version of pandoc is supported with security updates. ## Reporting a Vulnerability To report a vulnerability, email the maintainer, jgm@berkeley.edu. +But first please read the section of the manual entitled +[A note on security](https://pandoc.org/MANUAL.html#a-note-on-security), +which describes some security guarantees pandoc does NOT make. -- cgit v1.2.3 From 961268446c551e43b43cd3dffaf0f3c6be4dfa6f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 14 Jun 2021 12:35:44 -0700 Subject: Rephrase section on unsafe HTML in manual. --- MANUAL.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index 815ed0fdf..c5dba29f3 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -6504,9 +6504,9 @@ application, here are some things to keep in mind: 4. The HTML generated by pandoc is not guaranteed to be safe. If `raw_html` is enabled for the Markdown input, users can inject arbitrary HTML. Even if `raw_html` is disabled, - users can include dangerous content in attributes for - headings, spans, and code blocks. To be safe, you should - run all the generated HTML through an HTML sanitizer. + users can include dangerous content in URLs and attributes. + To be safe, you should run all the generated HTML through + an HTML sanitizer. # Authors -- cgit v1.2.3 From 3fb5499dd638dae7156fba63ba5c1522bed5e46d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 18 Jun 2021 12:06:20 -0700 Subject: insertMediaBag: ensure we get sane mediaPath for URLs. Long URLs cannot be treated as mediaPaths, but System.FilePath's `isRelative` often returns True for them. So we add a check for an absolute URL. We also ensure that extensions are derived only from the path portion of URLs (previously a following query was being included). Closes #7391. --- src/Text/Pandoc/MediaBag.hs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index 06fba5632..098e484ee 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -26,13 +26,14 @@ module Text.Pandoc.MediaBag ( import qualified Data.ByteString.Lazy as BL import Data.Data (Data) import qualified Data.Map as M -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isNothing) import Data.Typeable (Typeable) import System.FilePath import Text.Pandoc.MIME (MimeType, getMimeTypeDef, extensionFromMimeType) import Data.Text (Text) import qualified Data.Text as T import Data.Digest.Pure.SHA (sha1, showDigest) +import Network.URI (URI (..), parseURI) data MediaItem = MediaItem @@ -76,16 +77,20 @@ insertMedia fp mbMime contents (MediaBag mediamap) = , mediaContents = contents , mediaMimeType = mt } fp' = canonicalize fp - newpath = if isRelative fp && ".." `notElem` splitPath fp + uri = parseURI fp + newpath = if isRelative fp + && isNothing uri + && ".." `notElem` splitPath fp then T.unpack fp' else showDigest (sha1 contents) <> "." <> ext fallback = case takeExtension fp of ".gz" -> getMimeTypeDef $ dropExtension fp _ -> getMimeTypeDef fp mt = fromMaybe fallback mbMime - ext = case takeExtension fp of - '.':e -> e - _ -> maybe "" T.unpack $ extensionFromMimeType mt + path = maybe fp uriPath uri + ext = case takeExtension path of + '.':e -> e + _ -> maybe "" T.unpack $ extensionFromMimeType mt -- | Lookup a media item in a 'MediaBag', returning mime type and contents. -- cgit v1.2.3 From a8556ac06cccd84091fe913ac58aa1c9216576ea Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 20 Jun 2021 17:46:47 -0700 Subject: Require commonmark 0.2.1. --- pandoc.cabal | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandoc.cabal b/pandoc.cabal index a0acbe654..8fa28d426 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -451,7 +451,7 @@ library bytestring >= 0.9 && < 0.12, case-insensitive >= 1.2 && < 1.3, citeproc >= 0.4.0.1 && < 0.4.1, - commonmark >= 0.2 && < 0.3, + commonmark >= 0.2.1 && < 0.3, commonmark-extensions >= 0.2.1.2 && < 0.3, commonmark-pandoc >= 0.2.1 && < 0.3, connection >= 0.3.1, diff --git a/stack.yaml b/stack.yaml index 23d031681..5b56eda42 100644 --- a/stack.yaml +++ b/stack.yaml @@ -14,7 +14,7 @@ extra-deps: - random-1.2.0 - unicode-collation-0.1.3 - citeproc-0.4.0.1 -- commonmark-0.2 +- commonmark-0.2.1 - commonmark-extensions-0.2.1.2 - commonmark-pandoc-0.2.1 ghc-options: -- cgit v1.2.3 From 39d019ca73090cf645053ed1331dc6cd2eb9d1fd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 20 Jun 2021 18:17:38 -0700 Subject: Use lts-18.0 stack resolver. --- stack.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/stack.yaml b/stack.yaml index 5b56eda42..427de892a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -7,18 +7,9 @@ flags: packages: - '.' extra-deps: -- haddock-library-1.10.0 -- hslua-1.3.0 -- hslua-module-path-0.1.0 -- jira-wiki-markup-1.4.0 -- random-1.2.0 -- unicode-collation-0.1.3 -- citeproc-0.4.0.1 - commonmark-0.2.1 -- commonmark-extensions-0.2.1.2 -- commonmark-pandoc-0.2.1 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules -resolver: lts-17.12 +resolver: lts-18.0 nix: packages: [zlib] -- cgit v1.2.3 From 0c4ed0045a2adb611f83054ad29d68f46b0611c5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 20 Jun 2021 19:25:43 -0700 Subject: Bump to 2.14.0.3, update changelog, require latest skylighting. --- MANUAL.txt | 2 +- changelog.md | 12 ++++++++++++ man/pandoc.1 | 4 ++-- pandoc.cabal | 6 +++--- stack.yaml | 2 ++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/MANUAL.txt b/MANUAL.txt index c5dba29f3..52563a8fa 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane -date: June 11, 2021 +date: June 20, 2021 --- # Synopsis diff --git a/changelog.md b/changelog.md index 34615b948..049d1db57 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,17 @@ # Revision history for pandoc +## pandoc 2.14.0.3 (2021-06-20) + + * Text.Pandoc.MediaBag `insertMediaBag`: ensure we get a sane mediaPath + for URLs (#7391). In earlier 2.14.x versions, we'd get + incorrect paths for resources downloaded from URLs when the + media are extracted (including in PDF production). + * Use lts-18.0 stack resolver. + * Require skylighting 0.10.5.2 (adding support for Swift). + * Require commonmark 0.2.1. + * Rephrase section on unsafe HTML in manual. + * Create SECURITY.md + ## pandoc 2.14.0.2 (2021-06-13) * Fix MediaBag regressions (#7345). iIn the 2.14 release `--extract-media` diff --git a/man/pandoc.1 b/man/pandoc.1 index de2133cf2..c77ab93bd 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,7 +1,7 @@ '\" t .\" Automatically generated by Pandoc 2.14.0.1 .\" -.TH "Pandoc User\[cq]s Guide" "" "June 11, 2021" "pandoc 2.14.0.2" "" +.TH "Pandoc User\[cq]s Guide" "" "June 20, 2021" "pandoc 2.14.0.3" "" .hy .SH NAME pandoc - general markup converter @@ -7708,7 +7708,7 @@ The HTML generated by pandoc is not guaranteed to be safe. If \f[C]raw_html\f[R] is enabled for the Markdown input, users can inject arbitrary HTML. Even if \f[C]raw_html\f[R] is disabled, users can include dangerous -content in attributes for headings, spans, and code blocks. +content in URLs and attributes. To be safe, you should run all the generated HTML through an HTML sanitizer. .SH AUTHORS diff --git a/pandoc.cabal b/pandoc.cabal index 8fa28d426..eb9633bd0 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc -version: 2.14.0.2 +version: 2.14.0.3 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md @@ -484,8 +484,8 @@ library random >= 1 && < 1.3, safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, - skylighting >= 0.10.5.1 && < 0.10.6, - skylighting-core >= 0.10.5.1 && < 0.10.6, + skylighting >= 0.10.5.2 && < 0.10.6, + skylighting-core >= 0.10.5.2 && < 0.10.6, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, diff --git a/stack.yaml b/stack.yaml index 427de892a..80d0707e7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -8,6 +8,8 @@ packages: - '.' extra-deps: - commonmark-0.2.1 +- skylighting-core-0.10.5.2 +- skylighting-0.10.5.2 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-18.0 -- cgit v1.2.3 From 82ad855f38b8fa8dc1cbfc14fa294dfd5f9f02ab Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 08:49:00 -0700 Subject: Markdown writer: Fix regression in code blocks with attributes. Code blocks with a single class but nonempty attributes were having attributes drop as a result of #7242. Closes #7397. --- src/Text/Pandoc/Writers/Markdown.hs | 6 +++--- test/command/7397.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 test/command/7397.md diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 425ea07ca..b13ab57ee 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -256,10 +256,10 @@ noteToMarkdown opts num blocks = do then hang (writerTabStop opts) (marker <> spacer) contents else marker <> spacer <> contents --- | (Code) blocks with a single class can just use it standalone, --- no need to bother with curly braces. +-- | (Code) blocks with a single class and no attributes can just use it +-- standalone, no need to bother with curly braces. classOrAttrsToMarkdown :: Attr -> Doc Text -classOrAttrsToMarkdown ("",[cls],_) = literal cls +classOrAttrsToMarkdown ("",[cls],[]) = literal cls classOrAttrsToMarkdown attrs = attrsToMarkdown attrs -- | Ordered list start parser for use in Para below. diff --git a/test/command/7397.md b/test/command/7397.md new file mode 100644 index 000000000..ca8b6a482 --- /dev/null +++ b/test/command/7397.md @@ -0,0 +1,14 @@ +``` +% pandoc -t markdown +~~~~ { .haskell startFrom="100"} +qsort [] = [] +qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ + qsort (filter (>= x) xs) +~~~~ +^D +``` {.haskell startFrom="100"} +qsort [] = [] +qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ + qsort (filter (>= x) xs) +``` +``` -- cgit v1.2.3 From a39313eddbc84c7680d4bc7cef7770b18c89260a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 09:30:23 -0700 Subject: Fix test for #7397 --- test/command/7397.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/command/7397.md b/test/command/7397.md index ca8b6a482..24ce391b0 100644 --- a/test/command/7397.md +++ b/test/command/7397.md @@ -1,4 +1,4 @@ -``` +```` % pandoc -t markdown ~~~~ { .haskell startFrom="100"} qsort [] = [] @@ -11,4 +11,4 @@ qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) ``` -``` +```` -- cgit v1.2.3 From 14b2eb2aeb21b3dee7d07c7348ebd2c586d6a866 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 16:40:52 -0700 Subject: reveal.js writer: better handling of options. Previously it was impossible to specify false values for options that default to true; setting the option to false just caused the portion of the template setting the option to be omitted. Now we prepopulate all the variables with their default values, including them unconditionally and allowing them to be overridden. --- data/templates/default.revealjs | 198 ++++++++++++++-------------------------- src/Text/Pandoc/Writers/HTML.hs | 50 ++++++++++ 2 files changed, 120 insertions(+), 128 deletions(-) diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs index 8a77674dd..203983522 100644 --- a/data/templates/default.revealjs +++ b/data/templates/default.revealjs @@ -90,256 +90,198 @@ $endif$ // Full list of configuration options available at: // https://revealjs.com/config/ Reveal.initialize({ -$if(center)$ - // Determines whether slide content should be vertically centered - center: $center$, -$endif$ -$if(controls)$ // Display controls in the bottom right corner controls: $controls$, -$endif$ -$if(controlsTutorial)$ + // Help the user learn the controls by providing hints, for example by // bouncing the down arrow when they first encounter a vertical slide controlsTutorial: $controlsTutorial$, -$endif$ -$if(controlsLayout)$ + // Determines where controls appear, "edges" or "bottom-right" controlsLayout: '$controlsLayout$', -$endif$ -$if(controlsBackArrows)$ + // Visibility rule for backwards navigation arrows; "faded", "hidden" // or "visible" controlsBackArrows: '$controlsBackArrows$', -$endif$ -$if(progress)$ + // Display a presentation progress bar progress: $progress$, -$endif$ -$if(slideNumber)$ + // Display the page number of the current slide slideNumber: $slideNumber$, -$endif$ -$if(showSlideNumber)$ + // 'all', 'print', or 'speaker' showSlideNumber: '$showSlideNumber$', -$endif$ + // Add the current slide number to the URL hash so that reloading the // page/copying the URL will return you to the same slide - hash: $if(hash)$$hash$$else$true$endif$, -$if(hashOneBasedIndex)$ + hash: $hash$, + // Start with 1 for the hash rather than 0 hashOneBasedIndex: $hashOneBasedIndex$, -$endif$ -$if(history)$ + + // Flags if we should monitor the hash and change slides accordingly + respondToHashChanges: $respondToHashChanges$, + // Push each slide change to the browser history history: $history$, -$endif$ -$if(keyboard)$ + // Enable keyboard shortcuts for navigation keyboard: $keyboard$, -$endif$ -$if(overview)$ + // Enable the slide overview mode overview: $overview$, -$endif$ -$if(center)$ + + // Disables the default reveal.js slide layout (scaling and centering) + // so that you can use custom CSS layout + disableLayout: false, + // Vertical centering of slides center: $center$, -$endif$ -$if(touch)$ + // Enables touch navigation on devices with touch input touch: $touch$, -$endif$ -$if(loop)$ + // Loop the presentation loop: $loop$, -$endif$ -$if(rtl)$ + // Change the presentation direction to be RTL rtl: $rtl$, -$endif$ -$if(navigationMode)$ + // see https://revealjs.com/vertical-slides/#navigation-mode navigationMode: '$navigationMode$', -$endif$ -$if(shuffle)$ + // Randomizes the order of slides each time the presentation loads shuffle: $shuffle$, -$endif$ -$if(fragments)$ + // Turns fragments on and off globally fragments: $fragments$, -$endif$ -$if(fragmentInURL)$ + // Flags whether to include the current fragment in the URL, // so that reloading brings you to the same fragment position fragmentInURL: $fragmentInURL$, -$endif$ -$if(embedded)$ + // Flags if the presentation is running in an embedded mode, // i.e. contained within a limited portion of the screen embedded: $embedded$, -$endif$ -$if(help)$ + // Flags if we should show a help overlay when the questionmark // key is pressed help: $help$, -$endif$ -$if(pause)$ - // Flags if it should be possible to pause the presentation (blackout) - pause: $pause$, -$endif$ -$if(showNotes)$ + + // Flags if it should be possible to pause the presentation (blackout) + pause: $pause$, + // Flags if speaker notes should be visible to all viewers showNotes: $showNotes$, -$endif$ -$if(autoPlayMedia)$ - // Global override for autoplaying embedded media (video/audio/iframe) - // - null: Media will only autoplay if data-autoplay is present - // - true: All media will autoplay, regardless of individual setting - // - false: No media will autoplay, regardless of individual setting + + // Global override for autoplaying embedded media (null/true/false) autoPlayMedia: $autoPlayMedia$, -$endif$ -$if(preloadIframes)$ - // Global override for preloading lazy-loaded iframes - // - null: Iframes with data-src AND data-preload will be loaded when within - // the viewDistance, iframes with only data-src will be loaded when visible - // - true: All iframes with data-src will be loaded when within the viewDistance - // - false: All iframes with data-src will be loaded only when visible + + // Global override for preloading lazy-loaded iframes (null/true/false) preloadIframes: $preloadIframes$, -$endif$ -$if(autoSlide)$ + // Number of milliseconds between automatically proceeding to the // next slide, disabled when set to 0, this value can be overwritten // by using a data-autoslide attribute on your slides autoSlide: $autoSlide$, -$endif$ -$if(autoSlideStoppable)$ + // Stop auto-sliding after user input autoSlideStoppable: $autoSlideStoppable$, -$endif$ -$if(autoSlideMethod)$ + // Use this method for navigation when auto-sliding autoSlideMethod: $autoSlideMethod$, -$endif$ -$if(defaultTiming)$ + // Specify the average time in seconds that you think you will spend // presenting each slide. This is used to show a pacing timer in the // speaker view defaultTiming: $defaultTiming$, -$endif$ -$if(totalTime)$ - // Specify the total time in seconds that is available to - // present. If this is set to a nonzero value, the pacing - // timer will work out the time available for each slide, - // instead of using the defaultTiming value - totalTime: $totalTime$, -$endif$ -$if(minimumTimePerSlide)$ - // Specify the minimum amount of time you want to allot to - // each slide, if using the totalTime calculation method. If - // the automated time allocation causes slide pacing to fall - // below this threshold, then you will see an alert in the - // speaker notes window - minimumTimePerSlide: $minimumTimePerSlide$, -$endif$ -$if(mouseWheel)$ + // Enable slide navigation via mouse wheel mouseWheel: $mouseWheel$, -$endif$ -$if(rollingLinks)$ - // Apply a 3D roll to links on hover - rollingLinks: $rollingLinks$, -$endif$ -$if(hideInactiveCursor)$ + + // The display mode that will be used to show slides + display: '$display$', + // Hide cursor if inactive hideInactiveCursor: $hideInactiveCursor$, -$endif$ -$if(hideCursorTime)$ + // Time before the cursor is hidden (in ms) hideCursorTime: $hideCursorTime$, -$endif$ -$if(hideAddressBar)$ - // Hides the address bar on mobile devices - hideAddressBar: $hideAddressBar$, -$endif$ -$if(previewLinks)$ + // Opens links in an iframe preview overlay previewLinks: $previewLinks$, -$endif$ -$if(transition)$ - // Transition style - transition: '$transition$', // none/fade/slide/convex/concave/zoom -$endif$ -$if(transitionSpeed)$ - // Transition speed - transitionSpeed: '$transitionSpeed$', // default/fast/slow -$endif$ -$if(backgroundTransition)$ + + // Transition style (none/fade/slide/convex/concave/zoom) + transition: '$transition$', + + // Transition speed (default/fast/slow) + transitionSpeed: '$transitionSpeed$', + // Transition style for full page slide backgrounds - backgroundTransition: '$backgroundTransition$', // none/fade/slide/convex/concave/zoom -$endif$ -$if(viewDistance)$ + // (none/fade/slide/convex/concave/zoom) + backgroundTransition: '$backgroundTransition$', + // Number of slides away from the current that are visible viewDistance: $viewDistance$, -$endif$ -$if(mobileViewDistance)$ + // Number of slides away from the current that are visible on mobile // devices. It is advisable to set this to a lower number than // viewDistance in order to save resources. mobileViewDistance: $mobileViewDistance$, -$endif$ $if(parallaxBackgroundImage)$ + // Parallax background image parallaxBackgroundImage: '$parallaxBackgroundImage$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'" $else$ $if(background-image)$ + // Parallax background image parallaxBackgroundImage: '$background-image$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'" $endif$ $endif$ $if(parallaxBackgroundSize)$ + // Parallax background size parallaxBackgroundSize: '$parallaxBackgroundSize$', // CSS syntax, e.g. "2100px 900px" $endif$ $if(parallaxBackgroundHorizontal)$ + // Amount to move parallax background (horizontal and vertical) on slide change // Number, e.g. 100 parallaxBackgroundHorizontal: $parallaxBackgroundHorizontal$, $endif$ $if(parallaxBackgroundVertical)$ + parallaxBackgroundVertical: $parallaxBackgroundVertical$, $endif$ $if(width)$ + // The "normal" size of the presentation, aspect ratio will be preserved // when the presentation is scaled to fit different resolutions. Can be // specified using percentage units. width: $width$, $endif$ $if(height)$ + height: $height$, $endif$ $if(margin)$ + // Factor of the display size that should remain empty around the content margin: $margin$, $endif$ $if(minScale)$ + // Bounds for smallest/largest possible scale to apply to content minScale: $minScale$, $endif$ $if(maxScale)$ + maxScale: $maxScale$, $endif$ -$if(zoomKey)$ - // Modifier key used to click-zoom to part of the slide - zoomKey: '$zoomKey$', -$endif$ -$if(display)$ - // The display mode that will be used to show slides - display: '$display$', -$endif$ $if(mathjax)$ + math: { mathjax: '$mathjaxurl$', config: 'TeX-AMS_HTML-full', diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index b1dd9a659..4d513df3b 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -317,6 +317,10 @@ pandocToHtml opts (Pandoc meta blocks) = do | otherwise -> mempty Nothing -> mempty let mCss :: Maybe [Text] = lookupContext "css" metadata + let true :: Text + true = "true" + let false :: Text + false = "false" let context = (if stHighlighting st then case writerHighlightStyle opts of Just sty -> defField "highlighting-css" @@ -344,6 +348,52 @@ pandocToHtml opts (Pandoc meta blocks) = do PlainMath -> defField "displaymath-css" True WebTeX _ -> defField "displaymath-css" True _ -> id) . + (if slideVariant == RevealJsSlides + then -- set boolean options explicitly, since + -- template can't distinguish False/undefined + defField "controls" true . + defField "controlsTutorial" true . + defField "controlsLayout" ("bottom-right" :: Text) . + defField "controlsBackArrows" ("faded" :: Text) . + defField "progress" true . + defField "slideNumber" false . + defField "showSlideNumber" ("all" :: Text) . + defField "hashOneBasedIndex" false . + defField "hash" false . + defField "respondToHashChanges" true . + defField "history" false . + defField "keyboard" true . + defField "overview" true . + defField "disableLayout" false . + defField "center" true . + defField "touch" true . + defField "loop" false . + defField "rtl" false . + defField "navigationMode" ("default" :: Text) . + defField "shuffle" false . + defField "fragments" true . + defField "fragmentInURL" true . + defField "embedded" false . + defField "help" true . + defField "pause" true . + defField "showNotes" false . + defField "autoPlayMedia" ("null" :: Text) . + defField "preloadIframes" ("null" :: Text) . + defField "autoSlide" ("0" :: Text) . + defField "autoSlideStoppable" true . + defField "autoSlideMethod" ("null" :: Text) . + defField "defaultTiming" ("null" :: Text) . + defField "mouseWheel" false . + defField "display" ("block" :: Text) . + defField "hideInactiveCursor" true . + defField "hideCursorTime" ("5000" :: Text) . + defField "previewLinks" false . + defField "transition" ("slide" :: Text) . + defField "transitionSpeed" ("default" :: Text) . + defField "backgroundTransition" ("fade" :: Text) . + defField "viewDistance" ("3" :: Text) . + defField "mobileViewDistance" ("2" :: Text) + else id) . defField "document-css" (isNothing mCss && slideVariant == NoSlides) . defField "quotes" (stQuotes st) . -- for backwards compatibility we populate toc -- cgit v1.2.3 From eee648447a05accb5680b5a1a6e69a7765af2db5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 18:25:07 -0700 Subject: LaTeX writer: Use `\strut` instead of `~` before `\\` in empty line. --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 978f94ea0..063e347fb 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -919,7 +919,7 @@ inlineToLaTeX il@(RawInline f str) = do inlineToLaTeX LineBreak = do emptyLine <- gets stEmptyLine setEmptyLine True - return $ (if emptyLine then "~" else "") <> "\\\\" <> cr + return $ (if emptyLine then "\\strut " else "") <> "\\\\" <> cr inlineToLaTeX SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of -- cgit v1.2.3 From ed3974a254c3e0c4e7a34d5d25ddef90c25d2092 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 18:25:36 -0700 Subject: LaTeX writer: always use a minipage for cells with line breaks... if width information is available. Otherwise the way we treat them can lead to content that overflows a cell. Closes #7393. --- src/Text/Pandoc/Writers/LaTeX/Table.hs | 9 +++++++-- test/command/7272.md | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX/Table.hs b/src/Text/Pandoc/Writers/LaTeX/Table.hs index 8dc7d1162..abdc26649 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Table.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Table.hs @@ -26,7 +26,8 @@ import Text.DocLayout ( Doc, braces, cr, empty, hcat, hsep, isEmpty, literal, nest , text, vcat, ($$) ) import Text.Pandoc.Shared (blocksToInlines, splitBy, tshow) -import Text.Pandoc.Walk (walk) +import Text.Pandoc.Walk (walk, query) +import Data.Monoid (Any(..)) import Text.Pandoc.Writers.LaTeX.Caption (getCaption) import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX) import Text.Pandoc.Writers.LaTeX.Types @@ -261,8 +262,12 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do Para{} -> True Plain{} -> True _ -> False + let hasLineBreak LineBreak = Any True + hasLineBreak _ = Any False result <- - if not hasWidths || (celltype /= HeaderCell && all isPlainOrPara blocks) + if not hasWidths || (celltype /= HeaderCell + && all isPlainOrPara blocks + && not (getAny (query hasLineBreak blocks))) then blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks else do diff --git a/test/command/7272.md b/test/command/7272.md index d3a3b2137..3b9064c9c 100644 --- a/test/command/7272.md +++ b/test/command/7272.md @@ -18,7 +18,10 @@ >{\raggedright\arraybackslash}p{(\columnwidth - 0\tabcolsep) * \real{1.00}}@{}} \toprule \endhead -{\vtop{\hbox{\strut text}\hbox{\strut text2 }}} \\ +\begin{minipage}[t]{\linewidth}\raggedright +{ text\\ +text2 } +\end{minipage} \\ \bottomrule \end{longtable} ``` -- cgit v1.2.3 From 2ef2049b4e94dc51961e75edb27af1d2f83acd3b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 22:34:38 -0700 Subject: Update command test for change to LaTeX LineBreak handling. --- test/command/2874.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/command/2874.md b/test/command/2874.md index 1fb530dc1..99f46d2fb 100644 --- a/test/command/2874.md +++ b/test/command/2874.md @@ -3,12 +3,12 @@
^D -{}~\\ +{}\strut \\ ``` ``` % pandoc -f html -t latex
^D -\protect\hypertarget{foo}{}{}~\\ +\protect\hypertarget{foo}{}{}\strut \\ ``` -- cgit v1.2.3 From 0352f7845bfa2053797850c3639414978285b63e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 22:35:07 -0700 Subject: Improve emailAddress in Text.Pandoc.Parsing. Previously the parser would accept characters in domains that are illegal in domains, and this sometimes caused it to gobble bits of the following text. Closes #7398. Note that this change, by itself, caused some txt2tag reader tests to fail. txt2tags allows bare email addresses with a following form query. So, in addition to the change to emailAddress, we modify the txt2tags parser so it can still handle these cases. --- src/Text/Pandoc/Parsing.hs | 7 +++---- src/Text/Pandoc/Readers/Txt2Tags.hs | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 0bb794ba1..082d9565b 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -693,13 +693,12 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) mailbox = intercalate "." <$> (emailWord `sepBy1'` dot) domain = intercalate "." <$> (subdomain `sepBy1'` dot) dot = char '.' - subdomain = many1 $ alphaNum <|> innerPunct + subdomain = many1 $ alphaNum <|> innerPunct (=='-') -- this excludes some valid email addresses, since an -- email could contain e.g. '__', but gives better results -- for our purposes, when combined with markdown parsing: - innerPunct = try (satisfy (\c -> isEmailPunct c || c == '@') - <* notFollowedBy space - <* notFollowedBy (satisfy isPunctuation)) + innerPunct f = try (satisfy f + <* notFollowedBy (satisfy (not . isAlphaNum))) -- technically an email address could begin with a symbol, -- but allowing this creates too many problems. -- See e.g. https://github.com/jgm/pandoc/issues/2940 diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 6f92f0063..b5cf5a0f3 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -478,9 +478,29 @@ macro = try $ do -- raw URLs in text are automatically linked url :: T2T Inlines url = try $ do - (rawUrl, escapedUrl) <- try uri <|> emailAddress + (rawUrl, escapedUrl) <- try uri <|> emailAddress' return $ B.link rawUrl "" (B.str escapedUrl) +emailAddress' :: T2T (Text, Text) +emailAddress' = do + (base, mailURI) <- emailAddress + query <- option "" emailQuery + return (base <> query, mailURI <> query) + +emailQuery :: T2T Text +emailQuery = do + char '?' + parts <- kv `sepBy1` (char '&') + return $ "?" <> T.intercalate "&" parts + +kv :: T2T Text +kv = do + k <- T.pack <$> many1 alphaNum + char '=' + let vchar = alphaNum <|> try (oneOf "%._/~:,=$@&+-;*" <* lookAhead alphaNum) + v <- T.pack <$> many1 vchar + return (k <> "=" <> v) + uri :: T2T (Text, Text) uri = try $ do address <- t2tURI -- cgit v1.2.3 From e2a7ecb5f73b12c8141ebf873a494652fc53babd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 23:17:43 -0700 Subject: LaTeX writer: put a strut after a line break (`\\`). This ensures that we have proper spacing before the next line (which might e.g. be a table bottom border). This gives better results in cases like test/command/7272.md. --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 063e347fb..32d1bc2a5 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -919,7 +919,7 @@ inlineToLaTeX il@(RawInline f str) = do inlineToLaTeX LineBreak = do emptyLine <- gets stEmptyLine setEmptyLine True - return $ (if emptyLine then "\\strut " else "") <> "\\\\" <> cr + return $ (if emptyLine then "\\strut " else "") <> "\\\\ \\strut" <> cr inlineToLaTeX SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of -- cgit v1.2.3 From 9867231779c32ddc2cce85d869de2ef586707244 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 23:19:40 -0700 Subject: Revert "LaTeX writer: put a strut after a line break (`\\`)." This reverts commit e2a7ecb5f73b12c8141ebf873a494652fc53babd. --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 32d1bc2a5..063e347fb 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -919,7 +919,7 @@ inlineToLaTeX il@(RawInline f str) = do inlineToLaTeX LineBreak = do emptyLine <- gets stEmptyLine setEmptyLine True - return $ (if emptyLine then "\\strut " else "") <> "\\\\ \\strut" <> cr + return $ (if emptyLine then "\\strut " else "") <> "\\\\" <> cr inlineToLaTeX SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of -- cgit v1.2.3 From 8eed5b90d09a4a0c2592c92215fa96c69cf35234 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 21 Jun 2021 23:31:27 -0700 Subject: LaTeX writer: add strut at end of minipage if it contains... line breaks. Without them, the last line is shorter than it should be, at least in some cases. --- src/Text/Pandoc/Writers/LaTeX/Table.hs | 7 +++++-- test/command/7272.md | 2 +- test/tables/nordics.latex | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Text/Pandoc/Writers/LaTeX/Table.hs b/src/Text/Pandoc/Writers/LaTeX/Table.hs index abdc26649..27a8a0257 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Table.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Table.hs @@ -264,10 +264,11 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do _ -> False let hasLineBreak LineBreak = Any True hasLineBreak _ = Any False + let hasLineBreaks = getAny $ query hasLineBreak blocks result <- if not hasWidths || (celltype /= HeaderCell && all isPlainOrPara blocks - && not (getAny (query hasLineBreak blocks))) + && not hasLineBreaks) then blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks else do @@ -280,7 +281,9 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do let halign = literal $ alignCommand align return $ "\\begin{minipage}" <> valign <> braces "\\linewidth" <> halign <> cr <> - cellContents <> cr <> + cellContents <> + (if hasLineBreaks then "\\strut" else mempty) + <> cr <> "\\end{minipage}" modify $ \st -> st{ stExternalNotes = externalNotes } when (rowspan /= RowSpan 1) $ diff --git a/test/command/7272.md b/test/command/7272.md index 3b9064c9c..d27b25143 100644 --- a/test/command/7272.md +++ b/test/command/7272.md @@ -20,7 +20,7 @@ \endhead \begin{minipage}[t]{\linewidth}\raggedright { text\\ -text2 } +text2 }\strut \end{minipage} \\ \bottomrule \end{longtable} diff --git a/test/tables/nordics.latex b/test/tables/nordics.latex index 1dcac7319..6f17a163e 100644 --- a/test/tables/nordics.latex +++ b/test/tables/nordics.latex @@ -11,10 +11,10 @@ Name Capital \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Population\\ -(in 2018) +(in 2018)\strut \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Area\\ -(in km\textsuperscript{2}) +(in km\textsuperscript{2})\strut \end{minipage} \\ \midrule \endfirsthead @@ -25,10 +25,10 @@ Name Capital \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Population\\ -(in 2018) +(in 2018)\strut \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Area\\ -(in km\textsuperscript{2}) +(in km\textsuperscript{2})\strut \end{minipage} \\ \midrule \endhead -- cgit v1.2.3 From 086790d986af35a5e6d68013a2c15ae10511db40 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 22 Jun 2021 09:49:24 -0700 Subject: Fix unneeded import --- src/Text/Pandoc/Parsing.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 082d9565b..09445622d 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -188,7 +188,7 @@ import Control.Monad.Identity import Control.Monad.Reader ( asks, runReader, MonadReader(ask), Reader, ReaderT(ReaderT) ) import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isAsciiLower, - isPunctuation, isSpace, ord, toLower, toUpper) + isSpace, ord, toLower, toUpper) import Data.Default ( Default(..) ) import Data.Functor (($>)) import Data.List (intercalate, transpose) -- cgit v1.2.3 From 1b07997f4a6870650f20702ed6d962f9471e3d40 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 22 Jun 2021 09:55:50 -0700 Subject: Fix regression with comment-only YAML metadata blocks. Closes #7400. --- src/Text/Pandoc/Readers/Metadata.hs | 3 +++ test/command/7400.md | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/command/7400.md diff --git a/src/Text/Pandoc/Readers/Metadata.hs b/src/Text/Pandoc/Readers/Metadata.hs index 45eddf25a..cbc523b25 100644 --- a/src/Text/Pandoc/Readers/Metadata.hs +++ b/src/Text/Pandoc/Readers/Metadata.hs @@ -45,6 +45,9 @@ yamlBsToMeta pMetaValue bstr = do Right [] -> return . return $ mempty Right [YAML.Doc (YAML.Scalar _ YAML.SNull)] -> return . return $ mempty + -- the following is what we get from a comment: + Right [YAML.Doc (YAML.Scalar _ (YAML.SUnknown _ ""))] + -> return . return $ mempty Right _ -> Prelude.fail "expected YAML object" Left (yamlpos, err') -> do pos <- getPosition diff --git a/test/command/7400.md b/test/command/7400.md new file mode 100644 index 000000000..d4be32d72 --- /dev/null +++ b/test/command/7400.md @@ -0,0 +1,9 @@ +``` +% pandoc -t native -s +--- +# Comment only +... +^D +Pandoc (Meta {unMeta = fromList []}) +[] +``` -- cgit v1.2.3 From 227f675cc46a680d4b8f2fd71e0c4cbb92a3fd0b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 22 Jun 2021 11:22:53 -0700 Subject: Update changelog. --- changelog.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 049d1db57..2f188a3b8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,37 @@ # Revision history for pandoc -## pandoc 2.14.0.3 (2021-06-20) +## pandoc 2.14.0.3 (2021-06-22) * Text.Pandoc.MediaBag `insertMediaBag`: ensure we get a sane mediaPath for URLs (#7391). In earlier 2.14.x versions, we'd get incorrect paths for resources downloaded from URLs when the media are extracted (including in PDF production). + * Text.Pandoc.Parsing: improve `emailAddress` (#7398). + Previously the parser would accept characters in domains + that are illegal in domains, and this sometimes caused it + to gobble bits of the following text. + * txt2tags reader: modify the email address parser so + it still includes form parameters, even after the change to + `emailAddress` in Text.Pandoc.Parsing. + * Text.Pandoc.Readers.Metadata: Fix regression with comment-only YAML + metadata blocks (#7400). + * reveal.js writer and template: better handling of options. Previously + it was impossible to specify false values for options that default to + true (e.g. `center`); setting the option to false just caused the portion + of the template setting the option to be omitted. Now we prepopulate + all the variables with their default values, including them all + unconditionally and allowing them to be overridden. + * Markdown writer: Fix regression in code blocks with attributes (#7397). + Code blocks with a single class but nonempty attributes + were having attributes drop as a result of #7242. + * LaTeX writer: + + Add strut at end of minipage if it contains line breaks. + Without them, the last line is not as tall as it should be in + some cases. + + Always use a minipage for cells with line breaks, when + width information is available (#7393). Otherwise the way we treat them + can lead to content that overflows a cell. + + Use `\strut` instead of `~` before `\\` in empty line. * Use lts-18.0 stack resolver. * Require skylighting 0.10.5.2 (adding support for Swift). * Require commonmark 0.2.1. -- cgit v1.2.3 From 7dcf7273828a26f2f79d447ea2662ff6ef165903 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 22 Jun 2021 23:58:24 -0700 Subject: Use dev version of doctemplates. --- cabal.project | 5 +++++ stack.yaml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/cabal.project b/cabal.project index 26373c616..724c4da44 100644 --- a/cabal.project +++ b/cabal.project @@ -2,3 +2,8 @@ packages: pandoc.cabal tests: True flags: +embed_data_files +source-repository-package + type: git + location: https://github.com/jgm/doctemplates + tag: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 + diff --git a/stack.yaml b/stack.yaml index 80d0707e7..170323b62 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,6 +10,8 @@ extra-deps: - commonmark-0.2.1 - skylighting-core-0.10.5.2 - skylighting-0.10.5.2 +- git: https://github.com/jgm/doctemplates + commit: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-18.0 -- cgit v1.2.3 From 235cdea629de95f5a6c40a043e8dff6b19a46d3d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 23 Jun 2021 09:54:14 -0700 Subject: reveal.js writer: Go back to setting boolean values for variables. In a previous commit we used strings because boolean False wouldn't render as `false`. This is changed in the dev version ofdoctemplates, so we can go back to the more straightforward approach. --- src/Text/Pandoc/Writers/HTML.hs | 56 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 4d513df3b..b99b1a413 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -317,10 +317,6 @@ pandocToHtml opts (Pandoc meta blocks) = do | otherwise -> mempty Nothing -> mempty let mCss :: Maybe [Text] = lookupContext "css" metadata - let true :: Text - true = "true" - let false :: Text - false = "false" let context = (if stHighlighting st then case writerHighlightStyle opts of Just sty -> defField "highlighting-css" @@ -351,43 +347,43 @@ pandocToHtml opts (Pandoc meta blocks) = do (if slideVariant == RevealJsSlides then -- set boolean options explicitly, since -- template can't distinguish False/undefined - defField "controls" true . - defField "controlsTutorial" true . + defField "controls" True . + defField "controlsTutorial" True . defField "controlsLayout" ("bottom-right" :: Text) . defField "controlsBackArrows" ("faded" :: Text) . - defField "progress" true . - defField "slideNumber" false . + defField "progress" True . + defField "slideNumber" False . defField "showSlideNumber" ("all" :: Text) . - defField "hashOneBasedIndex" false . - defField "hash" false . - defField "respondToHashChanges" true . - defField "history" false . - defField "keyboard" true . - defField "overview" true . - defField "disableLayout" false . - defField "center" true . - defField "touch" true . - defField "loop" false . - defField "rtl" false . + defField "hashOneBasedIndex" False . + defField "hash" False . + defField "respondToHashChanges" True . + defField "history" False . + defField "keyboard" True . + defField "overview" True . + defField "disableLayout" False . + defField "center" True . + defField "touch" True . + defField "loop" False . + defField "rtl" False . defField "navigationMode" ("default" :: Text) . - defField "shuffle" false . - defField "fragments" true . - defField "fragmentInURL" true . - defField "embedded" false . - defField "help" true . - defField "pause" true . - defField "showNotes" false . + defField "shuffle" False . + defField "fragments" True . + defField "fragmentInURL" True . + defField "embedded" False . + defField "help" True . + defField "pause" True . + defField "showNotes" False . defField "autoPlayMedia" ("null" :: Text) . defField "preloadIframes" ("null" :: Text) . defField "autoSlide" ("0" :: Text) . - defField "autoSlideStoppable" true . + defField "autoSlideStoppable" True . defField "autoSlideMethod" ("null" :: Text) . defField "defaultTiming" ("null" :: Text) . - defField "mouseWheel" false . + defField "mouseWheel" False . defField "display" ("block" :: Text) . - defField "hideInactiveCursor" true . + defField "hideInactiveCursor" True . defField "hideCursorTime" ("5000" :: Text) . - defField "previewLinks" false . + defField "previewLinks" False . defField "transition" ("slide" :: Text) . defField "transitionSpeed" ("default" :: Text) . defField "backgroundTransition" ("fade" :: Text) . -- cgit v1.2.3 From 4a7a0cff294c48d1acd690ea593f4ab2c817ec27 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 23 Jun 2021 11:39:50 -0700 Subject: ImageSize: Add Tiff constructor for ImageType. [Minor API change] This allows pandoc to get size information from tiff images. Closes #7405. --- src/Text/Pandoc/ImageSize.hs | 6 +++++- src/Text/Pandoc/Writers/Docx.hs | 1 + src/Text/Pandoc/Writers/Powerpoint/Output.hs | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index bb1aa6351..2b7d10611 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -57,7 +57,8 @@ import Codec.Picture (decodeImageWithMetadata) -- quick and dirty functions to get image sizes -- algorithms borrowed from wwwis.pl -data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf deriving Show +data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf | Tiff + deriving Show data Direction = Width | Height instance Show Direction where show Width = "width" @@ -100,6 +101,8 @@ imageType :: ByteString -> Maybe ImageType imageType img = case B.take 4 img of "\x89\x50\x4e\x47" -> return Png "\x47\x49\x46\x38" -> return Gif + "\x49\x49\x2a\x00" -> return Tiff + "\x4D\x4D\x00\x2a" -> return Tiff "\xff\xd8\xff\xe0" -> return Jpeg -- JFIF "\xff\xd8\xff\xe1" -> return Jpeg -- Exif "%PDF" -> return Pdf @@ -124,6 +127,7 @@ imageSize opts img = checkDpi <$> Just Png -> getSize img Just Gif -> getSize img Just Jpeg -> getSize img + Just Tiff -> getSize img Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img Just Eps -> mbToEither "could not determine EPS size" $ epsSize img Just Pdf -> mbToEither "could not determine PDF size" $ pdfSize img diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index e7a49ba02..d1065eb7d 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1289,6 +1289,7 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do Just Eps -> ".eps" Just Svg -> ".svg" Just Emf -> ".emf" + Just Tiff -> ".tiff" Nothing -> "" imgpath = "media/" <> ident <> imgext mbMimeType = mt <|> getMimeType (T.unpack imgpath) diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index f2f54a91c..157810216 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -537,6 +537,7 @@ registerMedia fp caption = do Just Eps -> Just ".eps" Just Svg -> Just ".svg" Just Emf -> Just ".emf" + Just Tiff -> Just ".tiff" Nothing -> Nothing let newGlobalId = fromMaybe (maxGlobalId + 1) (M.lookup fp globalIds) -- cgit v1.2.3 From dd098d4e15090d12cc71301f91a159c5bfb29b50 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 11:27:37 -0700 Subject: Markdown writer: put space between Plain and following fenced Div. Closes #4465. --- src/Text/Pandoc/Writers/Markdown.hs | 3 +++ test/command/4465.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/command/4465.md diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index b13ab57ee..fda2bbcef 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -791,6 +791,9 @@ blockListToMarkdown opts blocks = do b1 : commentSep : fixBlocks (b2:bs) fixBlocks (Plain ils : bs@(RawBlock{}:_)) = Plain ils : fixBlocks bs + fixBlocks (Plain ils : bs@(Div{}:_)) + | isEnabled Ext_fenced_divs opts = + Para ils : fixBlocks bs fixBlocks (Plain ils : bs) | inlist = Plain ils : fixBlocks bs fixBlocks (Plain ils : bs) = diff --git a/test/command/4465.md b/test/command/4465.md new file mode 100644 index 000000000..eaffcf7f3 --- /dev/null +++ b/test/command/4465.md @@ -0,0 +1,15 @@ +``` +% pandoc -f html -t markdown +
    +
  1. An ordered list can contain block-level elements ind html, it means that divs are also allowed.
  2. +
  3. Let's see the problem!
    This is an example.
  4. +
+^D +1. An ordered list can contain block-level elements ind html, it means + that divs are also allowed. +2. Let\'s see the problem! + + ::: example + This is an example. + ::: +``` -- cgit v1.2.3 From 7ac8fcbcac366639662ca498ebdead949f5faf09 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 12:20:00 -0700 Subject: Require commonmark-pandoc >= 0.2.2.1. This fixes task lists with multiple paragraphs. --- pandoc.cabal | 2 +- stack.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pandoc.cabal b/pandoc.cabal index eb9633bd0..84a04c6b6 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -453,7 +453,7 @@ library citeproc >= 0.4.0.1 && < 0.4.1, commonmark >= 0.2.1 && < 0.3, commonmark-extensions >= 0.2.1.2 && < 0.3, - commonmark-pandoc >= 0.2.1 && < 0.3, + commonmark-pandoc >= 0.2.1.1 && < 0.3, connection >= 0.3.1, containers >= 0.4.2.1 && < 0.7, data-default >= 0.4 && < 0.8, diff --git a/stack.yaml b/stack.yaml index 170323b62..cde22f68c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -8,6 +8,7 @@ packages: - '.' extra-deps: - commonmark-0.2.1 +- commonmark-pandoc-0.2.1.1 - skylighting-core-0.10.5.2 - skylighting-0.10.5.2 - git: https://github.com/jgm/doctemplates -- cgit v1.2.3 From 4262898fe9cc71abecffc1ffa165dd714c04407d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 13:28:02 -0700 Subject: Set proper initial source name in parsing BibTeX. (For better error messages.) --- src/Text/Pandoc/Citeproc/BibTeX.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index b17240557..1fe4a2730 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -71,9 +71,11 @@ readBibtexString variant locale idpred contents = do filter (\item -> idpred (identifier item) && entryType item /= "xdata")) (fromMaybe defaultLang $ localeLanguage locale, Map.empty) - "" (toSources contents) of + (initialSourceName sources) sources of Left err -> Left err Right xs -> return xs + where + sources = toSources contents -- | Write BibTeX or BibLaTeX given given a 'Reference'. writeBibtexString :: WriterOptions -- ^ options (for writing LaTex) -- cgit v1.2.3 From f045e59248116228093bfebf03ce9f7f4f0cc4ce Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 13:28:22 -0700 Subject: Text.Pandoc.Error: fix line calculations in reporting parsec errors. Also remove a spurious initial newline in the error report. --- src/Text/Pandoc/Error.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 81eb41f85..9dee8356b 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -97,13 +97,13 @@ renderError e = [] -> "" ((pos,txt):_) -> let ls = T.lines txt <> [""] - ln = errLine - sourceLine pos - in if length ls > ln - 1 + ln = (errLine - sourceLine pos) + 1 + in if length ls > ln && ln >= 1 then T.concat ["\n", ls !! (ln - 1) ,"\n", T.replicate (errColumn - 1) " " ,"^"] else "" - in "\nError at " <> tshow err' <> errorInFile + in "Error at " <> tshow err' <> errorInFile PandocMakePDFError s -> s PandocOptionError s -> s PandocSyntaxMapError s -> s -- cgit v1.2.3 From 97b0aa667cce07e6552d0abb4f93469ba14f4eb0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 13:34:12 -0700 Subject: Allow `$` characters in bibtex keys. Closes #7409. --- src/Text/Pandoc/Citeproc/BibTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs index 1fe4a2730..c178de6e9 100644 --- a/src/Text/Pandoc/Citeproc/BibTeX.hs +++ b/src/Text/Pandoc/Citeproc/BibTeX.hs @@ -868,7 +868,7 @@ fieldName = resolveAlias . T.toLower isBibtexKeyChar :: Char -> Bool isBibtexKeyChar c = - isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]*&" :: [Char]) + isAlphaNum c || c `elem` (".:;?!`'()$/*@_+=-[]*&" :: [Char]) bibItem :: BibParser Item bibItem = do -- cgit v1.2.3 From 851d037b3eee4516fde50b81eb8a0fc9b2f1545b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 22:41:14 -0700 Subject: Improve punctuation moving with `--citeproc`. Previously, using `--citeproc` could cause punctuation to move in quotes even when there aer no citations. This has been changed; now, punctuation moving is limited to citations. In addition, we only move footnotes around punctuation if the style is a note style, even if `notes-after-punctuation` is `true`. --- src/Text/Pandoc/Citeproc.hs | 29 +++++++++++++++-------------- test/command/6890.md | 4 ++-- test/command/pandoc-citeproc-322.md | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index a5b26c9b4..a2fca106a 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -90,21 +90,15 @@ processCitations (Pandoc meta bs) = do walk (convertQuotes locale) . insertSpace $ out) (resultBibliography result) - let moveNotes = maybe True truish $ - lookupMeta "notes-after-punctuation" meta + let moveNotes = styleIsNoteStyle sopts && + maybe True truish (lookupMeta "notes-after-punctuation" meta) let cits = map (walk (convertQuotes locale)) $ resultCitations result - let fixQuotes = case localePunctuationInQuote locale of - Just True -> - B.toList . movePunctuationInsideQuotes . B.fromList - _ -> id - let metanocites = lookupMeta "nocite" meta let Pandoc meta'' bs' = maybe id (setMeta "nocite") metanocites . - walk (map capitalizeNoteCitation . - fixQuotes . mvPunct moveNotes locale) . + walk (map capitalizeNoteCitation . mvPunct moveNotes locale) . walk deNote . evalState (walkM insertResolvedCitations $ Pandoc meta' bs) $ cits @@ -375,7 +369,6 @@ formatFromExtension fp = case dropWhile (== '.') $ takeExtension fp of isNote :: Inline -> Bool -isNote (Note _) = True isNote (Cite _ [Note _]) = True -- the following allows citation styles that are "in-text" but use superscript -- references to be treated as if they are "notes" for the purposes of moving @@ -388,6 +381,12 @@ isSpacy Space = True isSpacy SoftBreak = True isSpacy _ = False +movePunctInsideQuotes :: Locale -> [Inline] -> [Inline] +movePunctInsideQuotes locale + | localePunctuationInQuote locale == Just True + = B.toList . movePunctuationInsideQuotes . B.fromList + | otherwise + = id mvPunct :: Bool -> Locale -> [Inline] -> [Inline] mvPunct moveNotes locale (x : xs) @@ -400,7 +399,8 @@ mvPunct moveNotes locale (q : s : x : ys) in if moveNotes then if T.null spunct then q : x : mvPunct moveNotes locale ys - else q : Str spunct : x : mvPunct moveNotes locale + else movePunctInsideQuotes locale + [q , Str spunct , x] ++ mvPunct moveNotes locale (B.toList (dropTextWhile isPunctuation (B.fromList ys))) else q : x : mvPunct moveNotes locale ys @@ -412,9 +412,10 @@ mvPunct moveNotes locale (Cite cs ils : ys) , moveNotes = let s = stringify ys spunct = T.takeWhile isPunctuation s - in Cite cs (init ils - ++ [Str spunct | not (endWithPunct False (init ils))] - ++ [last ils]) : + in Cite cs (movePunctInsideQuotes locale $ + init ils + ++ [Str spunct | not (endWithPunct False (init ils))] + ++ [last ils]) : mvPunct moveNotes locale (B.toList (dropTextWhile isPunctuation (B.fromList ys))) mvPunct moveNotes locale (s : x : ys) | isSpacy s, isNote x = diff --git a/test/command/6890.md b/test/command/6890.md index e4129e2a9..e36c12771 100644 --- a/test/command/6890.md +++ b/test/command/6890.md @@ -23,12 +23,12 @@ references: @fruchtel-sozialer-2013a -Some text [^1]. +Some text.[^1] [^1]: @fruchtel-sozialer-2013a ^D [Para [Cite [Citation {citationId = "fruchtel-sozialer-2013a", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 1, citationHash = 0}] [Str "Fr\252chtel,",Space,Str "Budde,",Space,Str "and",Space,Str "Cyprian",Space,Str "(2013)"]] -,Para [Str "Some",Space,Str "text",Str ".",Note [Para [Cite [Citation {citationId = "fruchtel-sozialer-2013a", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 2, citationHash = 0}] [Str "Fr\252chtel,",Space,Str "Budde,",Space,Str "and",Space,Str "Cyprian",Space,Str "(2013)"]]],Str ""] +,Para [Str "Some",Space,Str "text.",Note [Para [Cite [Citation {citationId = "fruchtel-sozialer-2013a", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 2, citationHash = 0}] [Str "Fr\252chtel,",Space,Str "Budde,",Space,Str "and",Space,Str "Cyprian",Space,Str "(2013)"]]]] ,Div ("refs",["references","csl-bib-body","hanging-indent"],[]) [Div ("ref-fruchtel-sozialer-2013a",["csl-entry"],[]) [Para [Str "Fr\252chtel,",Space,Str "Frank,",Space,Str "Wolfgang",Space,Str "Budde,",Space,Str "and",Space,Str "Gudrun",Space,Str "Cyprian.",Space,Str "2013.",Space,Emph [Str "Sozialer",Space,Str "Raum",Space,Str "und",Space,Str "Soziale",Space,Str "Arbeit",Space,Str "Fieldbook:",Space,Str "Methoden",Space,Str "und",Space,Str "Techniken"],Str ".",Space,Str "3rd",Space,Str "ed.",Space,Str "Wiesbaden,",Space,Str "Germany:",Space,Str "Springer",Space,Str "VS."]]]] diff --git a/test/command/pandoc-citeproc-322.md b/test/command/pandoc-citeproc-322.md index 78494f0c4..c70eae755 100644 --- a/test/command/pandoc-citeproc-322.md +++ b/test/command/pandoc-citeproc-322.md @@ -19,7 +19,7 @@ references: type: 'article-journal' --- -Foo[@timmory__justice_1950]. +Foo [@timmory__justice_1950]. ^D Foo.[^1] -- cgit v1.2.3 From 382be73d77eca007a6d333391159123235fe3561 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 28 Jun 2021 22:56:16 -0700 Subject: Use dev version of citeproc. This fixes moving of punctuation inside quotes to conform to the CSL spec: only comma and period are moved, not question mark or exclamation point. --- cabal.project | 4 ++++ stack.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/cabal.project b/cabal.project index 724c4da44..ba7f4f4cb 100644 --- a/cabal.project +++ b/cabal.project @@ -7,3 +7,7 @@ source-repository-package location: https://github.com/jgm/doctemplates tag: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 +source-repository-package + type: git + location: https://github.com/jgm/citeproc + tag: d201ddc812b1550d70c3e448eec6d9c3af72d96b diff --git a/stack.yaml b/stack.yaml index cde22f68c..f67da4ec7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,6 +13,8 @@ extra-deps: - skylighting-0.10.5.2 - git: https://github.com/jgm/doctemplates commit: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 +- git: https://github.com/jgm/citeproc + commit: d201ddc812b1550d70c3e448eec6d9c3af72d96b ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-18.0 -- cgit v1.2.3 From f4ef652a4165fefe4b587882cc75d1e24971ef1f Mon Sep 17 00:00:00 2001 From: Aner Lucero <4rgento@gmail.com> Date: Tue, 29 Jun 2021 08:49:36 -0300 Subject: Remove duplicated alt text in HTML output. --- src/Text/Pandoc/Writers/HTML.hs | 5 +++-- test/command/7416.md | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/command/7416.md diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index b99b1a413..df01fc35f 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1433,7 +1433,7 @@ inlineToHtml opts inline = do return $ if T.null tit then link' else link' ! A.title (toValue tit) - (Image attr txt (s,tit)) -> do + (Image attr@(_, _, attrList) txt (s, tit)) -> do let alternate = stringify txt slideVariant <- gets stSlideVariant let isReveal = slideVariant == RevealJsSlides @@ -1446,7 +1446,8 @@ inlineToHtml opts inline = do [A.title $ toValue tit | not (T.null tit)] ++ attrs imageTag = (if html5 then H5.img else H.img - , [A.alt $ toValue alternate | not (null txt)] ) + , [A.alt $ toValue alternate | not (null txt) && + isNothing (lookup "alt" attrList)] ) mediaTag tg fallbackTxt = let linkTxt = if null txt then fallbackTxt diff --git a/test/command/7416.md b/test/command/7416.md new file mode 100644 index 000000000..70a0257fa --- /dev/null +++ b/test/command/7416.md @@ -0,0 +1,19 @@ +``` +% pandoc -f markdown -t html +![caption](../media/rId25.jpg "title"){alt="alt"} + +^D +
+alt +
+``` + +``` +% pandoc -f markdown -t html +![caption](../media/rId25.jpg "title") + +^D +
+caption +
+``` -- cgit v1.2.3 From b7572db224123ab6c193ccdf24d2c3dd4fc0b0dc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 29 Jun 2021 09:18:00 -0700 Subject: Use dev version of citeproc. This eliminates double hyperlinks in author-in-text citations. Author-only citations are no longer hyperlinked. See jgm/citeproc#77. --- cabal.project | 2 +- stack.yaml | 2 +- test/command/pandoc-citeproc-chicago-author-date.md | 14 +++++++------- .../pandoc-citeproc-chicago-fullnote-bibliography.md | 13 ++++++------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/cabal.project b/cabal.project index ba7f4f4cb..99c7b3556 100644 --- a/cabal.project +++ b/cabal.project @@ -10,4 +10,4 @@ source-repository-package source-repository-package type: git location: https://github.com/jgm/citeproc - tag: d201ddc812b1550d70c3e448eec6d9c3af72d96b + tag: 85277c4baa6c0350baf29247583ae0f438f7c9c5 diff --git a/stack.yaml b/stack.yaml index f67da4ec7..2db0a626f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -14,7 +14,7 @@ extra-deps: - git: https://github.com/jgm/doctemplates commit: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 - git: https://github.com/jgm/citeproc - commit: d201ddc812b1550d70c3e448eec6d9c3af72d96b + commit: 85277c4baa6c0350baf29247583ae0f438f7c9c5 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-18.0 diff --git a/test/command/pandoc-citeproc-chicago-author-date.md b/test/command/pandoc-citeproc-chicago-author-date.md index a70bc5de8..503dfdc91 100644 --- a/test/command/pandoc-citeproc-chicago-author-date.md +++ b/test/command/pandoc-citeproc-chicago-author-date.md @@ -54,14 +54,14 @@ References {#references .unnumbered} ([**nonexistent?**](#ref-nonexistent)) -[Doe](#ref-item1) ([2005](#ref-item1)) says blah. +Doe ([2005](#ref-item1)) says blah. -[Doe](#ref-item1) ([2005, 30](#ref-item1)) says blah. +Doe ([2005, 30](#ref-item1)) says blah. -[Doe](#ref-item1) ([2005, 30](#ref-item1), with suffix) says blah. +Doe ([2005, 30](#ref-item1), with suffix) says blah. -[Doe](#ref-item1) ([2005](#ref-item1); [2006, 30](#ref-item2); see also -[Doe and Roe 2007](#ref-пункт3)) says blah. +Doe ([2005](#ref-item1); [2006, 30](#ref-item2); see also [Doe and Roe +2007](#ref-пункт3)) says blah. In a note.[^1] @@ -98,8 +98,8 @@ edited by Sam Smith. Oxford: Oxford University Press. ::: ::: -[^1]: [Doe and Roe](#ref-пункт3) ([2007, 12](#ref-пункт3)) and a - citation without locators ([Doe and Roe 2007](#ref-пункт3)). +[^1]: Doe and Roe ([2007, 12](#ref-пункт3)) and a citation without + locators ([Doe and Roe 2007](#ref-пункт3)). [^2]: Some citations (see [Doe 2005, chap. 3](#ref-item1); [2006](#ref-item2); [Doe and Roe 2007](#ref-пункт3)). diff --git a/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md b/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md index d997d818b..e5ab30a52 100644 --- a/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md +++ b/test/command/pandoc-citeproc-chicago-fullnote-bibliography.md @@ -55,13 +55,13 @@ References {#references .unnumbered} [^2] -[John Doe](#ref-item1)[^3] says blah. +John Doe[^3] says blah. -[Doe](#ref-item1)[^4] says blah. +Doe[^4] says blah. -[Doe](#ref-item1)[^5] says blah. +Doe[^5] says blah. -[Doe](#ref-item1)[^6] says blah. +Doe[^6] says blah. In a note.[^7] @@ -112,9 +112,8 @@ Sam Smith. Oxford: Oxford University Press, 2007. Roe, "Why Water Is Wet," in *Third Book*, ed. Sam Smith (Oxford: Oxford University Press, 2007)](#ref-пункт3). -[^7]: [Doe and Roe](#ref-пункт3), ["Why Water Is Wet," 12](#ref-пункт3) - and a citation without locators ([Doe and Roe, "Why Water Is - Wet"](#ref-пункт3)). +[^7]: Doe and Roe, ["Why Water Is Wet," 12](#ref-пункт3) and a citation + without locators ([Doe and Roe, "Why Water Is Wet"](#ref-пункт3)). [^8]: See [Doe, *First Book*, chap. 3](#ref-item1); also [Doe and Roe, "Why Water Is Wet," 34--35](#ref-пункт3). -- cgit v1.2.3 From a3d745e48560a55d9a9ea9fa43ffdd5a8b84987f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 29 Jun 2021 09:44:37 -0700 Subject: Docx writer: support figure numbers. These are set up in such a way that they will work with Word's automatic table of figures. Closes #7392. --- src/Text/Pandoc/Writers/Docx.hs | 22 +++++++++++++++++++--- src/Text/Pandoc/Writers/Docx/Types.hs | 2 ++ test/docx/golden/image.docx | Bin 26647 -> 26774 bytes 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d1065eb7d..b3e008b8a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -36,7 +36,8 @@ import Data.Time.Clock.POSIX import Data.Digest.Pure.SHA (sha1, showDigest) import Skylighting import Text.Collate.Lang (renderLang) -import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) +import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang, translateTerm) +import qualified Text.Pandoc.Translations as Term import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time import Text.Pandoc.UTF8 (fromTextLazy) @@ -854,14 +855,29 @@ blockToOpenXML' opts (Plain lst) = do -- title beginning with fig: indicates that the image is a figure blockToOpenXML' opts (Para [Image attr alt (src,T.stripPrefix "fig:" -> Just tit)]) = do setFirstPara + fignum <- gets stNextFigureNum + modify $ \st -> st{ stNextFigureNum = fignum + 1 } + let figid = "fig" <> tshow fignum + figname <- translateTerm Term.Figure prop <- pStyleM $ if null alt then "Figure" else "Captioned Figure" paraProps <- local (\env -> env { envParaProperties = EnvProps (Just prop) [] <> envParaProperties env }) (getParaProps False) contents <- inlinesToOpenXML opts [Image attr alt (src,tit)] - captionNode <- withParaPropM (pStyleM "Image Caption") - $ blockToOpenXML opts (Para alt) + captionNode <- if null alt + then return [] + else withParaPropM (pStyleM "Image Caption") + $ blockToOpenXML opts + (Para $ Span (figid,[],[]) + [Str "Figure\160", + RawInline (Format "openxml") + (" figname + <> " \\* ARABIC \">" + <> tshow fignum + <> ""), + Str ":", Space] : alt) return $ Elem (mknode "w:p" [] (map Elem paraProps ++ contents)) : captionNode diff --git a/src/Text/Pandoc/Writers/Docx/Types.hs b/src/Text/Pandoc/Writers/Docx/Types.hs index 006584c30..36ac45ad2 100644 --- a/src/Text/Pandoc/Writers/Docx/Types.hs +++ b/src/Text/Pandoc/Writers/Docx/Types.hs @@ -117,6 +117,7 @@ data WriterState = WriterState{ , stDynamicParaProps :: Set.Set ParaStyleName , stDynamicTextProps :: Set.Set CharStyleName , stCurId :: Int + , stNextFigureNum :: Int } defaultWriterState :: WriterState @@ -137,6 +138,7 @@ defaultWriterState = WriterState{ , stDynamicParaProps = Set.empty , stDynamicTextProps = Set.empty , stCurId = 20 + , stNextFigureNum = 1 } setFirstPara :: PandocMonad m => WS m () diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 48b72e283..9fe65326f 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ -- cgit v1.2.3 From a01ba4463f1f0d14a8032f147cddb76dadb4b853 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 29 Jun 2021 11:15:13 -0700 Subject: Docx writer: Fixed a couple bugs in Figure numbering. --- src/Text/Pandoc/Writers/Docx.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index b3e008b8a..a3c4b6be1 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -856,7 +856,7 @@ blockToOpenXML' opts (Plain lst) = do blockToOpenXML' opts (Para [Image attr alt (src,T.stripPrefix "fig:" -> Just tit)]) = do setFirstPara fignum <- gets stNextFigureNum - modify $ \st -> st{ stNextFigureNum = fignum + 1 } + unless (null alt) $ modify $ \st -> st{ stNextFigureNum = fignum + 1 } let figid = "fig" <> tshow fignum figname <- translateTerm Term.Figure prop <- pStyleM $ @@ -870,10 +870,9 @@ blockToOpenXML' opts (Para [Image attr alt (src,T.stripPrefix "fig:" -> Just tit else withParaPropM (pStyleM "Image Caption") $ blockToOpenXML opts (Para $ Span (figid,[],[]) - [Str "Figure\160", + [Str (figname <> "\160"), RawInline (Format "openxml") - (" figname + (" " \\* ARABIC \">" <> tshow fignum <> ""), -- cgit v1.2.3 From 0948af9cc549f0ea3b85fa760aa521b8deaad2c0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 29 Jun 2021 11:15:40 -0700 Subject: Docx writer: Add table numbering for captioned tables. The numbers are added using fields, so that Word can create a list of tables that will update automatically. --- src/Text/Pandoc/Writers/Docx/Table.hs | 31 ++++++++++++++++++++++++++++--- src/Text/Pandoc/Writers/Docx/Types.hs | 2 ++ test/Tests/Writers/OOXML.hs | 4 +++- test/docx/golden/image.docx | Bin 26774 -> 26776 bytes 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/Text/Pandoc/Writers/Docx/Table.hs b/src/Text/Pandoc/Writers/Docx/Table.hs index 49917e315..7a84c5278 100644 --- a/src/Text/Pandoc/Writers/Docx/Table.hs +++ b/src/Text/Pandoc/Writers/Docx/Table.hs @@ -17,7 +17,7 @@ import Control.Monad.State.Strict import Data.Array import Data.Text (Text) import Text.Pandoc.Definition -import Text.Pandoc.Class.PandocMonad (PandocMonad) +import Text.Pandoc.Class.PandocMonad (PandocMonad, translateTerm) import Text.Pandoc.Writers.Docx.Types import Text.Pandoc.Shared import Text.Printf (printf) @@ -25,6 +25,7 @@ import Text.Pandoc.Writers.GridTable hiding (Table) import Text.Pandoc.Writers.OOXML import Text.Pandoc.XML.Light as XML hiding (Attr) import qualified Data.Text as T +import qualified Text.Pandoc.Translations as Term import qualified Text.Pandoc.Writers.GridTable as Grid tableToOpenXML :: PandocMonad m @@ -33,15 +34,23 @@ tableToOpenXML :: PandocMonad m -> WS m [Content] tableToOpenXML blocksToOpenXML gridTable = do setFirstPara - let (Grid.Table _attr caption colspecs _rowheads thead tbodies tfoot) = + let (Grid.Table (ident,_,_) caption colspecs _rowheads thead tbodies tfoot) = gridTable let (Caption _maybeShortCaption captionBlocks) = caption + tablenum <- gets stNextTableNum + unless (null captionBlocks) $ + modify $ \st -> st{ stNextTableNum = tablenum + 1 } + let tableid = if T.null ident + then "table" <> tshow tablenum + else ident + tablename <- translateTerm Term.Table let captionStr = stringify captionBlocks let aligns = map fst $ elems colspecs captionXml <- if null captionBlocks then return [] else withParaPropM (pStyleM "Table Caption") - $ blocksToOpenXML captionBlocks + $ blocksToOpenXML + $ addLabel tableid tablename tablenum captionBlocks -- We set "in table" after processing the caption, because we don't -- want the "Table Caption" style to be overwritten with "Compact". modify $ \s -> s { stInTable = True } @@ -81,6 +90,22 @@ tableToOpenXML blocksToOpenXML gridTable = do modify $ \s -> s { stInTable = False } return $ captionXml ++ [Elem tbl] +addLabel :: Text -> Text -> Int -> [Block] -> [Block] +addLabel tableid tablename tablenum bs = + case bs of + (Para ils : rest) -> Para (label : Space : ils) : rest + (Plain ils : rest) -> Plain (label : Space : ils) : rest + _ -> Para [label] : bs + where + label = Span (tableid,[],[]) + [Str (tablename <> "\160"), + RawInline (Format "openxml") + (" " \\* ARABIC \">" + <> tshow tablenum + <> ""), + Str ":"] + -- | Parts of a table data RowType = HeadRow | BodyRow | FootRow diff --git a/src/Text/Pandoc/Writers/Docx/Types.hs b/src/Text/Pandoc/Writers/Docx/Types.hs index 36ac45ad2..74b8d2753 100644 --- a/src/Text/Pandoc/Writers/Docx/Types.hs +++ b/src/Text/Pandoc/Writers/Docx/Types.hs @@ -118,6 +118,7 @@ data WriterState = WriterState{ , stDynamicTextProps :: Set.Set CharStyleName , stCurId :: Int , stNextFigureNum :: Int + , stNextTableNum :: Int } defaultWriterState :: WriterState @@ -139,6 +140,7 @@ defaultWriterState = WriterState{ , stDynamicTextProps = Set.empty , stCurId = 20 , stNextFigureNum = 1 + , stNextTableNum = 1 } setFirstPara :: PandocMonad m => WS m () diff --git a/test/Tests/Writers/OOXML.hs b/test/Tests/Writers/OOXML.hs index c1e47622d..83f05cfec 100644 --- a/test/Tests/Writers/OOXML.hs +++ b/test/Tests/Writers/OOXML.hs @@ -55,7 +55,9 @@ testArchive :: (WriterOptions -> Pandoc -> PandocIO BL.ByteString) -> IO Archive testArchive writerFn opts fp = do txt <- T.readFile fp - bs <- runIOorExplode $ readNative def txt >>= writerFn opts + bs <- runIOorExplode $ do + setTranslations "en-US" + readNative def txt >>= writerFn opts return $ toArchive bs compareFileList :: FilePath -> Archive -> Archive -> Maybe String diff --git a/test/docx/golden/image.docx b/test/docx/golden/image.docx index 9fe65326f..7c2d8a9ac 100644 Binary files a/test/docx/golden/image.docx and b/test/docx/golden/image.docx differ -- cgit v1.2.3 From cb038bb3125028e17a5b05c32495219a17fb6537 Mon Sep 17 00:00:00 2001 From: Aner Lucero <4rgento@gmail.com> Date: Fri, 2 Jul 2021 11:17:14 -0300 Subject: HTML5 writer, remove aria-hidden when explicit atl text is provided. --- src/Text/Pandoc/Writers/HTML.hs | 11 +++++++---- test/command/7416.md | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index df01fc35f..7eb8dfe12 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -659,17 +659,20 @@ dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height figure :: PandocMonad m => WriterOptions -> Attr -> [Inline] -> (Text, Text) -> StateT WriterState m Html -figure opts attr txt (s,tit) = do +figure opts attr@(_, _, attrList) txt (s,tit) = do html5 <- gets stHtml5 -- Screen-readers will normally read the @alt@ text and the figure; we -- want to avoid them reading the same text twice. With HTML5 we can -- use aria-hidden for the caption; with HTML4, we use an empty -- alt-text instead. + -- When the alt text differs from the caption both should be read. let alt = if html5 then txt else [Str ""] let tocapt = if html5 - then H5.figcaption ! - H5.customAttribute (textTag "aria-hidden") - (toValue @Text "true") + then (H5.figcaption !) $ + if isJust (lookup "alt" attrList) + then mempty + else H5.customAttribute (textTag "aria-hidden") + (toValue @Text "true") else H.p ! A.class_ "caption" img <- inlineToHtml opts (Image attr alt (s,tit)) capt <- if null txt diff --git a/test/command/7416.md b/test/command/7416.md index 70a0257fa..2f9577f10 100644 --- a/test/command/7416.md +++ b/test/command/7416.md @@ -4,7 +4,7 @@ ^D
-alt +alt
caption
``` -- cgit v1.2.3 From 8ef7aa1f811579af29260561e9ce6124b4a6426a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Jul 2021 10:36:41 -0700 Subject: CircleCI: change to using xcode 11.1.0 (macOS 10.14.4). We previously built on 10.13, but 10.13 no longer gets security updates and CirclCI is deprecating. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5f569da65..b58f259e6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: "2.1" jobs: build: macos: - xcode: 9.4.1 + xcode: 11.1.0 environment: HOMEBREW_NO_AUTO_UPDATE: 1 steps: -- cgit v1.2.3 From 40a78ea05d7cf6f7b1e7b2ae7054fed1e5aa6772 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Jul 2021 11:38:04 -0700 Subject: Upgrade Debian 10 AMI for build-arm.sh. --- tools/build-arm.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/build-arm.sh b/tools/build-arm.sh index 0a14c7302..dc275e4b2 100644 --- a/tools/build-arm.sh +++ b/tools/build-arm.sh @@ -1,6 +1,8 @@ #!/bin/sh -IMAGE_ID=ami-0fa8979d18f69948b +#old version: +#IMAGE_ID=ami-0fa8979d18f69948b +IMAGE_ID=ami-0cd6b53a434812702 INSTANCE_TYPE=t4g.2xlarge KEY_NAME=debian-arm-us-east-2 SECURITY_GROUP_ID=sg-086ffbadc286c5c00 -- cgit v1.2.3 From e00e5a4cc27dd5b995887f8152bb71da4f2792c9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Jul 2021 15:19:39 -0700 Subject: Add doc/faqs.md. This is imported from the website; in the future the website version will be drawn from here. Added a FAQ on the use of `\AtEndPreamble` for cases when the contents of `header-includes` need to refer to definitions that come later in the preamble. See #7422. --- doc/faqs.md | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 doc/faqs.md diff --git a/doc/faqs.md b/doc/faqs.md new file mode 100644 index 000000000..0b53730f3 --- /dev/null +++ b/doc/faqs.md @@ -0,0 +1,110 @@ +% FAQs + +::: faqs + +## How can I convert a whole directory of files from Markdown to RTF? + +On linux or OSX: + + for f in *.txt; do pandoc "$f" -s -o "${f%.txt}.rtf"; done + + +In Windows Powershell: + + gci -r -i *.txt |foreach{$rtf=$_.directoryname+"\"+$_.basename+".rtf";pandoc -f markdown -s $_.name -o $rtf} + +## I used pandoc to convert a document to ICML (or OPML or RTF), and when I try to open it I'm told it's invalid. What have I done wrong? + +Be sure to use the `-s` or `--standalone` flag, or you just get a +fragment, not a full document with the required header: + + pandoc -s -f markdown -t icml -o my.icml my.md + +## I get a blank document when I try to convert a markdown document in Chinese to PDF. + +By default, pandoc uses pdflatex to generate the PDF, and pdflatex +doesn't handle Chinese characters. But you can change the default to +use xelatex instead. You should also make sure you're using a font +with Chinese glyphs. For example: + + pandoc -o c.pdf --pdf-engine=xelatex -V mainfont='Adobe Ming Std' + +## The Windows installer does a single user install, rather than installing pandoc for all users. How can I install pandoc for all users? + +Run the following command as admin: + + msiexec /i pandoc-VERSION.msi ALLUSERS=1 + +This will put pandoc in `C:\Program Files\Pandoc`. +You can install Pandoc to a different directory by setting APPLICATIONFOLDER parameter, +for example: + + msiexec /i pandoc-1.11.1.msi ALLUSERS=1 APPLICATIONFOLDER="C:\Pandoc" + +## How do I change the margins in PDF output? + +The option + + -V geometry:margin=1in + +will set the margins to one inch on each side. If you don't want uniform +margins, you can do something like + + -V geometry:"top=2cm, bottom=1.5cm, left=1cm, right=1cm" + +Or + + -V geometry:"left=3cm, width=10cm" + +For more options, see the documentation for the LaTeX [geometry +package](https://www.ctan.org/pkg/geometry). + +## How does pandoc compare to multimarkdown? + +Here is a [wiki +page](https://github.com/jgm/pandoc/wiki/Pandoc-vs-Multimarkdown) +comparing the two. + +## When I specify an image width of 50% and convert to LaTeX, pandoc sets the height to textheight and the aspect ratio isn't preserved. How can I prevent this? + +For example, if you convert an image with `{width="50%"}`, the LaTeX produced +will be `\includegraphics[width=0.5\textwidth,height=\textheight]`. + +This output presupposes the following code in pandoc's default latex +template: + +``` +% Scale images if necessary, so that they will not overflow the page +% margins by default, and it is still possible to overwrite the defaults +% using explicit options in \includegraphics[width, height, ...]{} +\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio} +``` + +If you don't have this in your custom template, you should +add it. If we didn't set the `height` explicitly in this way, +the image would not be resized correctly unless it was +being resized to smaller than its original size. + +## Pandoc sometimes uses too much memory. How can I limit the memory used by pandoc? + +`pandoc +RTS -M30m -RTS` will limit heap memory to 30MB. +When converting a document requires more than this, an out of +memory error will be issued. + +## When using `--include-in-header` with PDF or LaTeX output, how do I reference tex declarations coming after `$header-includes$` in the default template? + +For various reasons, the `$header-includes$` are not at the very +end of the LaTeX preamble. This poses a problem when the code +you are inserting depends on declarations in the preamble coming +after the `$header-includes$` location. For example, you might +want to reference the `\author` and `\title` metadata values +(set at the very bottom of the preamble) and present them in +margins. In that case you can wrap your code in `etoolbox`'s +`\AtEndPreamble`. The technique is demonstrated in [this +gist](https://gist.github.com/JohnLukeBentley/9dda6166b9ee5c4127afd2b8cd16b70a). +When using `\AtEndPreamble`, keep any `makeatletter` or +`makeatother` outside of the `\AtEndPreamble`, as shown in the +example. + +::: + -- cgit v1.2.3 From 972db3cdcac19bca0dd4e0957514a48bbf1feacb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Jul 2021 15:21:43 -0700 Subject: Revert "LaTeX template: move title, author, date up to top of preamble." This reverts commit cc088687b4013c2b8b744eb337ed04fc63f315f2 and PR #7295. This fixes issues people had when using LaTeX commands defined later in the preamble (or in some cases UTF-8 text) in the title or author fields. Closes #7422. --- data/templates/default.latex | 58 ++++++++++++++++++++--------------------- test/lhs-test.latex | 6 ++--- test/lhs-test.latex+lhs | 6 ++--- test/writer.latex | 8 +++--- test/writers-lang-and-dir.latex | 6 ++--- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index 60f9972c2..d06701675 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -36,35 +36,6 @@ $for(classoption)$ $classoption$$sep$, $endfor$ ]{$documentclass$} -$if(title)$ -\title{$title$$if(thanks)$\thanks{$thanks$}$endif$} -$endif$ -$if(subtitle)$ -$if(beamer)$ -$else$ -\usepackage{etoolbox} -\makeatletter -\providecommand{\subtitle}[1]{% add subtitle to \maketitle - \apptocmd{\@title}{\par {\large #1 \par}}{}{} -} -\makeatother -$endif$ -\subtitle{$subtitle$} -$endif$ -\author{$for(author)$$author$$sep$ \and $endfor$} -\date{$date$} -$if(beamer)$ -$if(institute)$ -\institute{$for(institute)$$institute$$sep$ \and $endfor$} -$endif$ -$if(titlegraphic)$ -\titlegraphic{\includegraphics{$titlegraphic$}} -$endif$ -$if(logo)$ -\logo{\includegraphics{$logo$}} -$endif$ -$endif$ - $if(beamer)$ $if(background-image)$ \usebackgroundtemplate{% @@ -452,6 +423,35 @@ $if(csquotes)$ \usepackage{csquotes} $endif$ +$if(title)$ +\title{$title$$if(thanks)$\thanks{$thanks$}$endif$} +$endif$ +$if(subtitle)$ +$if(beamer)$ +$else$ +\usepackage{etoolbox} +\makeatletter +\providecommand{\subtitle}[1]{% add subtitle to \maketitle + \apptocmd{\@title}{\par {\large #1 \par}}{}{} +} +\makeatother +$endif$ +\subtitle{$subtitle$} +$endif$ +\author{$for(author)$$author$$sep$ \and $endfor$} +\date{$date$} +$if(beamer)$ +$if(institute)$ +\institute{$for(institute)$$institute$$sep$ \and $endfor$} +$endif$ +$if(titlegraphic)$ +\titlegraphic{\includegraphics{$titlegraphic$}} +$endif$ +$if(logo)$ +\logo{\includegraphics{$logo$}} +$endif$ +$endif$ + \begin{document} $if(has-frontmatter)$ \frontmatter diff --git a/test/lhs-test.latex b/test/lhs-test.latex index 20bc6ae19..fdc344335 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -4,9 +4,6 @@ % \documentclass[ ]{article} -\author{} -\date{} - \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -88,6 +85,9 @@ \usepackage{selnolig} % disable illegal ligatures \fi +\author{} +\date{} + \begin{document} \hypertarget{lhs-test}{% diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index 327dac4ef..f9149ba02 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -4,9 +4,6 @@ % \documentclass[ ]{article} -\author{} -\date{} - \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -55,6 +52,9 @@ \usepackage{selnolig} % disable illegal ligatures \fi +\author{} +\date{} + \begin{document} \hypertarget{lhs-test}{% diff --git a/test/writer.latex b/test/writer.latex index 468c271bc..abf772df4 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -4,10 +4,6 @@ % \documentclass[ ]{article} -\title{Pandoc Test Suite} -\author{John MacFarlane \and Anonymous} -\date{July 17, 2006} - \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -71,6 +67,10 @@ \usepackage{selnolig} % disable illegal ligatures \fi +\title{Pandoc Test Suite} +\author{John MacFarlane \and Anonymous} +\date{July 17, 2006} + \begin{document} \maketitle diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index e06fe05ed..d91f77325 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -5,9 +5,6 @@ \documentclass[ english, ]{article} -\author{} -\date{} - \usepackage{amsmath,amssymb} \usepackage{lmodern} \usepackage{iftex} @@ -87,6 +84,9 @@ \newenvironment{LTR}{\beginL}{\endL} \fi +\author{} +\date{} + \begin{document} \hypertarget{empty-divs-and-spans}{% -- cgit v1.2.3 From 95541294d384d5bf6514b0b676bf93b44ba4d26d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Jul 2021 15:51:07 -0700 Subject: Add FAQ on converting from/to PDF --- doc/faqs.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/faqs.md b/doc/faqs.md index 0b53730f3..2f3368837 100644 --- a/doc/faqs.md +++ b/doc/faqs.md @@ -106,5 +106,23 @@ When using `\AtEndPreamble`, keep any `makeatletter` or `makeatother` outside of the `\AtEndPreamble`, as shown in the example. +## How can I convert PDFs to other formats using pandoc? + +You can't. You can try opening the PDF in Word or Google Docs +and saving in a format from which pandoc can convert directly. + +## Do I really need to install a 1 GB TeX installation to produce a PDF using pandoc? + +No. You can get by with a relatively small TeX installation, +for example, by starting with MacTeX's Basic TeX distribution +and using the `tlmgr` tool to install a few packages required by pandoc +(see https://pandoc.org/MANUAL.html#creating-a-pdf). + +Or, you can produce PDFs via HTML and `wkhtmltopdf`, +or via groff ms and `pdfroff`. (These don't produce as nice +topography as TeX, particularly when it comes to math, but they +may be fine for many purposes.) + + ::: -- cgit v1.2.3 From de4da56079663dd31464525c23be3db1c0631f11 Mon Sep 17 00:00:00 2001 From: Mauro Bieg Date: Fri, 2 Jul 2021 09:27:15 +0200 Subject: document-css: reset overflow-wrap on code blocks fixes #7423 --- data/templates/styles.html | 3 ++- test/lhs-test.html | 3 ++- test/lhs-test.html+lhs | 3 ++- test/writer.html4 | 3 ++- test/writer.html5 | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/data/templates/styles.html b/data/templates/styles.html index e744d83b8..d26a39cf9 100644 --- a/data/templates/styles.html +++ b/data/templates/styles.html @@ -14,7 +14,7 @@ body { padding-top: $if(margin-top)$$margin-top$$else$50px$endif$; padding-bottom: $if(margin-bottom)$$margin-bottom$$else$50px$endif$; hyphens: auto; - word-wrap: break-word; + overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @@ -93,6 +93,7 @@ $endif$ pre code { padding: 0; overflow: visible; + overflow-wrap: normal; } .sourceCode { background-color: transparent; diff --git a/test/lhs-test.html b/test/lhs-test.html index 89b49913e..be06d31c9 100644 --- a/test/lhs-test.html +++ b/test/lhs-test.html @@ -21,7 +21,7 @@ padding-top: 50px; padding-bottom: 50px; hyphens: auto; - word-wrap: break-word; + overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @@ -92,6 +92,7 @@ pre code { padding: 0; overflow: visible; + overflow-wrap: normal; } .sourceCode { background-color: transparent; diff --git a/test/lhs-test.html+lhs b/test/lhs-test.html+lhs index ddd08e98a..4b37aca53 100644 --- a/test/lhs-test.html+lhs +++ b/test/lhs-test.html+lhs @@ -21,7 +21,7 @@ padding-top: 50px; padding-bottom: 50px; hyphens: auto; - word-wrap: break-word; + overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @@ -92,6 +92,7 @@ pre code { padding: 0; overflow: visible; + overflow-wrap: normal; } .sourceCode { background-color: transparent; diff --git a/test/writer.html4 b/test/writer.html4 index 76578249a..215a1efb9 100644 --- a/test/writer.html4 +++ b/test/writer.html4 @@ -24,7 +24,7 @@ padding-top: 50px; padding-bottom: 50px; hyphens: auto; - word-wrap: break-word; + overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @@ -95,6 +95,7 @@ pre code { padding: 0; overflow: visible; + overflow-wrap: normal; } .sourceCode { background-color: transparent; diff --git a/test/writer.html5 b/test/writer.html5 index cb9333efa..387df4058 100644 --- a/test/writer.html5 +++ b/test/writer.html5 @@ -24,7 +24,7 @@ padding-top: 50px; padding-bottom: 50px; hyphens: auto; - word-wrap: break-word; + overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @@ -95,6 +95,7 @@ pre code { padding: 0; overflow: visible; + overflow-wrap: normal; } .sourceCode { background-color: transparent; -- cgit v1.2.3 From ff26af59acda8d57d1f0d5faf24a2da203dc2f4e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Jul 2021 13:18:25 -0700 Subject: Revamp note citation handling. Use latest citeproc, which uses a Span with a class rather than a Note for notes. This helps us distinguish between user notes and citation notes. Don't put citations at the beginning of a note in parentheses. (Closes #7394.) --- cabal.project | 2 +- src/Text/Pandoc/Citeproc.hs | 44 ++++++++++++++++++++++++++++++-------------- stack.yaml | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/cabal.project b/cabal.project index 99c7b3556..e18577471 100644 --- a/cabal.project +++ b/cabal.project @@ -10,4 +10,4 @@ source-repository-package source-repository-package type: git location: https://github.com/jgm/citeproc - tag: 85277c4baa6c0350baf29247583ae0f438f7c9c5 + tag: 4ce0501cd6f9c86eee087afcf878c256e49b9615 diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index a2fca106a..b19494dc0 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -27,7 +27,7 @@ import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Extensions (pandocExtensions) import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Options (ReaderOptions(..)) -import Text.Pandoc.Shared (stringify, ordNub, blocksToInlines, tshow) +import Text.Pandoc.Shared (stringify, ordNub, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk (query, walk, walkM) import Control.Applicative ((<|>)) @@ -49,6 +49,7 @@ import qualified Data.Text as T import System.FilePath (takeExtension) import Safe (lastMay, initSafe) + processCitations :: PandocMonad m => Pandoc -> m Pandoc processCitations (Pandoc meta bs) = do style <- getStyle (Pandoc meta bs) @@ -99,7 +100,9 @@ processCitations (Pandoc meta bs) = do let Pandoc meta'' bs' = maybe id (setMeta "nocite") metanocites . walk (map capitalizeNoteCitation . mvPunct moveNotes locale) . - walk deNote . + (if styleIsNoteStyle sopts + then walk addNote . walk deNote + else id) . evalState (walkM insertResolvedCitations $ Pandoc meta' bs) $ cits return $ Pandoc meta'' @@ -568,33 +571,46 @@ capitalizeNoteCitation (Cite cs [Note [Para ils]]) = $ B.fromList ils]] capitalizeNoteCitation x = x +addNote :: Inline -> Inline +addNote (Span ("",["csl-note"],[]) ils) = Note [Para ils] +addNote x = x + deNote :: [Inline] -> [Inline] deNote [] = [] deNote (Note bs:rest) = - Note (walk go bs) : deNote rest + case bs of + [Para (cit@(Cite (c:_) _) : ils)] + | citationMode c /= AuthorInText -> + -- if citation is first in note, no need to parenthesize. + Note [Para (walk removeNotes $ cit : walk addParens ils)] + : deNote rest + _ -> Note (walk removeNotes . walk addParens $ bs) : deNote rest where - go [] = [] - go (Cite (c:cs) ils : zs) + addParens [] = [] + addParens (Cite (c:cs) ils : zs) | citationMode c == AuthorInText - = Cite (c:cs) (concatMap (noteAfterComma (needsPeriod zs)) ils) : go zs + = Cite (c:cs) (concatMap (noteAfterComma (needsPeriod zs)) ils) : + addParens zs | otherwise - = Cite (c:cs) (concatMap noteInParens ils) : go zs - go (x:xs) = x : go xs + = Cite (c:cs) (concatMap noteInParens ils) : addParens zs + addParens (x:xs) = x : addParens xs + removeNotes (Span ("",["csl-note"],[]) ils) = Span ("",[],[]) ils + removeNotes x = x needsPeriod [] = True needsPeriod (Str t:_) = case T.uncons t of Nothing -> False Just (c,_) -> isUpper c needsPeriod (Space:zs) = needsPeriod zs needsPeriod _ = False - noteInParens (Note bs') + noteInParens (Span ("",["csl-note"],[]) ils) = Space : Str "(" : - removeFinalPeriod (blocksToInlines bs') ++ [Str ")"] + removeFinalPeriod ils ++ [Str ")"] noteInParens x = [x] - noteAfterComma needsPer (Note bs') + noteAfterComma needsPer (Span ("",["csl-note"],[]) ils) = Str "," : Space : - (if needsPer - then id - else removeFinalPeriod) (blocksToInlines bs') + if needsPer + then ils + else removeFinalPeriod ils noteAfterComma _ x = [x] deNote (x:xs) = x : deNote xs diff --git a/stack.yaml b/stack.yaml index 2db0a626f..48a608db3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -14,7 +14,7 @@ extra-deps: - git: https://github.com/jgm/doctemplates commit: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81 - git: https://github.com/jgm/citeproc - commit: 85277c4baa6c0350baf29247583ae0f438f7c9c5 + commit: 4ce0501cd6f9c86eee087afcf878c256e49b9615 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-18.0 -- cgit v1.2.3 From 77537b17657e29d047a2f8df1feda2ed540c6e08 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Jul 2021 13:28:58 -0700 Subject: Citeproc: cleanup and efficiency improvement in deNote. --- src/Text/Pandoc/Citeproc.hs | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index b19494dc0..6e2d9ce7e 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -99,7 +99,7 @@ processCitations (Pandoc meta bs) = do let metanocites = lookupMeta "nocite" meta let Pandoc meta'' bs' = maybe id (setMeta "nocite") metanocites . - walk (map capitalizeNoteCitation . mvPunct moveNotes locale) . + walk (mvPunct moveNotes locale) . (if styleIsNoteStyle sopts then walk addNote . walk deNote else id) . @@ -564,27 +564,28 @@ extractText (FancyVal x) = toText x extractText (NumVal n) = T.pack (show n) extractText _ = mempty -capitalizeNoteCitation :: Inline -> Inline -capitalizeNoteCitation (Cite cs [Note [Para ils]]) = - Cite cs - [Note [Para $ B.toList $ addTextCase Nothing CapitalizeFirst - $ B.fromList ils]] -capitalizeNoteCitation x = x - +-- Here we take the Spans with class csl-note that are left +-- after deNote has removed nested ones, and convert them +-- into real notes. addNote :: Inline -> Inline -addNote (Span ("",["csl-note"],[]) ils) = Note [Para ils] +addNote (Span ("",["csl-note"],[]) ils) = + Note [Para $ + B.toList . addTextCase Nothing CapitalizeFirst . B.fromList $ ils] addNote x = x -deNote :: [Inline] -> [Inline] -deNote [] = [] -deNote (Note bs:rest) = +-- Here we handle citation notes that occur inside footnotes +-- or other citation notes, in a note style. We don't want +-- notes inside notes, so we convert these to parenthesized +-- or comma-separated citations. +deNote :: Inline -> Inline +deNote (Note bs) = case bs of [Para (cit@(Cite (c:_) _) : ils)] | citationMode c /= AuthorInText -> -- if citation is first in note, no need to parenthesize. Note [Para (walk removeNotes $ cit : walk addParens ils)] - : deNote rest - _ -> Note (walk removeNotes . walk addParens $ bs) : deNote rest + _ -> Note (walk removeNotes . walk addParens $ bs) + where addParens [] = [] addParens (Cite (c:cs) ils : zs) @@ -594,25 +595,30 @@ deNote (Note bs:rest) = | otherwise = Cite (c:cs) (concatMap noteInParens ils) : addParens zs addParens (x:xs) = x : addParens xs + removeNotes (Span ("",["csl-note"],[]) ils) = Span ("",[],[]) ils removeNotes x = x + needsPeriod [] = True needsPeriod (Str t:_) = case T.uncons t of Nothing -> False Just (c,_) -> isUpper c needsPeriod (Space:zs) = needsPeriod zs needsPeriod _ = False + noteInParens (Span ("",["csl-note"],[]) ils) = Space : Str "(" : removeFinalPeriod ils ++ [Str ")"] noteInParens x = [x] + noteAfterComma needsPer (Span ("",["csl-note"],[]) ils) = Str "," : Space : if needsPer then ils else removeFinalPeriod ils noteAfterComma _ x = [x] -deNote (x:xs) = x : deNote xs + +deNote x = x -- Note: we can't use dropTextWhileEnd indiscriminately, -- because this would remove the final period on abbreviations like Ibid. -- cgit v1.2.3 From 3a31fe68efcf80888294a1d3d33751d266d4de07 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Jul 2021 14:45:07 -0700 Subject: Add command test for #7394. And fix a small bug in handling of citations in notes, which led to commas at the end of sentences in some cases. --- src/Text/Pandoc/Citeproc.hs | 1 + test/command/7394.md | 85 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/command/7394.md diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs index 6e2d9ce7e..246f54516 100644 --- a/src/Text/Pandoc/Citeproc.hs +++ b/src/Text/Pandoc/Citeproc.hs @@ -612,6 +612,7 @@ deNote (Note bs) = noteInParens x = [x] noteAfterComma needsPer (Span ("",["csl-note"],[]) ils) + | not (null ils) = Str "," : Space : if needsPer then ils diff --git a/test/command/7394.md b/test/command/7394.md new file mode 100644 index 000000000..d6eb769b9 --- /dev/null +++ b/test/command/7394.md @@ -0,0 +1,85 @@ +``` +% pandoc -f markdown -t plain --citeproc +--- +csl: command/chicago-fullnote-bibliography.csl +references: +- author: + - family: Wandt + given: Manfred + edition: 6 + id: wandt2014ges-sv + issued: 2014 + publisher: Franz Vahlen + publisher-place: München + title: Gesetzliche schuldverhältnisse + title-short: Gesetzl SV + type: book +- author: + - family: Smith + given: Zenda + edition: 6 + id: smith2015 + issued: 2015 + publisher: Macmillan + publisher-place: New York + title: A verb and a noun + type: book +--- + +Hi^[@wandt2014ges-sv.]. + +Hi^[[@wandt2014ges-sv].]. + +Hi^[[See also @wandt2014ges-sv].]. + +Hi^[See also @wandt2014ges-sv.]. + +Hi^[@wandt2014ges-sv [@smith2015].]. + +Hi^[[@wandt2014ges-sv; @smith2015].]. + +Hi [@wandt2014ges-sv]. + +Hi [see also @wandt2014ges-sv]. + +^D +Hi[1]. + +Hi[2]. + +Hi[3]. + +Hi[4]. + +Hi[5]. + +Hi[6]. + +Hi.[7] + +Hi.[8] + +Smith, Zenda. A Verb and a Noun. 6th ed. New York: Macmillan, 2015. + +Wandt, Manfred. Gesetzliche Schuldverhältnisse. 6th ed. München: Franz +Vahlen, 2014. + +[1] Manfred Wandt, Gesetzliche Schuldverhältnisse, 6th ed. (München: +Franz Vahlen, 2014). + +[2] Wandt. + +[3] See also Wandt. + +[4] See also Wandt. + +[5] Wandt, Zenda Smith, A Verb and a Noun, 6th ed. (New York: Macmillan, +2015). + +[6] Wandt, Gesetzl SV; Smith, A Verb and a Noun. + +[7] Wandt, Gesetzl SV. + +[8] See also Wandt. +``` + -- cgit v1.2.3 From 3ed37f00771d20a1b7516f2a37b7b424b3b2f1d8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 6 Jul 2021 10:21:59 -0700 Subject: HTML reader: add col, colgroup to 'closes' definitions --- src/Text/Pandoc/Readers/HTML/Parsing.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/HTML/Parsing.hs b/src/Text/Pandoc/Readers/HTML/Parsing.hs index 4c069e2c3..bd8d7c96c 100644 --- a/src/Text/Pandoc/Readers/HTML/Parsing.hs +++ b/src/Text/Pandoc/Readers/HTML/Parsing.hs @@ -161,10 +161,12 @@ _ `closes` "html" = False "li" `closes` "li" = True "th" `closes` t | t `elem` ["th","td"] = True "td" `closes` t | t `elem` ["th","td"] = True -"tr" `closes` t | t `elem` ["th","td","tr"] = True +"tr" `closes` t | t `elem` ["th","td","tr","colgroup"] = True "dd" `closes` t | t `elem` ["dt", "dd"] = True "dt" `closes` t | t `elem` ["dt","dd"] = True "rt" `closes` t | t `elem` ["rb", "rt", "rtc"] = True +"col" `closes` "col" = True +"colgroup" `closes` "col" = True "optgroup" `closes` "optgroup" = True "optgroup" `closes` "option" = True "option" `closes` "option" = True -- cgit v1.2.3 From f88ebf3ebf49e00ffa12778caf6817cc34459e6a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 6 Jul 2021 10:22:07 -0700 Subject: Markdown reader: don't try to read contents in self-closing HTML tag. Previously we had problems parsing raw HTML with self-closing tags like ``. The problem was that pandoc would look for a closing tag to close the markdown contents, but the closing tag had, in effect, already been parsed by `htmlTag`. This fixes the issue described in . --- src/Text/Pandoc/Readers/Markdown.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 1e9867d07..2dc7ddf52 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1121,6 +1121,7 @@ rawTeXBlock = do rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag + let selfClosing = "/>" `T.isSuffixOf` raw -- we don't want ' text' to be a code block: skipMany spaceChar indentlevel <- (blankline >> length <$> many (char ' ')) <|> return 0 @@ -1134,7 +1135,9 @@ rawHtmlBlocks = do gobbleAtMostSpaces indentlevel notFollowedBy' closer block - contents <- mconcat <$> many block' + contents <- if selfClosing + then return mempty + else mconcat <$> many block' result <- try (do gobbleAtMostSpaces indentlevel -- cgit v1.2.3 From e7f8cc57866b61ed354c4c3812aaced33832a0e0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 6 Jul 2021 10:39:47 -0700 Subject: T.P.PDF, convertImage: normalize paths. This will avoid paths on Windows with mixed path separators, which may cause problems with SVG conversion. See #7431. --- src/Text/Pandoc/PDF.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index f85ef5b1f..aae3f9806 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -215,9 +215,9 @@ convertImage opts tmpdir fname = do E.catch (Right pngOut <$ JP.savePngImage pngOut img) $ \(e :: E.SomeException) -> return (Left (tshow e)) where - pngOut = replaceDirectory (replaceExtension fname ".png") tmpdir - pdfOut = replaceDirectory (replaceExtension fname ".pdf") tmpdir - svgIn = tmpdir fname + pngOut = normalise $ replaceDirectory (replaceExtension fname ".png") tmpdir + pdfOut = normalise $ replaceDirectory (replaceExtension fname ".pdf") tmpdir + svgIn = normalise $ tmpdir fname mime = getMimeType fname doNothing = return (Right fname) -- cgit v1.2.3 From e56e2b0e0be9256ddef798d28f5d2af6e756508d Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Wed, 7 Jul 2021 01:06:29 +0200 Subject: Recognize data-external when reading HTML img tags (#7429) Preserve all attributes in img tags. If attributes have a `data-` prefix, it will be stripped. In particular, this preserves a `data-external` attribute as an `external` attribute in the pandoc AST. --- src/Text/Pandoc/Readers/HTML.hs | 11 +++-------- test/Tests/Readers/HTML.hs | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index fc4575f2d..fdf4f28e0 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -722,17 +722,12 @@ pLink = try $ do pImage :: PandocMonad m => TagParser m Inlines pImage = do - tag <- pSelfClosing (=="img") (isJust . lookup "src") + tag@(TagOpen _ attr') <- pSelfClosing (=="img") (isJust . lookup "src") url <- canonicalizeUrl $ fromAttrib "src" tag let title = fromAttrib "title" tag let alt = fromAttrib "alt" tag - let uid = fromAttrib "id" tag - let cls = T.words $ fromAttrib "class" tag - let getAtt k = case fromAttrib k tag of - "" -> [] - v -> [(k, v)] - let kvs = concatMap getAtt ["width", "height", "sizes", "srcset"] - return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt) + let attr = toAttr $ filter (\(k,_) -> k /= "alt" && k /= "title" && k /= "src") attr' + return $ B.imageWith attr (escapeURI url) title (B.text alt) pSvg :: PandocMonad m => TagParser m Inlines pSvg = do diff --git a/test/Tests/Readers/HTML.hs b/test/Tests/Readers/HTML.hs index 9bf567194..4ed1e44af 100644 --- a/test/Tests/Readers/HTML.hs +++ b/test/Tests/Readers/HTML.hs @@ -74,6 +74,12 @@ tests = [ testGroup "base tag" [ test html "anchor without href" $ "" =?> plain (spanWith ("anchor",[],[]) mempty) ] + , testGroup "img" + [ test html "data-external attribute" $ "" =?> + plain (imageWith ("", [], [("external", "1")]) "http://example.com/stickman.gif" "" "") + , test html "title" $ "" =?> + plain (imageWith ("", [], []) "http://example.com/stickman.gif" "The title" "") + ] , testGroup "lang" [ test html "lang on " $ "hola" =?> setMeta "lang" (text "es") (doc (plain (text "hola"))) -- cgit v1.2.3 From 565330033a623ed7bf4d0a3b57dd14710cf27703 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Wed, 7 Jul 2021 18:26:37 +0200 Subject: Don't incorporate externally linked images in EPUB documents (#7430) Just like it is possible to avoid incorporating an image in EPUB by passing `data-external="1"` to a raw HTML snippet, this makes the same possible for native Images, by looking for an associated `external` attribute. --- MANUAL.txt | 6 ++++++ src/Text/Pandoc/Writers/EPUB.hs | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/MANUAL.txt b/MANUAL.txt index 52563a8fa..c9dc9e62b 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -6177,6 +6177,12 @@ with the `src` attribute. For example: +If the input format already is HTML then `data-external="1"` will work +as expected for `` elements. Similarly, for Markdown, external +images can be declared with `![img](url){external=1}`. Note that this +only works for images; the other media elements have no native +representation in pandoc's AST and requires the use of raw HTML. + # Jupyter notebooks When creating a [Jupyter notebook], pandoc will try to infer the diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 3c092a2c1..508fb6a98 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -1131,7 +1131,8 @@ transformInline :: PandocMonad m => WriterOptions -> Inline -> E m Inline -transformInline _opts (Image attr lab (src,tit)) = do +transformInline _opts (Image attr@(_,_,kvs) lab (src,tit)) + | isNothing (lookup "external" kvs) = do newsrc <- modifyMediaRef $ T.unpack src return $ Image attr lab ("../" <> newsrc, tit) transformInline opts x@(Math t m) -- cgit v1.2.3 From ae22b1e977cfb1357bb21fabc227e76a6adb0599 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 9 Jul 2021 12:27:41 -0700 Subject: RST reader: fix regression with code includes. With the recent changes to include infrastructure, included code blocks were getting an extra newline. Closes #7436. Added regression test. --- pandoc.cabal | 1 + src/Text/Pandoc/Readers/RST.hs | 6 +++++- test/command/7436.md | 14 ++++++++++++++ test/command/three.txt | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/command/7436.md create mode 100644 test/command/three.txt diff --git a/pandoc.cabal b/pandoc.cabal index 84a04c6b6..e3cd7e54f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -213,6 +213,7 @@ extra-source-files: test/command/B.txt test/command/C.txt test/command/D.txt + test/command/three.txt test/command/01.csv test/command/chap1/spider.png test/command/chap2/spider.png diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 35292d949..3990f0cb5 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -474,6 +474,7 @@ includeDirective top fields body = do case lookup "literal" fields of Just _ -> B.rawBlock "rst" . sourcesToText <$> getInput Nothing -> parseBlocks + let isLiteral = isJust (lookup "code" fields `mplus` lookup "literal" fields) let selectLines = (case trim <$> lookup "end-before" fields of Just patt -> takeWhile (not . (patt `T.isInfixOf`)) @@ -482,8 +483,11 @@ includeDirective top fields body = do Just patt -> drop 1 . dropWhile (not . (patt `T.isInfixOf`)) Nothing -> id) + let toStream t = - toSources [(f, T.unlines . selectLines . T.lines $ t)] + Sources [(initialPos f, + (T.unlines . selectLines . T.lines $ t) <> + if isLiteral then mempty else "\n")] -- see #7436 currentDir <- takeDirectory . sourceName <$> getPosition insertIncludedFile parser toStream [currentDir] f startLine endLine diff --git a/test/command/7436.md b/test/command/7436.md new file mode 100644 index 000000000..ad4cb8c2f --- /dev/null +++ b/test/command/7436.md @@ -0,0 +1,14 @@ +``` +% pandoc -f rst -t native +.. include:: command/three.txt + :code: + +.. include:: command/three.txt + :literal: + +.. include:: command/three.txt +^D +[CodeBlock ("",[""],[("code","")]) "1st line.\n2nd line.\n3rd line.\n" +,RawBlock (Format "rst") "1st line.\n2nd line.\n3rd line.\n" +,Para [Str "1st",Space,Str "line.",SoftBreak,Str "2nd",Space,Str "line.",SoftBreak,Str "3rd",Space,Str "line."]] +``` diff --git a/test/command/three.txt b/test/command/three.txt new file mode 100644 index 000000000..3ca3fdd4e --- /dev/null +++ b/test/command/three.txt @@ -0,0 +1,3 @@ +1st line. +2nd line. +3rd line. -- cgit v1.2.3 From 477a67061f06827b7e807319404cc277a417e9d0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 9 Jul 2021 14:14:19 -0700 Subject: Always use / when adding directory to image path with extractMedia. Even on Windows. May help with #7431. --- src/Text/Pandoc/Class/IO.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs index 169074860..f4cfc8682 100644 --- a/src/Text/Pandoc/Class/IO.hs +++ b/src/Text/Pandoc/Class/IO.hs @@ -226,6 +226,6 @@ adjustImagePath dir mediabag (Image attr lab (src, tit)) = case lookupMedia (T.unpack src) mediabag of Nothing -> Image attr lab (src, tit) Just item -> - let fullpath = dir mediaPath item + let fullpath = dir <> "/" <> mediaPath item in Image attr lab (T.pack fullpath, tit) adjustImagePath _ _ x = x -- cgit v1.2.3 From ac0a9da6d85e9b7a73973a20019caa324b2c1aff Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Jul 2021 13:01:45 -0700 Subject: Improved parsing of raw LaTeX from Text streams (rawLaTeXParser). We now use source positions from the token stream to tell us how much of the text stream to consume. Getting this to work required a few other changes to make token source positions accurate. Closes #7434. --- src/Text/Pandoc/Readers/LaTeX.hs | 9 +++----- src/Text/Pandoc/Readers/LaTeX/Parsing.hs | 39 ++++++++++++++++++++++++++++---- test/command/7434.md | 15 ++++++++++++ 3 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 test/command/7434.md diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9e14c159a..31c8d9095 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -136,8 +136,7 @@ rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT Sources s m Text rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) - inp <- getInput - let toks = tokenizeSources inp + toks <- getInputTokens snd <$> (rawLaTeXParser toks False (macroDef (const mempty)) blocks <|> rawLaTeXParser toks True (do choice (map controlSeq @@ -167,8 +166,7 @@ rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT Sources s m Text rawLaTeXInline = do lookAhead (try (char '\\' >> letter)) - inp <- getInput - let toks = tokenizeSources inp + toks <- getInputTokens raw <- snd <$> ( rawLaTeXParser toks True (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced)) @@ -182,8 +180,7 @@ rawLaTeXInline = do inlineCommand :: PandocMonad m => ParserT Sources ParserState m Inlines inlineCommand = do lookAhead (try (char '\\' >> letter)) - inp <- getInput - let toks = tokenizeSources inp + toks <- getInputTokens fst <$> rawLaTeXParser toks True (inlineEnvironment <|> inlineCommand') inlines diff --git a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs index a17b1f324..9dac4d6ef 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Parsing.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Parsing.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} @@ -28,6 +29,7 @@ module Text.Pandoc.Readers.LaTeX.Parsing , applyMacros , tokenize , tokenizeSources + , getInputTokens , untokenize , untoken , totoks @@ -246,18 +248,23 @@ withVerbatimMode parser = do updateState $ \st -> st{ sVerbatimMode = False } return result -rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) +rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s, Show a) => [Tok] -> Bool -> LP m a -> LP m a -> ParserT Sources s m (a, Text) rawLaTeXParser toks retokenize parser valParser = do pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate } let lstate' = lstate { sMacros = extractMacros pstate } + let setStartPos = case toks of + Tok pos _ _ : _ -> setPosition pos + _ -> return () + let preparser = setStartPos >> parser let rawparser = (,) <$> withRaw valParser <*> getState - res' <- lift $ runParserT (snd <$> withRaw parser) lstate "chunk" toks + res' <- lift $ runParserT (withRaw (preparser >> getPosition)) + lstate "chunk" toks case res' of Left _ -> mzero - Right toks' -> do + Right (endpos, toks') -> do res <- lift $ runParserT (do when retokenize $ do -- retokenize, applying macros ts <- many (satisfyTok (const True)) @@ -268,7 +275,13 @@ rawLaTeXParser toks retokenize parser valParser = do Left _ -> mzero Right ((val, raw), st) -> do updateState (updateMacros (sMacros st <>)) - void $ count (T.length (untokenize toks')) anyChar + let skipTilPos stopPos = do + anyChar + pos <- getPosition + if pos >= stopPos + then return () + else skipTilPos stopPos + skipTilPos endpos let result = untokenize raw -- ensure we end with space if input did, see #4442 let result' = @@ -306,6 +319,17 @@ tokenizeSources = concatMap tokenizeSource . unSources where tokenizeSource (pos, t) = totoks pos t +-- Return tokens from input sources. Ensure that starting position is +-- correct. +getInputTokens :: PandocMonad m => ParserT Sources s m [Tok] +getInputTokens = do + pos <- getPosition + ss <- getInput + return $ + case ss of + Sources [] -> [] + Sources ((_,t):rest) -> tokenizeSources $ Sources ((pos,t):rest) + tokenize :: SourceName -> Text -> [Tok] tokenize sourcename = totoks (initialPos sourcename) @@ -433,8 +457,13 @@ parseFromToks :: PandocMonad m => LP m a -> [Tok] -> LP m a parseFromToks parser toks = do oldInput <- getInput setInput toks + oldpos <- getPosition + case toks of + Tok pos _ _ : _ -> setPosition pos + _ -> return () result <- disablingWithRaw parser setInput oldInput + setPosition oldpos return result disablingWithRaw :: PandocMonad m => LP m a -> LP m a @@ -458,7 +487,7 @@ satisfyTok f = do | otherwise = Nothing updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos updatePos _spos _ (Tok pos _ _ : _) = pos - updatePos spos _ [] = incSourceColumn spos 1 + updatePos spos (Tok _ _ t) [] = incSourceColumn spos (T.length t) doMacros :: PandocMonad m => LP m () doMacros = do diff --git a/test/command/7434.md b/test/command/7434.md new file mode 100644 index 000000000..be8732bc2 --- /dev/null +++ b/test/command/7434.md @@ -0,0 +1,15 @@ +``` +% pandoc -f markdown -t native +\begin{proof} +\newcommand{\x}{\left.\right.} +\x +\end{proof} + +1234567890abcdefghi + +[\*\a](x) +^D +[RawBlock (Format "tex") "\\begin{proof}\n\\newcommand{\\x}{\\left.\\right.}\n\\left.\\right.\n\\end{proof}" +,Para [Str "1234567890abcdefghi"] +,Para [Link ("",[],[]) [Str "*",RawInline (Format "tex") "\\a"] ("x","")]] +``` -- cgit v1.2.3 From 06408d08e5ccf06a6a04c9b77470e6a67d98e52c Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 12 Jul 2021 00:28:52 +0200 Subject: DocBook reader: add support for citerefentry (#7437) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally intended for referring to UNIX manual pages, either part of the same DocBook document as refentry element, or external – hence the manvolnum element. These days, refentry is more general, for example the element documentation pages linked below are each a refentry. As per the *Processing expectations* section of citerefentry, the element is supposed to be a hyperlink to a refentry (when in the same document) but pandoc does not support refentry tag at the moment so that is moot. https://tdg.docbook.org/tdg/5.1/citerefentry.html https://tdg.docbook.org/tdg/5.1/manvolnum.html https://tdg.docbook.org/tdg/5.1/refentry.html This roughly corresponds to a `manpage` role in rST syntax, which produces a `Code` AST node with attributes `.interpreted-text role=manpage` but that does not fit DocBook parser. https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-manpage --- src/Text/Pandoc/Readers/DocBook.hs | 6 +++++- test/docbook-reader.docbook | 3 +++ test/docbook-reader.native | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 6ac1c99f9..a31819ece 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -97,7 +97,7 @@ List of all DocBook tags, with [x] indicating implemented, [x] chapterinfo - Meta-information for a Chapter [ ] citation - An inline bibliographic reference to another published work [ ] citebiblioid - A citation of a bibliographic identifier -[ ] citerefentry - A citation to a reference page +[x] citerefentry - A citation to a reference page [ ] citetitle - The title of a cited work [ ] city - The name of a city in an address [x] classname - The name of a class, in the object-oriented programming sense @@ -1112,6 +1112,10 @@ parseInline (Elem e) = "segmentedlist" -> segmentedList "classname" -> codeWithLang "code" -> codeWithLang + "citerefentry" -> do + let title = maybe mempty strContent $ filterChild (named "refentrytitle") e + let manvolnum = maybe mempty (\el -> "(" <> strContent el <> ")") $ filterChild (named "manvolnum") e + return $ codeWith ("",["citerefentry"],[]) (title <> manvolnum) "filename" -> codeWithLang "envar" -> codeWithLang "literal" -> codeWithLang diff --git a/test/docbook-reader.docbook b/test/docbook-reader.docbook index 51e62942b..f021dc8be 100644 --- a/test/docbook-reader.docbook +++ b/test/docbook-reader.docbook @@ -725,6 +725,9 @@ These should not be escaped: \$ \\ \> \[ \{ More code: Class and Type + + Referencing a man page: nix.conf5 + This is strikeout. diff --git a/test/docbook-reader.native b/test/docbook-reader.native index 1961949d9..b1f5fd085 100644 --- a/test/docbook-reader.native +++ b/test/docbook-reader.native @@ -188,6 +188,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Sof ,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word."] ,Para [Str "This",Space,Str "is",Space,Str "code:",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",SoftBreak,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",SoftBreak,Code ("",[],[]) "",Str "."] ,Para [Str "More",Space,Str "code:",Space,Code ("",[],[]) "Class",Space,Str "and",Space,Code ("",[],[]) "Type"] +,Para [Str "Referencing",Space,Str "a",Space,Str "man",Space,Str "page:",Space,Code ("",["citerefentry"],[]) "nix.conf(5)"] ,Para [Strikeout [Str "This",Space,Str "is",SoftBreak,Emph [Str "strikeout"],Str "."]] ,Para [Str "Superscripts:",Space,Str "a",Superscript [Str "bc"],Str "d",SoftBreak,Str "a",Superscript [Emph [Str "hello"]],SoftBreak,Str "a",Superscript [Str "hello\160there"],Str "."] ,Para [Str "Subscripts:",Space,Str "H",Subscript [Str "2"],Str "O,",Space,Str "H",Subscript [Str "23"],Str "O,",SoftBreak,Str "H",Subscript [Str "many\160of\160them"],Str "O."] -- cgit v1.2.3 From 18270c7a390b5cb101e16790ccb5e50dd9f34a7a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 16 Jul 2021 11:39:02 -0700 Subject: PDF: Fix svgIn path error. We were duplicating the temp directory; this didn't show up on macOS or linux because there we use absolute paths for the temp directory. Closes #7431. --- src/Text/Pandoc/PDF.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index aae3f9806..c4e30af34 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -217,7 +217,7 @@ convertImage opts tmpdir fname = do where pngOut = normalise $ replaceDirectory (replaceExtension fname ".png") tmpdir pdfOut = normalise $ replaceDirectory (replaceExtension fname ".pdf") tmpdir - svgIn = normalise $ tmpdir fname + svgIn = normalise fname mime = getMimeType fname doNothing = return (Right fname) -- cgit v1.2.3 From 493522c5626464fb7a27216ccee381af4a199eef Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 16 Jul 2021 12:04:43 -0700 Subject: LaTeX reader: Support `\cline` in LaTeX tables. Closes #7442. --- src/Text/Pandoc/Readers/LaTeX/Table.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Text/Pandoc/Readers/LaTeX/Table.hs b/src/Text/Pandoc/Readers/LaTeX/Table.hs index 7833da081..f56728fe1 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Table.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Table.hs @@ -41,6 +41,7 @@ hline :: PandocMonad m => LP m () hline = try $ do spaces controlSeq "hline" <|> + (controlSeq "cline" <* braced) <|> -- booktabs rules: controlSeq "toprule" <|> controlSeq "bottomrule" <|> -- cgit v1.2.3 From 46099e79defe662e541b12548200caf29063c1c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 16 Jul 2021 13:10:45 -0700 Subject: DocBook reader: handle images with imageobjectco elements. Closes #7440. --- src/Text/Pandoc/Readers/DocBook.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index a31819ece..c49b82ccf 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -741,9 +741,9 @@ getMediaobject e = do figTitle <- gets dbFigureTitle ident <- gets dbFigureId (imageUrl, attr) <- - case filterChild (named "imageobject") e of - Nothing -> return (mempty, nullAttr) - Just z -> case filterChild (named "imagedata") z of + case filterElements (named "imageobject") e of + [] -> return (mempty, nullAttr) + (z:_) -> case filterChild (named "imagedata") z of Nothing -> return (mempty, nullAttr) Just i -> let atVal a = attrValue a i w = case atVal "width" of -- cgit v1.2.3