git.iko.soy/typeable/pandoc
on 3221537 help sign in
31 files changed+2513−1854
since splitsnapshots
unifiedsplit
.circleci/config.yml+2−2 .github/workflows/nightly.yml+7 AUTHORS.md+4 MANUAL.txt+43−10 Makefile+5−2 README.md+6−6 cabal.project+1−1 changelog.md+189−99 doc/libraries.md+59 linux/make_artifacts.sh+5−3 man/pandoc.1+54−13 pandoc.cabal+12−7 src/Text/Pandoc/App.hs+21−8 src/Text/Pandoc/App/CommandLineOptions.hs+28−6 src/Text/Pandoc/App/Opt.hs+143−56 src/Text/Pandoc/Logging.hs+6 src/Text/Pandoc/Readers/LaTeX.hs+108−396 src/Text/Pandoc/Readers/LaTeX/Accent.hs−78 src/Text/Pandoc/Readers/LaTeX/Inline.hs+397 src/Text/Pandoc/Readers/LaTeX/Lang.hs+60−2 src/Text/Pandoc/Readers/Markdown.hs+1−1 src/Text/Pandoc/Shared.hs+12−8 src/Text/Pandoc/Writers/LaTeX.hs+15−551 src/Text/Pandoc/Writers/LaTeX/Citation.hs+181 src/Text/Pandoc/Writers/LaTeX/Lang.hs+191 src/Text/Pandoc/Writers/LaTeX/Util.hs+274 src/Text/Pandoc/Writers/Markdown.hs+6−591 src/Text/Pandoc/Writers/Markdown/Inline.hs+601 src/Text/Pandoc/Writers/Markdown/Types.hs+81 stack.yaml+1−10 trypandoc/trypandoc.hs−4
.circleci/config.yml+2−2
@@ -20,10 +20,10 @@ jobs:
2020 command: stack --no-terminal setup
2121 - run:
2222 name: Run tests
23− command: stack --no-terminal test --ghc-options=-split-sections
23+ command: stack --no-terminal test
2424 - run:
2525 name: Install executable
26− command: stack --no-terminal install --ghc-options=-split-sections
26+ command: stack --no-terminal install
2727 - run:
2828 name: Create artifacts
2929 command: |
.github/workflows/nightly.yml+7
@@ -10,6 +10,13 @@ jobs:
1010 runs-on: ubuntu-18.04
1111 steps:
1212 - uses: actions/checkout@v2
13+
14+ - name: Install recent cabal/ghc
15+ uses: haskell/actions/setup@v1
16+ with:
17+ ghc-version: '8.10.3'
18+ cabal-version: '3.2'
19+
1320 - name: Install dependencies
1421 run: |
1522 ghc --version
AUTHORS.md+4
@@ -163,6 +163,8 @@
163163- Leif Metcalf
164164- Leonard Rosenthol
165165- Lila
166+- Loïc Grobol
167+- Lorenzo
166168- Lucas Escot
167169- Luke Plant
168170- Marc Schreiber
@@ -196,10 +198,12 @@
196198- Nathan Gass
197199- Neil Mayhew
198200- Nick Bart
201+- Nick Berendsen
199202- Nick Fleisher
200203- Nicolas Kaiser
201204- Nikolay Yakimov
202205- Nils Carlson
206+- Nixon Enraght-Moony
203207- Nokome Bentley
204208- Ole Martin Ruud
205209- Oliver Matthews
MANUAL.txt+43−10
@@ -1,7 +1,7 @@
11---
22title: Pandoc User's Guide
33author: John MacFarlane
4−date: January 21, 2021
4+date: March 4, 2021
55---
66
77# Synopsis
@@ -361,15 +361,15 @@ header when requesting a document from a URL:
361361 will be used. On \*nix and macOS systems this will be the `pandoc`
362362 subdirectory of the XDG data directory (by default,
363363 `$HOME/.local/share`, overridable by setting the `XDG_DATA_HOME`
364− environment variable). If that directory does not exist,
365− `$HOME/.pandoc` will be used (for backwards compatibility).
366− In Windows the default user data directory is
364+ environment variable). If that directory does not exist and
365+ `$HOME/.pandoc` exists, it will be used (for backwards compatibility).
366+ On Windows the default user data directory is
367367 `C:\Users\USERNAME\AppData\Roaming\pandoc`.
368368 You can find the default user data directory on your system by
369369 looking at the output of `pandoc --version`.
370− A `reference.odt`, `reference.docx`, `epub.css`, `templates`,
371− `slidy`, `slideous`, or `s5` directory
372− placed in this directory will override pandoc's normal defaults.
370+ Data files placed in this directory (for example, `reference.odt`,
371+ `reference.docx`, `epub.css`, `templates`) will override
372+ pandoc's normal defaults.
373373
374374`-d` *FILE*, `--defaults=`*FILE*
375375
@@ -691,9 +691,9 @@ header when requesting a document from a URL:
691691 system default, use
692692 `pandoc --print-default-data-file=abbreviations`. The only
693693 use pandoc makes of this list is in the Markdown reader.
694− Strings ending in a period that are found in this list will
695− be followed by a nonbreaking space, so that the period will
696− not produce sentence-ending space in formats like LaTeX.
694+ Strings found in this list will be followed by a nonbreaking
695+ space, and the period will not produce sentence-ending space
696+ in formats like LaTeX. The strings may not contain spaces.
697697
698698[`pandocfilters`]: https://github.com/jgm/pandocfilters
699699[PHP]: https://github.com/vinai/pandocfilters-php
@@ -891,6 +891,11 @@ header when requesting a document from a URL:
891891 will not be searched. For example:
892892 `--resource-path=.:test` will search the working directory
893893 and the `test` subdirectory, in that order.
894+ This option can be used repeatedly. Search path components
895+ that come later on the command line will be searched before
896+ those that come earlier, so
897+ `--resource-path foo:bar --resource-path baz:bim` is
898+ equivalent to `--resource-path baz:bim:foo:bar`.
894899
895900`--request-header=`*NAME*`:`*VAL*
896901
@@ -1672,6 +1677,34 @@ one line:
16721677verbosity: INFO
16731678```
16741679
1680+In fields that expect a file path (or list of file paths), the
1681+following syntax may be used to interpolate environment variables:
1682+
1683+``` yaml
1684+csl: ${HOME}/mycsldir/special.csl
1685+```
1686+
1687+`${USERDATA}` may also be used; this will always resolve to the
1688+user data directory that is current when the defaults file is
1689+parsed, regardless of the setting of the environment
1690+variable `USERDATA`.
1691+
1692+`${.}` will resolve to the directory containing the default
1693+file itself. This allows you to refer to resources contained
1694+in that directory:
1695+
1696+``` yaml
1697+epub-cover-image: ${.}/cover.jpg
1698+epub-metadata: ${.}/meta.xml
1699+resource-path:
1700+- . # the working directory from which pandoc is run
1701+- ${.}/images # the images subdirectory of the directory
1702+ # containing this defaults file
1703+```
1704+
1705+This environment variable interpolation syntax *only* works in
1706+fields that expect file paths.
1707+
16751708Default files can be placed in the `defaults` subdirectory of
16761709the user data directory and used from any directory. For
16771710example, one could create a file specifying defaults for writing
Makefile+5−2
@@ -2,9 +2,10 @@ version?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}')
22pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1)
33SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$")
44BRANCH?=master
5+DOCKERIMAGE=registry.gitlab.b-data.ch/ghc/ghc4pandoc:8.10.4
56COMMIT=$(shell git rev-parse --short HEAD)
67TIMESTAMP=$(shell date "+%Y%m%d_%H%M")
7−LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv))
8+LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv 2>/dev/null))
89ifeq ($(LATESTBENCH),)
910BASELINE=
1011else
@@ -83,7 +84,9 @@ debpkg: man/pandoc.1
8384 -v `pwd`/linux/artifacts:/artifacts \
8485 -e REVISION=$(REVISION) \
8586 -w /mnt \
86− utdemir/ghc-musl:v12-libgmp-ghc8101 bash \
87+ --memory=0 \
88+ $(DOCKERIMAGE) \
89+ bash \
8790 /mnt/linux/make_artifacts.sh
8891
8992man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after
README.md+6−6
@@ -8,10 +8,10 @@ pandoc --lua-filter tools/update-readme.lua README.template -o README.md
88[![github
99release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases)
1010[![hackage
11−release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](http://hackage.haskell.org/package/pandoc)
12−[![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](http://brewformulas.org/Pandoc)
11+release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](https://hackage.haskell.org/package/pandoc)
12+[![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](https://formulae.brew.sh/formula/pandoc)
1313[![stackage LTS
14−package](http://stackage.org/package/pandoc/badge/lts)](http://stackage.org/lts/package/pandoc)
14+package](https://stackage.org/package/pandoc/badge/lts)](https://www.stackage.org/lts/package/pandoc-types)
1515[![CI
1616tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions)
1717[![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html)
@@ -20,7 +20,7 @@ groups](https://img.shields.io/badge/pandoc-discuss-red.svg?style=social)](https
2020
2121## The universal markup converter
2222
23−Pandoc is a [Haskell](http://haskell.org) library for converting from
23+Pandoc is a [Haskell](https://haskell.org) library for converting from
2424one markup format to another, and a command-line tool that uses this
2525library. It can convert *from*
2626
@@ -175,7 +175,7 @@ It can convert *to*
175175 and JavaScript slide show)
176176- `slidy` ([Slidy](https://www.w3.org/Talks/Tools/Slidy2/) HTML and
177177 JavaScript slide show)
178−- `dzslides` ([DZSlides](http://paulrouget.com/dzslides/) HTML5 +
178+- `dzslides` ([DZSlides](https://paulrouget.com/dzslides/) HTML5 +
179179 JavaScript slide show),
180180- `revealjs` ([reveal.js](https://revealjs.com/) HTML5 + JavaScript
181181 slide show)
@@ -239,6 +239,6 @@ opening a new issue.
239239## License
240240
241241© 2006-2021 John MacFarlane (jgm@berkeley.edu). Released under the
242−[GPL](http://www.gnu.org/copyleft/gpl.html "GNU General Public License"),
242+[GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License"),
243243version 2 or greater. This software carries no warranty of any kind.
244244(See COPYRIGHT for full copyright and warranty notices.)
cabal.project+1−1
@@ -3,7 +3,7 @@ packages: pandoc.cabal
33tests: True
44
55package pandoc
6− flags: +embed_data_files -trypandoc
6+ flags: +embed_data_files
77 ghc-options: -j +RTS -A256m -RTS
88
99-- source-repository-package
changelog.md+189−99
@@ -2,6 +2,11 @@
22
33## pandoc 2.12 (UNRELEASED -- PROVISIONAL)
44
5+ * When downloading content from URL arguments, be sensitive to
6+ the character encoding (#5600). We can properly handle UTF-8 and latin1
7+ (ISO-8859-1); for others we raise an error. Fall back to latin1 if
8+ no charset is given in the mime type and UTF-8 decoding fails.
9+
510 * Add new unexported module Text.Pandoc.XML.Light, as well
611 as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc,
712 Text.Pandoc.XML.Light.Output. (Closes #6001, #6565, #7091).
@@ -34,75 +39,6 @@
3439 these have been added. And the docx golden tests have been updated,
3540 because the new parser does not preserve the order of attributes.
3641
37− * Text.Pandoc.App
38−
39− + Add `parseOptionsFromArgs` [API change, new exported function].
40−
41− * Text.Pandoc.Citeproc.BibTeX
42−
43− + `Text.Pandoc.Citeproc.writeBibTeXString` now returns
44− `Doc Text` instead of `Text` (#7068).
45− + Correctly handle `pages` (= `page` in CSL) (#7067).
46− + Correctly handle BibLaTeX `langid` (= `language` in CSL, #7067).
47− + In BibTeX output, protect foreign titles since there's no language
48− field (#7067).
49− + Clean up BibTeX parsing (#7049). Previously there was a messy code
50− path that gave strange results in some cases, not passing through raw
51− tex but trying to extract a string content. This was an artefact of
52− trying to handle some special bibtex-specific commands in the BibTeX
53− reader. Now we just handle these in the LaTeX reader and simplify
54− parsing in the BibTeX reader. This does mean that more raw tex will
55− be passed through (and currently this is not sensitive to the
56− `raw_tex` extension; this should be fixed).
57−
58− * Text.Pandoc.Citeproc.MetaValue
59−
60− + Correctly parse "raw" date value in markdown references metadata.
61− (See jgm/citeproc#53.)
62−
63− * Text.Pandoc.Class
64−
65− + Add `getTimestamp` [API change]. This attempts to read the
66− `SOURCE_DATE_EPOCH` environment variable and parse a UTC time
67− from it (treating it as a unix date stamp, see
68− https://reproducible-builds.org/specs/source-date-epoch/). If the
69− variable is not set or can't be parsed as a unix date stamp, then the
70− function returns the current date.
71−
72− * Text.Pandoc.Error
73−
74− + Remove unused variables (Albert Krewinkel)
75− + Export `renderError` [API change].
76− + Refactor `handleError` to use `renderError`. This allows us render
77− error messages without exiting.
78−
79− * Text.Pandoc.Extensions
80−
81− + `Ext_task_lists` is now supported by org (and turned
82− on by default) (Albert Krewinkel, #6336).
83− + Remove `Ext_fenced_code_attributes` from allowed commonmark attributes
84− (#7097). This attribute was listed as allowed, but it didn't actually
85− do anything. Use `attributes` for code attributes and more.
86−
87− * Lua subsystem:
88−
89− + Always load built-in Lua scripts from default data-dir (Albert
90− Krewinkel). The Lua modules `pandoc` and `pandoc.List` are now always
91− loaded from the system's default data directory. Loading from a
92− different directory by overriding the default path, e.g. via
93− `--data-dir`, is no longer supported to avoid unexpected behavior
94− and to address security concerns.
95− + Add module "pandoc.path" (Albert Krewinkel, #6001, #6565).
96− The module allows to work with file paths in a convenient and
97− platform-independent manner.
98−
99− * Text.Pandoc.PDF
100−
101− + Disable `smart` extension when building PDF via LaTeX.
102− This is to prevent accidental creation of ligatures like
103− `` ?` `` and `` !` `` (especially in languages with quotations like
104− German), and similar ligature issues. (See jgm/citeproc#54.)
105−
10642 * DocBook reader:
10743
10844 + Avoid expensive tree normalization step, as it is not necessary
@@ -137,6 +73,11 @@
13773
13874 * LaTeX reader:
13975
76+ + Don't export `tokenize`, `untokenize` [API change]. These are internal
77+ implementation details, which were only exported for testing.
78+ They don't belong in the public API.
79+ + Improved efficiency of the parser. With these changes the reader
80+ is almost twice as fast as in the last release in our benchmarks.
14081 + Code cleanup, removing some unnecessary things.
14182 + Rewrite `withRaw` so it doesn't rely on fragile assumptions
14283 about token positions (which break when macros are expanded)
@@ -156,11 +97,19 @@
15697 it doesn't change anything.
15798 + Improve `braced'`. Remove the parameter, have it parse the
15899 opening brace, and make it more efficient.
100+ + Factor out pieces of the LaTeX reader to make the module smaller.
101+ This reduces memory demands when compiling. Created
102+ Text.Pandoc.Readers.{LaTeX,Math,Citation,Table,Macro,Inline}.
103+ Changed Text.Pandoc.Readers.LaTeX.SIunitx to export a command map
104+ instead of individual commands.
105+
106+ * Make Text.Pandoc.Readers.LaTeX.Types an unexported module [API change].
159107
160108 * Markdown reader:
161109
162110 + Improved handling of mmd link attributes in references (#7080).
163111 Previously they only worked for links that had titles.
112+ + Improved efficiency of the parser (benchmarks show a 15% speedup).
164113
165114 * OPML reader:
166115
@@ -218,7 +167,7 @@
218167 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
219168 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds.
220169
221− * Text.Pandoc.Writers.EPUB
170+ * EPUB writer:
222171
223172 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
224173 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093).
@@ -233,6 +182,10 @@
233182 contains an ampersand or another character with a special meaning
234183 in XML.
235184
185+ * Jira writer:
186+
187+ + Use Span identifiers as anchors (Albert Krewinkel,
188+
236189 * LaTeX writer:
237190
238191 + Adjust hypertargets to beginnings of paragraphs (#7078).
@@ -241,12 +194,18 @@
241194 This makes a particular difference for links to citations using
242195 `--citeproc` and `link-citations: true`.
243196 + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047).
197+ + Use function instead of map for accent lookup (should be
198+ more efficient).
199+ + Split the module to make it easier to compile on low-memory
200+ systems: added Text.Pandoc.Writers.LaTeX.{Util,Citation,Lang}.
244201
245202 * Markdown writer:
246203
247204 + Handle math right before digit. We insert an HTML comment to
248205 avoid a `$` right before a digit, which pandoc will not recognize
249206 as a math delimiter.
207+ + Split the module to make it easier to compile on low-memory
208+ systems: added Text.Pandoc.Writers.Markdown.{Types,Inline}.
250209
251210 * ODT writer:
252211
@@ -279,40 +238,153 @@
279238 `--include-in-header`, `--include-before-body`, `--include-after-body`)
280239 may be used.
281240
282− * LaTeX template: Update to iftex package (#7073) (Andrew Dunning)
241+ * LaTeX template:
242+
243+ + Update to iftex package (#7073) (Andrew Dunning)
244+ + Wrap url colours in braces (#7121) (Loïc Grobol).
283245
284246 * revealjs template: Add 'center' option for vertical slide centering.
285247 (maurerle, #7104).
286248
287249 * Text.Pandoc.XML: Improve efficiency of `fromEntities`.
288250
289− * Test suite: a more robust way of testing the executable.
290− Many of our tests require running the pandoc executable. This is
291− problematic for a few different reasons. First, cabal-install will
292− sometimes run the test suite after building the library but before
293− building the executable, which means the executable isn't in place for
294− the tests. One can work around that by first building, then building and
295− running the tests, but that's fragile. Second, we have to find the
296− executable. So far, we've done that using a function `findPandoc` that
297− attempts to locate it relative to the test executable (which can be
298− located using findExecutablePath). But the logic here is delicate and
299− work with every combination of options. To solve both problems, we add
300− an `--emulate` option to the `test-pandoc` executable. When `--emulate`
301− occurs as the first argument passed to `test-pandoc`, the program simply
302− emulates the regular pandoc executable, using the rest of the arguments
303− (after `--emulate`). Thus, `test-pandoc --emulate -f markdown -t latex`
304− is just like `pandoc -f markdown -t latex`.
305− Since all the work is done by library functions, implementing this
306− emulation just takes a couple lines of code and should be entirely
307− reliable. With this change, we can test the pandoc executable by running
308− the test program itself (locatable using `findExecutablePath`) with the
309− `--emulate` option. This removes the need for the fragile `findPandoc`
310− step, and it means we can run our integration tests even when we're just
311− building the library, not the executable. [Note: part of this change
312− involved simplifying some complex handling to set environment variables
313− for dynamic library paths. I have tested a build with
314− `--enable-dynamic-executable`, and it works, but further testing may be
315− needed.]
251+ * Text.Pandoc.MIME
252+
253+ + Add exported function `getCharset` [API change].
254+
255+ * Text.Pandoc.UTF8: change IO functions to return Text, not String
256+ [API change]. This affects `readFile`, `getContents`, `writeFileWith`,
257+ `writeFile`, `putStrWith`, `putStr`, `putStrLnWith`, `putStrLn`.
258+ `hPutStrWith`, `hPutStr`, `hPutStrLnWith`, `hPutStrLn`, `hGetContents`.
259+ This avoids the need to uselessly create a linked list of characters
260+ when emiting output.
261+
262+ * Text.Pandoc.App
263+
264+ + Add `parseOptionsFromArgs` [API change, new exported function].
265+
266+ * Text.Pandoc.Citeproc.BibTeX
267+
268+ + `Text.Pandoc.Citeproc.writeBibTeXString` now returns
269+ `Doc Text` instead of `Text` (#7068).
270+ + Correctly handle `pages` (= `page` in CSL) (#7067).
271+ + Correctly handle BibLaTeX `langid` (= `language` in CSL, #7067).
272+ + In BibTeX output, protect foreign titles since there's no language
273+ field (#7067).
274+ + Clean up BibTeX parsing (#7049). Previously there was a messy code
275+ path that gave strange results in some cases, not passing through raw
276+ tex but trying to extract a string content. This was an artefact of
277+ trying to handle some special bibtex-specific commands in the BibTeX
278+ reader. Now we just handle these in the LaTeX reader and simplify
279+ parsing in the BibTeX reader. This does mean that more raw tex will
280+ be passed through (and currently this is not sensitive to the
281+ `raw_tex` extension; this should be fixed).
282+
283+ * Text.Pandoc.Citeproc.MetaValue
284+
285+ + Correctly parse "raw" date value in markdown references metadata.
286+ (See jgm/citeproc#53.)
287+
288+ * Text.Pandoc.Citeproc
289+
290+ + Use https URLs for links (Salim B, #7122).
291+
292+ * Text.Pandoc.Class
293+
294+ + Add `getTimestamp` [API change]. This attempts to read the
295+ `SOURCE_DATE_EPOCH` environment variable and parse a UTC time
296+ from it (treating it as a unix date stamp, see
297+ https://reproducible-builds.org/specs/source-date-epoch/). If the
298+ variable is not set or can't be parsed as a unix date stamp, then the
299+ function returns the current date.
300+
301+ * Text.Pandoc.Error
302+
303+ + Add `PandocUnsupportedCharsetError` constructor for
304+ `PandocError` [API change].
305+ + Export `renderError` [API change].
306+ + Refactor `handleError` to use `renderError`. This allows us render
307+ error messages without exiting.
308+ + Remove unused variables (Albert Krewinkel)
309+
310+ * Text.Pandoc.Extensions
311+
312+ + `Ext_task_lists` is now supported by org (and turned
313+ on by default) (Albert Krewinkel, #6336).
314+ + Remove `Ext_fenced_code_attributes` from allowed commonmark attributes
315+ (#7097). This attribute was listed as allowed, but it didn't actually
316+ do anything. Use `attributes` for code attributes and more.
317+
318+ * Lua subsystem:
319+
320+ + Always load built-in Lua scripts from default data-dir (Albert
321+ Krewinkel). The Lua modules `pandoc` and `pandoc.List` are now always
322+ loaded from the system's default data directory. Loading from a
323+ different directory by overriding the default path, e.g. via
324+ `--data-dir`, is no longer supported to avoid unexpected behavior
325+ and to address security concerns.
326+ + Add module "pandoc.path" (Albert Krewinkel, #6001, #6565).
327+ The module allows to work with file paths in a convenient and
328+ platform-independent manner.
329+ + Use strict evaluation when retrieving AST value from the stack
330+ (Albert Krewinkel, #6674).
331+
332+ * Text.Pandoc.PDF
333+
334+ + Disable `smart` extension when building PDF via LaTeX.
335+ This is to prevent accidental creation of ligatures like
336+ `` ?` `` and `` !` `` (especially in languages with quotations like
337+ German), and similar ligature issues. (See jgm/citeproc#54.)
338+
339+ * Text.Pandoc.CSV:
340+
341+ + Fix parsing of unquoted values (#7112). Previously we didn't allow
342+ unescaped quotes in unquoted values, but they are allowed
343+ in CSV.
344+
345+ * Test suite:
346+
347+ + Use a more robust method for testing the executable. Many
348+ of our tests require running the pandoc executable. This
349+ is problematic for a few different reasons. First,
350+ cabal-install will sometimes run the test suite after
351+ building the library but before building the executable,
352+ which means the executable isn't in place for the tests.
353+ One can work around that by first building, then building
354+ and running the tests, but that's fragile. Second, we
355+ have to find the executable. So far, we've done that using
356+ a function `findPandoc` that attempts to locate it
357+ relative to the test executable (which can be located
358+ using findExecutablePath). But the logic here is delicate
359+ and work with every combination of options. To solve both
360+ problems, we add an `--emulate` option to the
361+ `test-pandoc` executable. When `--emulate` occurs as the
362+ first argument passed to `test-pandoc`, the program simply
363+ emulates the regular pandoc executable, using the rest of
364+ the arguments (after `--emulate`). Thus, `test-pandoc
365+ --emulate -f markdown -t latex` is just like `pandoc -f
366+ markdown -t latex`. Since all the work is done by library
367+ functions, implementing this emulation just takes a couple
368+ lines of code and should be entirely reliable. With this
369+ change, we can test the pandoc executable by running the
370+ test program itself (locatable using `findExecutablePath`)
371+ with the `--emulate` option. This removes the need for the
372+ fragile `findPandoc` step, and it means we can run our
373+ integration tests even when we're just building the
374+ library, not the executable. [Note: part of this change
375+ involved simplifying some complex handling to set
376+ environment variables for dynamic library paths. I have
377+ tested a build with `--enable-dynamic-executable`, and it
378+ works, but further testing may be needed.]
379+ + Print accurate location if a test fails (Albert
380+ Krewinkel). Ensures that tasty-hunit reports the location
381+ of the failing test instead of the location of the helper
382+ `test` function.
383+
384+ * Documentation: Update URLs and use `https` where possible (#7122,
385+ Salim B).
386+
387+ * Add `doc/libraries.md`, a description of libraries that support pandoc.
316388
317389 * MANUAL.txt
318390
@@ -328,6 +400,9 @@
328400 * Makefile: in `make bench`, create CSV files for comparison and compare
329401 against previous benchmark run. Add timestamp to CSV filenames.
330402
403+ * cabal.project: don't explicitly set -trypandoc.
404+ If we do, this can't be overridden on the cabal command line.
405+
331406 * doc/lua-filters.md: improve documentation for
332407 `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`,
333408 `directory`, `normalize` (Albert Krewinkel).
@@ -336,9 +411,17 @@
336411
337412 * Require jira-wiki-markup 1.3.3 (Albert Krewinkel)
338413
339− * Require citeproc 0.3.0.7, which correctly titlecases when titles
414+ * Require citeproc 0.3.0.8, which correctly titlecases when titles
340415 contain non-ASCII characters.
341416
417+ * Use skylighting 0.10.4. This version of skylighting uses xml-conduit
418+ rather than hxt. This speeds up parsing of XML syntax definitions
419+ fourfold, and removes four packages from pandoc's dependency graph:
420+ hxt-charproperties, hxt-unicode, hxt-regex-xmlschema, hxt.
421+
422+ * Add script `tools/parseTimings.pl` to help pin down which
423+ modules take the most time and memory to compile.
424+
342425 * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert
343426 Krewinkel)
344427
@@ -353,6 +436,13 @@
353436 + Clean up benchmark code.
354437 + Allow specifying patterns using `-p blah'.
355438
439+ * trypandoc: add 2 second timeout.
440+
441+ * Use `-split-sections` in creating linux release binary.
442+ This reduces executable size significantly (by about 30%).
443+
444+ * Remove `weigh-pandoc`. It's not really useful any more, now that our
445+ regular benchmarks include data on allocation.
356446
357447
358448## pandoc 2.11.4 (2021-01-22)
doc/libraries.mdnew file+59
@@ -0,0 +1,59 @@
1+---
2+title: The pandoc family of libraries
3+author: John MacFarlane
4+---
5+
6+The following Haskell libraries have been developed to support
7+pandoc:
8+
9+[skylighting-core] and [skylighting]
10+: Syntax highlighting engine supporting over 140 languages.
11+
12+[citeproc]
13+: Citation processing using CSL stylesheets.
14+
15+[texmath]
16+: Conversion of math between tex, Word equation, MathML, and GNU eqn.
17+
18+[doclayout]
19+: Combinators for laying out a textual document, with support
20+ for line wrapping, tabular layout, and more.
21+
22+[doctemplates]
23+: Supports pandoc's templates.
24+
25+[commonmark], [commonmark-extensions], and [commonmark-pandoc]
26+: Efficient, standards-compliant parser for commonmark and extensions.
27+
28+[ipynb]
29+: Representation of Jupyter notebooks and conversion to and
30+ from JSON.
31+
32+[zip-archive]
33+: A pure zip file creator and extractor, used by pandoc for
34+ docx, ODT, and EPUB.
35+
36+[rfc5051]
37+: Simple unicode collation (used for citation sorting).
38+
39+[emojis]
40+: Conversion between emoji characters and aliases.
41+
42+[jira-wiki-markup]
43+: Support for parsing Jira wiki syntax.
44+
45+[skylighting]: https://hackage.haskell.org/package/skylighting
46+[skylighting-core]: https://hackage.haskell.org/package/skylighting-core
47+[citeproc]: https://hackage.haskell.org/package/citeproc
48+[texmath]: https://hackage.haskell.org/package/texmath
49+[doclayout]: https://hackage.haskell.org/package/doclayout
50+[doctemplates]: https://hackage.haskell.org/package/doctemplates
51+[commonmark]: https://hackage.haskell.org/package/commonmark
52+[commonmark-extensions]: https://hackage.haskell.org/package/commonmark-extensions
53+[commonmark-pandoc]: https://hackage.haskell.org/package/commonmark-pandoc
54+[ipynb]: https://hackage.haskell.org/package/ipynb
55+[zip-archive]: https://hackage.haskell.org/package/zip-archive
56+[rfc5051]: https://hackage.haskell.org/package/rfc5051
57+[emojis]: https://hackage.haskell.org/package/emojis
58+[jira-wiki-markup]: https://hackage.haskell.org/package/jira-wiki-markup
59+
linux/make_artifacts.sh+5−3
@@ -2,9 +2,11 @@ set -e
22
33MACHINE=$(uname -m)
44case "$MACHINE" in
5− x86_64) ARCHITECTURE=amd64;;
6− i686) ARCHITECTURE=i386;;
7− i386) ARCHITECTURE=i386;;
5+ x86_64) ARCHITECTURE=amd64;;
6+ i686) ARCHITECTURE=i386;;
7+ i386) ARCHITECTURE=i386;;
8+ aarch64) ARCHITECTURE=arm64;;
9+ *) ARCHITECTURE=unknown;;
810esac
911
1012ARTIFACTS="${ARTIFACTS:-/artifacts}"
man/pandoc.1+54−13
@@ -1,7 +1,7 @@
11'\" t
22.\" Automatically generated by Pandoc 2.11.4
33.\"
4−.TH "Pandoc User\[cq]s Guide" "" "January 21, 2021" "pandoc 2.11.4" ""
4+.TH "Pandoc User\[cq]s Guide" "" "March 4, 2021" "pandoc 2.12" ""
55.hy
66.SH NAME
77pandoc - general markup converter
@@ -172,13 +172,12 @@ You can then test it with \f[C]pdflatex test.tex\f[R].
172172When using LaTeX, the following packages need to be available (they are
173173included with all recent versions of TeX Live): \f[C]amsfonts\f[R],
174174\f[C]amsmath\f[R], \f[C]lm\f[R], \f[C]unicode-math\f[R],
175−\f[C]ifxetex\f[R], \f[C]ifluatex\f[R], \f[C]listings\f[R] (if the
176−\f[C]--listings\f[R] option is used), \f[C]fancyvrb\f[R],
177−\f[C]longtable\f[R], \f[C]booktabs\f[R], \f[C]graphicx\f[R] (if the
178−document contains images), \f[C]hyperref\f[R], \f[C]xcolor\f[R],
179−\f[C]ulem\f[R], \f[C]geometry\f[R] (with the \f[C]geometry\f[R] variable
180−set), \f[C]setspace\f[R] (with \f[C]linestretch\f[R]), and
181−\f[C]babel\f[R] (with \f[C]lang\f[R]).
175+\f[C]iftex\f[R], \f[C]listings\f[R] (if the \f[C]--listings\f[R] option
176+is used), \f[C]fancyvrb\f[R], \f[C]longtable\f[R], \f[C]booktabs\f[R],
177+\f[C]graphicx\f[R] (if the document contains images),
178+\f[C]hyperref\f[R], \f[C]xcolor\f[R], \f[C]ulem\f[R], \f[C]geometry\f[R]
179+(with the \f[C]geometry\f[R] variable set), \f[C]setspace\f[R] (with
180+\f[C]linestretch\f[R]), and \f[C]babel\f[R] (with \f[C]lang\f[R]).
182181The use of \f[C]xelatex\f[R] or \f[C]lualatex\f[R] as the PDF engine
183182requires \f[C]fontspec\f[R].
184183\f[C]lualatex\f[R] uses \f[C]selnolig\f[R].
@@ -1614,6 +1613,11 @@ T}@T{
16141613PandocPDFError
16151614T}
16161615T{
1616+44
1617+T}@T{
1618+PandocXMLError
1619+T}
1620+T{
1617162147
16181622T}@T{
16191623PandocPDFProgramNotFoundError
@@ -1674,6 +1678,11 @@ T}@T{
16741678PandocIpynbDecodingError
16751679T}
16761680T{
1681+94
1682+T}@T{
1683+PandocUnsupportedCharsetError
1684+T}
1685+T{
1677168697
16781687T}@T{
16791688PandocCouldNotFindDataFileError
@@ -1773,8 +1782,11 @@ data-dir:
17731782verbosity: INFO
17741783log-file: log.json
17751784
1776−# citeproc, natbib, or biblatex
1785+# citeproc, natbib, or biblatex. This only affects LaTeX
1786+# output. If you want to use citeproc to format citations,
1787+# you should also set \[aq]citeproc: true\[aq] (see above).
17771788cite-method: citeproc
1789+
17781790# part, chapter, section, or default:
17791791top-level-division: chapter
17801792abbreviations:
@@ -2134,9 +2146,9 @@ ${ styles.html() }
21342146\f[R]
21352147.fi
21362148.PP
2137−(If a partial is not found in the directory of the template, it will
2138−also be sought in the \f[C]templates\f[R] subdirectory of the user data
2139−directory.)
2149+(If a partial is not found in the directory of the template and the
2150+template path is given as a relative path, it will also be sought in the
2151+\f[C]templates\f[R] subdirectory of the user data directory.)
21402152.PP
21412153Partials may optionally be applied to variables using a colon:
21422154.IP
@@ -3928,6 +3940,9 @@ begin with a space.
39283940\f[R]
39293941.fi
39303942.PP
3943+Inline formatting (such as emphasis) is allowed in the content, but not
3944+block-level formatting (such as block quotes or lists).
3945+.PP
39313946This syntax is borrowed from reStructuredText.
39323947.SS Lists
39333948.SS Bullet lists
@@ -5048,7 +5063,9 @@ delimiters.
50485063.PP
50495064For display math, use \f[C]$$\f[R] delimiters.
50505065(In this case, the delimiters may be separated from the formula by
5051−whitespace.)
5066+whitespace.
5067+However, there can be no blank lines betwen the opening and closing
5068+\f[C]$$\f[R] delimiters.)
50525069.PP
50535070TeX math will be printed in all output formats.
50545071How it is rendered depends on the output format:
@@ -6938,6 +6955,16 @@ A string value.
69386955\f[B]\f[CB]rights\f[B]\f[R]
69396956A string value.
69406957.TP
6958+\f[B]\f[CB]belongs-to-collection\f[B]\f[R]
6959+A string value.
6960+identifies the name of a collection to which the EPUB Publication
6961+belongs.
6962+.TP
6963+\f[B]\f[CB]group-position\f[B]\f[R]
6964+The \f[C]group-position\f[R] field indicates the numeric position in
6965+which the EPUB Publication belongs relative to other works belonging to
6966+the same \f[C]belongs-to-collection\f[R] field.
6967+.TP
69416968\f[B]\f[CB]cover-image\f[B]\f[R]
69426969A string value (path to cover image).
69436970.TP
@@ -7453,6 +7480,20 @@ need to specify a template manually using \f[C]--template\f[R] or add a
74537480new default template with the name
74547481\f[C]default.NAME_OF_CUSTOM_WRITER.lua\f[R] to the \f[C]templates\f[R]
74557482subdirectory of your user data directory (see Templates).
7483+.SH REPRODUCIBLE BUILDS
7484+.PP
7485+Some of the document formats pandoc targets (such as EPUB, docx, and
7486+ODT) include build timestamps in the generated document.
7487+That means that the files generated on successive builds will differ,
7488+even if the source does not.
7489+To avoid this, set the \f[C]SOURCE_DATE_EPOCH\f[R] environment variable,
7490+and the timestamp will be taken from it instead of the current time.
7491+\f[C]SOURCE_DATE_EPOCH\f[R] should contain an integer unix timestamp
7492+(specifying the number of second since midnight UTC January 1, 1970).
7493+.PP
7494+Some document formats also include a unique identifier.
7495+For EPUB, this can be set explicitly by setting the \f[C]identifier\f[R]
7496+metadata field (see EPUB Metadata, above).
74567497.SH A NOTE ON SECURITY
74577498.PP
74587499If you use pandoc to convert user-contributed content in a web
pandoc.cabal+12−7
@@ -1,6 +1,6 @@
11cabal-version: 2.2
22name: pandoc
3−version: 2.11.4
3+version: 2.12
44build-type: Simple
55license: GPL-2.0-or-later
66license-file: COPYING.md
@@ -626,15 +626,15 @@ library
626626 Text.Pandoc.Readers.HTML.Table,
627627 Text.Pandoc.Readers.HTML.TagCategories,
628628 Text.Pandoc.Readers.HTML.Types,
629− Text.Pandoc.Readers.LaTeX.Types,
630− Text.Pandoc.Readers.LaTeX.Parsing,
631− Text.Pandoc.Readers.LaTeX.Lang,
632− Text.Pandoc.Readers.LaTeX.SIunitx,
633− Text.Pandoc.Readers.LaTeX.Accent,
629+ Text.Pandoc.Readers.LaTeX.Inline,
634630 Text.Pandoc.Readers.LaTeX.Citation,
631+ Text.Pandoc.Readers.LaTeX.Lang,
632+ Text.Pandoc.Readers.LaTeX.Macro,
635633 Text.Pandoc.Readers.LaTeX.Math,
634+ Text.Pandoc.Readers.LaTeX.Parsing,
635+ Text.Pandoc.Readers.LaTeX.SIunitx,
636636 Text.Pandoc.Readers.LaTeX.Table,
637− Text.Pandoc.Readers.LaTeX.Macro,
637+ Text.Pandoc.Readers.LaTeX.Types,
638638 Text.Pandoc.Readers.Odt.Base,
639639 Text.Pandoc.Readers.Odt.Namespaces,
640640 Text.Pandoc.Readers.Odt.StyleReader,
@@ -664,7 +664,12 @@ library
664664 Text.Pandoc.Writers.LaTeX.Caption,
665665 Text.Pandoc.Writers.LaTeX.Notes,
666666 Text.Pandoc.Writers.LaTeX.Table,
667+ Text.Pandoc.Writers.LaTeX.Lang,
667668 Text.Pandoc.Writers.LaTeX.Types,
669+ Text.Pandoc.Writers.LaTeX.Citation,
670+ Text.Pandoc.Writers.LaTeX.Util,
671+ Text.Pandoc.Writers.Markdown.Types,
672+ Text.Pandoc.Writers.Markdown.Inline,
668673 Text.Pandoc.Writers.Roff,
669674 Text.Pandoc.Writers.Powerpoint.Presentation,
670675 Text.Pandoc.Writers.Powerpoint.Output,
src/Text/Pandoc/App.hs+21−8
@@ -47,10 +47,11 @@ import System.FilePath ( takeBaseName, takeExtension )
4747import System.IO (nativeNewline, stdout)
4848import qualified System.IO as IO (Newline (..))
4949import Text.Pandoc
50+import Text.Pandoc.Builder (setMeta)
5051import Text.Pandoc.MIME (getCharset)
5152import Text.Pandoc.App.FormatHeuristics (formatFromFilePaths)
5253import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts,
53− IpynbOutput (..) )
54+ IpynbOutput (..))
5455import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs,
5556 options)
5657import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings)
@@ -60,7 +61,7 @@ import Text.Pandoc.PDF (makePDF)
6061import Text.Pandoc.SelfContained (makeSelfContained)
6162import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs,
6263 headerShift, isURI, tabFilter, uriPathToPath, filterIpynbOutput,
63− defaultUserDataDirs, tshow, findM)
64+ defaultUserDataDir, tshow)
6465import Text.Pandoc.Writers.Shared (lookupMetaString)
6566import Text.Pandoc.Readers.Markdown (yamlToMeta)
6667import qualified Text.Pandoc.UTF8 as UTF8
@@ -71,6 +72,15 @@ import System.Posix.Terminal (queryTerminal)
7172
7273convertWithOpts :: Opt -> IO ()
7374convertWithOpts opts = do
75+ datadir <- case optDataDir opts of
76+ Nothing -> do
77+ d <- defaultUserDataDir
78+ exists <- doesDirectoryExist d
79+ return $ if exists
80+ then Just d
81+ else Nothing
82+ Just _ -> return $ optDataDir opts
83+
7484 let outputFile = fromMaybe "-" (optOutputFile opts)
7585 let filters = optFilters opts
7686 let verbosity = optVerbosity opts
@@ -85,12 +95,6 @@ convertWithOpts opts = do
8595 Just xs | not (optIgnoreArgs opts) -> xs
8696 _ -> ["-"]
8797
88− datadir <- case optDataDir opts of
89− Nothing -> do
90− ds <- defaultUserDataDirs
91− findM doesDirectoryExist ds
92− Just _ -> return $ optDataDir opts
93−
9498 let runIO' :: PandocIO a -> IO a
9599 runIO' f = do
96100 (res, reports) <- runIOorExplode $ do
@@ -275,12 +279,21 @@ convertWithOpts opts = do
275279 report $ Deprecated "pandoc-citeproc filter"
276280 "Use --citeproc instead."
277281
282+ let cslMetadata =
283+ maybe id (setMeta "csl") (optCSL opts) .
284+ (case optBibliography opts of
285+ [] -> id
286+ xs -> setMeta "bibliography" xs) .
287+ maybe id (setMeta "citation-abbreviations")
288+ (optCitationAbbreviations opts) $ mempty
289+
278290 doc <- sourceToDoc sources >>=
279291 ( (if isJust (optExtractMedia opts)
280292 then fillMediaBag
281293 else return)
282294 >=> return . adjustMetadata (metadataFromFile <>)
283295 >=> return . adjustMetadata (<> optMetadata opts)
296+ >=> return . adjustMetadata (<> cslMetadata)
284297 >=> applyTransforms transforms
285298 >=> applyFilters readerOpts filters [T.unpack format]
286299 >=> maybe return extractMedia (optExtractMedia opts)
src/Text/Pandoc/App/CommandLineOptions.hs+28−6
@@ -48,12 +48,13 @@ import System.FilePath
4848import System.IO (stdout)
4949import Text.DocTemplates (Context (..), ToContext (toVal), Val (..))
5050import Text.Pandoc
51+import Text.Pandoc.Builder (setMeta)
5152import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), IpynbOutput (..),
52− DefaultsState (..), addMeta, applyDefaults,
53+ DefaultsState (..), applyDefaults,
5354 fullDefaultsPath)
5455import Text.Pandoc.Filter (Filter (..))
5556import Text.Pandoc.Highlighting (highlightingStyles)
56−import Text.Pandoc.Shared (ordNub, elemText, safeStrRead, defaultUserDataDirs)
57+import Text.Pandoc.Shared (ordNub, elemText, safeStrRead, defaultUserDataDir)
5758import Text.Printf
5859
5960#ifdef EMBED_DATA_FILES
@@ -287,7 +288,8 @@ options =
287288 , Option "" ["resource-path"]
288289 (ReqArg
289290 (\arg opt -> return opt { optResourcePath =
290− splitSearchPath arg })
291+ splitSearchPath arg ++
292+ optResourcePath opt })
291293 "SEARCHPATH")
292294 "" -- "Paths to search for images and other resources"
293295
@@ -939,13 +941,12 @@ options =
939941 (NoArg
940942 (\_ -> do
941943 prg <- getProgName
942− defaultDatadirs <- defaultUserDataDirs
944+ defaultDatadir <- defaultUserDataDir
943945 UTF8.hPutStrLn stdout
944946 $ T.pack
945947 $ prg ++ " " ++ T.unpack pandocVersion ++
946948 compileInfo ++
947− "\nUser data directory: " ++
948− intercalate " or " defaultDatadirs ++
949+ "\nUser data directory: " ++ defaultDatadir ++
949950 ('\n':copyrightMessage)
950951 exitSuccess ))
951952 "" -- "Print version"
@@ -1053,6 +1054,27 @@ setVariable key val (Context ctx) = Context $ M.alter go key ctx
10531054 go (Just (ListVal xs)) = Just $ ListVal $ xs ++ [toVal val]
10541055 go (Just x) = Just $ ListVal [x, toVal val]
10551056
1057+addMeta :: String -> String -> Meta -> Meta
1058+addMeta k v meta =
1059+ case lookupMeta k' meta of
1060+ Nothing -> setMeta k' v' meta
1061+ Just (MetaList xs) ->
1062+ setMeta k' (MetaList (xs ++ [v'])) meta
1063+ Just x -> setMeta k' (MetaList [x, v']) meta
1064+ where
1065+ v' = readMetaValue v
1066+ k' = T.pack k
1067+
1068+readMetaValue :: String -> MetaValue
1069+readMetaValue s
1070+ | s == "true" = MetaBool True
1071+ | s == "True" = MetaBool True
1072+ | s == "TRUE" = MetaBool True
1073+ | s == "false" = MetaBool False
1074+ | s == "False" = MetaBool False
1075+ | s == "FALSE" = MetaBool False
1076+ | otherwise = MetaString $ T.pack s
1077+
10561078-- On Windows with ghc 8.6+, we need to rewrite paths
10571079-- beginning with \\ to \\?\UNC\. -- See #5127.
10581080normalizePath :: FilePath -> FilePath
src/Text/Pandoc/App/Opt.hs+143−56
@@ -22,28 +22,29 @@ module Text.Pandoc.App.Opt (
2222 , IpynbOutput (..)
2323 , DefaultsState (..)
2424 , defaultOpts
25− , addMeta
2625 , applyDefaults
2726 , fullDefaultsPath
2827 ) where
2928import Control.Monad.Except (MonadIO, liftIO, throwError, (>=>), foldM)
3029import Control.Monad.State.Strict (StateT, modify, gets)
31−import System.FilePath ( addExtension, (</>), takeExtension )
30+import System.FilePath ( addExtension, (</>), takeExtension, takeDirectory )
31+import System.Directory ( canonicalizePath )
3232import Data.Char (isLower, toLower)
3333import Data.Maybe (fromMaybe)
3434import GHC.Generics hiding (Meta)
35−import Text.Pandoc.Builder (setMeta)
3635import Text.Pandoc.Filter (Filter (..))
37−import Text.Pandoc.Logging (Verbosity (WARNING))
36+import Text.Pandoc.Logging (Verbosity (WARNING), LogMessage(..))
3837import Text.Pandoc.Options (TopLevelDivision (TopLevelDefault),
3938 TrackChanges (AcceptChanges),
4039 WrapOption (WrapAuto), HTMLMathMethod (PlainMath),
4140 ReferenceLocation (EndOfDocument),
4241 ObfuscationMethod (NoObfuscation),
4342 CiteMethod (Citeproc))
44−import Text.Pandoc.Class (readFileLazy, fileExists, setVerbosity, PandocMonad)
43+import Text.Pandoc.Class (readFileLazy, fileExists, setVerbosity, report,
44+ PandocMonad(lookupEnv), getUserDataDir)
4545import Text.Pandoc.Error (PandocError (PandocParseError, PandocSomeError))
46−import Text.Pandoc.Shared (camelCaseStrToHyphenated, defaultUserDataDirs, findM, ordNub)
46+import Text.Pandoc.Shared (camelCaseStrToHyphenated, defaultUserDataDir,
47+ findM, ordNub)
4748import qualified Text.Pandoc.Parsing as P
4849import Text.Pandoc.Readers.Metadata (yamlMap)
4950import Text.Pandoc.Class.PandocPure
@@ -52,7 +53,7 @@ import Data.Text (Text, unpack)
5253import Data.Default (def)
5354import qualified Data.Text as T
5455import qualified Data.Map as M
55−import Text.Pandoc.Definition (Meta(..), MetaValue(..), lookupMeta)
56+import Text.Pandoc.Definition (Meta(..), MetaValue(..))
5657import Data.Aeson (defaultOptions, Options(..))
5758import Data.Aeson.TH (deriveJSON)
5859import Control.Applicative ((<|>))
@@ -156,6 +157,9 @@ data Opt = Opt
156157 , optNoCheckCertificate :: Bool -- ^ Disable certificate validation
157158 , optEol :: LineEnding -- ^ Style of line-endings to use
158159 , optStripComments :: Bool -- ^ Skip HTML comments
160+ , optCSL :: Maybe FilePath -- ^ CSL stylesheet
161+ , optBibliography :: [FilePath] -- ^ Bibliography files
162+ , optCitationAbbreviations :: Maybe FilePath -- ^ Citation abbreviations
159163 } deriving (Generic, Show)
160164
161165instance FromYAML (Opt -> Opt) where
@@ -175,17 +179,128 @@ instance (PandocMonad m, MonadIO m)
175179 dataDir <- case M.lookup "data-dir" opts of
176180 Nothing -> return Nothing
177181 Just v -> Just . unpack <$> parseYAML v
178− f <- parseOptions $ M.toList m
182+ f <- parseOptions (M.toList m)
179183 case M.lookup "defaults" opts of
180184 Just v -> do
181185 g <- parseDefaults v dataDir
182− return $ g >=> f
183− Nothing -> return f
186+ return $ g >=> f >=> resolveVarsInOpt
187+ Nothing -> return $ f >=> resolveVarsInOpt
184188 where
185189 toText (Scalar _ (SStr s)) = s
186190 toText _ = ""
187191 parseYAML n = failAtNode n "Expected a mapping"
188192
193+resolveVarsInOpt :: forall m. (PandocMonad m, MonadIO m)
194+ => Opt -> StateT DefaultsState m Opt
195+resolveVarsInOpt
196+ opt@Opt
197+ { optTemplate = oTemplate
198+ , optMetadataFiles = oMetadataFiles
199+ , optOutputFile = oOutputFile
200+ , optInputFiles = oInputFiles
201+ , optSyntaxDefinitions = oSyntaxDefinitions
202+ , optAbbreviations = oAbbreviations
203+ , optReferenceDoc = oReferenceDoc
204+ , optEpubMetadata = oEpubMetadata
205+ , optEpubFonts = oEpubFonts
206+ , optEpubCoverImage = oEpubCoverImage
207+ , optLogFile = oLogFile
208+ , optFilters = oFilters
209+ , optDataDir = oDataDir
210+ , optExtractMedia = oExtractMedia
211+ , optCss = oCss
212+ , optIncludeBeforeBody = oIncludeBeforeBody
213+ , optIncludeAfterBody = oIncludeAfterBody
214+ , optIncludeInHeader = oIncludeInHeader
215+ , optResourcePath = oResourcePath
216+ , optCSL = oCSL
217+ , optBibliography = oBibliography
218+ , optCitationAbbreviations = oCitationAbbreviations
219+ }
220+ = do
221+ oTemplate' <- mapM resolveVars oTemplate
222+ oMetadataFiles' <- mapM resolveVars oMetadataFiles
223+ oOutputFile' <- mapM resolveVars oOutputFile
224+ oInputFiles' <- mapM (mapM resolveVars) oInputFiles
225+ oSyntaxDefinitions' <- mapM resolveVars oSyntaxDefinitions
226+ oAbbreviations' <- mapM resolveVars oAbbreviations
227+ oReferenceDoc' <- mapM resolveVars oReferenceDoc
228+ oEpubMetadata' <- mapM resolveVars oEpubMetadata
229+ oEpubFonts' <- mapM resolveVars oEpubFonts
230+ oEpubCoverImage' <- mapM resolveVars oEpubCoverImage
231+ oLogFile' <- mapM resolveVars oLogFile
232+ oFilters' <- mapM resolveVarsInFilter oFilters
233+ oDataDir' <- mapM resolveVars oDataDir
234+ oExtractMedia' <- mapM resolveVars oExtractMedia
235+ oCss' <- mapM resolveVars oCss
236+ oIncludeBeforeBody' <- mapM resolveVars oIncludeBeforeBody
237+ oIncludeAfterBody' <- mapM resolveVars oIncludeAfterBody
238+ oIncludeInHeader' <- mapM resolveVars oIncludeInHeader
239+ oResourcePath' <- mapM resolveVars oResourcePath
240+ oCSL' <- mapM resolveVars oCSL
241+ oBibliography' <- mapM resolveVars oBibliography
242+ oCitationAbbreviations' <- mapM resolveVars oCitationAbbreviations
243+ return opt{ optTemplate = oTemplate'
244+ , optMetadataFiles = oMetadataFiles'
245+ , optOutputFile = oOutputFile'
246+ , optInputFiles = oInputFiles'
247+ , optSyntaxDefinitions = oSyntaxDefinitions'
248+ , optAbbreviations = oAbbreviations'
249+ , optReferenceDoc = oReferenceDoc'
250+ , optEpubMetadata = oEpubMetadata'
251+ , optEpubFonts = oEpubFonts'
252+ , optEpubCoverImage = oEpubCoverImage'
253+ , optLogFile = oLogFile'
254+ , optFilters = oFilters'
255+ , optDataDir = oDataDir'
256+ , optExtractMedia = oExtractMedia'
257+ , optCss = oCss'
258+ , optIncludeBeforeBody = oIncludeBeforeBody'
259+ , optIncludeAfterBody = oIncludeAfterBody'
260+ , optIncludeInHeader = oIncludeInHeader'
261+ , optResourcePath = oResourcePath'
262+ , optCSL = oCSL'
263+ , optBibliography = oBibliography'
264+ , optCitationAbbreviations = oCitationAbbreviations'
265+ }
266+
267+ where
268+ resolveVars :: FilePath -> StateT DefaultsState m FilePath
269+ resolveVars [] = return []
270+ resolveVars ('$':'{':xs) =
271+ let (ys, zs) = break (=='}') xs
272+ in if null zs
273+ then return $ '$':'{':xs
274+ else do
275+ val <- lookupEnv' ys
276+ (val ++) <$> resolveVars (drop 1 zs)
277+ resolveVars (c:cs) = (c:) <$> resolveVars cs
278+ lookupEnv' :: String -> StateT DefaultsState m String
279+ lookupEnv' "." = do
280+ mbCurDefaults <- gets curDefaults
281+ maybe (return "")
282+ (fmap takeDirectory . liftIO . canonicalizePath)
283+ mbCurDefaults
284+ lookupEnv' "USERDATA" = do
285+ mbodatadir <- mapM resolveVars oDataDir
286+ mbdatadir <- getUserDataDir
287+ defdatadir <- liftIO defaultUserDataDir
288+ return $ fromMaybe defdatadir (mbodatadir <|> mbdatadir)
289+ lookupEnv' v = do
290+ mbval <- fmap T.unpack <$> lookupEnv (T.pack v)
291+ case mbval of
292+ Nothing -> do
293+ report $ EnvironmentVariableUndefined (T.pack v)
294+ return mempty
295+ Just x -> return x
296+ resolveVarsInFilter (JSONFilter fp) =
297+ JSONFilter <$> resolveVars fp
298+ resolveVarsInFilter (LuaFilter fp) =
299+ LuaFilter <$> resolveVars fp
300+ resolveVarsInFilter CiteprocFilter = return CiteprocFilter
301+
302+
303+
189304parseDefaults :: (PandocMonad m, MonadIO m)
190305 => Node Pos
191306 -> Maybe FilePath
@@ -428,26 +543,18 @@ doOpt (k',v) = do
428543 (parseYAML v >>= \x -> return (\o -> o{ optCss = optCss o <>
429544 [unpack x] }))
430545 "bibliography" ->
431− do let addItem x o = o{ optMetadata =
432− addMeta "bibliography" (T.unpack x)
433− (optMetadata o) }
434− (parseYAML v >>= \(xs :: [Text]) -> return $ \o ->
435− foldr addItem o xs)
436− <|>
437− (parseYAML v >>= \(x :: Text) -> return $ \o -> addItem x o)
546+ (parseYAML v >>= \x -> return (\o ->
547+ o{ optBibliography = optBibliography o <>
548+ map unpack x }))
549+ <|>
550+ (parseYAML v >>= \x -> return (\o ->
551+ o{ optBibliography = optBibliography o <>
552+ [unpack x] }))
438553 "csl" ->
439− do let addItem x o = o{ optMetadata =
440− addMeta "csl" (T.unpack x)
441− (optMetadata o) }
442− (parseYAML v >>= \(xs :: [Text]) -> return $ \o ->
443− foldr addItem o xs)
444− <|>
445− (parseYAML v >>= \(x :: Text) -> return $ \o -> addItem x o)
554+ parseYAML v >>= \x -> return (\o -> o{ optCSL = unpack <$> x })
446555 "citation-abbreviations" ->
447− parseYAML v >>= \x ->
448− return (\o -> o{ optMetadata =
449− addMeta "citation-abbreviations" (T.unpack x)
450− (optMetadata o) })
556+ parseYAML v >>= \x -> return (\o -> o{ optCitationAbbreviations =
557+ unpack <$> x })
451558 "ipynb-output" ->
452559 parseYAML v >>= \x -> return (\o -> o{ optIpynbOutput = x })
453560 "include-before-body" ->
@@ -476,7 +583,8 @@ doOpt (k',v) = do
476583 optIncludeInHeader o <> [unpack x] }))
477584 "resource-path" ->
478585 parseYAML v >>= \x ->
479− return (\o -> o{ optResourcePath = map unpack x })
586+ return (\o -> o{ optResourcePath = map unpack x <>
587+ optResourcePath o })
480588 "request-headers" ->
481589 parseYAML v >>= \x ->
482590 return (\o -> o{ optRequestHeaders = x })
@@ -562,6 +670,9 @@ defaultOpts = Opt
562670 , optNoCheckCertificate = False
563671 , optEol = Native
564672 , optStripComments = False
673+ , optCSL = Nothing
674+ , optBibliography = []
675+ , optCitationAbbreviations = Nothing
565676 }
566677
567678parseStringKey :: Node Pos -> Parser Text
@@ -579,27 +690,6 @@ yamlToMeta (Mapping _ _ m) =
579690 >>= fmap (Meta . flip P.runF def)
580691yamlToMeta _ = return mempty
581692
582−addMeta :: String -> String -> Meta -> Meta
583−addMeta k v meta =
584− case lookupMeta k' meta of
585− Nothing -> setMeta k' v' meta
586− Just (MetaList xs) ->
587− setMeta k' (MetaList (xs ++ [v'])) meta
588− Just x -> setMeta k' (MetaList [x, v']) meta
589− where
590− v' = readMetaValue v
591− k' = T.pack k
592−
593−readMetaValue :: String -> MetaValue
594−readMetaValue s
595− | s == "true" = MetaBool True
596− | s == "True" = MetaBool True
597− | s == "TRUE" = MetaBool True
598− | s == "false" = MetaBool False
599− | s == "False" = MetaBool False
600− | s == "FALSE" = MetaBool False
601− | otherwise = MetaString $ T.pack s
602−
603693-- | Apply defaults from --defaults file.
604694applyDefaults :: (PandocMonad m, MonadIO m)
605695 => Opt
@@ -625,12 +715,9 @@ fullDefaultsPath dataDir file = do
625715 let fp = if null (takeExtension file)
626716 then addExtension file "yaml"
627717 else file
628− dataDirs <- liftIO defaultUserDataDirs
629− let fps = fp : case dataDir of
630− Nothing -> map (</> ("defaults" </> fp))
631− dataDirs
632− Just dd -> [dd </> "defaults" </> fp]
633− fromMaybe fp <$> findM fileExists fps
718+ defaultDataDir <- liftIO defaultUserDataDir
719+ let defaultFp = fromMaybe defaultDataDir dataDir </> "defaults" </> fp
720+ fromMaybe fp <$> findM fileExists [fp, defaultFp]
634721
635722-- | In a list of lists, append another list in front of every list which
636723-- starts with specific element.
src/Text/Pandoc/Logging.hs+6
@@ -100,6 +100,7 @@ data LogMessage =
100100 | FilterCompleted FilePath Integer
101101 | CiteprocWarning Text
102102 | ATXHeadingInLHS Int Text
103+ | EnvironmentVariableUndefined Text
103104 deriving (Show, Eq, Data, Ord, Typeable, Generic)
104105
105106instance ToJSON LogMessage where
@@ -229,6 +230,8 @@ instance ToJSON LogMessage where
229230 ATXHeadingInLHS lvl contents ->
230231 ["level" .= lvl
231232 ,"contents" .= contents]
233+ EnvironmentVariableUndefined var ->
234+ ["variable" .= var ]
232235
233236showPos :: SourcePos -> Text
234237showPos pos = Text.pack $ sn ++ "line " ++
@@ -345,6 +348,8 @@ showLogMessage msg =
345348 if lvl < 3
346349 then " Consider using --markdown-headings=setext."
347350 else ""
351+ EnvironmentVariableUndefined var ->
352+ "Undefined environment variable " <> var <> " in defaults file."
348353
349354messageVerbosity :: LogMessage -> Verbosity
350355messageVerbosity msg =
@@ -391,3 +396,4 @@ messageVerbosity msg =
391396 FilterCompleted{} -> INFO
392397 CiteprocWarning{} -> WARNING
393398 ATXHeadingInLHS{} -> WARNING
399+ EnvironmentVariableUndefined{}-> WARNING
src/Text/Pandoc/Readers/LaTeX.hs+108−396
@@ -34,14 +34,14 @@ import qualified Data.Set as Set
3434import Data.Text (Text)
3535import qualified Data.Text as T
3636import System.FilePath (addExtension, replaceExtension, takeExtension)
37−import Text.Pandoc.BCP47 (Lang (..), renderLang)
38−import Text.Pandoc.Builder
37+import Text.Pandoc.BCP47 (renderLang)
38+import Text.Pandoc.Builder as B
3939import Text.Pandoc.Class.PandocPure (PandocPure)
4040import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getResourcePath,
41− readFileFromDirs, report, setResourcePath,
42− translateTerm)
41+ readFileFromDirs, report,
42+ setResourcePath)
4343import Text.Pandoc.Error (PandocError (PandocParseError, PandocParsecError))
44−import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
44+import Text.Pandoc.Highlighting (languagesByExtension)
4545import Text.Pandoc.ImageSize (numUnit, showFl)
4646import Text.Pandoc.Logging
4747import Text.Pandoc.Options
@@ -49,7 +49,6 @@ import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,
4949 optional, space, spaces, withRaw, (<|>))
5050import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
5151import Text.Pandoc.Readers.LaTeX.Parsing
52−import Text.Pandoc.Readers.LaTeX.Accent (accentCommands)
5352import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites)
5453import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
5554 inlineEnvironment,
@@ -58,13 +57,18 @@ import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
5857 theoremEnvironment)
5958import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments)
6059import Text.Pandoc.Readers.LaTeX.Macro (macroDef)
61−import Text.Pandoc.Readers.LaTeX.Lang (polyglossiaLangToBCP47,
60+import Text.Pandoc.Readers.LaTeX.Lang (inlineLanguageCommands,
61+ enquoteCommands,
6262 babelLangToBCP47, setDefaultLanguage)
6363import Text.Pandoc.Readers.LaTeX.SIunitx (siunitxCommands)
64+import Text.Pandoc.Readers.LaTeX.Inline (acronymCommands, refCommands,
65+ nameCommands, charCommands,
66+ accentCommands,
67+ biblatexInlineCommands,
68+ verbCommands, rawInlineOr,
69+ listingsLanguage)
6470import Text.Pandoc.Shared
65−import qualified Text.Pandoc.Translations as Translations
6671import Text.Pandoc.Walk
67−import qualified Text.Pandoc.Builder as B
6872import Safe
6973
7074-- for debugging:
@@ -290,18 +294,6 @@ quoted' f starter ender = do
290294lit :: Text -> LP m Inlines
291295lit = pure . str
292296
293−enquote :: PandocMonad m => Bool -> Maybe Text -> LP m Inlines
294−enquote starred mblang = do
295− skipopts
296− let lang = mblang >>= babelLangToBCP47
297− let langspan = case lang of
298− Nothing -> id
299− Just l -> spanWith ("",[],[("lang", renderLang l)])
300− quoteContext <- sQuoteContext <$> getState
301− if starred || quoteContext == InDoubleQuote
302− then singleQuoted . langspan <$> withQuoteContext InSingleQuote tok
303− else doubleQuoted . langspan <$> withQuoteContext InDoubleQuote tok
304−
305297blockquote :: PandocMonad m => Bool -> Maybe Text -> LP m Blocks
306298blockquote cvariant mblang = do
307299 citepar <- if cvariant
@@ -317,76 +309,6 @@ blockquote cvariant mblang = do
317309 optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored
318310 return $ blockQuote . langdiv $ (bs <> citepar)
319311
320−doAcronym :: PandocMonad m => Text -> LP m Inlines
321−doAcronym form = do
322− acro <- braced
323− return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
324− ("acronym-form", "singular+" <> form)])
325− $ str $ untokenize acro]
326−
327−doAcronymPlural :: PandocMonad m => Text -> LP m Inlines
328−doAcronymPlural form = do
329− acro <- braced
330− plural <- lit "s"
331− return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
332− ("acronym-form", "plural+" <> form)]) $
333− mconcat [str $ untokenize acro, plural]]
334−
335−doverb :: PandocMonad m => LP m Inlines
336−doverb = do
337− Tok _ Symbol t <- anySymbol
338− marker <- case T.uncons t of
339− Just (c, ts) | T.null ts -> return c
340− _ -> mzero
341− withVerbatimMode $
342− code . untokenize <$>
343− manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker)
344−
345−verbTok :: PandocMonad m => Char -> LP m Tok
346−verbTok stopchar = do
347− t@(Tok pos toktype txt) <- anyTok
348− case T.findIndex (== stopchar) txt of
349− Nothing -> return t
350− Just i -> do
351− let (t1, t2) = T.splitAt i txt
352− inp <- getInput
353− setInput $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar)
354− : totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp
355− return $ Tok pos toktype t1
356−
357−listingsLanguage :: [(Text, Text)] -> Maybe Text
358−listingsLanguage opts =
359− case lookup "language" opts of
360− Nothing -> Nothing
361− Just l -> fromListingsLanguage l `mplus` Just l
362−
363−dolstinline :: PandocMonad m => LP m Inlines
364−dolstinline = do
365− options <- option [] keyvals
366− let classes = maybeToList $ listingsLanguage options
367− doinlinecode classes
368−
369−domintinline :: PandocMonad m => LP m Inlines
370−domintinline = do
371− skipopts
372− cls <- untokenize <$> braced
373− doinlinecode [cls]
374−
375−doinlinecode :: PandocMonad m => [Text] -> LP m Inlines
376−doinlinecode classes = do
377− Tok _ Symbol t <- anySymbol
378− marker <- case T.uncons t of
379− Just (c, ts) | T.null ts -> return c
380− _ -> mzero
381− let stopchar = if marker == '{' then '}' else marker
382− withVerbatimMode $
383− codeWith ("",classes,[]) . T.map nlToSpace . untokenize <$>
384− manyTill (verbTok stopchar) (symbol stopchar)
385−
386−nlToSpace :: Char -> Char
387−nlToSpace '\n' = ' '
388−nlToSpace x = x
389−
390312inlineCommand' :: PandocMonad m => LP m Inlines
391313inlineCommand' = try $ do
392314 Tok _ (CtrlSeq name) cmd <- anyControlSeq
@@ -405,9 +327,6 @@ inlineCommand' = try $ do
405327tok :: PandocMonad m => LP m Inlines
406328tok = tokWith inline
407329
408−inBrackets :: Inlines -> Inlines
409−inBrackets x = str "[" <> x <> str "]"
410−
411330unescapeURL :: Text -> Text
412331unescapeURL = T.concat . go . T.splitOn "\\"
413332 where
@@ -420,234 +339,101 @@ unescapeURL = T.concat . go . T.splitOn "\\"
420339 | otherwise = "\\" <> t
421340
422341inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
423−inlineCommands =
424− M.union inlineLanguageCommands $
425− M.union (accentCommands tok) $
426− M.union (citationCommands inline) $
427− M.union (siunitxCommands tok) $
428− M.fromList
429− [ ("emph", extractSpaces emph <$> tok)
430− , ("textit", extractSpaces emph <$> tok)
431− , ("textsl", extractSpaces emph <$> tok)
432− , ("textsc", extractSpaces smallcaps <$> tok)
433− , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
434− , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
435− , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
436− , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
437− , ("texttt", ttfamily)
438− , ("sout", extractSpaces strikeout <$> tok)
439− , ("alert", skipopts >> spanWith ("",["alert"],[]) <$> tok) -- beamer
440− , ("lq", return (str "‘"))
441− , ("rq", return (str "’"))
442− , ("textquoteleft", return (str "‘"))
443− , ("textquoteright", return (str "’"))
444− , ("textquotedblleft", return (str "“"))
445− , ("textquotedblright", return (str "”"))
446− , ("textsuperscript", extractSpaces superscript <$> tok)
447− , ("textsubscript", extractSpaces subscript <$> tok)
448− , ("textbackslash", lit "\\")
449− , ("backslash", lit "\\")
450− , ("slash", lit "/")
451− , ("textbf", extractSpaces strong <$> tok)
452− , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
453− , ("underline", underline <$> tok)
454− , ("ldots", lit "…")
455− , ("vdots", lit "\8942")
456− , ("dots", lit "…")
457− , ("mdots", lit "…")
458− , ("sim", lit "~")
459− , ("sep", lit ",")
460− , ("label", rawInlineOr "label" dolabel)
461− , ("ref", rawInlineOr "ref" $ doref "ref")
462− , ("cref", rawInlineOr "cref" $ doref "ref") -- from cleveref.sty
463− , ("vref", rawInlineOr "vref" $ doref "ref+page") -- from varioref.sty
464− , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty
465− , ("mbox", rawInlineOr "mbox" $ processHBox <$> tok)
466− , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
467− , ("lettrine", rawInlineOr "lettrine" lettrine)
468− , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
469− , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
470− , ("ensuremath", mathInline . untokenize <$> braced)
471− , ("texorpdfstring", const <$> tok <*> tok)
472− , ("P", lit "¶")
473− , ("S", lit "§")
474− , ("$", lit "$")
475− , ("%", lit "%")
476− , ("&", lit "&")
477− , ("#", lit "#")
478− , ("_", lit "_")
479− , ("{", lit "{")
480− , ("}", lit "}")
481− , ("qed", lit "\a0\x25FB")
482− -- old TeX commands
483− , ("em", extractSpaces emph <$> inlines)
484− , ("it", extractSpaces emph <$> inlines)
485− , ("sl", extractSpaces emph <$> inlines)
486− , ("bf", extractSpaces strong <$> inlines)
487− , ("tt", code . stringify . toList <$> inlines)
488− , ("rm", inlines)
489− , ("itshape", extractSpaces emph <$> inlines)
490− , ("slshape", extractSpaces emph <$> inlines)
491− , ("scshape", extractSpaces smallcaps <$> inlines)
492− , ("bfseries", extractSpaces strong <$> inlines)
493− , ("MakeUppercase", makeUppercase <$> tok)
494− , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
495− , ("uppercase", makeUppercase <$> tok)
496− , ("MakeLowercase", makeLowercase <$> tok)
497− , ("MakeTextLowercase", makeLowercase <$> tok)
498− , ("lowercase", makeLowercase <$> tok)
499− , ("/", pure mempty) -- italic correction
500− , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
501− guard $ not inTableCell
502− optional rawopt
503− spaces))
504− , (",", lit "\8198")
505− , ("@", pure mempty)
506− , (" ", lit "\160")
507− , ("ps", pure $ str "PS." <> space)
508− , ("TeX", lit "TeX")
509− , ("LaTeX", lit "LaTeX")
510− , ("bar", lit "|")
511− , ("textless", lit "<")
512− , ("textgreater", lit ">")
513− , ("thanks", skipopts >> note <$> grouped block)
514− , ("footnote", skipopts >> note <$> grouped block)
515− , ("passthrough", tok) -- \passthrough macro used by latex writer
516− -- for listings
517− , ("verb", doverb)
518− , ("lstinline", dolstinline)
519− , ("mintinline", domintinline)
520− , ("Verb", doverb)
521− , ("url", (\url -> link url "" (str url)) . unescapeURL . untokenize <$>
522− bracedUrl)
523− , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl)
524− , ("href", do url <- bracedUrl
525− sp
526− link (unescapeURL $ untokenize url) "" <$> tok)
527− , ("includegraphics", do options <- option [] keyvals
528− src <- braced
529− mkImage options . unescapeURL . removeDoubleQuotes $
530− untokenize src)
531− , ("enquote*", enquote True Nothing)
532− , ("enquote", enquote False Nothing)
533− -- foreignquote is supposed to use native quote marks
534− , ("foreignquote*", braced >>= enquote True . Just . untokenize)
535− , ("foreignquote", braced >>= enquote False . Just . untokenize)
536− -- hypehnquote uses regular quotes
537− , ("hyphenquote*", braced >>= enquote True . Just . untokenize)
538− , ("hyphenquote", braced >>= enquote False . Just . untokenize)
539− , ("figurename", doTerm Translations.Figure)
540− , ("prefacename", doTerm Translations.Preface)
541− , ("refname", doTerm Translations.References)
542− , ("bibname", doTerm Translations.Bibliography)
543− , ("chaptername", doTerm Translations.Chapter)
544− , ("partname", doTerm Translations.Part)
545− , ("contentsname", doTerm Translations.Contents)
546− , ("listfigurename", doTerm Translations.ListOfFigures)
547− , ("listtablename", doTerm Translations.ListOfTables)
548− , ("indexname", doTerm Translations.Index)
549− , ("abstractname", doTerm Translations.Abstract)
550− , ("tablename", doTerm Translations.Table)
551− , ("enclname", doTerm Translations.Encl)
552− , ("ccname", doTerm Translations.Cc)
553− , ("headtoname", doTerm Translations.To)
554− , ("pagename", doTerm Translations.Page)
555− , ("seename", doTerm Translations.See)
556− , ("seealsoname", doTerm Translations.SeeAlso)
557− , ("proofname", doTerm Translations.Proof)
558− , ("glossaryname", doTerm Translations.Glossary)
559− , ("lstlistingname", doTerm Translations.Listing)
560− , ("hyperlink", hyperlink)
561− , ("hypertarget", hypertargetInline)
562− -- glossaries package
563− , ("gls", doAcronym "short")
564− , ("Gls", doAcronym "short")
565− , ("glsdesc", doAcronym "long")
566− , ("Glsdesc", doAcronym "long")
567− , ("GLSdesc", doAcronym "long")
568− , ("acrlong", doAcronym "long")
569− , ("Acrlong", doAcronym "long")
570− , ("acrfull", doAcronym "full")
571− , ("Acrfull", doAcronym "full")
572− , ("acrshort", doAcronym "abbrv")
573− , ("Acrshort", doAcronym "abbrv")
574− , ("glspl", doAcronymPlural "short")
575− , ("Glspl", doAcronymPlural "short")
576− , ("glsdescplural", doAcronymPlural "long")
577− , ("Glsdescplural", doAcronymPlural "long")
578− , ("GLSdescplural", doAcronymPlural "long")
579− -- acronyms package
580− , ("ac", doAcronym "short")
581− , ("acf", doAcronym "full")
582− , ("acs", doAcronym "abbrv")
583− , ("acl", doAcronym "long")
584− , ("acp", doAcronymPlural "short")
585− , ("acfp", doAcronymPlural "full")
586− , ("acsp", doAcronymPlural "abbrv")
587− , ("aclp", doAcronymPlural "long")
588− , ("Ac", doAcronym "short")
589− , ("Acf", doAcronym "full")
590− , ("Acs", doAcronym "abbrv")
591− , ("Acl", doAcronym "long")
592− , ("Acp", doAcronymPlural "short")
593− , ("Acfp", doAcronymPlural "full")
594− , ("Acsp", doAcronymPlural "abbrv")
595− , ("Aclp", doAcronymPlural "long")
596− -- hyphenat
597− , ("bshyp", lit "\\\173")
598− , ("fshyp", lit "/\173")
599− , ("dothyp", lit ".\173")
600− , ("colonhyp", lit ":\173")
601− , ("hyp", lit "-")
602− , ("nohyphens", tok)
603− , ("textnhtt", ttfamily)
604− , ("nhttfamily", ttfamily)
605− -- LaTeX colors
606− , ("textcolor", coloredInline "color")
607− , ("colorbox", coloredInline "background-color")
608− -- fontawesome
609− , ("faCheck", lit "\10003")
610− , ("faClose", lit "\10007")
611− -- xspace
612− , ("xspace", doxspace)
613− -- etoolbox
614− , ("ifstrequal", ifstrequal)
615− , ("newtoggle", braced >>= newToggle)
616− , ("toggletrue", braced >>= setToggle True)
617− , ("togglefalse", braced >>= setToggle False)
618− , ("iftoggle", try $ ifToggle >> inline)
619− -- biblatex misc
620− , ("RN", romanNumeralUpper)
621− , ("Rn", romanNumeralLower)
622− -- babel
623− , ("foreignlanguage", foreignlanguage)
624− -- include
625− , ("input", rawInlineOr "input" $ include "input")
626− -- soul package
627− , ("ul", underline <$> tok)
628− -- ulem package
629− , ("uline", underline <$> tok)
630− -- plain tex stuff that should just be passed through as raw tex
631− , ("ifdim", ifdim)
632− -- bibtex
633− , ("mkbibquote", spanWith nullAttr . doubleQuoted <$> tok)
634− , ("mkbibemph", spanWith nullAttr . emph <$> tok)
635− , ("mkbibitalic", spanWith nullAttr . emph <$> tok)
636− , ("mkbibbold", spanWith nullAttr . strong <$> tok)
637− , ("mkbibparens",
638− spanWith nullAttr . (\x -> str "(" <> x <> str ")") <$> tok)
639− , ("mkbibbrackets",
640− spanWith nullAttr . (\x -> str "[" <> x <> str "]") <$> tok)
641− , ("autocap", spanWith nullAttr <$> tok)
642− , ("textnormal", spanWith ("",["nodecor"],[]) <$> tok)
643− , ("bibstring",
644− (\x -> spanWith ("",[],[("bibstring",x)]) (str x)) . untokenize
645− <$> braced)
646− , ("adddot", pure (str "."))
647− , ("adddotspace", pure (spanWith nullAttr (str "." <> space)))
648− , ("addabbrvspace", pure space)
649− , ("hyphen", pure (str "-"))
650− ]
342+inlineCommands = M.unions
343+ [ accentCommands tok
344+ , citationCommands inline
345+ , siunitxCommands tok
346+ , acronymCommands
347+ , refCommands
348+ , nameCommands
349+ , verbCommands
350+ , charCommands
351+ , enquoteCommands tok
352+ , inlineLanguageCommands tok
353+ , biblatexInlineCommands tok
354+ , rest ]
355+ where
356+ rest = M.fromList
357+ [ ("emph", extractSpaces emph <$> tok)
358+ , ("textit", extractSpaces emph <$> tok)
359+ , ("textsl", extractSpaces emph <$> tok)
360+ , ("textsc", extractSpaces smallcaps <$> tok)
361+ , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
362+ , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
363+ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
364+ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
365+ , ("texttt", ttfamily)
366+ , ("sout", extractSpaces strikeout <$> tok)
367+ , ("alert", skipopts >> spanWith ("",["alert"],[]) <$> tok) -- beamer
368+ , ("textsuperscript", extractSpaces superscript <$> tok)
369+ , ("textsubscript", extractSpaces subscript <$> tok)
370+ , ("textbf", extractSpaces strong <$> tok)
371+ , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
372+ , ("underline", underline <$> tok)
373+ , ("mbox", rawInlineOr "mbox" $ processHBox <$> tok)
374+ , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
375+ , ("lettrine", rawInlineOr "lettrine" lettrine)
376+ , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
377+ , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
378+ , ("ensuremath", mathInline . untokenize <$> braced)
379+ , ("texorpdfstring", const <$> tok <*> tok)
380+ -- old TeX commands
381+ , ("em", extractSpaces emph <$> inlines)
382+ , ("it", extractSpaces emph <$> inlines)
383+ , ("sl", extractSpaces emph <$> inlines)
384+ , ("bf", extractSpaces strong <$> inlines)
385+ , ("tt", code . stringify . toList <$> inlines)
386+ , ("rm", inlines)
387+ , ("itshape", extractSpaces emph <$> inlines)
388+ , ("slshape", extractSpaces emph <$> inlines)
389+ , ("scshape", extractSpaces smallcaps <$> inlines)
390+ , ("bfseries", extractSpaces strong <$> inlines)
391+ , ("MakeUppercase", makeUppercase <$> tok)
392+ , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
393+ , ("uppercase", makeUppercase <$> tok)
394+ , ("MakeLowercase", makeLowercase <$> tok)
395+ , ("MakeTextLowercase", makeLowercase <$> tok)
396+ , ("lowercase", makeLowercase <$> tok)
397+ , ("thanks", skipopts >> note <$> grouped block)
398+ , ("footnote", skipopts >> note <$> grouped block)
399+ , ("passthrough", tok) -- \passthrough macro used by latex writer
400+ -- for listings
401+ , ("url", (\url -> link url "" (str url)) . unescapeURL . untokenize <$>
402+ bracedUrl)
403+ , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl)
404+ , ("href", do url <- bracedUrl
405+ sp
406+ link (unescapeURL $ untokenize url) "" <$> tok)
407+ , ("includegraphics", do options <- option [] keyvals
408+ src <- braced
409+ mkImage options . unescapeURL . removeDoubleQuotes $
410+ untokenize src)
411+ , ("hyperlink", hyperlink)
412+ , ("hypertarget", hypertargetInline)
413+ -- hyphenat
414+ , ("nohyphens", tok)
415+ , ("textnhtt", ttfamily)
416+ , ("nhttfamily", ttfamily)
417+ -- LaTeX colors
418+ , ("textcolor", coloredInline "color")
419+ , ("colorbox", coloredInline "background-color")
420+ -- xspace
421+ , ("xspace", doxspace)
422+ -- etoolbox
423+ , ("ifstrequal", ifstrequal)
424+ , ("newtoggle", braced >>= newToggle)
425+ , ("toggletrue", braced >>= setToggle True)
426+ , ("togglefalse", braced >>= setToggle False)
427+ , ("iftoggle", try $ ifToggle >> inline)
428+ -- include
429+ , ("input", rawInlineOr "input" $ include "input")
430+ -- soul package
431+ , ("ul", underline <$> tok)
432+ -- ulem package
433+ , ("uline", underline <$> tok)
434+ -- plain tex stuff that should just be passed through as raw tex
435+ , ("ifdim", ifdim)
436+ ]
651437
652438lettrine :: PandocMonad m => LP m Inlines
653439lettrine = do
@@ -671,26 +457,6 @@ alterStr :: (Text -> Text) -> Inline -> Inline
671457alterStr f (Str xs) = Str (f xs)
672458alterStr _ x = x
673459
674−foreignlanguage :: PandocMonad m => LP m Inlines
675−foreignlanguage = do
676− babelLang <- untokenize <$> braced
677− case babelLangToBCP47 babelLang of
678− Just lang -> spanWith ("", [], [("lang", renderLang lang)]) <$> tok
679− _ -> tok
680−
681−inlineLanguageCommands :: PandocMonad m => M.Map Text (LP m Inlines)
682−inlineLanguageCommands = M.fromList $ mk <$> M.toList polyglossiaLangToBCP47
683− where
684− mk (polyglossia, bcp47Func) =
685− ("text" <> polyglossia, inlineLanguage bcp47Func)
686−
687−inlineLanguage :: PandocMonad m => (Text -> Lang) -> LP m Inlines
688−inlineLanguage bcp47Func = do
689− o <- option "" $ T.filter (\c -> c /= '[' && c /= ']')
690− <$> rawopt
691− let lang = renderLang $ bcp47Func o
692− extractSpaces (spanWith ("", [], [("lang", lang)])) <$> tok
693−
694460hyperlink :: PandocMonad m => LP m Inlines
695461hyperlink = try $ do
696462 src <- untokenize <$> braced
@@ -711,31 +477,6 @@ hypertargetInline = try $ do
711477 ils <- grouped inline
712478 return $ spanWith (ref, [], []) ils
713479
714−romanNumeralUpper :: (PandocMonad m) => LP m Inlines
715−romanNumeralUpper =
716− str . toRomanNumeral <$> romanNumeralArg
717−
718−romanNumeralLower :: (PandocMonad m) => LP m Inlines
719−romanNumeralLower =
720− str . T.toLower . toRomanNumeral <$> romanNumeralArg
721−
722−romanNumeralArg :: (PandocMonad m) => LP m Int
723−romanNumeralArg = spaces *> (parser <|> inBraces)
724− where
725− inBraces = do
726− symbol '{'
727− spaces
728− res <- parser
729− spaces
730− symbol '}'
731− return res
732− parser = do
733− s <- untokenize <$> many1 (satisfyTok isWordTok)
734− let (digits, rest) = T.span isDigit s
735− unless (T.null rest) $
736− Prelude.fail "Non-digits in argument to \\Rn or \\RN"
737− safeRead digits
738−
739480newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a
740481newToggle name = do
741482 updateState $ \st ->
@@ -766,9 +507,6 @@ ifToggle = do
766507 report $ UndefinedToggle name' pos
767508 return ()
768509
769−doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
770−doTerm term = str <$> translateTerm term
771−
772510ifstrequal :: (PandocMonad m, Monoid a) => LP m a
773511ifstrequal = do
774512 str1 <- tok
@@ -789,13 +527,6 @@ coloredInline stylename = do
789527ttfamily :: PandocMonad m => LP m Inlines
790528ttfamily = code . stringify . toList <$> tok
791529
792−rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines
793−rawInlineOr name' fallback = do
794− parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
795− if parseRaw
796− then rawInline "latex" <$> getRawCommand name' ("\\" <> name')
797− else fallback
798−
799530processHBox :: Inlines -> Inlines
800531processHBox = walk convert
801532 where
@@ -846,25 +577,6 @@ treatAsInline = Set.fromList
846577 , "pagebreak"
847578 ]
848579
849−dolabel :: PandocMonad m => LP m Inlines
850−dolabel = do
851− v <- braced
852− let refstr = untokenize v
853− updateState $ \st ->
854− st{ sLastLabel = Just refstr }
855− return $ spanWith (refstr,[],[("label", refstr)])
856− $ inBrackets $ str $ untokenize v
857−
858−doref :: PandocMonad m => Text -> LP m Inlines
859−doref cls = do
860− v <- braced
861− let refstr = untokenize v
862− return $ linkWith ("",[],[ ("reference-type", cls)
863− , ("reference", refstr)])
864− ("#" <> refstr)
865− ""
866− (inBrackets $ str refstr)
867−
868580lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
869581lookupListDefault d = (fromMaybe d .) . lookupList
870582 where lookupList l m = msum $ map (`M.lookup` m) l
src/Text/Pandoc/Readers/LaTeX/Accent.hsdeleted−78
@@ -1,78 +0,0 @@
1−{-# LANGUAGE ViewPatterns #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−module Text.Pandoc.Readers.LaTeX.Accent
4− ( accentCommands )
5−where
6−
7−import Text.Pandoc.Class
8−import Text.Pandoc.Readers.LaTeX.Parsing
9−import Text.Pandoc.Builder as B
10−import qualified Data.Map as M
11−import Data.Text (Text)
12−import Data.Maybe (fromMaybe)
13−import Text.Pandoc.Parsing
14−import qualified Data.Text as T
15−import qualified Data.Text.Normalize as Normalize
16−
17−accentCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines)
18−accentCommands tok =
19− let accent = accentWith tok
20− lit = pure . str
21− in M.fromList
22− [ ("aa", lit "å")
23− , ("AA", lit "Å")
24− , ("ss", lit "ß")
25− , ("o", lit "ø")
26− , ("O", lit "Ø")
27− , ("L", lit "Ł")
28− , ("l", lit "ł")
29− , ("ae", lit "æ")
30− , ("AE", lit "Æ")
31− , ("oe", lit "œ")
32− , ("OE", lit "Œ")
33− , ("pounds", lit "£")
34− , ("euro", lit "€")
35− , ("copyright", lit "©")
36− , ("textasciicircum", lit "^")
37− , ("textasciitilde", lit "~")
38− , ("H", accent '\779' Nothing) -- hungarumlaut
39− , ("`", accent '\768' (Just '`')) -- grave
40− , ("'", accent '\769' (Just '\'')) -- acute
41− , ("^", accent '\770' (Just '^')) -- circ
42− , ("~", accent '\771' (Just '~')) -- tilde
43− , ("\"", accent '\776' Nothing) -- umlaut
44− , (".", accent '\775' Nothing) -- dot
45− , ("=", accent '\772' Nothing) -- macron
46− , ("|", accent '\781' Nothing) -- vertical line above
47− , ("b", accent '\817' Nothing) -- macron below
48− , ("c", accent '\807' Nothing) -- cedilla
49− , ("G", accent '\783' Nothing) -- doublegrave
50− , ("h", accent '\777' Nothing) -- hookabove
51− , ("d", accent '\803' Nothing) -- dotbelow
52− , ("f", accent '\785' Nothing) -- inverted breve
53− , ("r", accent '\778' Nothing) -- ringabove
54− , ("t", accent '\865' Nothing) -- double inverted breve
55− , ("U", accent '\782' Nothing) -- double vertical line above
56− , ("v", accent '\780' Nothing) -- hacek
57− , ("u", accent '\774' Nothing) -- breve
58− , ("k", accent '\808' Nothing) -- ogonek
59− , ("textogonekcentered", accent '\808' Nothing) -- ogonek
60− , ("i", lit "ı") -- dotless i
61− , ("j", lit "ȷ") -- dotless j
62− , ("newtie", accent '\785' Nothing) -- inverted breve
63− , ("textcircled", accent '\8413' Nothing) -- combining circle
64− ]
65−
66−accentWith :: PandocMonad m
67− => LP m Inlines -> Char -> Maybe Char -> LP m Inlines
68−accentWith tok combiningAccent fallBack = try $ do
69− ils <- tok
70− case toList ils of
71− (Str (T.uncons -> Just (x, xs)) : ys) -> return $ fromList $
72− -- try to normalize to the combined character:
73− Str (Normalize.normalize Normalize.NFC
74− (T.pack [x, combiningAccent]) <> xs) : ys
75− [Space] -> return $ str $ T.singleton $ fromMaybe combiningAccent fallBack
76− [] -> return $ str $ T.singleton $ fromMaybe combiningAccent fallBack
77− _ -> return ils
78−
src/Text/Pandoc/Readers/LaTeX/Inline.hsnew file+397
@@ -0,0 +1,397 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ViewPatterns #-}
3+{- |
4+ Module : Text.Pandoc.Readers.LaTeX.Inline
5+ Copyright : Copyright (C) 2006-2021 John MacFarlane
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : John MacFarlane <jgm@berkeley.edu>
9+ Stability : alpha
10+ Portability : portable
11+-}
12+module Text.Pandoc.Readers.LaTeX.Inline
13+ ( acronymCommands
14+ , verbCommands
15+ , charCommands
16+ , accentCommands
17+ , nameCommands
18+ , biblatexInlineCommands
19+ , refCommands
20+ , rawInlineOr
21+ , listingsLanguage
22+ )
23+where
24+
25+import qualified Data.Map as M
26+import Data.Text (Text)
27+import qualified Data.Text as T
28+import Text.Pandoc.Builder
29+import Text.Pandoc.Shared (toRomanNumeral, safeRead)
30+import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
31+import Control.Applicative (optional, (<|>))
32+import Control.Monad (guard, mzero, mplus, unless)
33+import Text.Pandoc.Class.PandocMonad (PandocMonad (..), translateTerm)
34+import Text.Pandoc.Readers.LaTeX.Parsing
35+import Text.Pandoc.Extensions (extensionEnabled, Extension(..))
36+import Text.Pandoc.Parsing (getOption, updateState, getState, notFollowedBy,
37+ manyTill, getInput, setInput, incSourceColumn,
38+ option, many1, try)
39+import Data.Char (isDigit)
40+import Text.Pandoc.Highlighting (fromListingsLanguage,)
41+import Data.Maybe (maybeToList, fromMaybe)
42+import Text.Pandoc.Options (ReaderOptions(..))
43+import qualified Data.Text.Normalize as Normalize
44+import qualified Text.Pandoc.Translations as Translations
45+
46+rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines
47+rawInlineOr name' fallback = do
48+ parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
49+ if parseRaw
50+ then rawInline "latex" <$> getRawCommand name' ("\\" <> name')
51+ else fallback
52+
53+dolabel :: PandocMonad m => LP m Inlines
54+dolabel = do
55+ v <- braced
56+ let refstr = untokenize v
57+ updateState $ \st ->
58+ st{ sLastLabel = Just refstr }
59+ return $ spanWith (refstr,[],[("label", refstr)])
60+ $ inBrackets $ str $ untokenize v
61+
62+doref :: PandocMonad m => Text -> LP m Inlines
63+doref cls = do
64+ v <- braced
65+ let refstr = untokenize v
66+ return $ linkWith ("",[],[ ("reference-type", cls)
67+ , ("reference", refstr)])
68+ ("#" <> refstr)
69+ ""
70+ (inBrackets $ str refstr)
71+
72+inBrackets :: Inlines -> Inlines
73+inBrackets x = str "[" <> x <> str "]"
74+
75+doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
76+doTerm term = str <$> translateTerm term
77+
78+lit :: Text -> LP m Inlines
79+lit = pure . str
80+
81+doverb :: PandocMonad m => LP m Inlines
82+doverb = do
83+ Tok _ Symbol t <- anySymbol
84+ marker <- case T.uncons t of
85+ Just (c, ts) | T.null ts -> return c
86+ _ -> mzero
87+ withVerbatimMode $
88+ code . untokenize <$>
89+ manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker)
90+
91+verbTok :: PandocMonad m => Char -> LP m Tok
92+verbTok stopchar = do
93+ t@(Tok pos toktype txt) <- anyTok
94+ case T.findIndex (== stopchar) txt of
95+ Nothing -> return t
96+ Just i -> do
97+ let (t1, t2) = T.splitAt i txt
98+ inp <- getInput
99+ setInput $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar)
100+ : totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp
101+ return $ Tok pos toktype t1
102+
103+listingsLanguage :: [(Text, Text)] -> Maybe Text
104+listingsLanguage opts =
105+ case lookup "language" opts of
106+ Nothing -> Nothing
107+ Just l -> fromListingsLanguage l `mplus` Just l
108+
109+dolstinline :: PandocMonad m => LP m Inlines
110+dolstinline = do
111+ options <- option [] keyvals
112+ let classes = maybeToList $ listingsLanguage options
113+ doinlinecode classes
114+
115+domintinline :: PandocMonad m => LP m Inlines
116+domintinline = do
117+ skipopts
118+ cls <- untokenize <$> braced
119+ doinlinecode [cls]
120+
121+doinlinecode :: PandocMonad m => [Text] -> LP m Inlines
122+doinlinecode classes = do
123+ Tok _ Symbol t <- anySymbol
124+ marker <- case T.uncons t of
125+ Just (c, ts) | T.null ts -> return c
126+ _ -> mzero
127+ let stopchar = if marker == '{' then '}' else marker
128+ withVerbatimMode $
129+ codeWith ("",classes,[]) . T.map nlToSpace . untokenize <$>
130+ manyTill (verbTok stopchar) (symbol stopchar)
131+
132+nlToSpace :: Char -> Char
133+nlToSpace '\n' = ' '
134+nlToSpace x = x
135+
136+romanNumeralUpper :: (PandocMonad m) => LP m Inlines
137+romanNumeralUpper =
138+ str . toRomanNumeral <$> romanNumeralArg
139+
140+romanNumeralLower :: (PandocMonad m) => LP m Inlines
141+romanNumeralLower =
142+ str . T.toLower . toRomanNumeral <$> romanNumeralArg
143+
144+romanNumeralArg :: (PandocMonad m) => LP m Int
145+romanNumeralArg = spaces *> (parser <|> inBraces)
146+ where
147+ inBraces = do
148+ symbol '{'
149+ spaces
150+ res <- parser
151+ spaces
152+ symbol '}'
153+ return res
154+ parser = do
155+ s <- untokenize <$> many1 (satisfyTok isWordTok)
156+ let (digits, rest) = T.span isDigit s
157+ unless (T.null rest) $
158+ Prelude.fail "Non-digits in argument to \\Rn or \\RN"
159+ safeRead digits
160+
161+accentWith :: PandocMonad m
162+ => LP m Inlines -> Char -> Maybe Char -> LP m Inlines
163+accentWith tok combiningAccent fallBack = try $ do
164+ ils <- tok
165+ case toList ils of
166+ (Str (T.uncons -> Just (x, xs)) : ys) -> return $ fromList $
167+ -- try to normalize to the combined character:
168+ Str (Normalize.normalize Normalize.NFC
169+ (T.pack [x, combiningAccent]) <> xs) : ys
170+ [Space] -> return $ str $ T.singleton
171+ $ fromMaybe combiningAccent fallBack
172+ [] -> return $ str $ T.singleton
173+ $ fromMaybe combiningAccent fallBack
174+ _ -> return ils
175+
176+
177+verbCommands :: PandocMonad m => M.Map Text (LP m Inlines)
178+verbCommands = M.fromList
179+ [ ("verb", doverb)
180+ , ("lstinline", dolstinline)
181+ , ("mintinline", domintinline)
182+ , ("Verb", doverb)
183+ ]
184+
185+accentCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines)
186+accentCommands tok =
187+ let accent = accentWith tok
188+ in M.fromList
189+ [ ("aa", lit "å")
190+ , ("AA", lit "Å")
191+ , ("ss", lit "ß")
192+ , ("o", lit "ø")
193+ , ("O", lit "Ø")
194+ , ("L", lit "Ł")
195+ , ("l", lit "ł")
196+ , ("ae", lit "æ")
197+ , ("AE", lit "Æ")
198+ , ("oe", lit "œ")
199+ , ("OE", lit "Œ")
200+ , ("pounds", lit "£")
201+ , ("euro", lit "€")
202+ , ("copyright", lit "©")
203+ , ("textasciicircum", lit "^")
204+ , ("textasciitilde", lit "~")
205+ , ("H", accent '\779' Nothing) -- hungarumlaut
206+ , ("`", accent '\768' (Just '`')) -- grave
207+ , ("'", accent '\769' (Just '\'')) -- acute
208+ , ("^", accent '\770' (Just '^')) -- circ
209+ , ("~", accent '\771' (Just '~')) -- tilde
210+ , ("\"", accent '\776' Nothing) -- umlaut
211+ , (".", accent '\775' Nothing) -- dot
212+ , ("=", accent '\772' Nothing) -- macron
213+ , ("|", accent '\781' Nothing) -- vertical line above
214+ , ("b", accent '\817' Nothing) -- macron below
215+ , ("c", accent '\807' Nothing) -- cedilla
216+ , ("G", accent '\783' Nothing) -- doublegrave
217+ , ("h", accent '\777' Nothing) -- hookabove
218+ , ("d", accent '\803' Nothing) -- dotbelow
219+ , ("f", accent '\785' Nothing) -- inverted breve
220+ , ("r", accent '\778' Nothing) -- ringabove
221+ , ("t", accent '\865' Nothing) -- double inverted breve
222+ , ("U", accent '\782' Nothing) -- double vertical line above
223+ , ("v", accent '\780' Nothing) -- hacek
224+ , ("u", accent '\774' Nothing) -- breve
225+ , ("k", accent '\808' Nothing) -- ogonek
226+ , ("textogonekcentered", accent '\808' Nothing) -- ogonek
227+ , ("i", lit "ı") -- dotless i
228+ , ("j", lit "ȷ") -- dotless j
229+ , ("newtie", accent '\785' Nothing) -- inverted breve
230+ , ("textcircled", accent '\8413' Nothing) -- combining circle
231+ ]
232+
233+charCommands :: PandocMonad m => M.Map Text (LP m Inlines)
234+charCommands = M.fromList
235+ [ ("ldots", lit "…")
236+ , ("vdots", lit "\8942")
237+ , ("dots", lit "…")
238+ , ("mdots", lit "…")
239+ , ("sim", lit "~")
240+ , ("sep", lit ",")
241+ , ("P", lit "¶")
242+ , ("S", lit "§")
243+ , ("$", lit "$")
244+ , ("%", lit "%")
245+ , ("&", lit "&")
246+ , ("#", lit "#")
247+ , ("_", lit "_")
248+ , ("{", lit "{")
249+ , ("}", lit "}")
250+ , ("qed", lit "\a0\x25FB")
251+ , ("lq", return (str "‘"))
252+ , ("rq", return (str "’"))
253+ , ("textquoteleft", return (str "‘"))
254+ , ("textquoteright", return (str "’"))
255+ , ("textquotedblleft", return (str "“"))
256+ , ("textquotedblright", return (str "”"))
257+ , ("/", pure mempty) -- italic correction
258+ , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
259+ guard $ not inTableCell
260+ optional rawopt
261+ spaces))
262+ , (",", lit "\8198")
263+ , ("@", pure mempty)
264+ , (" ", lit "\160")
265+ , ("ps", pure $ str "PS." <> space)
266+ , ("TeX", lit "TeX")
267+ , ("LaTeX", lit "LaTeX")
268+ , ("bar", lit "|")
269+ , ("textless", lit "<")
270+ , ("textgreater", lit ">")
271+ , ("textbackslash", lit "\\")
272+ , ("backslash", lit "\\")
273+ , ("slash", lit "/")
274+ -- fontawesome
275+ , ("faCheck", lit "\10003")
276+ , ("faClose", lit "\10007")
277+ -- hyphenat
278+ , ("bshyp", lit "\\\173")
279+ , ("fshyp", lit "/\173")
280+ , ("dothyp", lit ".\173")
281+ , ("colonhyp", lit ":\173")
282+ , ("hyp", lit "-")
283+ ]
284+
285+biblatexInlineCommands :: PandocMonad m
286+ => LP m Inlines -> M.Map Text (LP m Inlines)
287+biblatexInlineCommands tok = M.fromList
288+ -- biblatex misc
289+ [ ("RN", romanNumeralUpper)
290+ , ("Rn", romanNumeralLower)
291+ , ("mkbibquote", spanWith nullAttr . doubleQuoted <$> tok)
292+ , ("mkbibemph", spanWith nullAttr . emph <$> tok)
293+ , ("mkbibitalic", spanWith nullAttr . emph <$> tok)
294+ , ("mkbibbold", spanWith nullAttr . strong <$> tok)
295+ , ("mkbibparens",
296+ spanWith nullAttr . (\x -> str "(" <> x <> str ")") <$> tok)
297+ , ("mkbibbrackets",
298+ spanWith nullAttr . (\x -> str "[" <> x <> str "]") <$> tok)
299+ , ("autocap", spanWith nullAttr <$> tok)
300+ , ("textnormal", spanWith ("",["nodecor"],[]) <$> tok)
301+ , ("bibstring",
302+ (\x -> spanWith ("",[],[("bibstring",x)]) (str x)) . untokenize
303+ <$> braced)
304+ , ("adddot", pure (str "."))
305+ , ("adddotspace", pure (spanWith nullAttr (str "." <> space)))
306+ , ("addabbrvspace", pure space)
307+ , ("hyphen", pure (str "-"))
308+ ]
309+
310+nameCommands :: PandocMonad m => M.Map Text (LP m Inlines)
311+nameCommands = M.fromList
312+ [ ("figurename", doTerm Translations.Figure)
313+ , ("prefacename", doTerm Translations.Preface)
314+ , ("refname", doTerm Translations.References)
315+ , ("bibname", doTerm Translations.Bibliography)
316+ , ("chaptername", doTerm Translations.Chapter)
317+ , ("partname", doTerm Translations.Part)
318+ , ("contentsname", doTerm Translations.Contents)
319+ , ("listfigurename", doTerm Translations.ListOfFigures)
320+ , ("listtablename", doTerm Translations.ListOfTables)
321+ , ("indexname", doTerm Translations.Index)
322+ , ("abstractname", doTerm Translations.Abstract)
323+ , ("tablename", doTerm Translations.Table)
324+ , ("enclname", doTerm Translations.Encl)
325+ , ("ccname", doTerm Translations.Cc)
326+ , ("headtoname", doTerm Translations.To)
327+ , ("pagename", doTerm Translations.Page)
328+ , ("seename", doTerm Translations.See)
329+ , ("seealsoname", doTerm Translations.SeeAlso)
330+ , ("proofname", doTerm Translations.Proof)
331+ , ("glossaryname", doTerm Translations.Glossary)
332+ , ("lstlistingname", doTerm Translations.Listing)
333+ ]
334+
335+refCommands :: PandocMonad m => M.Map Text (LP m Inlines)
336+refCommands = M.fromList
337+ [ ("label", rawInlineOr "label" dolabel)
338+ , ("ref", rawInlineOr "ref" $ doref "ref")
339+ , ("cref", rawInlineOr "cref" $ doref "ref") -- from cleveref.sty
340+ , ("vref", rawInlineOr "vref" $ doref "ref+page") -- from varioref.sty
341+ , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty
342+ ]
343+
344+acronymCommands :: PandocMonad m => M.Map Text (LP m Inlines)
345+acronymCommands = M.fromList
346+ -- glossaries package
347+ [ ("gls", doAcronym "short")
348+ , ("Gls", doAcronym "short")
349+ , ("glsdesc", doAcronym "long")
350+ , ("Glsdesc", doAcronym "long")
351+ , ("GLSdesc", doAcronym "long")
352+ , ("acrlong", doAcronym "long")
353+ , ("Acrlong", doAcronym "long")
354+ , ("acrfull", doAcronym "full")
355+ , ("Acrfull", doAcronym "full")
356+ , ("acrshort", doAcronym "abbrv")
357+ , ("Acrshort", doAcronym "abbrv")
358+ , ("glspl", doAcronymPlural "short")
359+ , ("Glspl", doAcronymPlural "short")
360+ , ("glsdescplural", doAcronymPlural "long")
361+ , ("Glsdescplural", doAcronymPlural "long")
362+ , ("GLSdescplural", doAcronymPlural "long")
363+ -- acronyms package
364+ , ("ac", doAcronym "short")
365+ , ("acf", doAcronym "full")
366+ , ("acs", doAcronym "abbrv")
367+ , ("acl", doAcronym "long")
368+ , ("acp", doAcronymPlural "short")
369+ , ("acfp", doAcronymPlural "full")
370+ , ("acsp", doAcronymPlural "abbrv")
371+ , ("aclp", doAcronymPlural "long")
372+ , ("Ac", doAcronym "short")
373+ , ("Acf", doAcronym "full")
374+ , ("Acs", doAcronym "abbrv")
375+ , ("Acl", doAcronym "long")
376+ , ("Acp", doAcronymPlural "short")
377+ , ("Acfp", doAcronymPlural "full")
378+ , ("Acsp", doAcronymPlural "abbrv")
379+ , ("Aclp", doAcronymPlural "long")
380+ ]
381+
382+doAcronym :: PandocMonad m => Text -> LP m Inlines
383+doAcronym form = do
384+ acro <- braced
385+ return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
386+ ("acronym-form", "singular+" <> form)])
387+ $ str $ untokenize acro]
388+
389+doAcronymPlural :: PandocMonad m => Text -> LP m Inlines
390+doAcronymPlural form = do
391+ acro <- braced
392+ let plural = str "s"
393+ return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
394+ ("acronym-form", "plural+" <> form)]) $
395+ mconcat [str $ untokenize acro, plural]]
396+
397+
src/Text/Pandoc/Readers/LaTeX/Lang.hs+60−2
@@ -15,15 +15,73 @@ module Text.Pandoc.Readers.LaTeX.Lang
1515 ( setDefaultLanguage
1616 , polyglossiaLangToBCP47
1717 , babelLangToBCP47
18+ , enquoteCommands
19+ , inlineLanguageCommands
1820 )
1921where
2022import qualified Data.Map as M
23+import Data.Text (Text)
2124import qualified Data.Text as T
25+import Text.Pandoc.Shared (extractSpaces)
2226import Text.Pandoc.BCP47 (Lang(..), renderLang)
2327import Text.Pandoc.Class (PandocMonad(..), setTranslations)
2428import Text.Pandoc.Readers.LaTeX.Parsing
25−import Text.Pandoc.Parsing (updateState, option)
26−import Text.Pandoc.Builder (Blocks, setMeta, str)
29+import Text.Pandoc.Parsing (updateState, option, getState, QuoteContext(..),
30+ withQuoteContext)
31+import Text.Pandoc.Builder (Blocks, Inlines, setMeta, str, spanWith,
32+ singleQuoted, doubleQuoted)
33+
34+enquote :: PandocMonad m
35+ => LP m Inlines
36+ -> Bool -> Maybe Text -> LP m Inlines
37+enquote tok starred mblang = do
38+ skipopts
39+ let lang = mblang >>= babelLangToBCP47
40+ let langspan = case lang of
41+ Nothing -> id
42+ Just l -> spanWith ("",[],[("lang", renderLang l)])
43+ quoteContext <- sQuoteContext <$> getState
44+ if starred || quoteContext == InDoubleQuote
45+ then singleQuoted . langspan <$> withQuoteContext InSingleQuote tok
46+ else doubleQuoted . langspan <$> withQuoteContext InDoubleQuote tok
47+
48+enquoteCommands :: PandocMonad m
49+ => LP m Inlines -> M.Map Text (LP m Inlines)
50+enquoteCommands tok = M.fromList
51+ [ ("enquote*", enquote tok True Nothing)
52+ , ("enquote", enquote tok False Nothing)
53+ -- foreignquote is supposed to use native quote marks
54+ , ("foreignquote*", braced >>= enquote tok True . Just . untokenize)
55+ , ("foreignquote", braced >>= enquote tok False . Just . untokenize)
56+ -- hypehnquote uses regular quotes
57+ , ("hyphenquote*", braced >>= enquote tok True . Just . untokenize)
58+ , ("hyphenquote", braced >>= enquote tok False . Just . untokenize)
59+ ]
60+
61+foreignlanguage :: PandocMonad m => LP m Inlines -> LP m Inlines
62+foreignlanguage tok = do
63+ babelLang <- untokenize <$> braced
64+ case babelLangToBCP47 babelLang of
65+ Just lang -> spanWith ("", [], [("lang", renderLang lang)]) <$> tok
66+ _ -> tok
67+
68+inlineLanguageCommands :: PandocMonad m
69+ => LP m Inlines -> M.Map Text (LP m Inlines)
70+inlineLanguageCommands tok =
71+ M.fromList $
72+ ("foreignlanguage", foreignlanguage tok) :
73+ (mk <$> M.toList polyglossiaLangToBCP47)
74+ where
75+ mk (polyglossia, bcp47Func) =
76+ ("text" <> polyglossia, inlineLanguage tok bcp47Func)
77+
78+inlineLanguage :: PandocMonad m
79+ => LP m Inlines -> (Text -> Lang) -> LP m Inlines
80+inlineLanguage tok bcp47Func = do
81+ o <- option "" $ T.filter (\c -> c /= '[' && c /= ']')
82+ <$> rawopt
83+ let lang = renderLang $ bcp47Func o
84+ extractSpaces (spanWith ("", [], [("lang", lang)])) <$> tok
2785
2886setDefaultLanguage :: PandocMonad m => LP m Blocks
2987setDefaultLanguage = do
src/Text/Pandoc/Readers/Markdown.hs+1−1
@@ -1725,7 +1725,7 @@ str = do
17251725 updateLastStrPos
17261726 (do guardEnabled Ext_smart
17271727 abbrevs <- getOption readerAbbreviations
1728− if not (T.null result) && T.last result == '.' && result `Set.member` abbrevs
1728+ if result `Set.member` abbrevs
17291729 then try (do ils <- whitespace
17301730 notFollowedBy (() <$ cite <|> () <$ note)
17311731 -- ?? lookAhead alphaNum
src/Text/Pandoc/Shared.hs+12−8
@@ -95,7 +95,7 @@ module Text.Pandoc.Shared (
9595 safeRead,
9696 safeStrRead,
9797 -- * User data directory
98− defaultUserDataDirs,
98+ defaultUserDataDir,
9999 -- * Version
100100 pandocVersion
101101 ) where
@@ -1012,12 +1012,16 @@ safeStrRead s = case reads s of
10121012--
10131013
10141014-- | Return appropriate user data directory for platform. We use
1015−-- XDG_DATA_HOME (or its default value), but fall back to the
1016−-- legacy user data directory ($HOME/.pandoc on *nix) if this is
1017−-- missing.
1018−defaultUserDataDirs :: IO [FilePath]
1019−defaultUserDataDirs = E.catch (do
1015+-- XDG_DATA_HOME (or its default value), but for backwards compatibility,
1016+-- we fall back to the legacy user data directory ($HOME/.pandoc on *nix)
1017+-- if the XDG_DATA_HOME is missing and this exists. If neither directory
1018+-- is present, we return the XDG data directory.
1019+defaultUserDataDir :: IO FilePath
1020+defaultUserDataDir = do
10201021 xdgDir <- getXdgDirectory XdgData "pandoc"
10211022 legacyDir <- getAppUserDataDirectory "pandoc"
1022− return $ ordNub [xdgDir, legacyDir])
1023− (\(_ :: E.SomeException) -> return [])
1023+ xdgExists <- doesDirectoryExist xdgDir
1024+ legacyDirExists <- doesDirectoryExist legacyDir
1025+ if not xdgExists && legacyDirExists
1026+ then return legacyDir
1027+ else return xdgDir
src/Text/Pandoc/Writers/LaTeX.hs+15−551
@@ -18,11 +18,9 @@ module Text.Pandoc.Writers.LaTeX (
1818 writeLaTeX
1919 , writeBeamer
2020 ) where
21−import Control.Applicative ((<|>))
2221import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
24− isPunctuation, ord)
25−import Data.List (foldl', intersperse, nubBy, (\\), uncons)
22+import Data.Char (isDigit)
23+import Data.List (intersperse, nubBy, (\\))
2624import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
2725import qualified Data.Map as M
2826import Data.Text (Text)
@@ -34,7 +32,7 @@ import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
3432import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
3533import Text.Pandoc.Definition
3634import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
37− styleToLaTeX, toListingsLanguage)
35+ styleToLaTeX)
3836import Text.Pandoc.ImageSize
3937import Text.Pandoc.Logging
4038import Text.Pandoc.Options
@@ -44,10 +42,15 @@ import Text.Pandoc.Slides
4442import Text.Pandoc.Walk (query, walk, walkM)
4543import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
4644import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
45+import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
46+ citationsToBiblatex)
4747import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
48+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossia, toBabel)
49+import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
50+ toLabel, inCmd,
51+ wrapDiv, hypertarget, labelFor,
52+ getListingsLanguage, mbBraced)
4853import Text.Pandoc.Writers.Shared
49−import Text.Printf (printf)
50−import qualified Data.Text.Normalize as Normalize
5154import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
5255
5356-- | Convert Pandoc to LaTeX.
@@ -241,152 +244,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
241244 Nothing -> main
242245 Just tpl -> renderTemplate tpl context'
243246
244−data StringContext = TextString
245− | URLString
246− | CodeString
247− deriving (Eq)
248−
249−-- escape things as needed for LaTeX
250−stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
251−stringToLaTeX context zs = do
252− opts <- gets stOptions
253− return $ T.pack $
254− foldr (go opts context) mempty $ T.unpack $
255− if writerPreferAscii opts
256− then Normalize.normalize Normalize.NFD zs
257− else zs
258− where
259− go :: WriterOptions -> StringContext -> Char -> String -> String
260− go opts ctx x xs =
261− let ligatures = isEnabled Ext_smart opts && ctx == TextString
262− isUrl = ctx == URLString
263− mbAccentCmd =
264− if writerPreferAscii opts && ctx == TextString
265− then uncons xs >>= \(c,_) -> lookupAccent c
266− else Nothing
267− emits s =
268− case mbAccentCmd of
269− Just cmd ->
270− cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
271− Nothing -> s <> xs
272− emitc c =
273− case mbAccentCmd of
274− Just cmd ->
275− cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
276− Nothing -> c : xs
277− emitcseq cs =
278− case xs of
279− c:_ | isLetter c
280− , ctx == TextString
281− -> cs <> " " <> xs
282− | isSpace c -> cs <> "{}" <> xs
283− | ctx == TextString
284− -> cs <> xs
285− _ -> cs <> "{}" <> xs
286− emitquote cs =
287− case xs of
288− '`':_ -> cs <> "\\," <> xs -- add thin space
289− '\'':_ -> cs <> "\\," <> xs -- add thin space
290− _ -> cs <> xs
291− in case x of
292− '?' | ligatures -> -- avoid ?` ligature
293− case xs of
294− '`':_ -> emits "?{}"
295− _ -> emitc x
296− '!' | ligatures -> -- avoid !` ligature
297− case xs of
298− '`':_ -> emits "!{}"
299− _ -> emitc x
300− '{' -> emits "\\{"
301− '}' -> emits "\\}"
302− '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
303− '$' | not isUrl -> emits "\\$"
304− '%' -> emits "\\%"
305− '&' -> emits "\\&"
306− '_' | not isUrl -> emits "\\_"
307− '#' -> emits "\\#"
308− '-' | not isUrl -> case xs of
309− -- prevent adjacent hyphens from forming ligatures
310− ('-':_) -> emits "-\\/"
311− _ -> emitc '-'
312− '~' | not isUrl -> emitcseq "\\textasciitilde"
313− '^' -> emits "\\^{}"
314− '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
315− | otherwise -> emitcseq "\\textbackslash"
316− '|' | not isUrl -> emitcseq "\\textbar"
317− '<' -> emitcseq "\\textless"
318− '>' -> emitcseq "\\textgreater"
319− '[' -> emits "{[}" -- to avoid interpretation as
320− ']' -> emits "{]}" -- optional arguments
321− '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
322− '\160' -> emits "~"
323− '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
324− '\x202F' -> emits "\\,"
325− '\x2026' -> emitcseq "\\ldots"
326− '\x2018' | ligatures -> emitquote "`"
327− '\x2019' | ligatures -> emitquote "'"
328− '\x201C' | ligatures -> emitquote "``"
329− '\x201D' | ligatures -> emitquote "''"
330− '\x2014' | ligatures -> emits "---"
331− '\x2013' | ligatures -> emits "--"
332− _ | writerPreferAscii opts
333− -> case x of
334− 'ı' -> emitcseq "\\i"
335− 'ȷ' -> emitcseq "\\j"
336− 'å' -> emitcseq "\\aa"
337− 'Å' -> emitcseq "\\AA"
338− 'ß' -> emitcseq "\\ss"
339− 'ø' -> emitcseq "\\o"
340− 'Ø' -> emitcseq "\\O"
341− 'Ł' -> emitcseq "\\L"
342− 'ł' -> emitcseq "\\l"
343− 'æ' -> emitcseq "\\ae"
344− 'Æ' -> emitcseq "\\AE"
345− 'œ' -> emitcseq "\\oe"
346− 'Œ' -> emitcseq "\\OE"
347− '£' -> emitcseq "\\pounds"
348− '€' -> emitcseq "\\euro"
349− '©' -> emitcseq "\\copyright"
350− _ -> emitc x
351− | otherwise -> emitc x
352−
353−lookupAccent :: Char -> Maybe String
354−lookupAccent '\779' = Just "\\H"
355−lookupAccent '\768' = Just "\\`"
356−lookupAccent '\769' = Just "\\'"
357−lookupAccent '\770' = Just "\\^"
358−lookupAccent '\771' = Just "\\~"
359−lookupAccent '\776' = Just "\\\""
360−lookupAccent '\775' = Just "\\."
361−lookupAccent '\772' = Just "\\="
362−lookupAccent '\781' = Just "\\|"
363−lookupAccent '\817' = Just "\\b"
364−lookupAccent '\807' = Just "\\c"
365−lookupAccent '\783' = Just "\\G"
366−lookupAccent '\777' = Just "\\h"
367−lookupAccent '\803' = Just "\\d"
368−lookupAccent '\785' = Just "\\f"
369−lookupAccent '\778' = Just "\\r"
370−lookupAccent '\865' = Just "\\t"
371−lookupAccent '\782' = Just "\\U"
372−lookupAccent '\780' = Just "\\v"
373−lookupAccent '\774' = Just "\\u"
374−lookupAccent '\808' = Just "\\k"
375−lookupAccent '\8413' = Just "\\textcircled"
376−lookupAccent _ = Nothing
377−
378−toLabel :: PandocMonad m => Text -> LW m Text
379−toLabel z = go `fmap` stringToLaTeX URLString z
380− where
381− go = T.concatMap $ \x -> case x of
382− _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
383− | x `elemText` "_-+=:;." -> T.singleton x
384− | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
385−
386−-- | Puts contents into LaTeX command.
387−inCmd :: Text -> Doc Text -> Doc Text
388−inCmd cmd contents = char '\\' <> literal cmd <> braces contents
389−
390247toSlides :: PandocMonad m => [Block] -> LW m [Block]
391248toSlides bs = do
392249 opts <- gets stOptions
@@ -851,81 +708,6 @@ sectionHeader classes ident level lst = do
851708 braces txtNoNotes
852709 else empty
853710
854−mapAlignment :: Text -> Text
855−mapAlignment a = case a of
856− "top" -> "T"
857− "top-baseline" -> "t"
858− "bottom" -> "b"
859− "center" -> "c"
860− _ -> a
861−
862−wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
863−wrapDiv (_,classes,kvs) t = do
864− beamer <- gets stBeamer
865− let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
866− lang <- toLang $ lookup "lang" kvs
867− let wrapColumns = if beamer && "columns" `elem` classes
868− then \contents ->
869− let valign = maybe "T" mapAlignment (lookup "align" kvs)
870− totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
871− (lookup "totalwidth" kvs)
872− onlytextwidth = filter ("onlytextwidth" ==) classes
873− options = text $ T.unpack $ T.intercalate "," $
874− valign : totalwidth ++ onlytextwidth
875− in inCmd "begin" "columns" <> brackets options
876− $$ contents
877− $$ inCmd "end" "columns"
878− else id
879− wrapColumn = if beamer && "column" `elem` classes
880− then \contents ->
881− let valign =
882− maybe ""
883− (brackets . text . T.unpack . mapAlignment)
884− (lookup "align" kvs)
885− w = maybe "0.48" fromPct (lookup "width" kvs)
886− in inCmd "begin" "column" <>
887− valign <>
888− braces (literal w <> "\\textwidth")
889− $$ contents
890− $$ inCmd "end" "column"
891− else id
892− fromPct xs =
893− case T.unsnoc xs of
894− Just (ds, '%') -> case safeRead ds of
895− Just digits -> showFl (digits / 100 :: Double)
896− Nothing -> xs
897− _ -> xs
898− wrapDir = case lookup "dir" kvs of
899− Just "rtl" -> align "RTL"
900− Just "ltr" -> align "LTR"
901− _ -> id
902− wrapLang txt = case lang of
903− Just lng -> let (l, o) = toPolyglossiaEnv lng
904− ops = if T.null o
905− then ""
906− else brackets $ literal o
907− in inCmd "begin" (literal l) <> ops
908− $$ blankline <> txt <> blankline
909− $$ inCmd "end" (literal l)
910− Nothing -> txt
911− return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
912−
913−hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
914−hypertarget _ "" x = return x
915−hypertarget addnewline ident x = do
916− ref <- literal `fmap` toLabel ident
917− return $ text "\\hypertarget"
918− <> braces ref
919− <> braces ((if addnewline && not (isEmpty x)
920− then "%" <> cr
921− else empty) <> x)
922−
923−labelFor :: PandocMonad m => Text -> LW m (Doc Text)
924−labelFor "" = return empty
925−labelFor ident = do
926− ref <- literal `fmap` toLabel ident
927− return $ text "\\label" <> braces ref
928−
929711-- | Convert list of inline elements to LaTeX.
930712inlineListToLaTeX :: PandocMonad m
931713 => [Inline] -- ^ Inlines to convert
@@ -950,10 +732,6 @@ inlineListToLaTeX lst = hcat <$>
950732 fixInitialLineBreaks xs
951733 fixInitialLineBreaks xs = xs
952734
953−isQuoted :: Inline -> Bool
954−isQuoted (Quoted _ _) = True
955−isQuoted _ = False
956−
957735-- | Convert inline element to LaTeX
958736inlineToLaTeX :: PandocMonad m
959737 => Inline -- ^ Inline to convert
@@ -1014,8 +792,8 @@ inlineToLaTeX (Cite cits lst) = do
1014792 st <- get
1015793 let opts = stOptions st
1016794 case writerCiteMethod opts of
1017− Natbib -> citationsToNatbib cits
1018− Biblatex -> citationsToBiblatex cits
795+ Natbib -> citationsToNatbib inlineListToLaTeX cits
796+ Biblatex -> citationsToBiblatex inlineListToLaTeX cits
1019797 _ -> inlineListToLaTeX lst
1020798
1021799inlineToLaTeX (Code (_,classes,kvs) str) = do
@@ -1089,6 +867,9 @@ inlineToLaTeX (Quoted qt lst) = do
1089867 if isEnabled Ext_smart opts
1090868 then char '`' <> inner <> char '\''
1091869 else char '\x2018' <> inner <> char '\x2019'
870+ where
871+ isQuoted (Quoted _ _) = True
872+ isQuoted _ = False
1092873inlineToLaTeX (Str str) = do
1093874 setEmptyLine False
1094875 liftM literal $ stringToLaTeX TextString str
@@ -1226,153 +1007,6 @@ protectCode x = [x]
12261007setEmptyLine :: PandocMonad m => Bool -> LW m ()
12271008setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
12281009
1229−citationsToNatbib :: PandocMonad m => [Citation] -> LW m (Doc Text)
1230−citationsToNatbib
1231− [one]
1232− = citeCommand c p s k
1233− where
1234− Citation { citationId = k
1235− , citationPrefix = p
1236− , citationSuffix = s
1237− , citationMode = m
1238− }
1239− = one
1240− c = case m of
1241− AuthorInText -> "citet"
1242− SuppressAuthor -> "citeyearpar"
1243− NormalCitation -> "citep"
1244−
1245−citationsToNatbib cits
1246− | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
1247− = citeCommand "citep" p s ks
1248− where
1249− noPrefix = all (null . citationPrefix)
1250− noSuffix = all (null . citationSuffix)
1251− ismode m = all ((==) m . citationMode)
1252− p = citationPrefix $
1253− head cits
1254− s = citationSuffix $
1255− last cits
1256− ks = T.intercalate ", " $ map citationId cits
1257−
1258−citationsToNatbib (c:cs) | citationMode c == AuthorInText = do
1259− author <- citeCommand "citeauthor" [] [] (citationId c)
1260− cits <- citationsToNatbib (c { citationMode = SuppressAuthor } : cs)
1261− return $ author <+> cits
1262−
1263−citationsToNatbib cits = do
1264− cits' <- mapM convertOne cits
1265− return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
1266− where
1267− combineTwo a b | isEmpty a = b
1268− | otherwise = a <> text "; " <> b
1269− convertOne Citation { citationId = k
1270− , citationPrefix = p
1271− , citationSuffix = s
1272− , citationMode = m
1273− }
1274− = case m of
1275− AuthorInText -> citeCommand "citealt" p s k
1276− SuppressAuthor -> citeCommand "citeyear" p s k
1277− NormalCitation -> citeCommand "citealp" p s k
1278−
1279−citeCommand :: PandocMonad m
1280− => Text -> [Inline] -> [Inline] -> Text -> LW m (Doc Text)
1281−citeCommand c p s k = do
1282− args <- citeArguments p s k
1283− return $ literal ("\\" <> c) <> args
1284−
1285−type Prefix = [Inline]
1286−type Suffix = [Inline]
1287−type CiteId = Text
1288−data CiteGroup = CiteGroup Prefix Suffix [CiteId]
1289−
1290−citeArgumentsList :: PandocMonad m
1291− => CiteGroup -> LW m (Doc Text)
1292−citeArgumentsList (CiteGroup _ _ []) = return empty
1293−citeArgumentsList (CiteGroup pfxs sfxs ids) = do
1294− pdoc <- inlineListToLaTeX pfxs
1295− sdoc <- inlineListToLaTeX sfxs'
1296− return $ optargs pdoc sdoc <>
1297− braces (literal (T.intercalate "," (reverse ids)))
1298− where sfxs' = stripLocatorBraces $ case sfxs of
1299− (Str t : r) -> case T.uncons t of
1300− Just (x, xs)
1301− | T.null xs
1302− , isPunctuation x -> dropWhile (== Space) r
1303− | isPunctuation x -> Str xs : r
1304− _ -> sfxs
1305− _ -> sfxs
1306− optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of
1307− (True, True ) -> empty
1308− (True, False) -> brackets sdoc
1309− (_ , _ ) -> brackets pdoc <> brackets sdoc
1310−
1311−citeArguments :: PandocMonad m
1312− => [Inline] -> [Inline] -> Text -> LW m (Doc Text)
1313−citeArguments p s k = citeArgumentsList (CiteGroup p s [k])
1314−
1315−-- strip off {} used to define locator in pandoc-citeproc; see #5722
1316−stripLocatorBraces :: [Inline] -> [Inline]
1317−stripLocatorBraces = walk go
1318− where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs
1319− go x = x
1320−
1321−citationsToBiblatex :: PandocMonad m => [Citation] -> LW m (Doc Text)
1322−citationsToBiblatex
1323− [one]
1324− = citeCommand cmd p s k
1325− where
1326− Citation { citationId = k
1327− , citationPrefix = p
1328− , citationSuffix = s
1329− , citationMode = m
1330− } = one
1331− cmd = case m of
1332− SuppressAuthor -> "autocite*"
1333− AuthorInText -> "textcite"
1334− NormalCitation -> "autocite"
1335−
1336−citationsToBiblatex (c:cs)
1337− | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs)
1338− = do
1339− let cmd = case citationMode c of
1340− SuppressAuthor -> "\\autocite*"
1341− AuthorInText -> "\\textcite"
1342− NormalCitation -> "\\autocite"
1343− return $ text cmd <>
1344− braces (literal (T.intercalate "," (map citationId (c:cs))))
1345− | otherwise
1346− = do
1347− let cmd = case citationMode c of
1348− SuppressAuthor -> "\\autocites*"
1349− AuthorInText -> "\\textcites"
1350− NormalCitation -> "\\autocites"
1351−
1352− groups <- mapM citeArgumentsList (reverse (foldl' grouper [] (c:cs)))
1353−
1354− return $ text cmd <> mconcat groups
1355−
1356− where grouper prev cit = case prev of
1357− ((CiteGroup oPfx oSfx ids):rest)
1358− | null oSfx && null pfx -> CiteGroup oPfx sfx (cid:ids) : rest
1359− _ -> CiteGroup pfx sfx [cid] : prev
1360− where pfx = citationPrefix cit
1361− sfx = citationSuffix cit
1362− cid = citationId cit
1363−
1364−citationsToBiblatex _ = return empty
1365−
1366−-- Determine listings language from list of class attributes.
1367−getListingsLanguage :: [Text] -> Maybe Text
1368−getListingsLanguage xs
1369− = foldr ((<|>) . toListingsLanguage) Nothing xs
1370−
1371−mbBraced :: Text -> Text
1372−mbBraced x = if not (T.all isAlphaNum x)
1373− then "{" <> x <> "}"
1374− else x
1375−
13761010-- Extract a key from divs and spans
13771011extract :: Text -> Block -> [Text]
13781012extract key (Div attr _) = lookKey key attr
@@ -1390,174 +1024,4 @@ extractInline _ _ = []
13901024lookKey :: Text -> Attr -> [Text]
13911025lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs
13921026
1393−-- In environments \Arabic instead of \arabic is used
1394−toPolyglossiaEnv :: Lang -> (Text, Text)
1395−toPolyglossiaEnv l =
1396− case toPolyglossia l of
1397− ("arabic", o) -> ("Arabic", o)
1398− x -> x
1399−
1400−-- Takes a list of the constituents of a BCP 47 language code and
1401−-- converts it to a Polyglossia (language, options) tuple
1402−-- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf
1403−toPolyglossia :: Lang -> (Text, Text)
1404−toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria")
1405−toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq")
1406−toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq")
1407−toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq")
1408−toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya")
1409−toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco")
1410−toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania")
1411−toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq")
1412−toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq")
1413−toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia")
1414−toPolyglossia (Lang "de" _ _ vars)
1415− | "1901" `elem` vars = ("german", "spelling=old")
1416−toPolyglossia (Lang "de" _ "AT" vars)
1417− | "1901" `elem` vars = ("german", "variant=austrian, spelling=old")
1418−toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian")
1419−toPolyglossia (Lang "de" _ "CH" vars)
1420− | "1901" `elem` vars = ("german", "variant=swiss, spelling=old")
1421−toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss")
1422−toPolyglossia (Lang "de" _ _ _) = ("german", "")
1423−toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "")
1424−toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly")
1425−toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian")
1426−toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian")
1427−toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british")
1428−toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand")
1429−toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british")
1430−toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american")
1431−toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient")
1432−toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "")
1433−toPolyglossia (Lang "la" _ _ vars)
1434− | "x-classic" `elem` vars = ("latin", "variant=classic")
1435−toPolyglossia (Lang "pt" _ "BR" _) = ("portuguese", "variant=brazilian")
1436−toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "")
1437−toPolyglossia x = (commonFromBcp47 x, "")
14381027
1439−-- Takes a list of the constituents of a BCP 47 language code and
1440−-- converts it to a Babel language string.
1441−-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
1442−-- List of supported languages (slightly outdated):
1443−-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
1444−toBabel :: Lang -> Text
1445−toBabel (Lang "de" _ "AT" vars)
1446− | "1901" `elem` vars = "austrian"
1447− | otherwise = "naustrian"
1448−toBabel (Lang "de" _ "CH" vars)
1449− | "1901" `elem` vars = "swissgerman"
1450− | otherwise = "nswissgerman"
1451−toBabel (Lang "de" _ _ vars)
1452− | "1901" `elem` vars = "german"
1453− | otherwise = "ngerman"
1454−toBabel (Lang "dsb" _ _ _) = "lowersorbian"
1455−toBabel (Lang "el" _ _ vars)
1456− | "polyton" `elem` vars = "polutonikogreek"
1457−toBabel (Lang "en" _ "AU" _) = "australian"
1458−toBabel (Lang "en" _ "CA" _) = "canadian"
1459−toBabel (Lang "en" _ "GB" _) = "british"
1460−toBabel (Lang "en" _ "NZ" _) = "newzealand"
1461−toBabel (Lang "en" _ "UK" _) = "british"
1462−toBabel (Lang "en" _ "US" _) = "american"
1463−toBabel (Lang "fr" _ "CA" _) = "canadien"
1464−toBabel (Lang "fra" _ _ vars)
1465− | "aca" `elem` vars = "acadian"
1466−toBabel (Lang "grc" _ _ _) = "polutonikogreek"
1467−toBabel (Lang "hsb" _ _ _) = "uppersorbian"
1468−toBabel (Lang "la" _ _ vars)
1469− | "x-classic" `elem` vars = "classiclatin"
1470−toBabel (Lang "pt" _ "BR" _) = "brazilian"
1471−toBabel (Lang "sl" _ _ _) = "slovene"
1472−toBabel x = commonFromBcp47 x
1473−
1474−-- Takes a list of the constituents of a BCP 47 language code
1475−-- and converts it to a string shared by Babel and Polyglossia.
1476−-- https://tools.ietf.org/html/bcp47#section-2.1
1477−commonFromBcp47 :: Lang -> Text
1478−commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc"
1479−commonFromBcp47 (Lang "zh" "Latn" _ vars)
1480− | "pinyin" `elem` vars = "pinyin"
1481−commonFromBcp47 (Lang l _ _ _) = fromIso l
1482− where
1483− fromIso "af" = "afrikaans"
1484− fromIso "am" = "amharic"
1485− fromIso "ar" = "arabic"
1486− fromIso "as" = "assamese"
1487− fromIso "ast" = "asturian"
1488− fromIso "bg" = "bulgarian"
1489− fromIso "bn" = "bengali"
1490− fromIso "bo" = "tibetan"
1491− fromIso "br" = "breton"
1492− fromIso "ca" = "catalan"
1493− fromIso "cy" = "welsh"
1494− fromIso "cs" = "czech"
1495− fromIso "cop" = "coptic"
1496− fromIso "da" = "danish"
1497− fromIso "dv" = "divehi"
1498− fromIso "el" = "greek"
1499− fromIso "en" = "english"
1500− fromIso "eo" = "esperanto"
1501− fromIso "es" = "spanish"
1502− fromIso "et" = "estonian"
1503− fromIso "eu" = "basque"
1504− fromIso "fa" = "farsi"
1505− fromIso "fi" = "finnish"
1506− fromIso "fr" = "french"
1507− fromIso "fur" = "friulan"
1508− fromIso "ga" = "irish"
1509− fromIso "gd" = "scottish"
1510− fromIso "gez" = "ethiopic"
1511− fromIso "gl" = "galician"
1512− fromIso "he" = "hebrew"
1513− fromIso "hi" = "hindi"
1514− fromIso "hr" = "croatian"
1515− fromIso "hu" = "magyar"
1516− fromIso "hy" = "armenian"
1517− fromIso "ia" = "interlingua"
1518− fromIso "id" = "indonesian"
1519− fromIso "ie" = "interlingua"
1520− fromIso "is" = "icelandic"
1521− fromIso "it" = "italian"
1522− fromIso "ja" = "japanese"
1523− fromIso "km" = "khmer"
1524− fromIso "kmr" = "kurmanji"
1525− fromIso "kn" = "kannada"
1526− fromIso "ko" = "korean"
1527− fromIso "la" = "latin"
1528− fromIso "lo" = "lao"
1529− fromIso "lt" = "lithuanian"
1530− fromIso "lv" = "latvian"
1531− fromIso "ml" = "malayalam"
1532− fromIso "mn" = "mongolian"
1533− fromIso "mr" = "marathi"
1534− fromIso "nb" = "norsk"
1535− fromIso "nl" = "dutch"
1536− fromIso "nn" = "nynorsk"
1537− fromIso "no" = "norsk"
1538− fromIso "nqo" = "nko"
1539− fromIso "oc" = "occitan"
1540− fromIso "pa" = "panjabi"
1541− fromIso "pl" = "polish"
1542− fromIso "pms" = "piedmontese"
1543− fromIso "pt" = "portuguese"
1544− fromIso "rm" = "romansh"
1545− fromIso "ro" = "romanian"
1546− fromIso "ru" = "russian"
1547− fromIso "sa" = "sanskrit"
1548− fromIso "se" = "samin"
1549− fromIso "sk" = "slovak"
1550− fromIso "sq" = "albanian"
1551− fromIso "sr" = "serbian"
1552− fromIso "sv" = "swedish"
1553− fromIso "syr" = "syriac"
1554− fromIso "ta" = "tamil"
1555− fromIso "te" = "telugu"
1556− fromIso "th" = "thai"
1557− fromIso "ti" = "ethiopic"
1558− fromIso "tk" = "turkmen"
1559− fromIso "tr" = "turkish"
1560− fromIso "uk" = "ukrainian"
1561− fromIso "ur" = "urdu"
1562− fromIso "vi" = "vietnamese"
1563− fromIso _ = ""
src/Text/Pandoc/Writers/LaTeX/Citation.hsnew file+181
@@ -0,0 +1,181 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Citation
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Citation
12+ ( citationsToNatbib,
13+ citationsToBiblatex
14+ ) where
15+
16+import Data.Text (Text)
17+import Data.Char (isPunctuation)
18+import qualified Data.Text as T
19+import Text.Pandoc.Class.PandocMonad (PandocMonad)
20+import Text.Pandoc.Definition
21+import Data.List (foldl')
22+import Text.DocLayout (Doc, brackets, empty, (<+>), text, isEmpty, literal,
23+ braces)
24+import Text.Pandoc.Walk
25+import Text.Pandoc.Writers.LaTeX.Types ( LW )
26+
27+citationsToNatbib :: PandocMonad m
28+ => ([Inline] -> LW m (Doc Text))
29+ -> [Citation]
30+ -> LW m (Doc Text)
31+citationsToNatbib inlineListToLaTeX [one]
32+ = citeCommand inlineListToLaTeX c p s k
33+ where
34+ Citation { citationId = k
35+ , citationPrefix = p
36+ , citationSuffix = s
37+ , citationMode = m
38+ }
39+ = one
40+ c = case m of
41+ AuthorInText -> "citet"
42+ SuppressAuthor -> "citeyearpar"
43+ NormalCitation -> "citep"
44+
45+citationsToNatbib inlineListToLaTeX cits
46+ | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
47+ = citeCommand inlineListToLaTeX "citep" p s ks
48+ where
49+ noPrefix = all (null . citationPrefix)
50+ noSuffix = all (null . citationSuffix)
51+ ismode m = all ((==) m . citationMode)
52+ p = citationPrefix $
53+ head cits
54+ s = citationSuffix $
55+ last cits
56+ ks = T.intercalate ", " $ map citationId cits
57+
58+citationsToNatbib inlineListToLaTeX (c:cs)
59+ | citationMode c == AuthorInText = do
60+ author <- citeCommand inlineListToLaTeX "citeauthor" [] [] (citationId c)
61+ cits <- citationsToNatbib inlineListToLaTeX
62+ (c { citationMode = SuppressAuthor } : cs)
63+ return $ author <+> cits
64+
65+citationsToNatbib inlineListToLaTeX cits = do
66+ cits' <- mapM convertOne cits
67+ return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
68+ where
69+ citeCommand' = citeCommand inlineListToLaTeX
70+ combineTwo a b | isEmpty a = b
71+ | otherwise = a <> text "; " <> b
72+ convertOne Citation { citationId = k
73+ , citationPrefix = p
74+ , citationSuffix = s
75+ , citationMode = m
76+ }
77+ = case m of
78+ AuthorInText -> citeCommand' "citealt" p s k
79+ SuppressAuthor -> citeCommand' "citeyear" p s k
80+ NormalCitation -> citeCommand' "citealp" p s k
81+
82+citeCommand :: PandocMonad m
83+ => ([Inline] -> LW m (Doc Text))
84+ -> Text
85+ -> [Inline]
86+ -> [Inline]
87+ -> Text
88+ -> LW m (Doc Text)
89+citeCommand inlineListToLaTeX c p s k = do
90+ args <- citeArguments inlineListToLaTeX p s k
91+ return $ literal ("\\" <> c) <> args
92+
93+type Prefix = [Inline]
94+type Suffix = [Inline]
95+type CiteId = Text
96+data CiteGroup = CiteGroup Prefix Suffix [CiteId]
97+
98+citeArgumentsList :: PandocMonad m
99+ => ([Inline] -> LW m (Doc Text))
100+ -> CiteGroup
101+ -> LW m (Doc Text)
102+citeArgumentsList _inlineListToLaTeX (CiteGroup _ _ []) = return empty
103+citeArgumentsList inlineListToLaTeX (CiteGroup pfxs sfxs ids) = do
104+ pdoc <- inlineListToLaTeX pfxs
105+ sdoc <- inlineListToLaTeX sfxs'
106+ return $ optargs pdoc sdoc <>
107+ braces (literal (T.intercalate "," (reverse ids)))
108+ where sfxs' = stripLocatorBraces $ case sfxs of
109+ (Str t : r) -> case T.uncons t of
110+ Just (x, xs)
111+ | T.null xs
112+ , isPunctuation x -> dropWhile (== Space) r
113+ | isPunctuation x -> Str xs : r
114+ _ -> sfxs
115+ _ -> sfxs
116+ optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of
117+ (True, True ) -> empty
118+ (True, False) -> brackets sdoc
119+ (_ , _ ) -> brackets pdoc <> brackets sdoc
120+
121+citeArguments :: PandocMonad m
122+ => ([Inline] -> LW m (Doc Text))
123+ -> [Inline]
124+ -> [Inline]
125+ -> Text
126+ -> LW m (Doc Text)
127+citeArguments inlineListToLaTeX p s k =
128+ citeArgumentsList inlineListToLaTeX (CiteGroup p s [k])
129+
130+-- strip off {} used to define locator in pandoc-citeproc; see #5722
131+stripLocatorBraces :: [Inline] -> [Inline]
132+stripLocatorBraces = walk go
133+ where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs
134+ go x = x
135+
136+citationsToBiblatex :: PandocMonad m
137+ => ([Inline] -> LW m (Doc Text))
138+ -> [Citation] -> LW m (Doc Text)
139+citationsToBiblatex inlineListToLaTeX [one]
140+ = citeCommand inlineListToLaTeX cmd p s k
141+ where
142+ Citation { citationId = k
143+ , citationPrefix = p
144+ , citationSuffix = s
145+ , citationMode = m
146+ } = one
147+ cmd = case m of
148+ SuppressAuthor -> "autocite*"
149+ AuthorInText -> "textcite"
150+ NormalCitation -> "autocite"
151+
152+citationsToBiblatex inlineListToLaTeX (c:cs)
153+ | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs)
154+ = do
155+ let cmd = case citationMode c of
156+ SuppressAuthor -> "\\autocite*"
157+ AuthorInText -> "\\textcite"
158+ NormalCitation -> "\\autocite"
159+ return $ text cmd <>
160+ braces (literal (T.intercalate "," (map citationId (c:cs))))
161+ | otherwise
162+ = do
163+ let cmd = case citationMode c of
164+ SuppressAuthor -> "\\autocites*"
165+ AuthorInText -> "\\textcites"
166+ NormalCitation -> "\\autocites"
167+
168+ groups <- mapM (citeArgumentsList inlineListToLaTeX)
169+ (reverse (foldl' grouper [] (c:cs)))
170+
171+ return $ text cmd <> mconcat groups
172+
173+ where grouper prev cit = case prev of
174+ ((CiteGroup oPfx oSfx ids):rest)
175+ | null oSfx && null pfx -> CiteGroup oPfx sfx (cid:ids) : rest
176+ _ -> CiteGroup pfx sfx [cid] : prev
177+ where pfx = citationPrefix cit
178+ sfx = citationSuffix cit
179+ cid = citationId cit
180+
181+citationsToBiblatex _ _ = return empty
src/Text/Pandoc/Writers/LaTeX/Lang.hsnew file+191
@@ -0,0 +1,191 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{- |
4+ Module : Text.Pandoc.Writers.LaTeX.Lang
5+ Copyright : Copyright (C) 2006-2021 John MacFarlane
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : John MacFarlane <jgm@berkeley.edu>
9+ Stability : alpha
10+ Portability : portable
11+-}
12+module Text.Pandoc.Writers.LaTeX.Lang
13+ ( toPolyglossiaEnv,
14+ toPolyglossia,
15+ toBabel
16+ ) where
17+import Data.Text (Text)
18+import Text.Pandoc.BCP47 (Lang (..))
19+
20+
21+-- In environments \Arabic instead of \arabic is used
22+toPolyglossiaEnv :: Lang -> (Text, Text)
23+toPolyglossiaEnv l =
24+ case toPolyglossia l of
25+ ("arabic", o) -> ("Arabic", o)
26+ x -> x
27+
28+-- Takes a list of the constituents of a BCP 47 language code and
29+-- converts it to a Polyglossia (language, options) tuple
30+-- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf
31+toPolyglossia :: Lang -> (Text, Text)
32+toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria")
33+toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq")
34+toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq")
35+toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq")
36+toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya")
37+toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco")
38+toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania")
39+toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq")
40+toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq")
41+toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia")
42+toPolyglossia (Lang "de" _ _ vars)
43+ | "1901" `elem` vars = ("german", "spelling=old")
44+toPolyglossia (Lang "de" _ "AT" vars)
45+ | "1901" `elem` vars = ("german", "variant=austrian, spelling=old")
46+toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian")
47+toPolyglossia (Lang "de" _ "CH" vars)
48+ | "1901" `elem` vars = ("german", "variant=swiss, spelling=old")
49+toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss")
50+toPolyglossia (Lang "de" _ _ _) = ("german", "")
51+toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "")
52+toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly")
53+toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian")
54+toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian")
55+toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british")
56+toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand")
57+toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british")
58+toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american")
59+toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient")
60+toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "")
61+toPolyglossia (Lang "la" _ _ vars)
62+ | "x-classic" `elem` vars = ("latin", "variant=classic")
63+toPolyglossia (Lang "pt" _ "BR" _) = ("portuguese", "variant=brazilian")
64+toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "")
65+toPolyglossia x = (commonFromBcp47 x, "")
66+
67+-- Takes a list of the constituents of a BCP 47 language code and
68+-- converts it to a Babel language string.
69+-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
70+-- List of supported languages (slightly outdated):
71+-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
72+toBabel :: Lang -> Text
73+toBabel (Lang "de" _ "AT" vars)
74+ | "1901" `elem` vars = "austrian"
75+ | otherwise = "naustrian"
76+toBabel (Lang "de" _ "CH" vars)
77+ | "1901" `elem` vars = "swissgerman"
78+ | otherwise = "nswissgerman"
79+toBabel (Lang "de" _ _ vars)
80+ | "1901" `elem` vars = "german"
81+ | otherwise = "ngerman"
82+toBabel (Lang "dsb" _ _ _) = "lowersorbian"
83+toBabel (Lang "el" _ _ vars)
84+ | "polyton" `elem` vars = "polutonikogreek"
85+toBabel (Lang "en" _ "AU" _) = "australian"
86+toBabel (Lang "en" _ "CA" _) = "canadian"
87+toBabel (Lang "en" _ "GB" _) = "british"
88+toBabel (Lang "en" _ "NZ" _) = "newzealand"
89+toBabel (Lang "en" _ "UK" _) = "british"
90+toBabel (Lang "en" _ "US" _) = "american"
91+toBabel (Lang "fr" _ "CA" _) = "canadien"
92+toBabel (Lang "fra" _ _ vars)
93+ | "aca" `elem` vars = "acadian"
94+toBabel (Lang "grc" _ _ _) = "polutonikogreek"
95+toBabel (Lang "hsb" _ _ _) = "uppersorbian"
96+toBabel (Lang "la" _ _ vars)
97+ | "x-classic" `elem` vars = "classiclatin"
98+toBabel (Lang "pt" _ "BR" _) = "brazilian"
99+toBabel (Lang "sl" _ _ _) = "slovene"
100+toBabel x = commonFromBcp47 x
101+
102+-- Takes a list of the constituents of a BCP 47 language code
103+-- and converts it to a string shared by Babel and Polyglossia.
104+-- https://tools.ietf.org/html/bcp47#section-2.1
105+commonFromBcp47 :: Lang -> Text
106+commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc"
107+commonFromBcp47 (Lang "zh" "Latn" _ vars)
108+ | "pinyin" `elem` vars = "pinyin"
109+commonFromBcp47 (Lang l _ _ _) = fromIso l
110+ where
111+ fromIso "af" = "afrikaans"
112+ fromIso "am" = "amharic"
113+ fromIso "ar" = "arabic"
114+ fromIso "as" = "assamese"
115+ fromIso "ast" = "asturian"
116+ fromIso "bg" = "bulgarian"
117+ fromIso "bn" = "bengali"
118+ fromIso "bo" = "tibetan"
119+ fromIso "br" = "breton"
120+ fromIso "ca" = "catalan"
121+ fromIso "cy" = "welsh"
122+ fromIso "cs" = "czech"
123+ fromIso "cop" = "coptic"
124+ fromIso "da" = "danish"
125+ fromIso "dv" = "divehi"
126+ fromIso "el" = "greek"
127+ fromIso "en" = "english"
128+ fromIso "eo" = "esperanto"
129+ fromIso "es" = "spanish"
130+ fromIso "et" = "estonian"
131+ fromIso "eu" = "basque"
132+ fromIso "fa" = "farsi"
133+ fromIso "fi" = "finnish"
134+ fromIso "fr" = "french"
135+ fromIso "fur" = "friulan"
136+ fromIso "ga" = "irish"
137+ fromIso "gd" = "scottish"
138+ fromIso "gez" = "ethiopic"
139+ fromIso "gl" = "galician"
140+ fromIso "he" = "hebrew"
141+ fromIso "hi" = "hindi"
142+ fromIso "hr" = "croatian"
143+ fromIso "hu" = "magyar"
144+ fromIso "hy" = "armenian"
145+ fromIso "ia" = "interlingua"
146+ fromIso "id" = "indonesian"
147+ fromIso "ie" = "interlingua"
148+ fromIso "is" = "icelandic"
149+ fromIso "it" = "italian"
150+ fromIso "ja" = "japanese"
151+ fromIso "km" = "khmer"
152+ fromIso "kmr" = "kurmanji"
153+ fromIso "kn" = "kannada"
154+ fromIso "ko" = "korean"
155+ fromIso "la" = "latin"
156+ fromIso "lo" = "lao"
157+ fromIso "lt" = "lithuanian"
158+ fromIso "lv" = "latvian"
159+ fromIso "ml" = "malayalam"
160+ fromIso "mn" = "mongolian"
161+ fromIso "mr" = "marathi"
162+ fromIso "nb" = "norsk"
163+ fromIso "nl" = "dutch"
164+ fromIso "nn" = "nynorsk"
165+ fromIso "no" = "norsk"
166+ fromIso "nqo" = "nko"
167+ fromIso "oc" = "occitan"
168+ fromIso "pa" = "panjabi"
169+ fromIso "pl" = "polish"
170+ fromIso "pms" = "piedmontese"
171+ fromIso "pt" = "portuguese"
172+ fromIso "rm" = "romansh"
173+ fromIso "ro" = "romanian"
174+ fromIso "ru" = "russian"
175+ fromIso "sa" = "sanskrit"
176+ fromIso "se" = "samin"
177+ fromIso "sk" = "slovak"
178+ fromIso "sq" = "albanian"
179+ fromIso "sr" = "serbian"
180+ fromIso "sv" = "swedish"
181+ fromIso "syr" = "syriac"
182+ fromIso "ta" = "tamil"
183+ fromIso "te" = "telugu"
184+ fromIso "th" = "thai"
185+ fromIso "ti" = "ethiopic"
186+ fromIso "tk" = "turkmen"
187+ fromIso "tr" = "turkish"
188+ fromIso "uk" = "ukrainian"
189+ fromIso "ur" = "urdu"
190+ fromIso "vi" = "vietnamese"
191+ fromIso _ = ""
src/Text/Pandoc/Writers/LaTeX/Util.hsnew file+274
@@ -0,0 +1,274 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Util
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Util (
12+ stringToLaTeX
13+ , StringContext(..)
14+ , toLabel
15+ , inCmd
16+ , wrapDiv
17+ , hypertarget
18+ , labelFor
19+ , getListingsLanguage
20+ , mbBraced
21+ )
22+where
23+
24+import Control.Applicative ((<|>))
25+import Text.Pandoc.Class (PandocMonad, toLang)
26+import Text.Pandoc.Options (WriterOptions(..), isEnabled)
27+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..))
28+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv)
29+import Text.Pandoc.Highlighting (toListingsLanguage)
30+import Text.DocLayout
31+import Text.Pandoc.Definition
32+import Text.Pandoc.ImageSize (showFl)
33+import Control.Monad.State.Strict (gets)
34+import Data.Text (Text)
35+import qualified Data.Text as T
36+import Text.Pandoc.Extensions (Extension(Ext_smart))
37+import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum)
38+import Text.Printf (printf)
39+import Text.Pandoc.Shared (safeRead, elemText)
40+import qualified Data.Text.Normalize as Normalize
41+import Data.List (uncons)
42+
43+data StringContext = TextString
44+ | URLString
45+ | CodeString
46+ deriving (Eq)
47+
48+-- escape things as needed for LaTeX
49+stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
50+stringToLaTeX context zs = do
51+ opts <- gets stOptions
52+ return $ T.pack $
53+ foldr (go opts context) mempty $ T.unpack $
54+ if writerPreferAscii opts
55+ then Normalize.normalize Normalize.NFD zs
56+ else zs
57+ where
58+ go :: WriterOptions -> StringContext -> Char -> String -> String
59+ go opts ctx x xs =
60+ let ligatures = isEnabled Ext_smart opts && ctx == TextString
61+ isUrl = ctx == URLString
62+ mbAccentCmd =
63+ if writerPreferAscii opts && ctx == TextString
64+ then uncons xs >>= \(c,_) -> lookupAccent c
65+ else Nothing
66+ emits s =
67+ case mbAccentCmd of
68+ Just cmd ->
69+ cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
70+ Nothing -> s <> xs
71+ emitc c =
72+ case mbAccentCmd of
73+ Just cmd ->
74+ cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
75+ Nothing -> c : xs
76+ emitcseq cs =
77+ case xs of
78+ c:_ | isLetter c
79+ , ctx == TextString
80+ -> cs <> " " <> xs
81+ | isSpace c -> cs <> "{}" <> xs
82+ | ctx == TextString
83+ -> cs <> xs
84+ _ -> cs <> "{}" <> xs
85+ emitquote cs =
86+ case xs of
87+ '`':_ -> cs <> "\\," <> xs -- add thin space
88+ '\'':_ -> cs <> "\\," <> xs -- add thin space
89+ _ -> cs <> xs
90+ in case x of
91+ '?' | ligatures -> -- avoid ?` ligature
92+ case xs of
93+ '`':_ -> emits "?{}"
94+ _ -> emitc x
95+ '!' | ligatures -> -- avoid !` ligature
96+ case xs of
97+ '`':_ -> emits "!{}"
98+ _ -> emitc x
99+ '{' -> emits "\\{"
100+ '}' -> emits "\\}"
101+ '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
102+ '$' | not isUrl -> emits "\\$"
103+ '%' -> emits "\\%"
104+ '&' -> emits "\\&"
105+ '_' | not isUrl -> emits "\\_"
106+ '#' -> emits "\\#"
107+ '-' | not isUrl -> case xs of
108+ -- prevent adjacent hyphens from forming ligatures
109+ ('-':_) -> emits "-\\/"
110+ _ -> emitc '-'
111+ '~' | not isUrl -> emitcseq "\\textasciitilde"
112+ '^' -> emits "\\^{}"
113+ '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
114+ | otherwise -> emitcseq "\\textbackslash"
115+ '|' | not isUrl -> emitcseq "\\textbar"
116+ '<' -> emitcseq "\\textless"
117+ '>' -> emitcseq "\\textgreater"
118+ '[' -> emits "{[}" -- to avoid interpretation as
119+ ']' -> emits "{]}" -- optional arguments
120+ '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
121+ '\160' -> emits "~"
122+ '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
123+ '\x202F' -> emits "\\,"
124+ '\x2026' -> emitcseq "\\ldots"
125+ '\x2018' | ligatures -> emitquote "`"
126+ '\x2019' | ligatures -> emitquote "'"
127+ '\x201C' | ligatures -> emitquote "``"
128+ '\x201D' | ligatures -> emitquote "''"
129+ '\x2014' | ligatures -> emits "---"
130+ '\x2013' | ligatures -> emits "--"
131+ _ | writerPreferAscii opts
132+ -> case x of
133+ 'ı' -> emitcseq "\\i"
134+ 'ȷ' -> emitcseq "\\j"
135+ 'å' -> emitcseq "\\aa"
136+ 'Å' -> emitcseq "\\AA"
137+ 'ß' -> emitcseq "\\ss"
138+ 'ø' -> emitcseq "\\o"
139+ 'Ø' -> emitcseq "\\O"
140+ 'Ł' -> emitcseq "\\L"
141+ 'ł' -> emitcseq "\\l"
142+ 'æ' -> emitcseq "\\ae"
143+ 'Æ' -> emitcseq "\\AE"
144+ 'œ' -> emitcseq "\\oe"
145+ 'Œ' -> emitcseq "\\OE"
146+ '£' -> emitcseq "\\pounds"
147+ '€' -> emitcseq "\\euro"
148+ '©' -> emitcseq "\\copyright"
149+ _ -> emitc x
150+ | otherwise -> emitc x
151+
152+lookupAccent :: Char -> Maybe String
153+lookupAccent '\779' = Just "\\H"
154+lookupAccent '\768' = Just "\\`"
155+lookupAccent '\769' = Just "\\'"
156+lookupAccent '\770' = Just "\\^"
157+lookupAccent '\771' = Just "\\~"
158+lookupAccent '\776' = Just "\\\""
159+lookupAccent '\775' = Just "\\."
160+lookupAccent '\772' = Just "\\="
161+lookupAccent '\781' = Just "\\|"
162+lookupAccent '\817' = Just "\\b"
163+lookupAccent '\807' = Just "\\c"
164+lookupAccent '\783' = Just "\\G"
165+lookupAccent '\777' = Just "\\h"
166+lookupAccent '\803' = Just "\\d"
167+lookupAccent '\785' = Just "\\f"
168+lookupAccent '\778' = Just "\\r"
169+lookupAccent '\865' = Just "\\t"
170+lookupAccent '\782' = Just "\\U"
171+lookupAccent '\780' = Just "\\v"
172+lookupAccent '\774' = Just "\\u"
173+lookupAccent '\808' = Just "\\k"
174+lookupAccent '\8413' = Just "\\textcircled"
175+lookupAccent _ = Nothing
176+
177+toLabel :: PandocMonad m => Text -> LW m Text
178+toLabel z = go `fmap` stringToLaTeX URLString z
179+ where
180+ go = T.concatMap $ \x -> case x of
181+ _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
182+ | x `elemText` "_-+=:;." -> T.singleton x
183+ | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
184+
185+-- | Puts contents into LaTeX command.
186+inCmd :: Text -> Doc Text -> Doc Text
187+inCmd cmd contents = char '\\' <> literal cmd <> braces contents
188+
189+mapAlignment :: Text -> Text
190+mapAlignment a = case a of
191+ "top" -> "T"
192+ "top-baseline" -> "t"
193+ "bottom" -> "b"
194+ "center" -> "c"
195+ _ -> a
196+
197+wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
198+wrapDiv (_,classes,kvs) t = do
199+ beamer <- gets stBeamer
200+ let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
201+ lang <- toLang $ lookup "lang" kvs
202+ let wrapColumns = if beamer && "columns" `elem` classes
203+ then \contents ->
204+ let valign = maybe "T" mapAlignment (lookup "align" kvs)
205+ totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
206+ (lookup "totalwidth" kvs)
207+ onlytextwidth = filter ("onlytextwidth" ==) classes
208+ options = text $ T.unpack $ T.intercalate "," $
209+ valign : totalwidth ++ onlytextwidth
210+ in inCmd "begin" "columns" <> brackets options
211+ $$ contents
212+ $$ inCmd "end" "columns"
213+ else id
214+ wrapColumn = if beamer && "column" `elem` classes
215+ then \contents ->
216+ let valign =
217+ maybe ""
218+ (brackets . text . T.unpack . mapAlignment)
219+ (lookup "align" kvs)
220+ w = maybe "0.48" fromPct (lookup "width" kvs)
221+ in inCmd "begin" "column" <>
222+ valign <>
223+ braces (literal w <> "\\textwidth")
224+ $$ contents
225+ $$ inCmd "end" "column"
226+ else id
227+ fromPct xs =
228+ case T.unsnoc xs of
229+ Just (ds, '%') -> case safeRead ds of
230+ Just digits -> showFl (digits / 100 :: Double)
231+ Nothing -> xs
232+ _ -> xs
233+ wrapDir = case lookup "dir" kvs of
234+ Just "rtl" -> align "RTL"
235+ Just "ltr" -> align "LTR"
236+ _ -> id
237+ wrapLang txt = case lang of
238+ Just lng -> let (l, o) = toPolyglossiaEnv lng
239+ ops = if T.null o
240+ then ""
241+ else brackets $ literal o
242+ in inCmd "begin" (literal l) <> ops
243+ $$ blankline <> txt <> blankline
244+ $$ inCmd "end" (literal l)
245+ Nothing -> txt
246+ return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
247+
248+hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
249+hypertarget _ "" x = return x
250+hypertarget addnewline ident x = do
251+ ref <- literal `fmap` toLabel ident
252+ return $ text "\\hypertarget"
253+ <> braces ref
254+ <> braces ((if addnewline && not (isEmpty x)
255+ then "%" <> cr
256+ else empty) <> x)
257+
258+labelFor :: PandocMonad m => Text -> LW m (Doc Text)
259+labelFor "" = return empty
260+labelFor ident = do
261+ ref <- literal `fmap` toLabel ident
262+ return $ text "\\label" <> braces ref
263+
264+-- Determine listings language from list of class attributes.
265+getListingsLanguage :: [Text] -> Maybe Text
266+getListingsLanguage xs
267+ = foldr ((<|>) . toListingsLanguage) Nothing xs
268+
269+mbBraced :: Text -> Text
270+mbBraced x = if not (T.all isAlphaNum x)
271+ then "{" <> x <> "}"
272+ else x
273+
274+
src/Text/Pandoc/Writers/Markdown.hs+6−591
@@ -22,15 +22,13 @@ module Text.Pandoc.Writers.Markdown (
2222 writePlain) where
2323import Control.Monad.Reader
2424import Control.Monad.State.Strict
25−import Data.Char (isAlphaNum, isDigit)
2625import Data.Default
27−import Data.List (find, intersperse, sortOn, transpose)
26+import Data.List (intersperse, sortOn, transpose)
2827import qualified Data.Map as M
2928import Data.Maybe (fromMaybe, mapMaybe)
3029import qualified Data.Set as Set
3130import Data.Text (Text)
3231import qualified Data.Text as T
33−import Network.HTTP (urlEncode)
3432import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
3533import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
3634import Text.Pandoc.Definition
@@ -44,59 +42,11 @@ import Text.Pandoc.Templates (renderTemplate)
4442import Text.DocTemplates (Val(..), Context(..), FromContext(..))
4543import Text.Pandoc.Walk
4644import Text.Pandoc.Writers.HTML (writeHtml5String)
47−import Text.Pandoc.Writers.Math (texMathToInlines)
48−import Text.Pandoc.XML (toHtml5Entities)
49−import Data.Coerce (coerce)
50−
51−type Notes = [[Block]]
52−type Ref = (Text, Target, Attr)
53−type Refs = [Ref]
54−
55−type MD m = ReaderT WriterEnv (StateT WriterState m)
56−
57−evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a
58−evalMD md env st = evalStateT (runReaderT md env) st
59−
60−data WriterEnv = WriterEnv { envInList :: Bool
61− , envVariant :: MarkdownVariant
62− , envRefShortcutable :: Bool
63− , envBlockLevel :: Int
64− , envEscapeSpaces :: Bool
65− }
66−
67−data MarkdownVariant =
68− PlainText
69− | Commonmark
70− | Markdown
71− deriving (Show, Eq)
72−
73−instance Default WriterEnv
74− where def = WriterEnv { envInList = False
75− , envVariant = Markdown
76− , envRefShortcutable = True
77− , envBlockLevel = 0
78− , envEscapeSpaces = False
79− }
80−
81−data WriterState = WriterState { stNotes :: Notes
82− , stPrevRefs :: Refs
83− , stRefs :: Refs
84− , stKeys :: M.Map Key
85− (M.Map (Target, Attr) Int)
86− , stLastIdx :: Int
87− , stIds :: Set.Set Text
88− , stNoteNum :: Int
89− }
90−
91−instance Default WriterState
92− where def = WriterState{ stNotes = []
93− , stPrevRefs = []
94− , stRefs = []
95− , stKeys = M.empty
96− , stLastIdx = 0
97− , stIds = Set.empty
98− , stNoteNum = 1
99− }
45+import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown)
46+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
47+ WriterState(..),
48+ WriterEnv(..),
49+ Ref, Refs, MD, evalMD)
10050
10151-- | Convert Pandoc to Markdown.
10252writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
@@ -297,49 +247,6 @@ noteToMarkdown opts num blocks = do
297247 then hang (writerTabStop opts) (marker <> spacer) contents
298248 else marker <> spacer <> contents
299249
300−-- | Escape special characters for Markdown.
301−escapeText :: WriterOptions -> Text -> Text
302−escapeText opts = T.pack . go . T.unpack
303− where
304− go [] = []
305− go (c:cs) =
306− case c of
307− '<' | isEnabled Ext_all_symbols_escapable opts ->
308− '\\' : '<' : go cs
309− | otherwise -> "&lt;" ++ go cs
310− '>' | isEnabled Ext_all_symbols_escapable opts ->
311− '\\' : '>' : go cs
312− | otherwise -> "&gt;" ++ go cs
313− '@' | isEnabled Ext_citations opts ->
314− case cs of
315− (d:_)
316− | isAlphaNum d || d == '_'
317− -> '\\':'@':go cs
318− _ -> '@':go cs
319− _ | c `elem` ['\\','`','*','_','[',']','#'] ->
320− '\\':c:go cs
321− '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
322− '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
323− '~' | isEnabled Ext_subscript opts ||
324− isEnabled Ext_strikeout opts -> '\\':'~':go cs
325− '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
326− '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
327− '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
328− '-' | isEnabled Ext_smart opts ->
329− case cs of
330− '-':_ -> '\\':'-':go cs
331− _ -> '-':go cs
332− '.' | isEnabled Ext_smart opts ->
333− case cs of
334− '.':'.':rest -> '\\':'.':'.':'.':go rest
335− _ -> '.':go cs
336− _ -> case cs of
337− '_':x:xs
338− | isEnabled Ext_intraword_underscores opts
339− , isAlphaNum c
340− , isAlphaNum x -> c : '_' : x : go xs
341− _ -> c : go cs
342−
343250attrsToMarkdown :: Attr -> Doc Text
344251attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
345252 where attribId = case attribs of
@@ -912,499 +819,7 @@ blockListToMarkdown opts blocks = do
912819 | otherwise = RawBlock "markdown" "&nbsp;\n"
913820 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
914821
915−getKey :: Doc Text -> Key
916−getKey = toKey . render Nothing
917−
918−findUsableIndex :: [Text] -> Int -> Int
919−findUsableIndex lbls i = if tshow i `elem` lbls
920− then findUsableIndex lbls (i + 1)
921− else i
922−
923−getNextIndex :: PandocMonad m => MD m Int
924−getNextIndex = do
925− prevRefs <- gets stPrevRefs
926− refs <- gets stRefs
927− i <- (+ 1) <$> gets stLastIdx
928− modify $ \s -> s{ stLastIdx = i }
929− let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs
930− return $ findUsableIndex refLbls i
931−
932−-- | Get reference for target; if none exists, create unique one and return.
933−-- Prefer label if possible; otherwise, generate a unique key.
934−getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text
935−getReference attr label target = do
936− refs <- gets stRefs
937− case find (\(_,t,a) -> t == target && a == attr) refs of
938− Just (ref, _, _) -> return ref
939− Nothing -> do
940− keys <- gets stKeys
941− let key = getKey label
942− let rawkey = coerce key
943− case M.lookup key keys of
944− Nothing -> do -- no other refs with this label
945− (lab', idx) <- if T.null rawkey ||
946− T.length rawkey > 999 ||
947− T.any (\c -> c == '[' || c == ']') rawkey
948− then do
949− i <- getNextIndex
950− return (tshow i, i)
951− else
952− return (render Nothing label, 0)
953− modify (\s -> s{
954− stRefs = (lab', target, attr) : refs,
955− stKeys = M.insert (getKey label)
956− (M.insert (target, attr) idx mempty)
957− (stKeys s) })
958− return lab'
959−
960− Just km -> -- we have refs with this label
961− case M.lookup (target, attr) km of
962− Just i -> do
963− let lab' = render Nothing $
964− label <> if i == 0
965− then mempty
966− else literal (tshow i)
967− -- make sure it's in stRefs; it may be
968− -- a duplicate that was printed in a previous
969− -- block:
970− when ((lab', target, attr) `notElem` refs) $
971− modify (\s -> s{
972− stRefs = (lab', target, attr) : refs })
973− return lab'
974− Nothing -> do -- but this one is to a new target
975− i <- getNextIndex
976− let lab' = tshow i
977− modify (\s -> s{
978− stRefs = (lab', target, attr) : refs,
979− stKeys = M.insert key
980− (M.insert (target, attr) i km)
981− (stKeys s) })
982− return lab'
983−
984−-- | Convert list of Pandoc inline elements to markdown.
985−inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text)
986−inlineListToMarkdown opts lst = do
987− inlist <- asks envInList
988− go (if inlist then avoidBadWrapsInList lst else lst)
989− where go [] = return empty
990− go (x@Math{}:y@(Str t):zs)
991− | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058
992− = liftM2 (<>) (inlineToMarkdown opts x)
993− (go (RawInline (Format "html") "<!-- -->" : y : zs))
994− go (i:is) = case i of
995− Link {} -> case is of
996− -- If a link is followed by another link, or '[', '(' or ':'
997− -- then we don't shortcut
998− Link {}:_ -> unshortcutable
999− Space:Link {}:_ -> unshortcutable
1000− Space:(Str(thead -> Just '[')):_ -> unshortcutable
1001− Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1002− Space:(Cite _ _):_ -> unshortcutable
1003− SoftBreak:Link {}:_ -> unshortcutable
1004− SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable
1005− SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1006− SoftBreak:(Cite _ _):_ -> unshortcutable
1007− LineBreak:Link {}:_ -> unshortcutable
1008− LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable
1009− LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1010− LineBreak:(Cite _ _):_ -> unshortcutable
1011− (Cite _ _):_ -> unshortcutable
1012− Str (thead -> Just '['):_ -> unshortcutable
1013− Str (thead -> Just '('):_ -> unshortcutable
1014− Str (thead -> Just ':'):_ -> unshortcutable
1015− (RawInline _ (thead -> Just '[')):_ -> unshortcutable
1016− (RawInline _ (thead -> Just '(')):_ -> unshortcutable
1017− (RawInline _ (thead -> Just ':')):_ -> unshortcutable
1018− (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable
1019− _ -> shortcutable
1020− _ -> shortcutable
1021− where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
1022− unshortcutable = do
1023− iMark <- local
1024− (\env -> env { envRefShortcutable = False })
1025− (inlineToMarkdown opts i)
1026− fmap (iMark <>) (go is)
1027− thead = fmap fst . T.uncons
1028−
1029−isSp :: Inline -> Bool
1030−isSp Space = True
1031−isSp SoftBreak = True
1032−isSp _ = False
1033−
1034−avoidBadWrapsInList :: [Inline] -> [Inline]
1035−avoidBadWrapsInList [] = []
1036−avoidBadWrapsInList (s:Str (T.uncons -> Just ('>',cs)):xs) | isSp s =
1037− Str (" >" <> cs) : avoidBadWrapsInList xs
1038−avoidBadWrapsInList [s, Str (T.uncons -> Just (c, cs))]
1039− | T.null cs && isSp s && c `elem` ['-','*','+'] = [Str $ T.pack [' ', c]]
1040−avoidBadWrapsInList (s:Str (T.uncons -> Just (c, cs)):Space:xs)
1041− | T.null cs && isSp s && c `elem` ['-','*','+'] =
1042− Str (T.pack [' ', c]) : Space : avoidBadWrapsInList xs
1043−avoidBadWrapsInList (s:Str cs:Space:xs)
1044− | isSp s && isOrderedListMarker cs =
1045− Str (" " <> cs) : Space : avoidBadWrapsInList xs
1046−avoidBadWrapsInList [s, Str cs]
1047− | isSp s && isOrderedListMarker cs = [Str $ " " <> cs]
1048−avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs
1049−
1050−isOrderedListMarker :: Text -> Bool
1051−isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) &&
1052− isRight (runParser (anyOrderedListMarker >> eof)
1053− defaultParserState "" xs)
1054−
1055−isRight :: Either a b -> Bool
1056−isRight (Right _) = True
1057−isRight (Left _) = False
1058−
1059−-- | Convert Pandoc inline element to markdown.
1060−inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text)
1061−inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) =
1062− case lookup "data-emoji" kvs of
1063− Just emojiname | isEnabled Ext_emoji opts ->
1064− return $ ":" <> literal emojiname <> ":"
1065− _ -> inlineToMarkdown opts (Str s)
1066−inlineToMarkdown opts (Span attrs ils) = do
1067− variant <- asks envVariant
1068− contents <- inlineListToMarkdown opts ils
1069− return $ case attrs of
1070− (_,["csl-block"],_) -> (cr <>)
1071− (_,["csl-left-margin"],_) -> (cr <>)
1072− (_,["csl-indent"],_) -> (cr <>)
1073− _ -> id
1074− $ case variant of
1075− PlainText -> contents
1076− _ | attrs == nullAttr -> contents
1077− | isEnabled Ext_bracketed_spans opts ->
1078− let attrs' = if attrs /= nullAttr
1079− then attrsToMarkdown attrs
1080− else empty
1081− in "[" <> contents <> "]" <> attrs'
1082− | isEnabled Ext_raw_html opts ||
1083− isEnabled Ext_native_spans opts ->
1084− tagWithAttrs "span" attrs <> contents <> literal "</span>"
1085− | otherwise -> contents
1086−inlineToMarkdown _ (Emph []) = return empty
1087−inlineToMarkdown opts (Emph lst) = do
1088− variant <- asks envVariant
1089− contents <- inlineListToMarkdown opts lst
1090− return $ case variant of
1091− PlainText
1092− | isEnabled Ext_gutenberg opts -> "_" <> contents <> "_"
1093− | otherwise -> contents
1094− _ -> "*" <> contents <> "*"
1095−inlineToMarkdown _ (Underline []) = return empty
1096−inlineToMarkdown opts (Underline lst) = do
1097− variant <- asks envVariant
1098− contents <- inlineListToMarkdown opts lst
1099− case variant of
1100− PlainText -> return contents
1101− _ | isEnabled Ext_bracketed_spans opts ->
1102− return $ "[" <> contents <> "]" <> "{.ul}"
1103− | isEnabled Ext_native_spans opts ->
1104− return $ tagWithAttrs "span" ("", ["underline"], [])
1105− <> contents
1106− <> literal "</span>"
1107− | isEnabled Ext_raw_html opts ->
1108− return $ "<u>" <> contents <> "</u>"
1109− | otherwise -> inlineToMarkdown opts (Emph lst)
1110−inlineToMarkdown _ (Strong []) = return empty
1111−inlineToMarkdown opts (Strong lst) = do
1112− variant <- asks envVariant
1113− case variant of
1114− PlainText ->
1115− inlineListToMarkdown opts $
1116− if isEnabled Ext_gutenberg opts
1117− then capitalize lst
1118− else lst
1119− _ -> do
1120− contents <- inlineListToMarkdown opts lst
1121− return $ "**" <> contents <> "**"
1122−inlineToMarkdown _ (Strikeout []) = return empty
1123−inlineToMarkdown opts (Strikeout lst) = do
1124− contents <- inlineListToMarkdown opts lst
1125− return $ if isEnabled Ext_strikeout opts
1126− then "~~" <> contents <> "~~"
1127− else if isEnabled Ext_raw_html opts
1128− then "<s>" <> contents <> "</s>"
1129− else contents
1130−inlineToMarkdown _ (Superscript []) = return empty
1131−inlineToMarkdown opts (Superscript lst) =
1132− local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
1133− contents <- inlineListToMarkdown opts lst
1134− if isEnabled Ext_superscript opts
1135− then return $ "^" <> contents <> "^"
1136− else if isEnabled Ext_raw_html opts
1137− then return $ "<sup>" <> contents <> "</sup>"
1138− else
1139− case traverse toSuperscriptInline lst of
1140− Just xs' | not (writerPreferAscii opts)
1141− -> inlineListToMarkdown opts xs'
1142− _ -> do
1143− let rendered = render Nothing contents
1144− return $
1145− case mapM toSuperscript (T.unpack rendered) of
1146− Just r -> literal $ T.pack r
1147− Nothing -> literal $ "^(" <> rendered <> ")"
1148−inlineToMarkdown _ (Subscript []) = return empty
1149−inlineToMarkdown opts (Subscript lst) =
1150− local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
1151− contents <- inlineListToMarkdown opts lst
1152− if isEnabled Ext_subscript opts
1153− then return $ "~" <> contents <> "~"
1154− else if isEnabled Ext_raw_html opts
1155− then return $ "<sub>" <> contents <> "</sub>"
1156− else
1157− case traverse toSubscriptInline lst of
1158− Just xs' | not (writerPreferAscii opts)
1159− -> inlineListToMarkdown opts xs'
1160− _ -> do
1161− let rendered = render Nothing contents
1162− return $
1163− case mapM toSuperscript (T.unpack rendered) of
1164− Just r -> literal $ T.pack r
1165− Nothing -> literal $ "_(" <> rendered <> ")"
1166−inlineToMarkdown opts (SmallCaps lst) = do
1167− variant <- asks envVariant
1168− if variant /= PlainText &&
1169− (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
1170− then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst)
1171− else inlineListToMarkdown opts $ capitalize lst
1172−inlineToMarkdown opts (Quoted SingleQuote lst) = do
1173− contents <- inlineListToMarkdown opts lst
1174− return $ if isEnabled Ext_smart opts
1175− then "'" <> contents <> "'"
1176− else
1177− if writerPreferAscii opts
1178− then "&lsquo;" <> contents <> "&rsquo;"
1179− else "‘" <> contents <> "’"
1180−inlineToMarkdown opts (Quoted DoubleQuote lst) = do
1181− contents <- inlineListToMarkdown opts lst
1182− return $ if isEnabled Ext_smart opts
1183− then "\"" <> contents <> "\""
1184− else
1185− if writerPreferAscii opts
1186− then "&ldquo;" <> contents <> "&rdquo;"
1187− else "“" <> contents <> "”"
1188−inlineToMarkdown opts (Code attr str) = do
1189− let tickGroups = filter (T.any (== '`')) $ T.group str
1190− let longest = if null tickGroups
1191− then 0
1192− else maximum $ map T.length tickGroups
1193− let marker = T.replicate (longest + 1) "`"
1194− let spacer = if longest == 0 then "" else " "
1195− let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
1196− then attrsToMarkdown attr
1197− else empty
1198− variant <- asks envVariant
1199− case variant of
1200− PlainText -> return $ literal str
1201− _ -> return $ literal
1202− (marker <> spacer <> str <> spacer <> marker) <> attrs
1203−inlineToMarkdown opts (Str str) = do
1204− variant <- asks envVariant
1205− let str' = (if writerPreferAscii opts
1206− then toHtml5Entities
1207− else id) .
1208− (if isEnabled Ext_smart opts
1209− then unsmartify opts
1210− else id) .
1211− (if variant == PlainText
1212− then id
1213− else escapeText opts) $ str
1214− return $ literal str'
1215−inlineToMarkdown opts (Math InlineMath str) =
1216− case writerHTMLMathMethod opts of
1217− WebTeX url -> inlineToMarkdown opts
1218− (Image nullAttr [Str str] (url <> T.pack (urlEncode $ T.unpack str), str))
1219− _ | isEnabled Ext_tex_math_dollars opts ->
1220− return $ "$" <> literal str <> "$"
1221− | isEnabled Ext_tex_math_single_backslash opts ->
1222− return $ "\\(" <> literal str <> "\\)"
1223− | isEnabled Ext_tex_math_double_backslash opts ->
1224− return $ "\\\\(" <> literal str <> "\\\\)"
1225− | otherwise -> do
1226− variant <- asks envVariant
1227− texMathToInlines InlineMath str >>=
1228− inlineListToMarkdown opts .
1229− (if variant == PlainText then makeMathPlainer else id)
1230−inlineToMarkdown opts (Math DisplayMath str) =
1231− case writerHTMLMathMethod opts of
1232− WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
1233− inlineToMarkdown opts (Image nullAttr [Str str]
1234− (url <> T.pack (urlEncode $ T.unpack str), str))
1235− _ | isEnabled Ext_tex_math_dollars opts ->
1236− return $ "$$" <> literal str <> "$$"
1237− | isEnabled Ext_tex_math_single_backslash opts ->
1238− return $ "\\[" <> literal str <> "\\]"
1239− | isEnabled Ext_tex_math_double_backslash opts ->
1240− return $ "\\\\[" <> literal str <> "\\\\]"
1241− | otherwise -> (\x -> cr <> x <> cr) `fmap`
1242− (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
1243−inlineToMarkdown opts il@(RawInline f str) = do
1244− let tickGroups = filter (T.any (== '`')) $ T.group str
1245− let numticks = if null tickGroups
1246− then 1
1247− else 1 + maximum (map T.length tickGroups)
1248− variant <- asks envVariant
1249− let Format fmt = f
1250− let rawAttribInline = return $
1251− literal (T.replicate numticks "`") <> literal str <>
1252− literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}"
1253− let renderEmpty = mempty <$ report (InlineNotRendered il)
1254− case variant of
1255− PlainText -> renderEmpty
1256− Commonmark
1257− | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"]
1258− -> return $ literal str
1259− Markdown
1260− | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
1261− "markdown_mmd", "markdown_strict"]
1262− -> return $ literal str
1263− _ | isEnabled Ext_raw_attribute opts -> rawAttribInline
1264− | f `elem` ["html", "html5", "html4"]
1265− , isEnabled Ext_raw_html opts
1266− -> return $ literal str
1267− | f `elem` ["latex", "tex"]
1268− , isEnabled Ext_raw_tex opts
1269− -> return $ literal str
1270− _ -> renderEmpty
1271−
1272−
1273−inlineToMarkdown opts LineBreak = do
1274− variant <- asks envVariant
1275− if variant == PlainText || isEnabled Ext_hard_line_breaks opts
1276− then return cr
1277− else return $
1278− if isEnabled Ext_escaped_line_breaks opts
1279− then "\\" <> cr
1280− else " " <> cr
1281−inlineToMarkdown _ Space = do
1282− escapeSpaces <- asks envEscapeSpaces
1283− return $ if escapeSpaces then "\\ " else space
1284−inlineToMarkdown opts SoftBreak = do
1285− escapeSpaces <- asks envEscapeSpaces
1286− let space' = if escapeSpaces then "\\ " else space
1287− return $ case writerWrapText opts of
1288− WrapNone -> space'
1289− WrapAuto -> space'
1290− WrapPreserve -> cr
1291−inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst
1292−inlineToMarkdown opts (Cite (c:cs) lst)
1293− | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst
1294− | otherwise =
1295− if citationMode c == AuthorInText
1296− then do
1297− suffs <- inlineListToMarkdown opts $ citationSuffix c
1298− rest <- mapM convertOne cs
1299− let inbr = suffs <+> joincits rest
1300− br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
1301− return $ literal ("@" <> citationId c) <+> br
1302− else do
1303− cits <- mapM convertOne (c:cs)
1304− return $ literal "[" <> joincits cits <> literal "]"
1305− where
1306− joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
1307− convertOne Citation { citationId = k
1308− , citationPrefix = pinlines
1309− , citationSuffix = sinlines
1310− , citationMode = m }
1311− = do
1312− pdoc <- inlineListToMarkdown opts pinlines
1313− sdoc <- inlineListToMarkdown opts sinlines
1314− let k' = literal (modekey m <> "@" <> k)
1315− r = case sinlines of
1316− Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
1317− _ -> k' <+> sdoc
1318− return $ pdoc <+> r
1319− modekey SuppressAuthor = "-"
1320− modekey _ = ""
1321−inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do
1322− variant <- asks envVariant
1323− linktext <- inlineListToMarkdown opts txt
1324− let linktitle = if T.null tit
1325− then empty
1326− else literal $ " \"" <> tit <> "\""
1327− let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
1328− let useAuto = isURI src &&
1329− case txt of
1330− [Str s] | escapeURI s == srcSuffix -> True
1331− _ -> False
1332− let useRefLinks = writerReferenceLinks opts && not useAuto
1333− shortcutable <- asks envRefShortcutable
1334− let useShortcutRefLinks = shortcutable &&
1335− isEnabled Ext_shortcut_reference_links opts
1336− reftext <- if useRefLinks
1337− then literal <$> getReference attr linktext (src, tit)
1338− else return mempty
1339− case variant of
1340− PlainText
1341− | useAuto -> return $ literal srcSuffix
1342− | otherwise -> return linktext
1343− _ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
1344− | useRefLinks ->
1345− let first = "[" <> linktext <> "]"
1346− second = if getKey linktext == getKey reftext
1347− then if useShortcutRefLinks
1348− then ""
1349− else "[]"
1350− else "[" <> reftext <> "]"
1351− in return $ first <> second
1352− | isEnabled Ext_raw_html opts
1353− , not (isEnabled Ext_link_attributes opts)
1354− , attr /= nullAttr -> -- use raw HTML to render attributes
1355− literal . T.strip <$>
1356− writeHtml5String opts{ writerTemplate = Nothing }
1357− (Pandoc nullMeta [Plain [lnk]])
1358− | otherwise -> return $
1359− "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <>
1360− linkAttributes opts attr
1361−inlineToMarkdown opts img@(Image attr alternate (source, tit))
1362− | isEnabled Ext_raw_html opts &&
1363− not (isEnabled Ext_link_attributes opts) &&
1364− attr /= nullAttr = -- use raw HTML
1365− literal . T.strip <$>
1366− writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]])
1367− | otherwise = do
1368− variant <- asks envVariant
1369− let txt = if null alternate || alternate == [Str source]
1370− -- to prevent autolinks
1371− then [Str ""]
1372− else alternate
1373− linkPart <- inlineToMarkdown opts (Link attr txt (source, tit))
1374− return $ case variant of
1375− PlainText -> "[" <> linkPart <> "]"
1376− _ -> "!" <> linkPart
1377−inlineToMarkdown opts (Note contents) = do
1378− modify (\st -> st{ stNotes = contents : stNotes st })
1379− st <- get
1380− let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1)
1381− if isEnabled Ext_footnotes opts
1382− then return $ "[^" <> ref <> "]"
1383− else return $ "[" <> ref <> "]"
1384−
1385−makeMathPlainer :: [Inline] -> [Inline]
1386−makeMathPlainer = walk go
1387− where
1388− go (Emph xs) = Span nullAttr xs
1389− go x = x
1390−
1391822lineBreakToSpace :: Inline -> Inline
1392823lineBreakToSpace LineBreak = Space
1393824lineBreakToSpace SoftBreak = Space
1394825lineBreakToSpace x = x
1395−
1396−toSubscriptInline :: Inline -> Maybe Inline
1397−toSubscriptInline Space = Just Space
1398−toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
1399−toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
1400−toSubscriptInline LineBreak = Just LineBreak
1401−toSubscriptInline SoftBreak = Just SoftBreak
1402−toSubscriptInline _ = Nothing
1403−
1404−toSuperscriptInline :: Inline -> Maybe Inline
1405−toSuperscriptInline Space = Just Space
1406−toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
1407−toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
1408−toSuperscriptInline LineBreak = Just LineBreak
1409−toSuperscriptInline SoftBreak = Just SoftBreak
1410−toSuperscriptInline _ = Nothing
src/Text/Pandoc/Writers/Markdown/Inline.hsnew file+601
@@ -0,0 +1,601 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE ViewPatterns #-}
4+{- |
5+ Module : Text.Pandoc.Writers.Markdown.Inline
6+ Copyright : Copyright (C) 2006-2021 John MacFarlane
7+ License : GNU GPL, version 2 or above
8+
9+ Maintainer : John MacFarlane <jgm@berkeley.edu>
10+ Stability : alpha
11+ Portability : portable
12+-}
13+module Text.Pandoc.Writers.Markdown.Inline (
14+ inlineListToMarkdown
15+ ) where
16+import Control.Monad.Reader
17+import Control.Monad.State.Strict
18+import Data.Char (isAlphaNum, isDigit)
19+import Data.List (find, intersperse)
20+import qualified Data.Map as M
21+import Data.Maybe (fromMaybe)
22+import Data.Text (Text)
23+import qualified Data.Text as T
24+import Network.HTTP (urlEncode)
25+import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
26+import Text.Pandoc.Definition
27+import Text.Pandoc.Logging
28+import Text.Pandoc.Options
29+import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space)
30+import Text.DocLayout
31+import Text.Pandoc.Shared
32+import Text.Pandoc.Writers.Shared
33+import Text.Pandoc.Walk
34+import Text.Pandoc.Writers.HTML (writeHtml5String)
35+import Text.Pandoc.Writers.Math (texMathToInlines)
36+import Text.Pandoc.XML (toHtml5Entities)
37+import Data.Coerce (coerce)
38+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
39+ WriterState(..),
40+ WriterEnv(..), MD)
41+
42+-- | Escape special characters for Markdown.
43+escapeText :: WriterOptions -> Text -> Text
44+escapeText opts = T.pack . go . T.unpack
45+ where
46+ go [] = []
47+ go (c:cs) =
48+ case c of
49+ '<' | isEnabled Ext_all_symbols_escapable opts ->
50+ '\\' : '<' : go cs
51+ | otherwise -> "&lt;" ++ go cs
52+ '>' | isEnabled Ext_all_symbols_escapable opts ->
53+ '\\' : '>' : go cs
54+ | otherwise -> "&gt;" ++ go cs
55+ '@' | isEnabled Ext_citations opts ->
56+ case cs of
57+ (d:_)
58+ | isAlphaNum d || d == '_'
59+ -> '\\':'@':go cs
60+ _ -> '@':go cs
61+ _ | c `elem` ['\\','`','*','_','[',']','#'] ->
62+ '\\':c:go cs
63+ '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
64+ '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
65+ '~' | isEnabled Ext_subscript opts ||
66+ isEnabled Ext_strikeout opts -> '\\':'~':go cs
67+ '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
68+ '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
69+ '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
70+ '-' | isEnabled Ext_smart opts ->
71+ case cs of
72+ '-':_ -> '\\':'-':go cs
73+ _ -> '-':go cs
74+ '.' | isEnabled Ext_smart opts ->
75+ case cs of
76+ '.':'.':rest -> '\\':'.':'.':'.':go rest
77+ _ -> '.':go cs
78+ _ -> case cs of
79+ '_':x:xs
80+ | isEnabled Ext_intraword_underscores opts
81+ , isAlphaNum c
82+ , isAlphaNum x -> c : '_' : x : go xs
83+ _ -> c : go cs
84+
85+attrsToMarkdown :: Attr -> Doc Text
86+attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
87+ where attribId = case attribs of
88+ ("",_,_) -> empty
89+ (i,_,_) -> "#" <> escAttr i
90+ attribClasses = case attribs of
91+ (_,[],_) -> empty
92+ (_,cs,_) -> hsep $
93+ map (escAttr . ("."<>))
94+ cs
95+ attribKeys = case attribs of
96+ (_,_,[]) -> empty
97+ (_,_,ks) -> hsep $
98+ map (\(k,v) -> escAttr k
99+ <> "=\"" <>
100+ escAttr v <> "\"") ks
101+ escAttr = mconcat . map escAttrChar . T.unpack
102+ escAttrChar '"' = literal "\\\""
103+ escAttrChar '\\' = literal "\\\\"
104+ escAttrChar c = literal $ T.singleton c
105+
106+linkAttributes :: WriterOptions -> Attr -> Doc Text
107+linkAttributes opts attr =
108+ if isEnabled Ext_link_attributes opts && attr /= nullAttr
109+ then attrsToMarkdown attr
110+ else empty
111+
112+getKey :: Doc Text -> Key
113+getKey = toKey . render Nothing
114+
115+findUsableIndex :: [Text] -> Int -> Int
116+findUsableIndex lbls i = if tshow i `elem` lbls
117+ then findUsableIndex lbls (i + 1)
118+ else i
119+
120+getNextIndex :: PandocMonad m => MD m Int
121+getNextIndex = do
122+ prevRefs <- gets stPrevRefs
123+ refs <- gets stRefs
124+ i <- (+ 1) <$> gets stLastIdx
125+ modify $ \s -> s{ stLastIdx = i }
126+ let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs
127+ return $ findUsableIndex refLbls i
128+
129+-- | Get reference for target; if none exists, create unique one and return.
130+-- Prefer label if possible; otherwise, generate a unique key.
131+getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text
132+getReference attr label target = do
133+ refs <- gets stRefs
134+ case find (\(_,t,a) -> t == target && a == attr) refs of
135+ Just (ref, _, _) -> return ref
136+ Nothing -> do
137+ keys <- gets stKeys
138+ let key = getKey label
139+ let rawkey = coerce key
140+ case M.lookup key keys of
141+ Nothing -> do -- no other refs with this label
142+ (lab', idx) <- if T.null rawkey ||
143+ T.length rawkey > 999 ||
144+ T.any (\c -> c == '[' || c == ']') rawkey
145+ then do
146+ i <- getNextIndex
147+ return (tshow i, i)
148+ else
149+ return (render Nothing label, 0)
150+ modify (\s -> s{
151+ stRefs = (lab', target, attr) : refs,
152+ stKeys = M.insert (getKey label)
153+ (M.insert (target, attr) idx mempty)
154+ (stKeys s) })
155+ return lab'
156+
157+ Just km -> -- we have refs with this label
158+ case M.lookup (target, attr) km of
159+ Just i -> do
160+ let lab' = render Nothing $
161+ label <> if i == 0
162+ then mempty
163+ else literal (tshow i)
164+ -- make sure it's in stRefs; it may be
165+ -- a duplicate that was printed in a previous
166+ -- block:
167+ when ((lab', target, attr) `notElem` refs) $
168+ modify (\s -> s{
169+ stRefs = (lab', target, attr) : refs })
170+ return lab'
171+ Nothing -> do -- but this one is to a new target
172+ i <- getNextIndex
173+ let lab' = tshow i
174+ modify (\s -> s{
175+ stRefs = (lab', target, attr) : refs,
176+ stKeys = M.insert key
177+ (M.insert (target, attr) i km)
178+ (stKeys s) })
179+ return lab'
180+
181+-- | Convert list of Pandoc inline elements to markdown.
182+inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text)
183+inlineListToMarkdown opts lst = do
184+ inlist <- asks envInList
185+ go (if inlist then avoidBadWrapsInList lst else lst)
186+ where go [] = return empty
187+ go (x@Math{}:y@(Str t):zs)
188+ | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058
189+ = liftM2 (<>) (inlineToMarkdown opts x)
190+ (go (RawInline (Format "html") "<!-- -->" : y : zs))
191+ go (i:is) = case i of
192+ Link {} -> case is of
193+ -- If a link is followed by another link, or '[', '(' or ':'
194+ -- then we don't shortcut
195+ Link {}:_ -> unshortcutable
196+ Space:Link {}:_ -> unshortcutable
197+ Space:(Str(thead -> Just '[')):_ -> unshortcutable
198+ Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
199+ Space:(Cite _ _):_ -> unshortcutable
200+ SoftBreak:Link {}:_ -> unshortcutable
201+ SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable
202+ SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
203+ SoftBreak:(Cite _ _):_ -> unshortcutable
204+ LineBreak:Link {}:_ -> unshortcutable
205+ LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable
206+ LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
207+ LineBreak:(Cite _ _):_ -> unshortcutable
208+ (Cite _ _):_ -> unshortcutable
209+ Str (thead -> Just '['):_ -> unshortcutable
210+ Str (thead -> Just '('):_ -> unshortcutable
211+ Str (thead -> Just ':'):_ -> unshortcutable
212+ (RawInline _ (thead -> Just '[')):_ -> unshortcutable
213+ (RawInline _ (thead -> Just '(')):_ -> unshortcutable
214+ (RawInline _ (thead -> Just ':')):_ -> unshortcutable
215+ (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable
216+ _ -> shortcutable
217+ _ -> shortcutable
218+ where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
219+ unshortcutable = do
220+ iMark <- local
221+ (\env -> env { envRefShortcutable = False })
222+ (inlineToMarkdown opts i)
223+ fmap (iMark <>) (go is)
224+ thead = fmap fst . T.uncons
225+
226+isSp :: Inline -> Bool
227+isSp Space = True
228+isSp SoftBreak = True
229+isSp _ = False
230+
231+avoidBadWrapsInList :: [Inline] -> [Inline]
232+avoidBadWrapsInList [] = []
233+avoidBadWrapsInList (s:Str (T.uncons -> Just ('>',cs)):xs) | isSp s =
234+ Str (" >" <> cs) : avoidBadWrapsInList xs
235+avoidBadWrapsInList [s, Str (T.uncons -> Just (c, cs))]
236+ | T.null cs && isSp s && c `elem` ['-','*','+'] = [Str $ T.pack [' ', c]]
237+avoidBadWrapsInList (s:Str (T.uncons -> Just (c, cs)):Space:xs)
238+ | T.null cs && isSp s && c `elem` ['-','*','+'] =
239+ Str (T.pack [' ', c]) : Space : avoidBadWrapsInList xs
240+avoidBadWrapsInList (s:Str cs:Space:xs)
241+ | isSp s && isOrderedListMarker cs =
242+ Str (" " <> cs) : Space : avoidBadWrapsInList xs
243+avoidBadWrapsInList [s, Str cs]
244+ | isSp s && isOrderedListMarker cs = [Str $ " " <> cs]
245+avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs
246+
247+isOrderedListMarker :: Text -> Bool
248+isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) &&
249+ isRight (runParser (anyOrderedListMarker >> eof)
250+ defaultParserState "" xs)
251+ where
252+ isRight (Right _) = True
253+ isRight (Left _) = False
254+
255+-- | Convert Pandoc inline element to markdown.
256+inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text)
257+inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) =
258+ case lookup "data-emoji" kvs of
259+ Just emojiname | isEnabled Ext_emoji opts ->
260+ return $ ":" <> literal emojiname <> ":"
261+ _ -> inlineToMarkdown opts (Str s)
262+inlineToMarkdown opts (Span attrs ils) = do
263+ variant <- asks envVariant
264+ contents <- inlineListToMarkdown opts ils
265+ return $ case attrs of
266+ (_,["csl-block"],_) -> (cr <>)
267+ (_,["csl-left-margin"],_) -> (cr <>)
268+ (_,["csl-indent"],_) -> (cr <>)
269+ _ -> id
270+ $ case variant of
271+ PlainText -> contents
272+ _ | attrs == nullAttr -> contents
273+ | isEnabled Ext_bracketed_spans opts ->
274+ let attrs' = if attrs /= nullAttr
275+ then attrsToMarkdown attrs
276+ else empty
277+ in "[" <> contents <> "]" <> attrs'
278+ | isEnabled Ext_raw_html opts ||
279+ isEnabled Ext_native_spans opts ->
280+ tagWithAttrs "span" attrs <> contents <> literal "</span>"
281+ | otherwise -> contents
282+inlineToMarkdown _ (Emph []) = return empty
283+inlineToMarkdown opts (Emph lst) = do
284+ variant <- asks envVariant
285+ contents <- inlineListToMarkdown opts lst
286+ return $ case variant of
287+ PlainText
288+ | isEnabled Ext_gutenberg opts -> "_" <> contents <> "_"
289+ | otherwise -> contents
290+ _ -> "*" <> contents <> "*"
291+inlineToMarkdown _ (Underline []) = return empty
292+inlineToMarkdown opts (Underline lst) = do
293+ variant <- asks envVariant
294+ contents <- inlineListToMarkdown opts lst
295+ case variant of
296+ PlainText -> return contents
297+ _ | isEnabled Ext_bracketed_spans opts ->
298+ return $ "[" <> contents <> "]" <> "{.ul}"
299+ | isEnabled Ext_native_spans opts ->
300+ return $ tagWithAttrs "span" ("", ["underline"], [])
301+ <> contents
302+ <> literal "</span>"
303+ | isEnabled Ext_raw_html opts ->
304+ return $ "<u>" <> contents <> "</u>"
305+ | otherwise -> inlineToMarkdown opts (Emph lst)
306+inlineToMarkdown _ (Strong []) = return empty
307+inlineToMarkdown opts (Strong lst) = do
308+ variant <- asks envVariant
309+ case variant of
310+ PlainText ->
311+ inlineListToMarkdown opts $
312+ if isEnabled Ext_gutenberg opts
313+ then capitalize lst
314+ else lst
315+ _ -> do
316+ contents <- inlineListToMarkdown opts lst
317+ return $ "**" <> contents <> "**"
318+inlineToMarkdown _ (Strikeout []) = return empty
319+inlineToMarkdown opts (Strikeout lst) = do
320+ contents <- inlineListToMarkdown opts lst
321+ return $ if isEnabled Ext_strikeout opts
322+ then "~~" <> contents <> "~~"
323+ else if isEnabled Ext_raw_html opts
324+ then "<s>" <> contents <> "</s>"
325+ else contents
326+inlineToMarkdown _ (Superscript []) = return empty
327+inlineToMarkdown opts (Superscript lst) =
328+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
329+ contents <- inlineListToMarkdown opts lst
330+ if isEnabled Ext_superscript opts
331+ then return $ "^" <> contents <> "^"
332+ else if isEnabled Ext_raw_html opts
333+ then return $ "<sup>" <> contents <> "</sup>"
334+ else
335+ case traverse toSuperscriptInline lst of
336+ Just xs' | not (writerPreferAscii opts)
337+ -> inlineListToMarkdown opts xs'
338+ _ -> do
339+ let rendered = render Nothing contents
340+ return $
341+ case mapM toSuperscript (T.unpack rendered) of
342+ Just r -> literal $ T.pack r
343+ Nothing -> literal $ "^(" <> rendered <> ")"
344+inlineToMarkdown _ (Subscript []) = return empty
345+inlineToMarkdown opts (Subscript lst) =
346+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
347+ contents <- inlineListToMarkdown opts lst
348+ if isEnabled Ext_subscript opts
349+ then return $ "~" <> contents <> "~"
350+ else if isEnabled Ext_raw_html opts
351+ then return $ "<sub>" <> contents <> "</sub>"
352+ else
353+ case traverse toSubscriptInline lst of
354+ Just xs' | not (writerPreferAscii opts)
355+ -> inlineListToMarkdown opts xs'
356+ _ -> do
357+ let rendered = render Nothing contents
358+ return $
359+ case mapM toSuperscript (T.unpack rendered) of
360+ Just r -> literal $ T.pack r
361+ Nothing -> literal $ "_(" <> rendered <> ")"
362+inlineToMarkdown opts (SmallCaps lst) = do
363+ variant <- asks envVariant
364+ if variant /= PlainText &&
365+ (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
366+ then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst)
367+ else inlineListToMarkdown opts $ capitalize lst
368+inlineToMarkdown opts (Quoted SingleQuote lst) = do
369+ contents <- inlineListToMarkdown opts lst
370+ return $ if isEnabled Ext_smart opts
371+ then "'" <> contents <> "'"
372+ else
373+ if writerPreferAscii opts
374+ then "&lsquo;" <> contents <> "&rsquo;"
375+ else "‘" <> contents <> "’"
376+inlineToMarkdown opts (Quoted DoubleQuote lst) = do
377+ contents <- inlineListToMarkdown opts lst
378+ return $ if isEnabled Ext_smart opts
379+ then "\"" <> contents <> "\""
380+ else
381+ if writerPreferAscii opts
382+ then "&ldquo;" <> contents <> "&rdquo;"
383+ else "“" <> contents <> "”"
384+inlineToMarkdown opts (Code attr str) = do
385+ let tickGroups = filter (T.any (== '`')) $ T.group str
386+ let longest = if null tickGroups
387+ then 0
388+ else maximum $ map T.length tickGroups
389+ let marker = T.replicate (longest + 1) "`"
390+ let spacer = if longest == 0 then "" else " "
391+ let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
392+ then attrsToMarkdown attr
393+ else empty
394+ variant <- asks envVariant
395+ case variant of
396+ PlainText -> return $ literal str
397+ _ -> return $ literal
398+ (marker <> spacer <> str <> spacer <> marker) <> attrs
399+inlineToMarkdown opts (Str str) = do
400+ variant <- asks envVariant
401+ let str' = (if writerPreferAscii opts
402+ then toHtml5Entities
403+ else id) .
404+ (if isEnabled Ext_smart opts
405+ then unsmartify opts
406+ else id) .
407+ (if variant == PlainText
408+ then id
409+ else escapeText opts) $ str
410+ return $ literal str'
411+inlineToMarkdown opts (Math InlineMath str) =
412+ case writerHTMLMathMethod opts of
413+ WebTeX url -> inlineToMarkdown opts
414+ (Image nullAttr [Str str] (url <> T.pack (urlEncode $ T.unpack str), str))
415+ _ | isEnabled Ext_tex_math_dollars opts ->
416+ return $ "$" <> literal str <> "$"
417+ | isEnabled Ext_tex_math_single_backslash opts ->
418+ return $ "\\(" <> literal str <> "\\)"
419+ | isEnabled Ext_tex_math_double_backslash opts ->
420+ return $ "\\\\(" <> literal str <> "\\\\)"
421+ | otherwise -> do
422+ variant <- asks envVariant
423+ texMathToInlines InlineMath str >>=
424+ inlineListToMarkdown opts .
425+ (if variant == PlainText then makeMathPlainer else id)
426+inlineToMarkdown opts (Math DisplayMath str) =
427+ case writerHTMLMathMethod opts of
428+ WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
429+ inlineToMarkdown opts (Image nullAttr [Str str]
430+ (url <> T.pack (urlEncode $ T.unpack str), str))
431+ _ | isEnabled Ext_tex_math_dollars opts ->
432+ return $ "$$" <> literal str <> "$$"
433+ | isEnabled Ext_tex_math_single_backslash opts ->
434+ return $ "\\[" <> literal str <> "\\]"
435+ | isEnabled Ext_tex_math_double_backslash opts ->
436+ return $ "\\\\[" <> literal str <> "\\\\]"
437+ | otherwise -> (\x -> cr <> x <> cr) `fmap`
438+ (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
439+inlineToMarkdown opts il@(RawInline f str) = do
440+ let tickGroups = filter (T.any (== '`')) $ T.group str
441+ let numticks = if null tickGroups
442+ then 1
443+ else 1 + maximum (map T.length tickGroups)
444+ variant <- asks envVariant
445+ let Format fmt = f
446+ let rawAttribInline = return $
447+ literal (T.replicate numticks "`") <> literal str <>
448+ literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}"
449+ let renderEmpty = mempty <$ report (InlineNotRendered il)
450+ case variant of
451+ PlainText -> renderEmpty
452+ Commonmark
453+ | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"]
454+ -> return $ literal str
455+ Markdown
456+ | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
457+ "markdown_mmd", "markdown_strict"]
458+ -> return $ literal str
459+ _ | isEnabled Ext_raw_attribute opts -> rawAttribInline
460+ | f `elem` ["html", "html5", "html4"]
461+ , isEnabled Ext_raw_html opts
462+ -> return $ literal str
463+ | f `elem` ["latex", "tex"]
464+ , isEnabled Ext_raw_tex opts
465+ -> return $ literal str
466+ _ -> renderEmpty
467+
468+
469+inlineToMarkdown opts LineBreak = do
470+ variant <- asks envVariant
471+ if variant == PlainText || isEnabled Ext_hard_line_breaks opts
472+ then return cr
473+ else return $
474+ if isEnabled Ext_escaped_line_breaks opts
475+ then "\\" <> cr
476+ else " " <> cr
477+inlineToMarkdown _ Space = do
478+ escapeSpaces <- asks envEscapeSpaces
479+ return $ if escapeSpaces then "\\ " else space
480+inlineToMarkdown opts SoftBreak = do
481+ escapeSpaces <- asks envEscapeSpaces
482+ let space' = if escapeSpaces then "\\ " else space
483+ return $ case writerWrapText opts of
484+ WrapNone -> space'
485+ WrapAuto -> space'
486+ WrapPreserve -> cr
487+inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst
488+inlineToMarkdown opts (Cite (c:cs) lst)
489+ | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst
490+ | otherwise =
491+ if citationMode c == AuthorInText
492+ then do
493+ suffs <- inlineListToMarkdown opts $ citationSuffix c
494+ rest <- mapM convertOne cs
495+ let inbr = suffs <+> joincits rest
496+ br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
497+ return $ literal ("@" <> citationId c) <+> br
498+ else do
499+ cits <- mapM convertOne (c:cs)
500+ return $ literal "[" <> joincits cits <> literal "]"
501+ where
502+ joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
503+ convertOne Citation { citationId = k
504+ , citationPrefix = pinlines
505+ , citationSuffix = sinlines
506+ , citationMode = m }
507+ = do
508+ pdoc <- inlineListToMarkdown opts pinlines
509+ sdoc <- inlineListToMarkdown opts sinlines
510+ let k' = literal (modekey m <> "@" <> k)
511+ r = case sinlines of
512+ Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
513+ _ -> k' <+> sdoc
514+ return $ pdoc <+> r
515+ modekey SuppressAuthor = "-"
516+ modekey _ = ""
517+inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do
518+ variant <- asks envVariant
519+ linktext <- inlineListToMarkdown opts txt
520+ let linktitle = if T.null tit
521+ then empty
522+ else literal $ " \"" <> tit <> "\""
523+ let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
524+ let useAuto = isURI src &&
525+ case txt of
526+ [Str s] | escapeURI s == srcSuffix -> True
527+ _ -> False
528+ let useRefLinks = writerReferenceLinks opts && not useAuto
529+ shortcutable <- asks envRefShortcutable
530+ let useShortcutRefLinks = shortcutable &&
531+ isEnabled Ext_shortcut_reference_links opts
532+ reftext <- if useRefLinks
533+ then literal <$> getReference attr linktext (src, tit)
534+ else return mempty
535+ case variant of
536+ PlainText
537+ | useAuto -> return $ literal srcSuffix
538+ | otherwise -> return linktext
539+ _ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
540+ | useRefLinks ->
541+ let first = "[" <> linktext <> "]"
542+ second = if getKey linktext == getKey reftext
543+ then if useShortcutRefLinks
544+ then ""
545+ else "[]"
546+ else "[" <> reftext <> "]"
547+ in return $ first <> second
548+ | isEnabled Ext_raw_html opts
549+ , not (isEnabled Ext_link_attributes opts)
550+ , attr /= nullAttr -> -- use raw HTML to render attributes
551+ literal . T.strip <$>
552+ writeHtml5String opts{ writerTemplate = Nothing }
553+ (Pandoc nullMeta [Plain [lnk]])
554+ | otherwise -> return $
555+ "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <>
556+ linkAttributes opts attr
557+inlineToMarkdown opts img@(Image attr alternate (source, tit))
558+ | isEnabled Ext_raw_html opts &&
559+ not (isEnabled Ext_link_attributes opts) &&
560+ attr /= nullAttr = -- use raw HTML
561+ literal . T.strip <$>
562+ writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]])
563+ | otherwise = do
564+ variant <- asks envVariant
565+ let txt = if null alternate || alternate == [Str source]
566+ -- to prevent autolinks
567+ then [Str ""]
568+ else alternate
569+ linkPart <- inlineToMarkdown opts (Link attr txt (source, tit))
570+ return $ case variant of
571+ PlainText -> "[" <> linkPart <> "]"
572+ _ -> "!" <> linkPart
573+inlineToMarkdown opts (Note contents) = do
574+ modify (\st -> st{ stNotes = contents : stNotes st })
575+ st <- get
576+ let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1)
577+ if isEnabled Ext_footnotes opts
578+ then return $ "[^" <> ref <> "]"
579+ else return $ "[" <> ref <> "]"
580+
581+makeMathPlainer :: [Inline] -> [Inline]
582+makeMathPlainer = walk go
583+ where
584+ go (Emph xs) = Span nullAttr xs
585+ go x = x
586+
587+toSubscriptInline :: Inline -> Maybe Inline
588+toSubscriptInline Space = Just Space
589+toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
590+toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
591+toSubscriptInline LineBreak = Just LineBreak
592+toSubscriptInline SoftBreak = Just SoftBreak
593+toSubscriptInline _ = Nothing
594+
595+toSuperscriptInline :: Inline -> Maybe Inline
596+toSuperscriptInline Space = Just Space
597+toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
598+toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
599+toSuperscriptInline LineBreak = Just LineBreak
600+toSuperscriptInline SoftBreak = Just SoftBreak
601+toSuperscriptInline _ = Nothing
src/Text/Pandoc/Writers/Markdown/Types.hsnew file+81
@@ -0,0 +1,81 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.Markdown.Types
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.Markdown.Types (
12+ MarkdownVariant(..),
13+ WriterState(..),
14+ WriterEnv(..),
15+ Notes,
16+ Ref,
17+ Refs,
18+ MD,
19+ evalMD
20+ ) where
21+import Control.Monad.Reader
22+import Control.Monad.State.Strict
23+import Data.Default
24+import qualified Data.Map as M
25+import qualified Data.Set as Set
26+import Data.Text (Text)
27+import Text.Pandoc.Parsing (Key)
28+import Text.Pandoc.Class.PandocMonad (PandocMonad)
29+import Text.Pandoc.Definition
30+
31+type Notes = [[Block]]
32+type Ref = (Text, Target, Attr)
33+type Refs = [Ref]
34+
35+type MD m = ReaderT WriterEnv (StateT WriterState m)
36+
37+evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a
38+evalMD md env st = evalStateT (runReaderT md env) st
39+
40+data WriterEnv = WriterEnv { envInList :: Bool
41+ , envVariant :: MarkdownVariant
42+ , envRefShortcutable :: Bool
43+ , envBlockLevel :: Int
44+ , envEscapeSpaces :: Bool
45+ }
46+
47+data MarkdownVariant =
48+ PlainText
49+ | Commonmark
50+ | Markdown
51+ deriving (Show, Eq)
52+
53+instance Default WriterEnv
54+ where def = WriterEnv { envInList = False
55+ , envVariant = Markdown
56+ , envRefShortcutable = True
57+ , envBlockLevel = 0
58+ , envEscapeSpaces = False
59+ }
60+
61+data WriterState = WriterState { stNotes :: Notes
62+ , stPrevRefs :: Refs
63+ , stRefs :: Refs
64+ , stKeys :: M.Map Key
65+ (M.Map (Target, Attr) Int)
66+ , stLastIdx :: Int
67+ , stIds :: Set.Set Text
68+ , stNoteNum :: Int
69+ }
70+
71+instance Default WriterState
72+ where def = WriterState{ stNotes = []
73+ , stPrevRefs = []
74+ , stRefs = []
75+ , stKeys = M.empty
76+ , stLastIdx = 0
77+ , stIds = Set.empty
78+ , stNoteNum = 1
79+ }
80+
81+
stack.yaml+1−10
@@ -7,20 +7,11 @@ packages:
77extra-deps:
88- hslua-1.3.0
99- hslua-module-path-0.1.0
10−- jira-wiki-markup-1.3.3
11−- commonmark-0.1.1.4
12−- commonmark-extensions-0.2.0.4
13−- commonmark-pandoc-0.2.0.1
14−- tasty-bench-0.2.1
15−- citeproc-0.3.0.7
16−- skylighting-0.10.4
17−- skylighting-core-0.10.4
18−- xml-conduit-1.9.1.0
1910#- citeproc:
2011# git: https://github.com/jgm/citeproc.git
2112# commit: feb3b7580c6738eec3b23921f7c1739cfba611aa
2213ghc-options:
2314 "$locals": -fhide-source-paths -Wno-missing-home-modules
24−resolver: lts-17.2
15+resolver: lts-17.5
2516nix:
2617 packages: [zlib]
trypandoc/trypandoc.hs−4
@@ -14,16 +14,12 @@ module Main where
1414import Network.Wai.Handler.CGI
1515import Network.Wai.Middleware.Timeout (timeout)
1616import Network.Wai
17−import Control.Applicative ((<$>))
1817import Data.Maybe (fromMaybe)
1918import Network.HTTP.Types.Status (status200)
2019import Network.HTTP.Types.Header (hContentType)
2120import Network.HTTP.Types.URI (queryToQueryText)
2221import Text.Pandoc
23−import Text.Pandoc.Writers.Math (defaultMathJaxURL)
2422import Text.Pandoc.Highlighting (pygments)
25−import Text.Pandoc.Readers (getReader, Reader(..))
26−import Text.Pandoc.Writers (getWriter, Writer(..))
2723import Text.Pandoc.Shared (tabFilter)
2824import Data.Aeson
2925import qualified Data.Text as T