git.iko.soy/typeable/pandoc
on master help sign in
.circleci/config.yml+3−3
since splitsnapshots
unifiedsplit
@@ -2,7 +2,7 @@ version: "2.1"
2jobs:
2jobs:
3 build:
3 build:
4 macos:
4 macos:
5− xcode: 9.4.1
5+ xcode: 11.1.0
6 environment:
6 environment:
7 HOMEBREW_NO_AUTO_UPDATE: 1
7 HOMEBREW_NO_AUTO_UPDATE: 1
8 steps:
8 steps:
@@ -20,10 +20,10 @@ jobs:
20 command: stack --no-terminal setup
20 command: stack --no-terminal setup
21 - run:
21 - run:
22 name: Run tests
22 name: Run tests
23− command: stack --no-terminal test --ghc-options=-split-sections
23+ command: stack --no-terminal test
24 - run:
24 - run:
25 name: Install executable
25 name: Install executable
26− command: stack --no-terminal install --ghc-options=-split-sections
26+ command: stack --no-terminal install
27 - run:
27 - run:
28 name: Create artifacts
28 name: Create artifacts
29 command: |
29 command: |
.editorconfig+8
@@ -8,6 +8,14 @@ indent_size = 2
8insert_final_newline = true
8insert_final_newline = true
9trim_trailing_whitespace = true
9trim_trailing_whitespace = true
10
10
11+[*.{zip,docx}]
12+charset =
13+end_of_line =
14+indent_style =
15+indent_size =
16+insert_final_newline =
17+trim_trailing_whitespace =
18+
11[*.{markdown,md}]
19[*.{markdown,md}]
12trim_trailing_whitespace = false
20trim_trailing_whitespace = false
13
21
.github/ISSUE_TEMPLATEdeleted−11
@@ -1,11 +0,0 @@
1−Asking a question?
2−Please use the pandoc-discuss mailing list instead:
3−https://groups.google.com/forum/#!forum/pandoc-discuss
4−This tracker is for bug reports and enhancement requests.
5−
6−Reporting a bug?
7−Please include your pandoc version and an example that allows us
8−to reproduce the problem (complete input, exact command line
9−used, expected and actual output). The smaller the example, the
10−better.
11−
.github/ISSUE_TEMPLATE/bug_report.mdnew file+25
@@ -0,0 +1,25 @@
1+---
2+name: Bug report
3+about: Create a report to help us improve
4+title: ''
5+labels: 'bug'
6+assignees: ''
7+
8+---
9+
10+<!--
11+Thank you for reporting an issue! Before you continue, please make sure that you have
12+
13+- searched the issue tracker for similar issues (including closed issues): https://github.com/jgm/pandoc/issues
14+- searched the pandoc-discuss mailing list for solutions: https://groups.google.com/forum/#!forum/pandoc-discuss
15+- reproduced your issue on the latest release of pandoc, or on https://pandoc.org/try
16+
17+Note that this bug tracker is for reporting bugs, not asking questions. For questions, use the pandoc-discuss mailing list.
18+-->
19+
20+**Explain the problem.**
21+Include the exact command line you used and all inputs necessary to reproduce the issue. Please create as minimal an example as possible, to help the maintainers isolate the problem. Explain the output you received and how it differs from what you expected.
22+
23+**Pandoc version?**
24+What version of pandoc are you using, on what OS?
25+
.github/ISSUE_TEMPLATE/config.ymlnew file+9
@@ -0,0 +1,9 @@
1+blank_issues_enabled: false
2+contact_links:
3+ - name: Ask a question
4+ url: https://groups.google.com/forum/#!forum/pandoc-discuss
5+ about: "Discussion forum for pandoc (may be used as an email list)"
6+ - name: Contributing Guidelines
7+ url: https://pandoc.org/CONTRIBUTING.html
8+ about: "Guidelines for submitting bug reports and code improvements"
9+
.github/ISSUE_TEMPLATE/feature_request.mdnew file+21
@@ -0,0 +1,21 @@
1+---
2+name: Feature request
3+about: Suggest an idea for this project
4+title: ''
5+labels: 'enhancement'
6+assignees: ''
7+
8+---
9+
10+<!--
11+Thank you for suggesting a feature! Before you continue, please make sure that you have
12+
13+- read the contributing guidelines: https://pandoc.org/CONTRIBUTING.html
14+- searched the issue tracker for similar issues (including closed issues): https://github.com/jgm/pandoc/issues
15+- searched the pandoc-discuss mailing list for relevant discussions: https://groups.google.com/forum/#!forum/pandoc-discuss
16+-->
17+
18+**Describe your proposed improvement and the problem it solves.**
19+
20+**Describe alternatives you've considered.**
21+
.github/workflows/ci.yml+21−6
@@ -44,32 +44,47 @@ jobs:
44 matrix:
44 matrix:
45 versions:
45 versions:
46 - ghc: '8.0.2'
46 - ghc: '8.0.2'
47− cabal: '2.2'
47+ cabal: '2.4'
48− prefix: 'new-'
48+ prefix: 'v2-'
49+ cabalopts: ''
49 testopts: ''
50 testopts: ''
50 - ghc: '8.2.2'
51 - ghc: '8.2.2'
51 cabal: '2.4'
52 cabal: '2.4'
52 prefix: 'v2-'
53 prefix: 'v2-'
54+ cabalopts: ''
53 testopts: ''
55 testopts: ''
54 - ghc: '8.4.4'
56 - ghc: '8.4.4'
55 cabal: '2.4'
57 cabal: '2.4'
56 prefix: 'v2-'
58 prefix: 'v2-'
59+ cabalopts: '-f-embed_data_files'
57 testopts: ''
60 testopts: ''
58 - ghc: '8.6.5'
61 - ghc: '8.6.5'
59 cabal: '3.2'
62 cabal: '3.2'
60 prefix: ''
63 prefix: ''
64+ cabalopts: ''
61 testopts: '--test-option=--hide-successes'
65 testopts: '--test-option=--hide-successes'
62 - ghc: '8.8.4'
66 - ghc: '8.8.4'
63 cabal: '3.2'
67 cabal: '3.2'
64 prefix: ''
68 prefix: ''
69+ cabalopts: ''
65 testopts: '--test-option=--hide-successes'
70 testopts: '--test-option=--hide-successes'
66− - ghc: '8.10.2'
71+ - ghc: '8.10.4'
67 cabal: '3.2'
72 cabal: '3.2'
68 prefix: ''
73 prefix: ''
74+ cabalopts: '-ftrypandoc'
75+ testopts: '--test-option=--hide-successes'
76+ - ghc: '9.0.1'
77+ cabal: '3.4'
78+ prefix: ''
79+ cabalopts: ''
69 testopts: '--test-option=--hide-successes'
80 testopts: '--test-option=--hide-successes'
70 steps:
81 steps:
71 - uses: actions/checkout@v2
82 - uses: actions/checkout@v2
72
83
84+ # needed by memory
85+ - name: Install numa
86+ run: sudo apt-get install libnuma-dev
87+
73 # need to install older cabal/ghc versions from ppa repository
88 # need to install older cabal/ghc versions from ppa repository
74
89
75 - name: Install recent cabal/ghc
90 - name: Install recent cabal/ghc
@@ -101,14 +116,14 @@ jobs:
101 - name: Install dependencies
116 - name: Install dependencies
102 run: |
117 run: |
103 cabal ${{ matrix.versions.prefix }}update
118 cabal ${{ matrix.versions.prefix }}update
104− cabal ${{ matrix.versions.prefix }}build --dependencies-only --enable-tests --disable-optimization
119+ cabal ${{ matrix.versions.prefix }}build ${{ matrix.versions.cabalopts }} --dependencies-only --enable-tests --disable-optimization
105
120
106 - name: Build and test
121 - name: Build and test
107 run: |
122 run: |
108− cabal ${{ matrix.versions.prefix }}build --enable-tests --disable-optimization 2>&1 | tee build.log
123+ cabal ${{ matrix.versions.prefix }}build ${{ matrix.versions.cabalopts }} --enable-tests --disable-optimization 2>&1 | tee build.log
109 # fail if warnings in local build
124 # fail if warnings in local build
110 ! grep -q ": *[Ww]arning:" build.log || exit 1
125 ! grep -q ": *[Ww]arning:" build.log || exit 1
111− cabal ${{ matrix.versions.prefix }}test --disable-optimization ${{ matrix.versions.testopts }}
126+ cabal ${{ matrix.versions.prefix }}test ${{ matrix.versions.cabalopts }} --disable-optimization ${{ matrix.versions.testopts }}
112
127
113 windows:
128 windows:
114
129
.github/workflows/nightly.yml+9−2
@@ -10,15 +10,22 @@ jobs:
10 runs-on: ubuntu-18.04
10 runs-on: ubuntu-18.04
11 steps:
11 steps:
12 - uses: actions/checkout@v2
12 - 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+
13 - name: Install dependencies
20 - name: Install dependencies
14 run: |
21 run: |
15 ghc --version
22 ghc --version
16 cabal --version
23 cabal --version
17 cabal v2-update
24 cabal v2-update
18− cabal v2-build --dependencies-only all
25+ cabal v2-build --dependencies-only
19 - name: Build
26 - name: Build
20 run: |
27 run: |
21− cabal v2-install all
28+ cabal v2-install
22 strip $HOME/.cabal/bin/pandoc
29 strip $HOME/.cabal/bin/pandoc
23 - name: Install artifact
30 - name: Install artifact
24 run: |
31 run: |
.hlint.yaml+3−5
@@ -9,6 +9,7 @@
9# Ignore some builtin hints
9# Ignore some builtin hints
10#
10#
11- ignore: {name: "Avoid lambda"}
11- ignore: {name: "Avoid lambda"}
12+- ignore: {name: "Use bimap"}
12- ignore: {name: "Eta reduce"}
13- ignore: {name: "Eta reduce"}
13- ignore: {name: "Evaluate"}
14- ignore: {name: "Evaluate"}
14- ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained
15- ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained
@@ -39,13 +40,10 @@
39 name: "Redundant return"
40 name: "Redundant return"
40 within: Text.Pandoc.Citeproc.BibTeX
41 within: Text.Pandoc.Citeproc.BibTeX
41
42
42−# TODO: check
43+# With recent hlint this rule has given false positives on TH
43- ignore:
44- ignore:
44 name: "Redundant bracket"
45 name: "Redundant bracket"
45− within:
46+
46− - Text.Pandoc.Citeproc
47− - Text.Pandoc.Citeproc.BibTeX
48− - Text.Pandoc.Citeproc.MetaValue
49
47
50- ignore:
48- ignore:
51 name: "Use <$>"
49 name: "Use <$>"
AUTHORS.md+18
@@ -20,6 +20,7 @@
20- Andreas Lööw
20- Andreas Lööw
21- Andrew Dunning
21- Andrew Dunning
22- Andy Morris
22- Andy Morris
23+- Anti-Distinctlyminty
23- Antoine Latter
24- Antoine Latter
24- Antonio Terceiro
25- Antonio Terceiro
25- Arata Mizuki
26- Arata Mizuki
@@ -35,6 +36,7 @@
35- Benoit Schweblin
36- Benoit Schweblin
36- Bjorn Buckwalter
37- Bjorn Buckwalter
37- Blake Eryx
38- Blake Eryx
39+- Bodigrim
38- Bradley Kuhn
40- Bradley Kuhn
39- Brent Yorgey
41- Brent Yorgey
40- Brian Leung
42- Brian Leung
@@ -45,6 +47,7 @@
45- Carlos Sosa
47- Carlos Sosa
46- Cécile Chemin
48- Cécile Chemin
47- Cédric Couralet
49- Cédric Couralet
50+- Charanjit Singh
48- Chris Black
51- Chris Black
49- Christian Conkle
52- Christian Conkle
50- Christian Despres
53- Christian Despres
@@ -75,8 +78,10 @@
75- Eigil Rischel
78- Eigil Rischel
76- Emanuel Evans
79- Emanuel Evans
77- Emerson Harkin
80- Emerson Harkin
81+- Emily Bourke
78- Emily Eisenberg
82- Emily Eisenberg
79- Eric Kow
83- Eric Kow
84+- Erik Rask
80- Eric Seidel
85- Eric Seidel
81- Eric Schrijver
86- Eric Schrijver
82- Ethan Riley
87- Ethan Riley
@@ -86,6 +91,7 @@
86- Florian Beeres
91- Florian Beeres
87- Florian Eitel
92- Florian Eitel
88- Florian Klink
93- Florian Klink
94+- Florian Kohrt
89- Francesco Occhipinti
95- Francesco Occhipinti
90- François Gannaz
96- François Gannaz
91- Frederik Elwert
97- Frederik Elwert
@@ -146,6 +152,7 @@
146- Josef Svenningsson
152- Josef Svenningsson
147- Joseph C. Sible
153- Joseph C. Sible
148- Julien Cretel
154- Julien Cretel
155+- Julien Dutant
149- Juliette Fourcot
156- Juliette Fourcot
150- Juliusz Gonera
157- Juliusz Gonera
151- Justin Bogner
158- Justin Bogner
@@ -163,6 +170,8 @@
163- Leif Metcalf
170- Leif Metcalf
164- Leonard Rosenthol
171- Leonard Rosenthol
165- Lila
172- Lila
173+- Loïc Grobol
174+- Lorenzo
166- Lucas Escot
175- Lucas Escot
167- Luke Plant
176- Luke Plant
168- Marc Schreiber
177- Marc Schreiber
@@ -196,10 +205,12 @@
196- Nathan Gass
205- Nathan Gass
197- Neil Mayhew
206- Neil Mayhew
198- Nick Bart
207- Nick Bart
208+- Nick Berendsen
199- Nick Fleisher
209- Nick Fleisher
200- Nicolas Kaiser
210- Nicolas Kaiser
201- Nikolay Yakimov
211- Nikolay Yakimov
202- Nils Carlson
212- Nils Carlson
213+- Nixon Enraght-Moony
203- Nokome Bentley
214- Nokome Bentley
204- Ole Martin Ruud
215- Ole Martin Ruud
205- Oliver Matthews
216- Oliver Matthews
@@ -224,6 +235,7 @@
224- Raymond Ehlers
235- Raymond Ehlers
225- Recai Oktaş
236- Recai Oktaş
226- Roland Hieber
237- Roland Hieber
238+- Roman Beránek
227- RyanGlScott
239- RyanGlScott
228- Salim B
240- Salim B
229- Sascha Wilde
241- Sascha Wilde
@@ -241,6 +253,7 @@
241- Stefan Dresselhaus
253- Stefan Dresselhaus
242- Sumit Sahrawat
254- Sumit Sahrawat
243- Tarik Graba
255- Tarik Graba
256+- Tatiana Porras
244- Thenaesh Elango
257- Thenaesh Elango
245- Thomas Hodgson
258- Thomas Hodgson
246- Thomas Weißschuh
259- Thomas Weißschuh
@@ -274,6 +287,7 @@
274- a-vrma
287- a-vrma
275- andrebauer
288- andrebauer
276- argent0
289- argent0
290+- badumont
277- blmage
291- blmage
278- bucklereed
292- bucklereed
279- bumper314
293- bumper314
@@ -289,8 +303,11 @@
289- kaizshang91
303- kaizshang91
290- lux-lth
304- lux-lth
291- lwolfsonkin
305- lwolfsonkin
306+- mbrackeantidot
292- nkalvi
307- nkalvi
293- niszet
308- niszet
309+- nuew
310+- obcat
294- oltolm
311- oltolm
295- oquechy
312- oquechy
296- quasicomputational
313- quasicomputational
@@ -302,6 +319,7 @@
302- shreevatsa.public
319- shreevatsa.public
303- takahashim
320- takahashim
304- taotieren
321- taotieren
322+- tecosaur
305- TEC
323- TEC
306- tgkokk
324- tgkokk
307- the-solipsist
325- the-solipsist
CONTRIBUTING.md+16−11
@@ -244,9 +244,16 @@ Or with stack:
244
244
245 stack test --test-arguments='-p markdown'
245 stack test --test-arguments='-p markdown'
246
246
247−It is often helpful to add `-j4` (run tests in parallel)
247+It is often helpful to add `-j4` (run tests in parallel) and
248−and `--hide-successes` (don't clutter output with successes)
248+`--hide-successes` (don't clutter output with successes) to the test
249−to the test arguments as well.
249+arguments as well. Collecting all options in a `cabal.project.local`
250+file in the project's root directory can help to keep `cabal`
251+commands short. E.g.:
252+
253+ flags: +embed_data_files
254+ tests: True
255+ test-show-details: direct
256+ test-options: -j4 --hide-successes
250
257
251If you add a new feature to pandoc, please add tests as well, following
258If you add a new feature to pandoc, please add tests as well, following
252the pattern of the existing tests. The test suite code is in
259the pattern of the existing tests. The test suite code is in
@@ -311,9 +318,6 @@ With stack:
311
318
312 stack bench
319 stack bench
313
320
314−You can also build `weigh-pandoc` (`stack build pandoc:weigh-pandoc`)
315−to get some statistics on memory usage. (Eventually this should
316−be incorporated into the benchmark suite.)
317
321
318Using the REPL
322Using the REPL
319--------------
323--------------
@@ -402,11 +406,11 @@ The library is structured as follows:
402 tree automatically.
406 tree automatically.
403 - `Text.Pandoc.Readers.*` are the readers, and `Text.Pandoc.Writers.*`
407 - `Text.Pandoc.Readers.*` are the readers, and `Text.Pandoc.Writers.*`
404 are the writers.
408 are the writers.
405− - `Text.Pandoc.Biblio` is a utility module for formatting citations
409+ - `Text.Pandoc.Citeproc.*` contain the code for citation handling,
406− using citeproc-hs.
410+ including an interface to the [citeproc] library.
407 - `Text.Pandoc.Data` is used to embed data files when the `embed_data_files`
411 - `Text.Pandoc.Data` is used to embed data files when the `embed_data_files`
408− cabal flag is used. It is generated from `src/Text/Pandoc/Data.hsb` using
412+ cabal flag is used.
409− the preprocessor [hsb2hs].
413+ - `Text.Pandoc.Emoji` is a thin wrapper around [emojis].
410 - `Text.Pandoc.Highlighting` contains the interface to the
414 - `Text.Pandoc.Highlighting` contains the interface to the
411 skylighting library, which is used for code syntax highlighting.
415 skylighting library, which is used for code syntax highlighting.
412 - `Text.Pandoc.ImageSize` is a utility module containing functions for
416 - `Text.Pandoc.ImageSize` is a utility module containing functions for
@@ -417,7 +421,6 @@ The library is structured as follows:
417 - `Text.Pandoc.PDF` contains functions for producing a PDF from a
421 - `Text.Pandoc.PDF` contains functions for producing a PDF from a
418 LaTeX source.
422 LaTeX source.
419 - `Text.Pandoc.Parsing` contains parsing functions used in multiple readers.
423 - `Text.Pandoc.Parsing` contains parsing functions used in multiple readers.
420− - `Text.Pandoc.Pretty` is a pretty-printing library specialized to
421 the needs of pandoc.
424 the needs of pandoc.
422 - `Text.Pandoc.SelfContained` contains functions for making an HTML
425 - `Text.Pandoc.SelfContained` contains functions for making an HTML
423 file "self-contained," by importing remotely linked images, CSS,
426 file "self-contained," by importing remotely linked images, CSS,
@@ -452,6 +455,8 @@ you may want to consider submitting a pull request to the
452[EditorConfig]: https://editorconfig.org/
455[EditorConfig]: https://editorconfig.org/
453[Haskell platform]: https://www.haskell.org/platform/
456[Haskell platform]: https://www.haskell.org/platform/
454[hlint]: https://hackage.haskell.org/package/hlint
457[hlint]: https://hackage.haskell.org/package/hlint
458+[citeproc]: https://hackage.haskell.org/package/citeproc
459+[emojis]: https://hackage.haskell.org/package/emojis
455[hsb2hs]: https://hackage.haskell.org/package/hsb2hs
460[hsb2hs]: https://hackage.haskell.org/package/hsb2hs
456[pre-commit hook]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
461[pre-commit hook]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
457[GitHub labels]: https://github.com/jgm/pandoc/labels
462[GitHub labels]: https://github.com/jgm/pandoc/labels
INSTALL.md+4
@@ -34,6 +34,10 @@ without SVG support), [Python] (to use Pandoc filters), and
34
34
35 choco install rsvg-convert python miktex
35 choco install rsvg-convert python miktex
36
36
37+Using multiple installation methods can result in two separate
38+installations of pandoc; it is recommended to properly uninstall
39+pandoc before switching to an alternative installation method.
40+
37By default, Pandoc creates PDFs using LaTeX.
41By default, Pandoc creates PDFs using LaTeX.
38We recommend installing it via [MiKTeX].
42We recommend installing it via [MiKTeX].
39
43
MANUAL.txt+218−71
@@ -1,7 +1,7 @@
1---
1---
2title: Pandoc User's Guide
2title: Pandoc User's Guide
3author: John MacFarlane
3author: John MacFarlane
4−date: January 21, 2021
4+date: June 20, 2021
5---
5---
6
6
7# Synopsis
7# Synopsis
@@ -361,15 +361,15 @@ header when requesting a document from a URL:
361 will be used. On \*nix and macOS systems this will be the `pandoc`
361 will be used. On \*nix and macOS systems this will be the `pandoc`
362 subdirectory of the XDG data directory (by default,
362 subdirectory of the XDG data directory (by default,
363 `$HOME/.local/share`, overridable by setting the `XDG_DATA_HOME`
363 `$HOME/.local/share`, overridable by setting the `XDG_DATA_HOME`
364− environment variable). If that directory does not exist,
364+ environment variable). If that directory does not exist and
365− `$HOME/.pandoc` will be used (for backwards compatibility).
365+ `$HOME/.pandoc` exists, it will be used (for backwards compatibility).
366− In Windows the default user data directory is
366+ On Windows the default user data directory is
367 `C:\Users\USERNAME\AppData\Roaming\pandoc`.
367 `C:\Users\USERNAME\AppData\Roaming\pandoc`.
368 You can find the default user data directory on your system by
368 You can find the default user data directory on your system by
369 looking at the output of `pandoc --version`.
369 looking at the output of `pandoc --version`.
370+ Data files placed in this directory (for example, `reference.odt`,
370− A `reference.odt`, `reference.docx`, `epub.css`, `templates`,
371+ `reference.docx`, `epub.css`, `templates`) will override
371− `slidy`, `slideous`, or `s5` directory
372− placed in this directory will override pandoc's normal defaults.
372+ pandoc's normal defaults.
373
373
374`-d` *FILE*, `--defaults=`*FILE*
374`-d` *FILE*, `--defaults=`*FILE*
375
375
@@ -675,12 +675,12 @@ header when requesting a document from a URL:
675: Extract images and other media contained in or linked from
675: Extract images and other media contained in or linked from
676 the source document to the path *DIR*, creating it if
676 the source document to the path *DIR*, creating it if
677 necessary, and adjust the images references in the document
677 necessary, and adjust the images references in the document
678− so they point to the extracted files. If the source format is
678+ so they point to the extracted files. Media are downloaded,
679− a binary container (docx, epub, or odt), the media is
679+ read from the file system, or extracted from a binary
680− extracted from the container and the original
680+ container (e.g. docx), as needed. The original file paths
681− filenames are used. Otherwise the media is read from the
681+ are used if they are relative paths not containing `..`.
682− file system or downloaded, and new filenames are constructed
682+ Otherwise filenames are constructed from the SHA1 hash of
683− based on SHA1 hashes of the contents.
683+ the contents.
684
684
685`--abbreviations=`*FILE*
685`--abbreviations=`*FILE*
686
686
@@ -691,9 +691,9 @@ header when requesting a document from a URL:
691 system default, use
691 system default, use
692 `pandoc --print-default-data-file=abbreviations`. The only
692 `pandoc --print-default-data-file=abbreviations`. The only
693 use pandoc makes of this list is in the Markdown reader.
693 use pandoc makes of this list is in the Markdown reader.
694− Strings ending in a period that are found in this list will
694+ Strings found in this list will be followed by a nonbreaking
695− be followed by a nonbreaking space, so that the period will
696− not produce sentence-ending space in formats like LaTeX.
695+ space, and the period will not produce sentence-ending space
696+ in formats like LaTeX. The strings may not contain spaces.
697
697
698[`pandocfilters`]: https://github.com/jgm/pandocfilters
698[`pandocfilters`]: https://github.com/jgm/pandocfilters
699[PHP]: https://github.com/vinai/pandocfilters-php
699[PHP]: https://github.com/vinai/pandocfilters-php
@@ -891,6 +891,11 @@ header when requesting a document from a URL:
891 will not be searched. For example:
891 will not be searched. For example:
892 `--resource-path=.:test` will search the working directory
892 `--resource-path=.:test` will search the working directory
893 and the `test` subdirectory, in that order.
893 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`.
894
899
895`--request-header=`*NAME*`:`*VAL*
900`--request-header=`*NAME*`:`*VAL*
896
901
@@ -1410,13 +1415,12 @@ results only for basic math, usually you will want to use
1410`--gladtex`
1415`--gladtex`
1411
1416
1412: Enclose TeX math in `<eq>` tags in HTML output. The resulting HTML
1417: Enclose TeX math in `<eq>` tags in HTML output. The resulting HTML
1413− can then be processed by [GladTeX] to produce images of the typeset
1418+ can then be processed by [GladTeX] to produce SVG images of the typeset
1414− formulas and an HTML file with links to these images.
1419+ formulas and an HTML file with these images embedded.
1415− So, the procedure is:
1416
1420
1417 pandoc -s --gladtex input.md -o myfile.htex
1421 pandoc -s --gladtex input.md -o myfile.htex
1418− gladtex -d myfile-images myfile.htex
1422+ gladtex -d image_dir myfile.htex
1419− # produces myfile.html and images in myfile-images
1423+ # produces myfile.html and images in image_dir
1420
1424
1421[MathML]: https://www.w3.org/Math/
1425[MathML]: https://www.w3.org/Math/
1422[MathJax]: https://www.mathjax.org
1426[MathJax]: https://www.mathjax.org
@@ -1672,6 +1676,34 @@ one line:
1672verbosity: INFO
1676verbosity: INFO
1673```
1677```
1674
1678
1679+In fields that expect a file path (or list of file paths), the
1680+following syntax may be used to interpolate environment variables:
1681+
1682+``` yaml
1683+csl: ${HOME}/mycsldir/special.csl
1684+```
1685+
1686+`${USERDATA}` may also be used; this will always resolve to the
1687+user data directory that is current when the defaults file is
1688+parsed, regardless of the setting of the environment
1689+variable `USERDATA`.
1690+
1691+`${.}` will resolve to the directory containing the default
1692+file itself. This allows you to refer to resources contained
1693+in that directory:
1694+
1695+``` yaml
1696+epub-cover-image: ${.}/cover.jpg
1697+epub-metadata: ${.}/meta.xml
1698+resource-path:
1699+- . # the working directory from which pandoc is run
1700+- ${.}/images # the images subdirectory of the directory
1701+ # containing this defaults file
1702+```
1703+
1704+This environment variable interpolation syntax *only* works in
1705+fields that expect file paths.
1706+
1675Default files can be placed in the `defaults` subdirectory of
1707Default files can be placed in the `defaults` subdirectory of
1676the user data directory and used from any directory. For
1708the user data directory and used from any directory. For
1677example, one could create a file specifying defaults for writing
1709example, one could create a file specifying defaults for writing
@@ -2282,8 +2314,8 @@ math equations flush left using [YAML metadata](#layout) or with
2282
2314
2283These affect HTML output when [producing slide shows with pandoc].
2315These affect HTML output when [producing slide shows with pandoc].
2284
2316
2285−All [reveal.js configuration options] are available as variables.
2317+`institute`
2286−To turn off boolean flags that default to true in reveal.js, use `0`.
2318+: author affiliations: can be a list when there are multiple authors
2287
2319
2288`revealjs-url`
2320`revealjs-url`
2289: base URL for reveal.js documents (defaults to
2321: base URL for reveal.js documents (defaults to
@@ -2303,6 +2335,9 @@ To turn off boolean flags that default to true in reveal.js, use `0`.
2303: additional attributes for the title slide of reveal.js slide shows.
2335: additional attributes for the title slide of reveal.js slide shows.
2304 See [background in reveal.js and beamer] for an example.
2336 See [background in reveal.js and beamer] for an example.
2305
2337
2338+All [reveal.js configuration options] are available as variables.
2339+To turn off boolean flags that default to true in reveal.js, use `0`.
2340+
2306[reveal.js configuration options]: https://revealjs.com/config/
2341[reveal.js configuration options]: https://revealjs.com/config/
2307
2342
2308### Variables for Beamer slides
2343### Variables for Beamer slides
@@ -3719,7 +3754,7 @@ definition:
3719Note that space between items in a definition list is required.
3754Note that space between items in a definition list is required.
3720(A variant that loosens this requirement, but disallows "lazy"
3755(A variant that loosens this requirement, but disallows "lazy"
3721hard wrapping, can be activated with `compact_definition_lists`: see
3756hard wrapping, can be activated with `compact_definition_lists`: see
3722−[Non-pandoc extensions], below.)
3757+[Non-default extensions], below.)
3723
3758
3724[^3]: I have been influenced by the suggestions of [David
3759[^3]: I have been influenced by the suggestions of [David
3725 Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/).
3760 Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/).
@@ -4144,10 +4179,12 @@ option is used. All of the metadata will appear in a single block
4144at the beginning of the document.
4179at the beginning of the document.
4145
4180
4146Note that [YAML] escaping rules must be followed. Thus, for example,
4181Note that [YAML] escaping rules must be followed. Thus, for example,
4147−if a title contains a colon, it must be quoted. The pipe character
4182+if a title contains a colon, it must be quoted, and if it contains a
4183+backslash escape, then it must be ensured that it is not treated as a
4184+[YAML escape sequence]. The pipe character (`|`) can be used to begin
4148−(`|`) can be used to begin an indented block that will be interpreted
4185+an indented block that will be interpreted literally, without need for
4149−literally, without need for escaping. This form is necessary
4186+escaping. This form is necessary when the field contains blank lines
4150−when the field contains blank lines or block-level formatting:
4187+or block-level formatting:
4151
4188
4152 ---
4189 ---
4153 title: 'This is the title: it contains a colon'
4190 title: 'This is the title: it contains a colon'
@@ -4161,6 +4198,12 @@ when the field contains blank lines or block-level formatting:
4161 It consists of two paragraphs.
4198 It consists of two paragraphs.
4162 ...
4199 ...
4163
4200
4201+The literal block after the `|` must be indented relative to the
4202+line containing the `|`. If it is not, the YAML will be invalid
4203+and pandoc will not interpret it as metadata. For an overview
4204+of the complex rules governing YAML, see the [Wikipedia entry on
4205+YAML syntax].
4206+
4164Template variables will be set automatically from the metadata. Thus, for
4207Template variables will be set automatically from the metadata. Thus, for
4165example, in writing HTML, the variable `abstract` will be set to the HTML
4208example, in writing HTML, the variable `abstract` will be set to the HTML
4166equivalent of the Markdown in the `abstract` field:
4209equivalent of the Markdown in the `abstract` field:
@@ -4207,6 +4250,24 @@ will be interpreted as markdown. For example:
4207 \renewcommand{\section}[1]{\clearpage\oldsection{#1}}
4250 \renewcommand{\section}[1]{\clearpage\oldsection{#1}}
4208 ```
4251 ```
4209
4252
4253+Note: the `yaml_metadata_block` extension works with
4254+`commonmark` as well as `markdown` (and it is enabled by default
4255+in `gfm` and `commonmark_x`). However, in these formats the
4256+following restrictions apply:
4257+
4258+- The YAML metadata block must occur at the beginning of the
4259+ document (and there can be only one). If multiple files are
4260+ given as arguments to pandoc, only the first can be a YAML
4261+ metadata block.
4262+
4263+- The leaf nodes of the YAML structure are parsed in isolation from
4264+ each other and from the rest of the document. So, for
4265+ example, you can't use a reference link in these contexts
4266+ if the link definition is somewhere else in the document.
4267+
4268+[YAML escape sequence]: https://yaml.org/spec/1.2/spec.html#id2776092
4269+[Wikipedia entry on YAML syntax]: https://en.m.wikipedia.org/wiki/YAML#Syntax
4270+
4210## Backslash escapes
4271## Backslash escapes
4211
4272
4212#### Extension: `all_symbols_escapable` ####
4273#### Extension: `all_symbols_escapable` ####
@@ -4899,37 +4960,68 @@ Inline and regular footnotes may be mixed freely.
4899
4960
4900#### Extension: `citations` ####
4961#### Extension: `citations` ####
4901
4962
4902−Markdown citations go inside square brackets and are separated
4963+To cite a bibliographic item with an identifier foo, use the
4903−by semicolons. Each citation must have a key, composed of '@' +
4964+syntax `@foo`. Normal citations should be included in square
4904−the citation identifier from the database, and may optionally
4965+brackets, with semicolons separating distinct items:
4905−have a prefix, a locator, and a suffix. The citation key must
4966+
4967+ Blah blah [@doe99; @smith2000; @smith2004].
4968+
4969+How this is rendered depends on the citation style. In an
4970+author-date style, it might render as
4971+
4972+ Blah blah (Doe 1999, Smith 2000, 2004).
4973+
4974+In a footnote style, it might render as
4975+
4976+ Blah blah.[^1]
4977+
4978+ [^1]: John Doe, "Frogs," *Journal of Amphibians* 44 (1999);
4979+ Susan Smith, "Flies," *Journal of Insects* (2000);
4980+ Susan Smith, "Bees," *Journal of Insects* (2004).
4981+
4982+See the [CSL user documentation] for more information about CSL
4983+styles and how they affect rendering.
4984+
4906−begin with a letter, digit, or `_`, and may contain
4985+Unless a citation key start with a letter, digit, or `_`,
4907−alphanumerics, `_`, and internal punctuation characters
4986+and contains only alphanumerics and internal punctuation
4908−(`:.#$%&-+?<>~/`). Here are some examples:
4987+characters (`:.#$%&-+?<>~/`), it must be surrounded
4909−
4988+by curly braces, which are not considered part of the key.
4910− Blah blah [see @doe99, pp. 33-35; also @smith04, chap. 1].
4989+In `@Foo_bar.baz.`, the key is `Foo_bar.baz`. The final
4911−
4990+period is not *internal* punctuation, so it is not included in
4912− Blah blah [@doe99, pp. 33-35, 38-39 and *passim*].
4991+the key. In `@{Foo_bar.baz.}`, the key is `Foo_bar.baz.`, including
4913−
4992+the final period. The curly braces are recommended if you use URLs as
4914− Blah blah [@smith04; @doe99].
4993+keys: `[@{https://example.com/bib?name=foobar&date=2000}, p. 33]`.
4915−
4994+
4995+Citation items may optionally include a prefix, a locator, and
4996+a suffix. In
4997+
4998+ Blah blah [see @doe99, pp. 33-35 and *passim*; @smith04, chap. 1].
4999+
5000+The first item (`doe99`) has prefix `see `, locator `pp. 33-35`,
5001+and suffix `and *passim*`. The second item (`smith04`) has
5002+locator `chap. 1` and no prefix or suffix.
5003+
5004+Pandoc uses some heuristics to separate the locator from the
5005+rest of the subject. It is sensitive to the locator terms
4916−`pandoc` detects locator terms in the [CSL locale files].
5006+defined in the [CSL locale files]. Either abbreviated or
4917−Either abbreviated or unabbreviated forms are accepted. In the `en-US`
5007+unabbreviated forms are accepted. In the `en-US` locale, locator
4918−locale, locator terms can be written in either singular or plural forms,
5008+terms can be written in either singular or plural forms, as
4919−as `book`, `bk.`/`bks.`; `chapter`, `chap.`/`chaps.`; `column`,
5009+`book`, `bk.`/`bks.`; `chapter`, `chap.`/`chaps.`; `column`,
4920−`col.`/`cols.`; `figure`, `fig.`/`figs.`; `folio`, `fol.`/`fols.`;
5010+`col.`/`cols.`; `figure`, `fig.`/`figs.`; `folio`,
4921−`number`, `no.`/`nos.`; `line`, `l.`/`ll.`; `note`, `n.`/`nn.`; `opus`,
5011+`fol.`/`fols.`; `number`, `no.`/`nos.`; `line`, `l.`/`ll.`;
5012+`note`, `n.`/`nn.`; `opus`, `op.`/`opp.`; `page`, `p.`/`pp.`;
4922−`op.`/`opp.`; `page`, `p.`/`pp.`; `paragraph`, `para.`/`paras.`; `part`,
5013+`paragraph`, `para.`/`paras.`; `part`, `pt.`/`pts.`; `section`,
4923−`pt.`/`pts.`; `section`, `sec.`/`secs.`; `sub verbo`, `s.v.`/`s.vv.`;
5014+`sec.`/`secs.`; `sub verbo`, `s.v.`/`s.vv.`; `verse`,
4924−`verse`, `v.`/`vv.`; `volume`, `vol.`/`vols.`; `¶`/`¶¶`; `§`/`§§`. If no
5015+`v.`/`vv.`; `volume`, `vol.`/`vols.`; `¶`/`¶¶`; `§`/`§§`. If no
4925locator term is used, "page" is assumed.
5016locator term is used, "page" is assumed.
4926
5017
4927−`pandoc` will use heuristics to distinguish the locator
5018+In complex cases, you can force something to be treated as
4928−from the suffix. In complex cases, the locator can be enclosed
4929−in curly braces:
5019+a locator by enclosing it in curly braces or prevent parsing
5020+the suffix as locator by prepending curly braces:
4930
5021
4931 [@smith{ii, A, D-Z}, with a suffix]
5022 [@smith{ii, A, D-Z}, with a suffix]
4932 [@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
5023 [@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
5024+ [@smith{}, 99 years later]
4933
5025
4934A minus sign (`-`) before the `@` will suppress mention of
5026A minus sign (`-`) before the `@` will suppress mention of
4935the author in the citation. This can be useful when the
5027the author in the citation. This can be useful when the
@@ -4937,13 +5029,26 @@ author is already mentioned in the text:
4937
5029
4938 Smith says blah [-@smith04].
5030 Smith says blah [-@smith04].
4939
5031
4940−You can also write an in-text citation, as follows:
5032+You can also write an author-in-text citation, by omitting the
5033+square brackets:
4941
5034
4942 @smith04 says blah.
5035 @smith04 says blah.
4943
5036
4944 @smith04 [p. 33] says blah.
5037 @smith04 [p. 33] says blah.
4945
5038
5039+This will cause the author's name to be rendered, followed by
5040+the bibliographical details. Use this form when you want to
5041+make the citation the subject of a sentence.
5042+
5043+When you are using a note style, it is usually better to let
5044+citeproc create the footnotes from citations rather than writing
5045+an explicit note. If you do write an explicit note that
5046+contains a citation, note that normal citations will be put in
5047+parentheses, while author-in-text citations will not. For
5048+this reason, it is sometimes preferable to use the
5049+author-in-text style inside notes when using a note style.
4946
5050
5051+[CSL user documentation]: https://citationstyles.org/authors/
4947[CSL]: https://docs.citationstyles.org/en/stable/specification.html
5052[CSL]: https://docs.citationstyles.org/en/stable/specification.html
4948[CSL markup specs]: https://docs.citationstyles.org/en/1.0/release-notes.html#rich-text-markup-within-fields
5053[CSL markup specs]: https://docs.citationstyles.org/en/1.0/release-notes.html#rich-text-markup-within-fields
4949[Chicago Manual of Style]: https://chicagomanualofstyle.org
5054[Chicago Manual of Style]: https://chicagomanualofstyle.org
@@ -4952,13 +5057,44 @@ You can also write an in-text citation, as follows:
4952[finding and editing styles]: https://citationstyles.org/authors/
5057[finding and editing styles]: https://citationstyles.org/authors/
4953[CSL locale files]: https://github.com/citation-style-language/locales
5058[CSL locale files]: https://github.com/citation-style-language/locales
4954
5059
4955−## Non-pandoc extensions
5060+## Non-default extensions
4956
5061
4957The following Markdown syntax extensions are not enabled by default
5062The following Markdown syntax extensions are not enabled by default
4958in pandoc, but may be enabled by adding `+EXTENSION` to the format
5063in pandoc, but may be enabled by adding `+EXTENSION` to the format
4959name, where `EXTENSION` is the name of the extension. Thus, for
5064name, where `EXTENSION` is the name of the extension. Thus, for
4960example, `markdown+hard_line_breaks` is Markdown with hard line breaks.
5065example, `markdown+hard_line_breaks` is Markdown with hard line breaks.
4961
5066
5067+#### Extension: `rebase_relative_paths` ####
5068+
5069+Rewrite relative paths for Markdown links and images, depending
5070+on the path of the file containing the link or image link. For
5071+each link or image, pandoc will compute the directory of the
5072+containing file, relative to the working directory, and prepend
5073+the resulting path to the link or image path.
5074+
5075+The use of this extension is best understood by example.
5076+Suppose you have a a subdirectory for each chapter of a book,
5077+`chap1`, `chap2`, `chap3`. Each contains a file `text.md` and a
5078+number of images used in the chapter. You would like to have
5079+`![image](spider.jpg)` in `chap1/text.md` refer to
5080+`chap1/spider.jpg` and `![image](spider.jpg)` in `chap2/text.md`
5081+refer to `chap2/spider.jpg`. To do this, use
5082+
5083+ pandoc chap*/*.md -f markdown+rebase_relative_paths
5084+
5085+Without this extension, you would have to use
5086+`![image](chap1/spider.jpg)` in `chap1/text.md` and
5087+`![image](chap2/spider.jpg)` in `chap2/text.md`. Links with
5088+relative paths will be rewritten in the same way as images.
5089+
5090+Absolute paths and URLs are not changed. Neither are empty
5091+paths or paths consisting entirely of a fragment, e.g., `#foo`.
5092+
5093+Note that relative paths in reference links and images will
5094+be rewritten relative to the file containing the link
5095+reference definition, not the file containing the reference link
5096+or image itself, if these differ.
5097+
4962#### Extension: `attributes` ####
5098#### Extension: `attributes` ####
4963
5099
4964Allows attributes to be attached to any inline or block-level
5100Allows attributes to be attached to any inline or block-level
@@ -5492,10 +5628,23 @@ A few other metadata fields affect bibliography formatting:
5492
5628
5493`lang`
5629`lang`
5494: The `lang` field will affect how the style is localized,
5630: The `lang` field will affect how the style is localized,
5495− for example in the translation of labels and the use
5631+ for example in the translation of labels, the use
5632+ of quotation marks, and the way items are sorted.
5496− of quotation marks. (For backwards compatibility,
5633+ (For backwards compatibility, `locale` may be used instead
5497− `locale` may be used instead of `lang`, but this use
5498− is deprecated.)
5634+ of `lang`, but this use is deprecated.)
5635+
5636+ A BCP 47 language tag is expected: for example, `en`,
5637+ `de`, `en-US`, `fr-CA`, `ug-Cyrl`. The unicode extension
5638+ syntax (after `-u-`) may be used to specify options for
5639+ collation (sorting) more precisely. Here are some examples:
5640+
5641+ - `zh-u-co-pinyin` -- Chinese with the Pinyin collation.
5642+ - `es-u-co-trad` -- Spanish with the traditional collation
5643+ (with `Ch` sorting after `C`).
5644+ - `fr-u-kb` -- French with "backwards" accent sorting
5645+ (with `coté` sorting after `côte`).
5646+ - `en-US-u-kf-upper` -- English with uppercase letters sorting
5647+ before lower (default is lower before upper).
5499
5648
5500`notes-after-punctuation`
5649`notes-after-punctuation`
5501: If true (the default), pandoc will put footnote citations
5650: If true (the default), pandoc will put footnote citations
@@ -5507,12 +5656,6 @@ A few other metadata fields affect bibliography formatting:
5507 punctuation.
5656 punctuation.
5508
5657
5509
5658
5510−
5511−
5512−
5513−
5514−
5515−
5516# Slide shows
5659# Slide shows
5517
5660
5518You can use pandoc to produce an HTML + JavaScript slide presentation
5661You can use pandoc to produce an HTML + JavaScript slide presentation
@@ -5605,7 +5748,11 @@ rules:
5605 * A heading at the slide level always starts a new slide.
5748 * A heading at the slide level always starts a new slide.
5606
5749
5607 * Headings *below* the slide level in the hierarchy create
5750 * Headings *below* the slide level in the hierarchy create
5608− headings *within* a slide.
5751+ headings *within* a slide. (In beamer, a "block" will be
5752+ created. If the heading has the class `example`, an
5753+ `exampleblock` environment will be used; if it has the class
5754+ `alert`, an `alertblock` will be used; otherwise a regular
5755+ `block` will be used.)
5609
5756
5610 * Headings *above* the slide level in the hierarchy create
5757 * Headings *above* the slide level in the hierarchy create
5611 "title slides," which just contain the section title
5758 "title slides," which just contain the section title
@@ -6183,7 +6330,7 @@ Then edit `my.theme` and use it like this:
6183If you are not satisfied with the built-in highlighting, or you
6330If you are not satisfied with the built-in highlighting, or you
6184want highlight a language that isn't supported, you can use the
6331want highlight a language that isn't supported, you can use the
6185`--syntax-definition` option to load a [KDE-style XML syntax definition
6332`--syntax-definition` option to load a [KDE-style XML syntax definition
6186−file](https://docs.kde.org/stable5/en/applications/katepart/highlight.html).
6333+file](https://docs.kde.org/stable5/en/kate/katepart/highlight.html).
6187Before writing your own, have a look at KDE's [repository of syntax
6334Before writing your own, have a look at KDE's [repository of syntax
6188definitions](https://github.com/KDE/syntax-highlighting/tree/master/data/syntax).
6335definitions](https://github.com/KDE/syntax-highlighting/tree/master/data/syntax).
6189
6336
@@ -6357,9 +6504,9 @@ application, here are some things to keep in mind:
63574. The HTML generated by pandoc is not guaranteed to be safe.
65044. The HTML generated by pandoc is not guaranteed to be safe.
6358 If `raw_html` is enabled for the Markdown input, users can
6505 If `raw_html` is enabled for the Markdown input, users can
6359 inject arbitrary HTML. Even if `raw_html` is disabled,
6506 inject arbitrary HTML. Even if `raw_html` is disabled,
6360− users can include dangerous content in attributes for
6507+ users can include dangerous content in URLs and attributes.
6361− headings, spans, and code blocks. To be safe, you should
6362− run all the generated HTML through an HTML sanitizer.
6508+ To be safe, you should run all the generated HTML through
6509+ an HTML sanitizer.
6363
6510
6364# Authors
6511# Authors
6365
6512
Makefile+28−15
@@ -2,25 +2,31 @@ version?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}')
2pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1)
2pandoc=$(shell find dist -name pandoc -type f -exec ls -t {} \; | head -1)
3SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$")
3SOURCEFILES?=$(shell git ls-tree -r master --name-only | grep "\.hs$$")
4BRANCH?=master
4BRANCH?=master
5+ARCH=$(shell uname -m)
6+DOCKERIMAGE=registry.gitlab.b-data.ch/ghc/ghc4pandoc:8.10.4
5COMMIT=$(shell git rev-parse --short HEAD)
7COMMIT=$(shell git rev-parse --short HEAD)
6TIMESTAMP=$(shell date "+%Y%m%d_%H%M")
8TIMESTAMP=$(shell date "+%Y%m%d_%H%M")
7−LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv))
9+LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv 2>/dev/null))
8−ifeq ($(LATESTBENCH),)
10+BASELINE?=$(LATESTBENCH)
9−BASELINE=
11+ifeq ($(BASELINE),)
12+BASELINECMD=
10else
13else
11−BASELINE=--baseline $(LATESTBENCH)
14+BASELINECMD=--baseline $(BASELINE)
12endif
15endif
13−GHCOPTS=-fdiagnostics-color=always
16+GHCOPTS=-fdiagnostics-color=always -j4 +RTS -A8m -RTS
14WEBSITE=../../web/pandoc.org
17WEBSITE=../../web/pandoc.org
15REVISION?=1
18REVISION?=1
19+# For gauge:
20+# BENCHARGS?=--small --ci=0.90 --match=pattern $(PATTERN)
21+# For tasty-bench:
16−BENCHARGS?=--timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",)
22+BENCHARGS?=--csv bench_$(TIMESTAMP).csv $(BASELINECMD) --timeout=6 +RTS -T -RTS $(if $(PATTERN),--pattern "$(PATTERN)",)
17
23
18quick:
24quick:
19− stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='-j +RTS -A64m -RTS' --test-arguments='-j4 --hide-successes $(TESTARGS)'
25+ stack install --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --ghc-options='$(GHCOPTS)' --test-arguments='-j4 --hide-successes $(TESTARGS)'
20
26
21quick-cabal:
27quick-cabal:
22 cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests
28 cabal new-configure . --ghc-options '$(GHCOPTS)' --disable-optimization --enable-tests
23− cabal new-build . --disable-optimization
29+ cabal new-build -j4 . --disable-optimization
24 cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS)
30 cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS)
25
31
26full-cabal:
32full-cabal:
@@ -29,7 +35,7 @@ full-cabal:
29 cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS)
35 cabal new-run test-pandoc --disable-optimization -- --hide-successes $(TESTARGS)
30
36
31full:
37full:
32− stack install --flag 'pandoc:embed_data_files' --flag 'pandoc:trypandoc' --bench --no-run-benchmarks --test --test-arguments='-j4 --hide-successes' --ghc-options '-Wall -Werror -fno-warn-unused-do-bind -O0 -j4 $(GHCOPTS)'
38+ stack install --flag 'pandoc:embed_data_files' --flag 'pandoc:trypandoc' --bench --no-run-benchmarks --test --test-arguments='-j4 --hide-successes' --ghc-options '-Wall -Werror -fno-warn-unused-do-bind -O0 $(GHCOPTS)'
33
39
34ghci:
40ghci:
35 stack ghci --flag 'pandoc:embed_data_files'
41 stack ghci --flag 'pandoc:embed_data_files'
@@ -45,11 +51,14 @@ test:
45ghcid:
51ghcid:
46 ghcid -c "stack repl --flag 'pandoc:embed_data_files'"
52 ghcid -c "stack repl --flag 'pandoc:embed_data_files'"
47
53
48−bench:
54+ghcid-test:
49− stack bench --benchmark-arguments='$(BENCHARGS) $(BASELINE) --csv bench_$(TIMESTAMP).csv' --ghc-options '$(GHCOPTS)'
55+ ghcid -c "stack repl --ghc-options=-XNoImplicitPrelude --flag 'pandoc:embed_data_files' --ghci-options=-fobject-code pandoc:lib pandoc:test-pandoc"
50
56
51−weigh:
57+bench:
58+ stack bench \
52− stack build --ghc-options '$(GHCOPTS)' pandoc:weigh-pandoc && stack exec weigh-pandoc
59+ --ghc-options '$(GHCOPTS)' \
60+ --benchmark-arguments='$(BENCHARGS)' 2>&1 | \
61+ tee "bench_latest.txt"
53
62
54reformat:
63reformat:
55 for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done
64 for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done
@@ -81,10 +90,14 @@ checkdocs:
81debpkg: man/pandoc.1
90debpkg: man/pandoc.1
82 docker run -v `pwd`:/mnt \
91 docker run -v `pwd`:/mnt \
83 -v `pwd`/linux/artifacts:/artifacts \
92 -v `pwd`/linux/artifacts:/artifacts \
93+ --user $(id -u):$(id -g) \
84 -e REVISION=$(REVISION) \
94 -e REVISION=$(REVISION) \
85 -w /mnt \
95 -w /mnt \
86− utdemir/ghc-musl:v12-libgmp-ghc8101 bash \
96+ --memory=0 \
97+ --rm \
98+ $(DOCKERIMAGE) \
99+ bash \
87− /mnt/linux/make_artifacts.sh
100+ /mnt/linux/make_artifacts.sh 2>&1 > docker.log
88
101
89man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after
102man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after
90 pandoc $< -f markdown -t man -s \
103 pandoc $< -f markdown -t man -s \
README.md+6−6
@@ -8,10 +8,10 @@ pandoc --lua-filter tools/update-readme.lua README.template -o README.md
8[![github
8[![github
9release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases)
9release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases)
10[![hackage
10[![hackage
11−release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](http://hackage.haskell.org/package/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)](http://brewformulas.org/Pandoc)
12+[![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](https://formulae.brew.sh/formula/pandoc)
13[![stackage LTS
13[![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)
15[![CI
15[![CI
16tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions)
16tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions)
17[![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html)
17[![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
20
20
21## The universal markup converter
21## The universal markup converter
22
22
23−Pandoc is a [Haskell](http://haskell.org) library for converting from
23+Pandoc is a [Haskell](https://haskell.org) library for converting from
24one markup format to another, and a command-line tool that uses this
24one markup format to another, and a command-line tool that uses this
25library. It can convert *from*
25library. It can convert *from*
26
26
@@ -175,7 +175,7 @@ It can convert *to*
175 and JavaScript slide show)
175 and JavaScript slide show)
176- `slidy` ([Slidy](https://www.w3.org/Talks/Tools/Slidy2/) HTML and
176- `slidy` ([Slidy](https://www.w3.org/Talks/Tools/Slidy2/) HTML and
177 JavaScript slide show)
177 JavaScript slide show)
178−- `dzslides` ([DZSlides](http://paulrouget.com/dzslides/) HTML5 +
178+- `dzslides` ([DZSlides](https://paulrouget.com/dzslides/) HTML5 +
179 JavaScript slide show),
179 JavaScript slide show),
180- `revealjs` ([reveal.js](https://revealjs.com/) HTML5 + JavaScript
180- `revealjs` ([reveal.js](https://revealjs.com/) HTML5 + JavaScript
181 slide show)
181 slide show)
@@ -239,6 +239,6 @@ opening a new issue.
239## License
239## License
240
240
241© 2006-2021 John MacFarlane (jgm@berkeley.edu). Released under the
241© 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"),
243version 2 or greater. This software carries no warranty of any kind.
243version 2 or greater. This software carries no warranty of any kind.
244(See COPYRIGHT for full copyright and warranty notices.)
244(See COPYRIGHT for full copyright and warranty notices.)
RELEASE-CHECKLIST+1
@@ -4,6 +4,7 @@
4[ ] Finalize changelog
4[ ] Finalize changelog
5[ ] push release candidate branch rc/VERSION and (if it builds
5[ ] push release candidate branch rc/VERSION and (if it builds
6 successfully) download artifacts
6 successfully) download artifacts
7+[ ] run tools/build-arm.sh to create and download arm64 linux package
7[ ] download macos artifacts from CircleCI
8[ ] download macos artifacts from CircleCI
8[ ] Use 'make' in macos and windows artifacts to sign code
9[ ] Use 'make' in macos and windows artifacts to sign code
9[ ] make update-website
10[ ] make update-website
SECURITY.mdnew file+12
@@ -0,0 +1,12 @@
1+# Security Policy
2+
3+## Supported Versions
4+
5+Only the most recent version of pandoc is supported with security updates.
6+
7+## Reporting a Vulnerability
8+
9+To report a vulnerability, email the maintainer, jgm@berkeley.edu.
10+But first please read the section of the manual entitled
11+[A note on security](https://pandoc.org/MANUAL.html#a-note-on-security),
12+which describes some security guarantees pandoc does NOT make.
benchmark/benchmark-pandoc.hs+13−12
@@ -18,12 +18,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18-}
18-}
19import Text.Pandoc
19import Text.Pandoc
20import Text.Pandoc.MIME
20import Text.Pandoc.MIME
21−import Control.Monad (when)
21+import Control.DeepSeq (force)
22import Control.Monad.Except (throwError)
22import Control.Monad.Except (throwError)
23import qualified Text.Pandoc.UTF8 as UTF8
23import qualified Text.Pandoc.UTF8 as UTF8
24import qualified Data.ByteString as B
24import qualified Data.ByteString as B
25import qualified Data.Text as T
25import qualified Data.Text as T
26import Test.Tasty.Bench
26import Test.Tasty.Bench
27+-- import Gauge
27import qualified Data.ByteString.Lazy as BL
28import qualified Data.ByteString.Lazy as BL
28import Data.Maybe (mapMaybe)
29import Data.Maybe (mapMaybe)
29import Data.List (sortOn)
30import Data.List (sortOn)
@@ -31,10 +32,10 @@ import Data.List (sortOn)
31readerBench :: Pandoc
32readerBench :: Pandoc
32 -> T.Text
33 -> T.Text
33 -> Maybe Benchmark
34 -> Maybe Benchmark
35+readerBench _ name
36+ | name `elem` ["bibtex", "biblatex", "csljson"] = Nothing
34readerBench doc name = either (const Nothing) Just $
37readerBench doc name = either (const Nothing) Just $
35 runPure $ do
38 runPure $ do
36− when (name `elem` ["bibtex", "biblatex", "csljson"]) $
37− throwError $ PandocSomeError $ name <> " not supported for benchmark"
38 (rdr, rexts) <- getReader name
39 (rdr, rexts) <- getReader name
39 (wtr, wexts) <- getWriter name
40 (wtr, wexts) <- getWriter name
40 case (rdr, wtr) of
41 case (rdr, wtr) of
@@ -56,19 +57,19 @@ readerBench doc name = either (const Nothing) Just $
56
57
57getImages :: IO [(FilePath, MimeType, BL.ByteString)]
58getImages :: IO [(FilePath, MimeType, BL.ByteString)]
58getImages = do
59getImages = do
59− ll <- BL.readFile "test/lalune.jpg"
60+ ll <- B.readFile "test/lalune.jpg"
60− mv <- BL.readFile "test/movie.jpg"
61+ mv <- B.readFile "test/movie.jpg"
61− return [("lalune.jpg", "image/jpg", ll)
62+ return [("lalune.jpg", "image/jpg", BL.fromStrict ll)
62− ,("movie.jpg", "image/jpg", mv)]
63+ ,("movie.jpg", "image/jpg", BL.fromStrict mv)]
63
64
64writerBench :: [(FilePath, MimeType, BL.ByteString)]
65writerBench :: [(FilePath, MimeType, BL.ByteString)]
65 -> Pandoc
66 -> Pandoc
66 -> T.Text
67 -> T.Text
67 -> Maybe Benchmark
68 -> Maybe Benchmark
69+writerBench _ _ name
70+ | name `elem` ["bibtex", "biblatex", "csljson"] = Nothing
68writerBench imgs doc name = either (const Nothing) Just $
71writerBench imgs doc name = either (const Nothing) Just $
69 runPure $ do
72 runPure $ do
70− when (name `elem` ["bibtex", "biblatex", "csljson"]) $
71− throwError $ PandocSomeError $ name <> " not supported for benchmark"
72 (wtr, wexts) <- getWriter name
73 (wtr, wexts) <- getWriter name
73 case wtr of
74 case wtr of
74 TextWriter writerFun ->
75 TextWriter writerFun ->
@@ -90,10 +91,10 @@ main :: IO ()
90main = do
91main = do
91 inp <- UTF8.toText <$> B.readFile "test/testsuite.txt"
92 inp <- UTF8.toText <$> B.readFile "test/testsuite.txt"
92 let opts = def
93 let opts = def
93− let doc = either (error . show) id $ runPure $ readMarkdown opts inp
94+ let doc = either (error . show) force $ runPure $ readMarkdown opts inp
94− imgs <- getImages
95 defaultMain
95 defaultMain
96+ [ env getImages $ \imgs ->
96− [ bgroup "writers" $ mapMaybe (writerBench imgs doc . fst)
97+ bgroup "writers" $ mapMaybe (writerBench imgs doc . fst)
97 (sortOn fst
98 (sortOn fst
98 writers :: [(T.Text, Writer PandocPure)])
99 writers :: [(T.Text, Writer PandocPure)])
99 , bgroup "readers" $ mapMaybe (readerBench doc . fst)
100 , bgroup "readers" $ mapMaybe (readerBench doc . fst)
cabal.project+9−8
@@ -1,12 +1,13 @@
1packages: pandoc.cabal
1packages: pandoc.cabal
2−
3tests: True
2tests: True
3+flags: +embed_data_files
4
4
5−package pandoc
5+source-repository-package
6− flags: +embed_data_files -trypandoc
6+ type: git
7− ghc-options: -j +RTS -A256m -RTS
7+ location: https://github.com/jgm/doctemplates
8+ tag: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81
8
9
9−-- source-repository-package
10+source-repository-package
10−-- type: git
11+ type: git
11−-- location: https://github.com/jgm/citeproc
12+ location: https://github.com/jgm/citeproc
12−-- tag: d44e24696ab444090d0e63e321c3a573f68b2e74
13+ tag: 4ce0501cd6f9c86eee087afcf878c256e49b9615
changelog.md+982−105
@@ -1,6 +1,782 @@
1# Revision history for pandoc
1# Revision history for pandoc
2
2
3−## pandoc 2.12 (UNRELEASED -- PROVISIONAL)
3+## pandoc 2.14.0.3 (2021-06-22)
4+
5+ * Text.Pandoc.MediaBag `insertMediaBag`: ensure we get a sane mediaPath
6+ for URLs (#7391). In earlier 2.14.x versions, we'd get
7+ incorrect paths for resources downloaded from URLs when the
8+ media are extracted (including in PDF production).
9+ * Text.Pandoc.Parsing: improve `emailAddress` (#7398).
10+ Previously the parser would accept characters in domains
11+ that are illegal in domains, and this sometimes caused it
12+ to gobble bits of the following text.
13+ * txt2tags reader: modify the email address parser so
14+ it still includes form parameters, even after the change to
15+ `emailAddress` in Text.Pandoc.Parsing.
16+ * Text.Pandoc.Readers.Metadata: Fix regression with comment-only YAML
17+ metadata blocks (#7400).
18+ * reveal.js writer and template: better handling of options. Previously
19+ it was impossible to specify false values for options that default to
20+ true (e.g. `center`); setting the option to false just caused the portion
21+ of the template setting the option to be omitted. Now we prepopulate
22+ all the variables with their default values, including them all
23+ unconditionally and allowing them to be overridden.
24+ * Markdown writer: Fix regression in code blocks with attributes (#7397).
25+ Code blocks with a single class but nonempty attributes
26+ were having attributes drop as a result of #7242.
27+ * LaTeX writer:
28+ + Add strut at end of minipage if it contains line breaks.
29+ Without them, the last line is not as tall as it should be in
30+ some cases.
31+ + Always use a minipage for cells with line breaks, when
32+ width information is available (#7393). Otherwise the way we treat them
33+ can lead to content that overflows a cell.
34+ + Use `\strut` instead of `~` before `\\` in empty line.
35+ * Use lts-18.0 stack resolver.
36+ * Require skylighting 0.10.5.2 (adding support for Swift).
37+ * Require commonmark 0.2.1.
38+ * Rephrase section on unsafe HTML in manual.
39+ * Create SECURITY.md
40+
41+## pandoc 2.14.0.2 (2021-06-13)
42+
43+ * Fix MediaBag regressions (#7345). iIn the 2.14 release `--extract-media`
44+ stopped working as before; there could be mismatches between the
45+ paths in the rendered document and the extracted media.
46+ This patch makes several changes that restore the earlier behavior
47+ (while keeping the same API). The `mediaPath` in 2.14 was always
48+ constructed from the SHA1 hash of the media contents. Now, we
49+ preserve the original path unless it's an absolute path or contains
50+ `..` segments (in that case we use a path based on the SHA1 hash of
51+ the contents).
52+
53+ In Text.Pandoc.MediaBag, `mediaDirectory` and `mediaItems` now use the
54+ `mediaPath`, rather than the mediabag key, for the first component of the
55+ tuple. This makes more sense, I think, and fits with the documentation of
56+ these functions; eventually, though, we should rework the API so that
57+ `mediaItems` returns both the keys and the MediaItems.
58+
59+ In Text.Pandoc.Class.IO, rewriting of source paths in `extractMedia` has
60+ been fixed.
61+
62+ In Text.Pandoc.Class.PandocMonad, `fillMediaBag` has been modified so that
63+ it doesn't modify image paths (that was part of the problem in #7345).
64+
65+ We now do path normalization (e.g. `\` separators on Windows) in
66+ writing the media.
67+
68+ * Text.Pandoc.PDF:
69+
70+ + Text.Pandoc.PDF: Fix regression in 2.14 for generation of PDFs with
71+ SVGs (#7344).
72+ + Only print relevant part of environment on `--verbose`. Since
73+ `--verbose` output might be put in an issue, we want to avoid
74+ spilling out secrets in environment variables.
75+
76+ * Markdown reader: fix pipe table regression in 2.11.4 (#7343).
77+ Previously pipe tables with empty headers (that is, a header
78+ line with all empty cells) would be rendered as headerless
79+ tables. This broke in 2.11.4. The fix here is to produce an
80+ AST with an empty table head when a pipe table has all empty
81+ header cells.
82+
83+ * LaTeX reader: don't allow optional `*` on symbol control sequences
84+ (#7340). Generally we allow optional starred variants of LaTeX commands
85+ (since many allow them, and if we don't accept these explicitly,
86+ ignoring the star usually gives acceptable results). But we
87+ don't want to do this for `\(*\)` and similar cases.
88+
89+ * Docx reader: handle absolute URIs in Relationship Target (#7374).
90+
91+ * Docx writer: fix handling of empty table headers (Albert Krewinkel,
92+ #7369). A table header which does not contain any cells is now treated as
93+ an empty header.
94+
95+ * LaTeX writer: Fix regression in table header position (#7347).
96+ In recent versions the table headers were no longer bottom-aligned
97+ (if more than one line). This patch fixes that by using minipages
98+ for table headers in non-simple tables.
99+
100+ * CommonMark writer:
101+
102+ + Do not use simple class for fenced-divs (Jan Tojnar, amends #7242.)
103+ + Do not throw away attributes when `Ext_attributes` is enabled.
104+ `Ext_attributes` covers at least the following:
105+ `Ext_fenced_code_attributes`, `Ext_header_attributes`,
106+ `Ext_inline_code_attributes`, `Ext_link_attributes`.
107+
108+ * Markdown writer:
109+
110+ + Allow `pipe_tables` to be disabled for commonmark formats
111+ (`commonmark_x`, `gfm`) (#7375).
112+ + Re-use functions from Text.Pandoc.Markdown.Inline (Jan Tojnar).
113+
114+ * DocBook writer: Remove non-existent admonitions (Jan Tojnar).
115+ `attention`, `error` and `hint` are reStructuredText specific.
116+
117+ * HTML writer: Don't omit width attribute on div (#7342).
118+
119+ * Text.Pandoc.MIME, `extensionFromMimeType`: add a few special cases.
120+ When we do a reverse lookup in the MIME table, we just get the
121+ last match, so when the same mime type is associated with several
122+ different extensions, we sometimes got weird results, e.g. `.vs`
123+ for `text/plain`. These special cases help us get the most standard
124+ extensions for mime types like `text/plain`.
125+
126+ * Lua utils: fix handling of table headers in `from_simple_table` (Albert
127+ Krewinkel, #7369). Passing an empty list of header cells now results
128+ in an empty table header.
129+
130+ * Text.Pandoc.Citeproc:
131+
132+ + Avoid duplicate classes and attributes on references div.
133+ + Fix regression in citeproc processing (#7376). If inline
134+ references are used (in the metadata `references` field), we
135+ should still only include in the bibliography items that are
136+ actually cited (unless `nocite` is used).
137+
138+ * Require citeproc 0.4.0.1. This fixes a bug which led to doubled
139+ "et al." in some (rare) circumstances.
140+
141+ * MANUAL.txt:
142+
143+ + Mention GladTeX for EPUB export (Sebastian Humenda).
144+ This updates the manual and the web site about the GladTeX usage.
145+ + More details and a useful link for YAML syntax.
146+
147+ * CONTRIBUTING.md: update modules overview (Albert Krewinkel).
148+
149+ * using-the-pandoc-api.md: switch from String to Text (Albert Krewinkel).
150+
151+
152+## pandoc 2.14.0.1 (2021-06-01)
153+
154+ * Commonmark reader: Fix regression in 2.14 with YAML metdata block parsing,
155+ which could cause the document body to be omitted after metadata (#7339).
156+
157+ * HTML reader: fix column width regression in 2.14 (#7334).
158+ Column widths specified with a style attribute were off by a factor of 100.
159+
160+ * Markdown reader: in `rebasePaths`, check for both Windows and Posix
161+ absolute paths. Previously Windows pandoc was treating
162+ `/foo/bar.jpg` as non-absolute.
163+
164+ * Text.Pandoc.Logging: In rendering `LoadedResource`, use relative paths.
165+
166+ * Docx writer: fix regression on captions (#7328). The "Table Caption"
167+ style was no longer getting applied. (It was overwritten by "Compact.")
168+
169+ * Use commonmark-extensions 0.2.1.2
170+
171+## pandoc 2.14 (2021-05-28)
172+
173+ * Change reader types, allowing better tracking of source positions
174+ [API change]. Previously, when multiple file arguments were provided,
175+ pandoc simply concatenated them and passed the contents to the readers,
176+ which took a Text argument. As a result, the readers had no way of knowing
177+ which file was the source of any particular bit of text. This meant that
178+ we couldn't report accurate source positions on errors or include accurate
179+ source positions as attributes in the AST. More seriously, it meant that
180+ we couldn't resolve resource paths relative to the files containing them
181+ (see e.g. #5501, #6632, #6384, #3752).
182+
183+ * Add `rebase_relative_paths` extension (#3752). When enabled, this
184+ extension rewrites relative image and link paths by prepending
185+ the (relative) directory of the containing file. This
186+ behavior is useful when your input sources are split
187+ into multiple files, across several directories, with files
188+ referring to images stored in the same directory. The
189+ extension can be enabled for all markdown and commonmark-based formats.
190+
191+ * Add Text.Pandoc.Sources (exported module), with a `Sources` type and a
192+ `ToSources` class. A `Sources` wraps a list of `(SourcePos, Text)` pairs
193+ [API change]. A parsec `Stream` instance is provided for `Sources`. The
194+ module also exports versions of parsec's `satisfy` and other Char parsers
195+ that track source positions accurately from a `Sources` stream (or any
196+ instance of the new `UpdateSourcePos` class).
197+
198+ * Text.Pandoc.Parsing
199+
200+ + Export the modified Char parsers defined in Text.Pandoc.Sources
201+ instead of the ones parsec provides. Modified parsers to use a
202+ `Sources` as stream [API change].
203+ + Improve include file functions [API change]. Remove old
204+ `insertIncludedFileF`. Give `insertIncludedFile` a more general type,
205+ allowing it to be used where `insertIncludedFileF` was.
206+ + Add parameter to the `citeKey` parser from
207+ Text.Pandoc.Parsing, which controls whether the `@{..}`
208+ syntax is allowed [API change].
209+
210+ * Text.Pandoc.Error: Modified the constructor `PandocParsecError` to take a
211+ `Sources` rather than a `Text` as first argument, so parse error locations
212+ can be accurately reported.
213+
214+ * Fix source position reporting for YAML bibliographies (#7273).
215+
216+ * Issue error message when reader or writer format is malformed
217+ (#7231). Previously we exited with an error status but (due to a bug)
218+ no message.
219+
220+ * Smarter smart quotes (#7216, #2103). Treat a leading `"` with no
221+ closing `"` as a left curly quote. This supports the practice, in
222+ fiction, of continuing paragraphs quoting the same speaker without an
223+ end quote. It also helps with quotes that break over lines in line blocks.
224+
225+ * Markdown reader:
226+
227+ + Use MetaInlines not MetaBlocks for multimarkdown metadata fields.
228+ This gives better results in converting to e.g. pandoc markdown.
229+ + Implement curly-brace syntax for Markdown citation keys (#6026).
230+ The change provides a way to use citation keys that contain
231+ special characters not usable with the standard citation key syntax.
232+ Example: `@{foo_bar{x}'}` for the key `foo_bar{x}`. It also allows
233+ separating citation keys from immediately following text, e.g. `@{foo}A`.
234+
235+ * RST reader:
236+
237+ + Seek include files in the directory of the file
238+ containing the include directive, as RST requires (#6632).
239+ + Use `insertIncludedFile` from Text.Pandoc.Parsing
240+ instead of reproducing much of its code.
241+
242+ * Org reader: Resolve org includes relative to the directory containing the
243+ file containing the INCLUDE directive (#5501).
244+
245+ * ODT reader: Treat tabs as spaces (#7185, niszet).
246+
247+ * Docx reader:
248+
249+ + Add handling of vml image objects (#7257, mbrackeantidot).
250+ + Support new table features (Emily Bourke, #6316): column
251+ spans, row spans, multiple header rows, table description
252+ (parsed as a simple caption), captions, column widths.
253+
254+ * LaTeX reader:
255+
256+ + Improved siunitx support (#6658, #6620).
257+ + Better support for `\xspace` (#7299).
258+ + Improve parsing of `\def` macros. We previously set "verbatim mode"
259+ even for parsing the initial `\def`; this caused problems
260+ for `\def` nested inside another `\def`.
261+ + Implement `\newif`.
262+
263+ * ConTeXt writer: improve ordered lists (#5016, Denis Maier).
264+ Change ordered list from itemize to enumerate. Add new
265+ itemgroup for ordered lists. Remove manual insertion of
266+ width attributes. Use tabular figures in ordered list
267+ enumerators.
268+
269+ * HTML reader:
270+
271+ + Don't fail on unmatched closing "script" tag (Albert Krenkel, #7282).
272+ + Keep h1 tags as normal headers (#2293, Albert Krewinkel).
273+ The tags `<title>` and `<h1 class="title">` often contain the same
274+ information, so the latter was dropped from the document. However, as
275+ this can lead to loss of information, the heading is now always
276+ retained. Use `--shift-heading-level-by=-1` to turn the `<h1>`
277+ into the document title, or a filter to restore the previous behavior.
278+ + Handle relative lengths (e.g. `2*`) in HTML column widths (#4063).
279+ See <https://www.w3.org/TR/html4/types.html#h-6.6>.
280+
281+ * DocBook/JATS readers:
282+
283+ + Fix mathml regression caused by the switch in XML libraries (#7173).
284+ + Fix "phrase" in DocBook: take classes from "role" not "class" (#7195).
285+
286+ * DocBook reader: ensure that first and last names are separated (#6541).
287+
288+ * Jira reader (Albert Krewinkel, #7218):
289+
290+ + Support "smart" links: `[alias|https://example.com|smart-card]` syntax.
291+ + Allow spaces and most unicode characters in attachment links.
292+ + No longer require a newline character after `{noformat}`.
293+ + Only allow URI path segment characters in bare links.
294+ + The `file:` schema is no longer allowed in bare links; these
295+ rarely make sense.
296+
297+ * Plain writer: handle superscript unicode minus (#7276).
298+
299+ * LaTeX writer:
300+
301+ + Better handling of line breaks in simple tables (#7272).
302+ Now we also handle the case where they're embedded in other elements,
303+ e.g. spans.
304+ + For beamer output, support `exampleblock` and `alertblock` (#7278).
305+ A block will be rendered as an `exampleblock` if the heading
306+ has class `example` and an `alertblock` if it has class `alert`.
307+ + Separate successive quote chars with thin space (#6958,
308+ Albert Krewinkel). Successive quote characters are separated with
309+ a thin space to improve readability and to prevent unwanted ligatures.
310+ Detection of these quotes sometimes had failed if the second quote
311+ was nested in a span element.
312+ + Separate successive quote chars with thin space (#6958, Albert
313+ Krewinkel).
314+
315+ * EPUB Writer: Fix belongs-to-collection XML id choice (#7267, nuew).
316+ The epub writer previously used the same XML id for both the book
317+ identifier and the epub collection. This causes an error on epubcheck.
318+
319+ * BibTeX/BibLaTeX writer: Handle `annote` field (#7266).
320+
321+ * ZimWiki writer: allow links and emphasis in headers (#6605,
322+ Albert Krewinkel).
323+
324+ * ConTeXt writer:
325+
326+ + Support blank lines in line blocks (#6564, Albert Krewinkel,
327+ thanks to @denismaier).
328+ + Use span identifiers as reference anchors (#7246, Albert Krewinkel).
329+
330+ * HTML writer:
331+
332+ + Keep attributes from code nested below `pre` tag (#7221,
333+ Albert Krewinkel). If a code block is defined with `<pre><code
334+ class="language-x">…</code></pre>`, where the `<pre>` element has no
335+ attributes, then the attributes from the `<code>` element are used
336+ instead. Any leading `language-` prefix is dropped in the code's
337+ `class` attribute are dropped to improve syntax highlighting.
338+ + Ensure headings only have valid attribs in HTML4 (#5944, Albert
339+ Krewinkel).
340+ + Parse `<header>` as a Div (Albert Krewinkel).
341+
342+ * Org writer:
343+
344+ + Inline latex envs need newlines (#7252, tecosaur).
345+ As specified in https://orgmode.org/manual/LaTeX-fragments.html, an
346+ inline \begin{}...\end{} LaTeX block must start on a new line.
347+ + Use LaTeX style maths deliminators (#7196, tecosaur).
348+
349+ * JATS writer (Albert Krewinkel):
350+
351+ + Use either styled-content or named-content for spans (#7211).
352+ If the element has a content-type attribute, or at least one class,
353+ then that value is used as `content-type` and the span is put inside
354+ a `<named-content>` element. Otherwise a `<styled-content>` element
355+ is used instead.
356+ + Reduce unnecessary use of `<p>` elements for wrapping (#7227).
357+ The `<p>` element is used for wrapping in cases were the contents
358+ would otherwise not be allowed in a certain context. Unnecessary
359+ wrapping is avoided, especially around quotes (`<disp-quote>` elements).
360+ + Convert spans to `<named-content>` elements (#7211). Spans with
361+ attributes are converted to `<named-content>` elements instead of
362+ being wrapped with `<milestone-start/>` and `<milestone-end>`
363+ elements. Milestone elements are not allowed in documents using the
364+ articleauthoring tag set, so this change ensures the creation of valid
365+ documents.
366+ + Add footnote number as label in backmatter (#7210). Footnotes in the
367+ backmatter are given the footnote's number as a label. The
368+ articleauthoring output is unaffected from this change, as footnotes
369+ are placed inline there.
370+ + Escape disallows chars in identifiers. XML identifiers must start
371+ with an underscore or letter, and can contain only a limited set
372+ of punctuation characters. Any IDs not adhering to these rules are
373+ rewritten by writing the offending characters as `Uxxxx`,
374+ where `xxxx` is the character's hex code.
375+
376+ * Jira writer: use `{color}` when span has a color attribute
377+ (Albert Krewinkel, tarleb/jira-wiki-markup#10).
378+
379+ * Docx writer:
380+
381+ + Autoset table width if no column has an explicit width (Albert
382+ Krewinkel).
383+ + Extract Table handling into separate module (Albert Krewinkel).
384+ + Support colspans and rowspans in tables (Albert Krewinkel, #6315).
385+ + Support multirow table headers (Albert Krewinkel).
386+ + Improve integration of settings from reference.docx (#1209).
387+ This change allows users to create a reference.docx that
388+ sets `w:proofState` for spelling or grammar to `dirty`,
389+ so that spell/grammar checking will be triggered on the
390+ generated docx.
391+ + Copy over more settings from reference.docx (#7240). From settings.xml
392+ in the reference-doc, we now include: `zoom`, `embedSystemFonts`,
393+ `doNotTrackMoves`, `defaultTabStop`, `drawingGridHorizontalSpacing`,
394+ `drawingGridVerticalSpacing`, `displayHorizontalDrawingGridEvery`,
395+ `displayVerticalDrawingGridEvery`, `characterSpacingControl`,
396+ `savePreviewPicture`, `mathPr`, `themeFontLang`, `decimalSymbol`,
397+ `listSeparator`, `autoHyphenation`, `compat`.
398+ + Set zoom to 100% by default in settings.xml.
399+ + Align math options more with current Word defaults (e.g. Cambria Math
400+ font).
401+ + Remove `rsid`s from default settings.xml. Word will add these
402+ when revisions are made.
403+
404+ * Ms writer: Handle tables with multiple paragraphs (#7288).
405+ Previously they overflowed the table cell width. We now set line lengths
406+ per-cell and restore them after the table has been written.
407+
408+ * Markdown writer:
409+
410+ + Use cleaner braceless syntax for code blocks with a
411+ single class (#7242, Jan Tojnar).
412+ + Add quotes properly in markdown YAML metadata fields (#7245).
413+ This fixes a bug, which caused the writer to look at the *last*
414+ rather than the *first* character in determining whether quotes
415+ were needed. So we got spurious quotes in some cases and
416+ didn't get necessary quotes in others.
417+ + Use `@{..}` syntax for citations when needed.
418+ + Use fewer unneeded escapes for `#` (see #6259).
419+ + Improve escaping of `@`. We need to escape literal `@` before
420+ `{` because of the new citation syntax.
421+
422+ * Commonmark writer: Use backslash escapes for `<` and `|`...
423+ instead of entities (#7208).
424+
425+ * Powerpoint writer: allow `monofont` to be specified in metadata
426+ (#7187).
427+
428+ * LaTeX template:
429+
430+ + Use non-starred names for xcolor color names (#6109).
431+ This should make svgnames and x11names work properly.
432+ + Fix bad vertical spacing after bibliography (#7234, badumont).
433+ + List of figures before list of tables (#7235, Julien Dutant).
434+ + Move CSL macro definitions before header-includes so they can be
435+ overridden (#7286).
436+ + Improve treatment of CSL `entry-spacing` (#7296).
437+ Previously with the default template settings (`indent` variable
438+ not set), we would get interparagraph spaces separating bib
439+ entries even with `entry-spacing="0"`. On the other hand,
440+ setting `entry-spacing="2"` gave ridiculously large spacing.
441+ This change makes the spacing caused by `entry-spacing` a multiple
442+ of `\parskip` by default, which gives aesthetically reasonable
443+ output. Those who want a larger or smaller unit (e.g. because
444+ they use `indent` which sets `\parskip` to 0) may
445+ `\setlength{\cslentryspacingunit}{10pt}` in header-includes
446+ to override the defaults.
447+ + Move title, author, date up to top of preamble (#7295).
448+ This allows header-includes to use them, and puts them
449+ in a position where you can see them immediately.
450+ + Define commands for zero width non-joiner character
451+ (#6639, Albert Krewinkel). The zero-width non-joiner character
452+ is used to avoid ligatures (e.g. in German).
453+
454+ * ConTeXt template:
455+
456+ + Define `enumerate` itemgroup (#5016, Denis Maier).
457+ + List of figures before list of tables (#7235, Julien Dutant).
458+
459+ * reveal.js template:
460+
461+ + Support `toc-title` (#7171, Florian Kohrt).
462+ + Use `hash: true` by default rather than `history: true` (#6968).
463+
464+ * HTML-based slide shows: add support for `institute` (#7289, Thomas
465+ Hodgson).
466+
467+ * Text.Pandoc.Extensions: Add constructor `Ext_rebase_relative_paths` to
468+ `Extensions` [API change].
469+
470+ * Text.Pandoc.XML.Light: add Eq, Ord instances for Content,
471+ Element, Attr, CDataKind [API change].
472+
473+ * Text.Pandoc.MediaBag:
474+
475+ + Change type to use a `Text` key instead of `[FilePath]`.
476+ We normalize the path and use `/` separators for consistency.
477+ + Export `MediaItem` type [API change].
478+ + Change `MediaBag` type to a map from Text to MediaItem [API change].
479+ + `lookupMedia` now returns a `MediaItem` [API change].
480+ + Change `insertMedia` so it sets the `mediaPath` to a filename based on
481+ the SHA1 hash of the contents. This will be used when contents
482+ are extracted.
483+
484+ * Text.Pandoc.Class.PandocMonad:
485+
486+ + Remove `fetchMediaResource` [API change]. Use `fetchItem` to get
487+ resources in `fillMediaBag`.
488+ + Add informational message in `downloadOrRead` indicating what path
489+ local resources have been loaded from.
490+
491+ * Text.Pandoc.Logging:
492+
493+ + Remove single quotes around paths in messages.
494+ + Add LoadedResource constructor to LogMessage [API change].
495+ This is for INFO-level messages telling where image data has been
496+ loaded from. (This can vary because of the resource path.)
497+
498+ * Text.Pandoc.Asciify: simplify code and export `toAsciiText` [API change].
499+ Instead of encoding a giant (and incomplete) map, we now
500+ just use unicode-transforms to normalize the text to
501+ a canonical decomposition, and manipulate the result.
502+
503+ * App: allow tabs expansion even if file-scope is used (Albert Krewinkel,
504+ #6709). Tabs in plain-text inputs are now handled correctly, even if
505+ the `--file-scope` flag is used.
506+
507+ * Add new internal module Text.Pandoc.Writers.GridTable (Albert Krewinkel).
508+
509+ * Text.Pandoc.Highlighting: Change type of `languagesByExtension`, adding
510+ a parameter for a `SyntaxMap` [API change] (Jan Tojnar, #7241).
511+ Languages defined using `--syntax-definition` were not recognized by
512+ `languagesByExtension`. This patch corrects that, allowing the writers
513+ to see all custom definitions. The LaTeX writer still uses the default
514+ syntax map, but that's okay in that context, since
515+ `--syntax-definition` won't create new listings styles.
516+
517+ * Text.Pandoc.Citeproc:
518+
519+ + Ensure that CSL-related attributes are passed on to a Div with id
520+ 'refs'. Otherwise things like `entry-spacing` won't work when
521+ such Divs are used.
522+ + Use metadata's `lang` for the lang parameter of citeproc, overriding
523+ `localeLanguage`.
524+ + Recognize locators spelled with a capital letter (#7323).
525+ + Add a comma and a space in front of the suffix if it doesn't start
526+ with space or punctuation (#7324).
527+ + Don't detect math elements as locators (#7321).
528+
529+ * Remove Text.Pandoc.BCP47 module [API change]. Use types and functions
530+ from UnicodeCollation.Lang instead. This is a richer implementation
531+ of BCP 47.
532+
533+ * Text.Pandoc.Shared:
534+
535+ + Fix regression in grid tables for wide characters (#7214).
536+ In the translation from String to Text, a char-width-sensitive
537+ `splitAt'` was dropped. This commit reinstates it and uses it to make
538+ `splitTextByInstances` char-width sensitive.
539+ + Add `getLang` (formerly in the now-removed BCP47) [API change].
540+
541+ * Text.Pandoc.SelfContained: use `application/octet-stream`
542+ for unknown mime types instead of halting with an error (#7202).
543+
544+ * Lua filters: respect Inlines/Blocks filter functions in `pandoc.walk_*`
545+ (Albert Krewinkel).
546+
547+ * Add text as build-depend for trypandoc (#7193, Roman Beránek).
548+
549+ * Bump upper-bounds for network-uri, time, attoparsec.
550+
551+ * Use citeproc 0.4.
552+
553+ * Use texmath 0.12.3.
554+
555+ * Use jira-wiki-markup 1.3.5 (Albert Krewinkel).
556+
557+ * Require latest skylighting (fixes a bug in XML syntax highlighting).
558+
559+ * Use latest xml-conduit.
560+
561+ * Use latest commonmark, commonmark-extensions, commonmark-pandoc.
562+
563+ * Use haddock-library-1.10.0 (Albert Krewinkel).
564+
565+ * Allow compilation with base 4.15 (Albert Krewinkel).
566+
567+ * MANUAL:
568+
569+ + Add information about `lang` and bibliography sorting.
570+ + Add info about YAML escape sequences, link to spec (#7152,
571+ Albert Krewinkel).
572+ + Note that `institute` variable works for HTML-based slides.
573+ + Update documentation on citation syntax.
574+ + Add citation example for locators and suffixes (Tristan Stenner)
575+
576+ * Updated and fixed typos in documentation (Charanjit Singh,
577+ Anti-Distinctlyminty, Tatiana Porras, obcat).
578+
579+ * Add instructions for installing pandoc-types before compiling filter.
580+
581+ * INSTALL: add note that parallel installations should be avoided
582+ (#6865).
583+
584+ * Remove `biblatex-nussbaum.md` test. It is basically the same
585+ as `biblaetx-quotes.md`.
586+
587+ * Command tests: fail if a file contains no tests---and fix a
588+ test that failed in that way!
589+
590+ * Use smaller images in tests, reducing the size of the source tarball by 8 MB.
591+
592+
593+## pandoc 2.13 (2021-03-21)
594+
595+ * Support `yaml_metadata_block` extension for `commonmark`, `gfm` (#6537).
596+ This supported is a bit more limited than with pandoc's
597+ `markdown`. The YAML block must be the first thing in the input,
598+ and the leaf notes are parsed in isolation from the rest of
599+ the document. So, for example, you can't use reference
600+ links if the references are defined later in the document.
601+
602+ * Fix fallback to default partials when custom templates are used.
603+ If the directory containing a template does not contain the partial,
604+ it should be sought in the default templates, but this was not
605+ working properly (#7164).
606+
607+ * Handle `nocite` better with `--biblatex` and `--natbib` (#4585).
608+ Previously the nocite metadata field was ignored with these formats.
609+ Now it populates a `nocite-ids` template variable and causes a
610+ `\nocite` command to be issued.
611+
612+ * Text.Pandoc.Citeproc: apply `fixLinks` correctly (#7130). This is code
613+ that incorporates a prefix like `https://doi.org/` into a following link
614+ when appropriate.
615+
616+ * Text.Pandoc.Shared:
617+
618+ + Remove `backslashEscapes`, `escapeStringUsing` [API change]. Replace
619+ these inefficient association list lookups with more efficient escaping
620+ functions in the writers that used them (for a 10-25% performance boost
621+ in org, haddock, rtf, texinfo writers).
622+ + Remove `ToString`, `ToText` typeclasses [API change]. These were needed
623+ for the transition from String to Text, but they are no longer used and
624+ may clash with other things.
625+ + Simplify `compactDL`.
626+
627+ * Text.Pandoc.Parsing:
628+
629+ + Change type of `readWithM` so that it is no longer polymorphic
630+ [API change]. The `ToText` class has been removed, and now that we've
631+ completed the transition to Text we no longer need this to operate
632+ on Strings.
633+ + Remove `F` type synonym [API change]. Muse and Org were defining their
634+ own `F` anyway.
635+
636+ * Text.Pandoc.Readers.Metadata:
637+
638+ + Export `yamlMetaBlock` [API change].
639+ + Make `yamlBsToMeta`, `yamlBsToRefs` polymorphic on the parser state
640+ [API change].
641+
642+ * Markdown reader: Fix regression with `tex_math_backslash` (#7155).
643+
644+ * MediaWiki reader: Allow block-level content in notes (ref) (#7145).
645+
646+ * Jira reader (Albert Krewinkel):
647+
648+ + Fixed parsing of autolinks (i.e., of bare URLs in the text).
649+ Previously an autolink would take up the rest of a line, as spaces
650+ were allowed characters in these items.
651+ + Emoji character sequences no longer cause parsing failures. This was
652+ due to missing backtracking when emoji parsing fails.
653+ + Mark divs created from panels with class "panel".
654+
655+ * RST reader: fix logic for ending comments (#7134). Previously comments
656+ sometimes got extended too far.
657+
658+ * DocBook writer: include Header attributes as XML attributes on
659+ section (Erik Rask). Attributes with key names that are not allowed
660+ as XML attributes are dropped, as are attributes with invalid values
661+ and `xml:id` (DocBook 5) and `id` (DocBook 4).
662+
663+ * Docx writer:
664+
665+ + Make `nsid` in `abstractNum` deterministic. Previously we assigned
666+ a random number, but we don't need random values, so now we just
667+ assign a value based on the list marker.
668+ + Use integral values for `w:tblW` (#7141).
669+
670+ * Jira writer (Albert Krewinkel):
671+
672+ + Block quotes are only rendered as `bq.` if they do not contain a
673+ linebreak.
674+ + Jira writer: improve div/panel handling. Include div attributes in
675+ panels, always render divs with class `panel` as panels, and
676+ avoid nesting of panels.
677+
678+ * HTML writer: Add warnings on duplicate attribute values.
679+ This prevents emitting invalid HTML. Ultimately it would be good to
680+ prevent this in the types themselves, but this is better for now.
681+
682+ * Org writer: Prevent unintended creation of ordered list items (#7132,
683+ Albert Krewinkel). Adjust line wrapping if default wrapping would cause
684+ a line to be read as an ordered list item.
685+
686+ * JATS templates: support 'equal-contrib' attrib for authors (Albert
687+ Krewinkel). Authors who contributed equally to a paper may be marked
688+ with `equal-contrib`.
689+
690+ * reveal.js template: replace JS comment with HTML (#7154, Florian Kohrt).
691+
692+ * Text.Pandoc.Logging: Add `DuplicateAttribute` constructor to `LogMessage`.
693+ [API change]
694+
695+ * Use `-j4` for linux release build. This speeds up the build dramatically
696+ on arm.
697+
698+ * cabal.project: remove ghcoptions. Move flags to top level, so they can
699+ be set differently on the command line.
700+
701+ * Require latest texmath, skylighting, citeproc, jira-wiki-markup.
702+ (The latest skylighting fixes a bad bug with Haskell syntax highlighting.)
703+ Narrow version bounds for texmath, skylighting, and citeproc, since
704+ the test output depend on them.
705+
706+ * Use doclayout 0.3.0.2. This significantly reduces the time and memory
707+ needed to compile pandoc.
708+
709+ * Use `foldl'` instead of `foldl` everywhere.
710+
711+ * Update bounds for random (#7156, Alexey Kuleshevich).
712+
713+ * Remove uses of some partial functions.
714+
715+ * Don't bake in a larger stack size for the executable.
716+
717+ * Test improvements:
718+
719+ + Use `getExecutablePath` from base, avoiding the dependency on
720+ `executable-path`.
721+ + Factor out `setupEnvironment` in Helpers, to avoid code duplication.
722+ + Fix finding of data files by setting teh `pandoc_datadir` environment
723+ variable when we shell out to pandoc. This avoids the need to use
724+ `--data-dir` for the tests, which caused problems finding `pandoc.lua`
725+ when compiling without the `embed_data_files` flag (#7163).
726+
727+ * Benchmark improvements:
728+
729+ + Build `+RTS -A8m -RTS` into default ghc-options for benchmark.
730+ This is necessary to get accurate benchmark results; otherwise we
731+ are largely measuring garbage collecting, some not related to the
732+ current benchmark.
733+ + Allow specifying BASELINE file in 'make bench' for comparison
734+ (otherwise the latest benchmark is chosen by default).
735+ + Force `readFile` in benchmarks early (Bodigrim).
736+
737+ * CONTRIBUTING: suggest using a `cabal.project.local` file (#7153,
738+ Albert Krewinkel).
739+
740+ * Add ghcid-test to Makefile. This loads the test suite in ghcid.
741+
742+
743+## pandoc 2.12 (2021-03-08)
744+
745+ * `--resource-path` now accumulates if specified multiple
746+ times (#6152). Resource paths specified later on the command line are
747+ prepended to those specified earlier. Thus,
748+ `--resource-path foo --resource-path bar:baz` is equivalent to
749+ `--resource-path bar:bas:foo`. (The previous behavior was
750+ for the last `--resource-path` to replace all the rest.)
751+ `resource-path` in defaults files behaves the same way: it
752+ will be prepended to the resource path set by earlier
753+ command line options or defaults files. This change
754+ facilitates the use of multiple defaults files: each can
755+ specify a directory containing resources it refers to
756+ without clobbering the resource paths set by the others.
757+
758+ * Allow defaults files to refer to the home directory, the
759+ user data directory, and the directory containing the defaults file
760+ itself (#5871, #5982, #5977). In fields that expect file paths
761+ (and only in these fields),
762+
763+ + `${VARIABLE}` will expand to the value of the environment variable
764+ `VARIABLE` (and in particular `${HOME}` will expand to the path
765+ of the home directory). A warning will be raised for undefined
766+ variables.
767+ + `${USERDATA}` will expand to the path of the user data
768+ directory in force when the defaults file is being processed.
769+ + `${.}` will expand to the directory containing the defaults file.
770+ (This allows default files to be placed in a directory containing
771+ resources they make use of.)
772+
773+ * When downloading content from URL arguments, be sensitive to
774+ the character encoding (#5600). We can properly handle UTF-8 and latin1
775+ (ISO-8859-1); for others we raise an error. Fall back to latin1 if
776+ no charset is given in the mime type and UTF-8 decoding fails.
777+
778+ * Allow abbreviations that don't end in a period to be
779+ specified using `--abbreviations` (#7124).
4
780
5 * Add new unexported module Text.Pandoc.XML.Light, as well
781 * Add new unexported module Text.Pandoc.XML.Light, as well
6 as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc,
782 as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc,
@@ -34,75 +810,6 @@
34 these have been added. And the docx golden tests have been updated,
810 these have been added. And the docx golden tests have been updated,
35 because the new parser does not preserve the order of attributes.
811 because the new parser does not preserve the order of attributes.
36
812
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−
106 * DocBook reader:
813 * DocBook reader:
107
814
108 + Avoid expensive tree normalization step, as it is not necessary
815 + Avoid expensive tree normalization step, as it is not necessary
@@ -137,6 +844,11 @@
137
844
138 * LaTeX reader:
845 * LaTeX reader:
139
846
847+ + Don't export `tokenize`, `untokenize` [API change]. These are internal
848+ implementation details, which were only exported for testing.
849+ They don't belong in the public API.
850+ + Improved efficiency of the parser. With these changes the reader
851+ is almost twice as fast as in the last release in our benchmarks.
140 + Code cleanup, removing some unnecessary things.
852 + Code cleanup, removing some unnecessary things.
141 + Rewrite `withRaw` so it doesn't rely on fragile assumptions
853 + Rewrite `withRaw` so it doesn't rely on fragile assumptions
142 about token positions (which break when macros are expanded)
854 about token positions (which break when macros are expanded)
@@ -156,11 +868,20 @@
156 it doesn't change anything.
868 it doesn't change anything.
157 + Improve `braced'`. Remove the parameter, have it parse the
869 + Improve `braced'`. Remove the parameter, have it parse the
158 opening brace, and make it more efficient.
870 opening brace, and make it more efficient.
871+ + Factor out pieces of the LaTeX reader to make the module smaller.
872+ This reduces memory demands when compiling. Created
873+ Text.Pandoc.Readers.{LaTeX,Math,Citation,Table,Macro,Inline}.
874+ Changed Text.Pandoc.Readers.LaTeX.SIunitx to export a command map
875+ instead of individual commands.
876+ + Handle table cells containing `&` in `\verb` (#7129).
877+
878+ * Make Text.Pandoc.Readers.LaTeX.Types an unexported module [API change].
159
879
160 * Markdown reader:
880 * Markdown reader:
161
881
162 + Improved handling of mmd link attributes in references (#7080).
882 + Improved handling of mmd link attributes in references (#7080).
163 Previously they only worked for links that had titles.
883 Previously they only worked for links that had titles.
884+ + Improved efficiency of the parser (benchmarks show a 15% speedup).
164
885
165 * OPML reader:
886 * OPML reader:
166
887
@@ -190,9 +911,10 @@
190 * Jira reader:
911 * Jira reader:
191
912
192 + Modified the Doc parser to skip leading blank lines. This fixes
913 + Modified the Doc parser to skip leading blank lines. This fixes
193− parsing of documents which start with multiple blank lines (#7095).
914+ parsing of documents which start with multiple blank lines (Albert
915+ Krewinkel, #7095).
194− + Prevent URLs within link aliases to be treated as autolinks
916+ + Prevent URLs within link aliases to be treated as autolinks (Albert
195− (#6944).
917+ Krewinkel, #6944).
196
918
197 * Text.Pandoc.Shared
919 * Text.Pandoc.Shared
198
920
@@ -201,6 +923,10 @@
201 and `underlineSpan` (which had been deprecated in April 2020)
923 and `underlineSpan` (which had been deprecated in April 2020)
202 [API change].
924 [API change].
203 + Export `handleTaskListItem` (Albert Krewinkel) [API change].
925 + Export `handleTaskListItem` (Albert Krewinkel) [API change].
926+ + Change `defaultUserDataDirs` to `defaultUserDataDir` [API
927+ change]. We determine what is the default user data directory
928+ by seeing whether the XDG directory and/or legacy
929+ directory exist.
204
930
205 * BibTeX writer:
931 * BibTeX writer:
206
932
@@ -218,7 +944,7 @@
218 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
944 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
219 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds.
945 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds.
220
946
221− * Text.Pandoc.Writers.EPUB
947+ * EPUB writer:
222
948
223 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
949 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
224 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093).
950 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093).
@@ -233,6 +959,13 @@
233 contains an ampersand or another character with a special meaning
959 contains an ampersand or another character with a special meaning
234 in XML.
960 in XML.
235
961
962+ * Jira writer:
963+
964+ + Use Span identifiers as anchors (Albert Krewinkel).
965+ + Use `{noformat}` instead of `{code}` for unknown languages (Albert
966+ Krewinkel). Code blocks which are not marked as a language supported
967+ by Jira are rendered as preformatted text via `{noformat}` blocks.
968+
236 * LaTeX writer:
969 * LaTeX writer:
237
970
238 + Adjust hypertargets to beginnings of paragraphs (#7078).
971 + Adjust hypertargets to beginnings of paragraphs (#7078).
@@ -241,12 +974,18 @@
241 This makes a particular difference for links to citations using
974 This makes a particular difference for links to citations using
242 `--citeproc` and `link-citations: true`.
975 `--citeproc` and `link-citations: true`.
243 + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047).
976 + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047).
977+ + Use function instead of map for accent lookup (should be
978+ more efficient).
979+ + Split the module to make it easier to compile on low-memory
980+ systems: added Text.Pandoc.Writers.LaTeX.{Util,Citation,Lang}.
244
981
245 * Markdown writer:
982 * Markdown writer:
246
983
247 + Handle math right before digit. We insert an HTML comment to
984 + Handle math right before digit. We insert an HTML comment to
248 avoid a `$` right before a digit, which pandoc will not recognize
985 avoid a `$` right before a digit, which pandoc will not recognize
249 as a math delimiter.
986 as a math delimiter.
987+ + Split the module to make it easier to compile on low-memory
988+ systems: added Text.Pandoc.Writers.Markdown.{Types,Inline}.
250
989
251 * ODT writer:
990 * ODT writer:
252
991
@@ -279,40 +1018,154 @@
279 `--include-in-header`, `--include-before-body`, `--include-after-body`)
1018 `--include-in-header`, `--include-before-body`, `--include-after-body`)
280 may be used.
1019 may be used.
281
1020
1021+ * LaTeX template:
1022+
282− * LaTeX template: Update to iftex package (#7073) (Andrew Dunning)
1023+ + Update to iftex package (#7073) (Andrew Dunning)
1024+ + Wrap url colours in braces (#7121) (Loïc Grobol).
283
1025
284 * revealjs template: Add 'center' option for vertical slide centering.
1026 * revealjs template: Add 'center' option for vertical slide centering.
285 (maurerle, #7104).
1027 (maurerle, #7104).
286
1028
287 * Text.Pandoc.XML: Improve efficiency of `fromEntities`.
1029 * Text.Pandoc.XML: Improve efficiency of `fromEntities`.
288
1030
1031+ * Text.Pandoc.MIME
1032+
1033+ + Add exported function `getCharset` [API change].
1034+
1035+ * Text.Pandoc.UTF8: change IO functions to return Text, not String
1036+ [API change]. This affects `readFile`, `getContents`, `writeFileWith`,
1037+ `writeFile`, `putStrWith`, `putStr`, `putStrLnWith`, `putStrLn`.
1038+ `hPutStrWith`, `hPutStr`, `hPutStrLnWith`, `hPutStrLn`, `hGetContents`.
1039+ This avoids the need to uselessly create a linked list of characters
1040+ when emiting output.
1041+
1042+ * Text.Pandoc.App
1043+
1044+ + Add `parseOptionsFromArgs` [API change, new exported function].
1045+ + Add fields for CSL options to `Opt` [API change]:
1046+ `optCSL`, `optbibliography`, `optCitationAbbreviations`.
1047+
1048+ * Text.Pandoc.Citeproc.BibTeX
1049+
1050+ + `Text.Pandoc.Citeproc.writeBibTeXString` now returns
1051+ `Doc Text` instead of `Text` (#7068).
1052+ + Correctly handle `pages` (= `page` in CSL) (#7067).
1053+ + Correctly handle BibLaTeX `langid` (= `language` in CSL, #7067).
1054+ + In BibTeX output, protect foreign titles since there's no language
1055+ field (#7067).
1056+ + Clean up BibTeX parsing (#7049). Previously there was a messy code
1057+ path that gave strange results in some cases, not passing through raw
1058+ tex but trying to extract a string content. This was an artefact of
1059+ trying to handle some special bibtex-specific commands in the BibTeX
1060+ reader. Now we just handle these in the LaTeX reader and simplify
1061+ parsing in the BibTeX reader. This does mean that more raw tex will
1062+ be passed through (and currently this is not sensitive to the
1063+ `raw_tex` extension; this should be fixed).
1064+
1065+ * Text.Pandoc.Citeproc.MetaValue
1066+
1067+ + Correctly parse "raw" date value in markdown references metadata.
1068+ (See jgm/citeproc#53.)
1069+
1070+ * Text.Pandoc.Citeproc
1071+
1072+ + Use https URLs for links (Salim B, #7122).
1073+
1074+ * Text.Pandoc.Class
1075+
1076+ + Add `getTimestamp` [API change]. This attempts to read the
1077+ `SOURCE_DATE_EPOCH` environment variable and parse a UTC time
1078+ from it (treating it as a unix date stamp, see
1079+ https://reproducible-builds.org/specs/source-date-epoch/). If the
1080+ variable is not set or can't be parsed as a unix date stamp, then the
1081+ function returns the current date.
1082+
1083+ * Text.Pandoc.Error
1084+
1085+ + Add `PandocUnsupportedCharsetError` constructor for
1086+ `PandocError` [API change].
1087+ + Export `renderError` [API change].
1088+ + Refactor `handleError` to use `renderError`. This allows us render
1089+ error messages without exiting.
1090+
1091+ * Text.Pandoc.Extensions
1092+
1093+ + `Ext_task_lists` is now supported by org (and turned
1094+ on by default) (Albert Krewinkel, #6336).
1095+ + Remove `Ext_fenced_code_attributes` from allowed commonmark attributes
1096+ (#7097). This attribute was listed as allowed, but it didn't actually
1097+ do anything. Use `attributes` for code attributes and more.
1098+
1099+ * Lua subsystem:
1100+
1101+ + Always load built-in Lua scripts from default data-dir (Albert
1102+ Krewinkel). The Lua modules `pandoc` and `pandoc.List` are now always
1103+ loaded from the system's default data directory. Loading from a
1104+ different directory by overriding the default path, e.g. via
1105+ `--data-dir`, is no longer supported to avoid unexpected behavior
1106+ and to address security concerns.
1107+ + Add module "pandoc.path" (Albert Krewinkel, #6001, #6565).
1108+ The module allows to work with file paths in a convenient and
1109+ platform-independent manner.
1110+ + Use strict evaluation when retrieving AST value from the stack
1111+ (Albert Krewinkel, #6674).
1112+
1113+ * Text.Pandoc.PDF
1114+
1115+ + Disable `smart` extension when building PDF via LaTeX.
1116+ This is to prevent accidental creation of ligatures like
1117+ `` ?` `` and `` !` `` (especially in languages with quotations like
1118+ German), and similar ligature issues. (See jgm/citeproc#54.)
1119+
1120+ * Text.Pandoc.CSV:
1121+
1122+ + Fix parsing of unquoted values (#7112). Previously we didn't allow
1123+ unescaped quotes in unquoted values, but they are allowed
1124+ in CSV.
1125+
1126+ * Test suite:
1127+
289− * Test suite: a more robust way of testing the executable.
1128+ + Use a more robust method for testing the executable. Many
290− Many of our tests require running the pandoc executable. This is
1129+ of our tests require running the pandoc executable. This
291− problematic for a few different reasons. First, cabal-install will
1130+ is problematic for a few different reasons. First,
292− sometimes run the test suite after building the library but before
1131+ cabal-install will sometimes run the test suite after
1132+ building the library but before building the executable,
293− building the executable, which means the executable isn't in place for
1133+ which means the executable isn't in place for the tests.
294− the tests. One can work around that by first building, then building and
1134+ One can work around that by first building, then building
295− running the tests, but that's fragile. Second, we have to find the
1135+ and running the tests, but that's fragile. Second, we
296− executable. So far, we've done that using a function `findPandoc` that
1136+ have to find the executable. So far, we've done that using
1137+ a function `findPandoc` that attempts to locate it
297− attempts to locate it relative to the test executable (which can be
1138+ relative to the test executable (which can be located
298− located using findExecutablePath). But the logic here is delicate and
1139+ using findExecutablePath). But the logic here is delicate
299− work with every combination of options. To solve both problems, we add
1140+ and work with every combination of options. To solve both
1141+ problems, we add an `--emulate` option to the
300− an `--emulate` option to the `test-pandoc` executable. When `--emulate`
1142+ `test-pandoc` executable. When `--emulate` occurs as the
301− occurs as the first argument passed to `test-pandoc`, the program simply
1143+ first argument passed to `test-pandoc`, the program simply
302− emulates the regular pandoc executable, using the rest of the arguments
1144+ emulates the regular pandoc executable, using the rest of
303− (after `--emulate`). Thus, `test-pandoc --emulate -f markdown -t latex`
1145+ the arguments (after `--emulate`). Thus, `test-pandoc
304− is just like `pandoc -f markdown -t latex`.
1146+ --emulate -f markdown -t latex` is just like `pandoc -f
305− Since all the work is done by library functions, implementing this
1147+ markdown -t latex`. Since all the work is done by library
1148+ functions, implementing this emulation just takes a couple
306− emulation just takes a couple lines of code and should be entirely
1149+ lines of code and should be entirely reliable. With this
307− reliable. With this change, we can test the pandoc executable by running
1150+ change, we can test the pandoc executable by running the
308− the test program itself (locatable using `findExecutablePath`) with the
1151+ test program itself (locatable using `findExecutablePath`)
309− `--emulate` option. This removes the need for the fragile `findPandoc`
1152+ with the `--emulate` option. This removes the need for the
1153+ fragile `findPandoc` step, and it means we can run our
310− step, and it means we can run our integration tests even when we're just
1154+ integration tests even when we're just building the
311− building the library, not the executable. [Note: part of this change
1155+ library, not the executable. [Note: part of this change
312− involved simplifying some complex handling to set environment variables
1156+ involved simplifying some complex handling to set
313− for dynamic library paths. I have tested a build with
1157+ environment variables for dynamic library paths. I have
1158+ tested a build with `--enable-dynamic-executable`, and it
314− `--enable-dynamic-executable`, and it works, but further testing may be
1159+ works, but further testing may be needed.]
315− needed.]
1160+ + Print accurate location if a test fails (Albert
1161+ Krewinkel). Ensures that tasty-hunit reports the location
1162+ of the failing test instead of the location of the helper
1163+ `test` function.
1164+
1165+ * Documentation: Update URLs and use `https` where possible (#7122,
1166+ Salim B).
1167+
1168+ * Add `doc/libraries.md`, a description of libraries that support pandoc.
316
1169
317 * MANUAL.txt
1170 * MANUAL.txt
318
1171
@@ -328,6 +1181,9 @@
328 * Makefile: in `make bench`, create CSV files for comparison and compare
1181 * Makefile: in `make bench`, create CSV files for comparison and compare
329 against previous benchmark run. Add timestamp to CSV filenames.
1182 against previous benchmark run. Add timestamp to CSV filenames.
330
1183
1184+ * cabal.project: don't explicitly set -trypandoc.
1185+ If we do, this can't be overridden on the cabal command line.
1186+
331 * doc/lua-filters.md: improve documentation for
1187 * doc/lua-filters.md: improve documentation for
332 `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`,
1188 `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`,
333 `directory`, `normalize` (Albert Krewinkel).
1189 `directory`, `normalize` (Albert Krewinkel).
@@ -336,9 +1192,17 @@
336
1192
337 * Require jira-wiki-markup 1.3.3 (Albert Krewinkel)
1193 * Require jira-wiki-markup 1.3.3 (Albert Krewinkel)
338
1194
339− * Require citeproc 0.3.0.7, which correctly titlecases when titles
1195+ * Require citeproc 0.3.0.8, which correctly titlecases when titles
340 contain non-ASCII characters.
1196 contain non-ASCII characters.
341
1197
1198+ * Use skylighting 0.10.4. This version of skylighting uses xml-conduit
1199+ rather than hxt. This speeds up parsing of XML syntax definitions
1200+ fourfold, and removes four packages from pandoc's dependency graph:
1201+ hxt-charproperties, hxt-unicode, hxt-regex-xmlschema, hxt.
1202+
1203+ * Add script `tools/parseTimings.pl` to help pin down which
1204+ modules take the most time and memory to compile.
1205+
342 * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert
1206 * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert
343 Krewinkel)
1207 Krewinkel)
344
1208
@@ -353,6 +1217,16 @@
353 + Clean up benchmark code.
1217 + Clean up benchmark code.
354 + Allow specifying patterns using `-p blah'.
1218 + Allow specifying patterns using `-p blah'.
355
1219
1220+ * trypandoc: add 2 second timeout.
1221+
1222+ * Use `-split-sections` in creating linux release binary.
1223+ This reduces executable size significantly (by about 30%).
1224+
1225+ * Remove `weigh-pandoc`. It's not really useful any more, now that our
1226+ regular benchmarks include data on allocation.
1227+
1228+ * Improve linux package build process and add script to
1229+ automate building an arm64 binary package.
356
1230
357
1231
358## pandoc 2.11.4 (2021-01-22)
1232## pandoc 2.11.4 (2021-01-22)
@@ -946,8 +1820,11 @@
946 translated to `C:\/foo/bar`, which caused problems.
1820 translated to `C:\/foo/bar`, which caused problems.
947 With this fix, the backslashes are removed.
1821 With this fix, the backslashes are removed.
948
1822
1823+ * Text.Pandoc.Logging:
1824+
949− * Text.Pandoc.Logging: Add constructor `ATXHeadingInLHS` constructor
1825+ + Add constructor `ATXHeadingInLHS` to `LogMessage` [API change].
1826+ + Add constructor `EnvironmentVariableUndefined` to
950− to `LogMessage` [API change].
1827+ `LogMessage` [API change].
951
1828
952 * Fix error that is given when people specify `doc` output (#6834,
1829 * Fix error that is given when people specify `doc` output (#6834,
953 gison93).
1830 gison93).
data/docx/word/settings.xml+6−16
@@ -1,6 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
1<?xml version="1.0" encoding="UTF-8"?>
2<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main">
2<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main">
3− <w:zoom w:percent="90" />
3+ <w:zoom w:percent="100" />
4 <w:embedSystemFonts />
4 <w:embedSystemFonts />
5 <w:proofState w:spelling="clean" w:grammar="clean" />
5 <w:proofState w:spelling="clean" w:grammar="clean" />
6 <w:stylePaneFormatFilter w:val="0004" />
6 <w:stylePaneFormatFilter w:val="0004" />
@@ -17,31 +17,21 @@
17 <w:characterSpacingControl w:val="doNotCompress" />
17 <w:characterSpacingControl w:val="doNotCompress" />
18 <w:savePreviewPicture />
18 <w:savePreviewPicture />
19 <w:rsids>
19 <w:rsids>
20− <w:rsidRoot w:val="00590D07" />
21− <w:rsid w:val="00011C8B" />
22− <w:rsid w:val="004E29B3" />
23− <w:rsid w:val="00590D07" />
24− <w:rsid w:val="00784D58" />
25− <w:rsid w:val="008D6863" />
26− <w:rsid w:val="00B86B75" />
27− <w:rsid w:val="00BC48D5" />
28− <w:rsid w:val="00C36279" />
29− <w:rsid w:val="00E315A3" />
30 </w:rsids>
20 </w:rsids>
31 <m:mathPr>
21 <m:mathPr>
32− <m:mathFont m:val="Lucida Grande" />
22+ <m:mathFont m:val="Cambria Math" />
33 <m:brkBin m:val="before" />
23 <m:brkBin m:val="before" />
34 <m:brkBinSub m:val="--" />
24 <m:brkBinSub m:val="--" />
35− <m:smallFrac m:val="false" />
25+ <m:smallFrac m:val="0" />
36− <m:dispDef m:val="false" />
26+ <m:dispDef />
37 <m:lMargin m:val="0" />
27 <m:lMargin m:val="0" />
38 <m:rMargin m:val="0" />
28 <m:rMargin m:val="0" />
39 <m:wrapRight />
29 <m:wrapRight />
40 <m:intLim m:val="subSup" />
30 <m:intLim m:val="subSup" />
41− <m:naryLim m:val="subSup" />
31+ <m:naryLim m:val="undOvr" />
42 </m:mathPr>
32 </m:mathPr>
43 <w:themeFontLang w:val="en-US" />
33 <w:themeFontLang w:val="en-US" />
44 <w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink" />
34 <w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink" />
45 <w:decimalSymbol w:val="." />
35 <w:decimalSymbol w:val="." />
46 <w:listSeparator w:val="," />
36 <w:listSeparator w:val="," />
47−</w:settings>
37+</w:settings>
data/templates/article.jats_publishing+1−1
@@ -86,7 +86,7 @@ $endif$
86$if(author)$
86$if(author)$
87<contrib-group>
87<contrib-group>
88$for(author)$
88$for(author)$
89−<contrib contrib-type="author">
89+<contrib contrib-type="author"$if(author.equal-contrib)$ equal-contrib="true"$endif$>
90$if(author.orcid)$
90$if(author.orcid)$
91<contrib-id contrib-id-type="orcid">$author.orcid$</contrib-id>
91<contrib-id contrib-id-type="orcid">$author.orcid$</contrib-id>
92$endif$
92$endif$
data/templates/default.context+7−3
@@ -51,6 +51,7 @@ $endif$
51
51
52% use microtypography
52% use microtypography
53\definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes]
53\definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes]
54+\definefontfeature[default:tnum][default][tnum=yes, pnum=no]
54\definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes]
55\definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes]
55\setupalign[hz,hanging]
56\setupalign[hz,hanging]
56\setupitaliccorrection[global, always]
57\setupitaliccorrection[global, always]
@@ -97,6 +98,9 @@ $endif$
97\setupitemize[autointro] % prevent orphan list intro
98\setupitemize[autointro] % prevent orphan list intro
98\setupitemize[indentnext=no]
99\setupitemize[indentnext=no]
99
100
101+\defineitemgroup[enumerate]
102+\setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}]
103+
100\setupfloat[figure][default={here,nonumber}]
104\setupfloat[figure][default={here,nonumber}]
101\setupfloat[table][default={here,nonumber}]
105\setupfloat[table][default={here,nonumber}]
102
106
@@ -160,12 +164,12 @@ $endfor$
160$if(toc)$
164$if(toc)$
161\completecontent
165\completecontent
162$endif$
166$endif$
163−$if(lot)$
164−\completelistoftables
165−$endif$
166$if(lof)$
167$if(lof)$
167\completelistoffigures
168\completelistoffigures
168$endif$
169$endif$
170+$if(lot)$
171+\completelistoftables
172+$endif$
169
173
170$body$
174$body$
171
175
data/templates/default.dzslides+1−1
@@ -171,7 +171,7 @@ $if(subtitle)$
171 <h1 class="subtitle">$subtitle$</h1>
171 <h1 class="subtitle">$subtitle$</h1>
172$endif$
172$endif$
173 <footer>
173 <footer>
174− $if(author)$<span class="author">$for(author)$$author$$sep$, $endfor$</span>$endif$ · $if(date)$<span class="date">$date$</span>$endif$
174+ $if(author)$<span class="author">$for(author)$$author$$sep$, $endfor$</span> · $endif$$if(institute)$<span class="institute">$for(institute)$$institute$$sep$, $endfor$</span> · $endif$$if(date)$<span class="date">$date$</span>$endif$
175 </footer>
175 </footer>
176</section>
176</section>
177$endif$
177$endif$
data/templates/default.jats_articleauthoring+1−1
@@ -19,7 +19,7 @@ $endif$
19$if(author)$
19$if(author)$
20<contrib-group>
20<contrib-group>
21$for(author)$
21$for(author)$
22−<contrib contrib-type="author">
22+<contrib contrib-type="author"$if(author.equal-contrib)$ equal-contrib="true"$endif$>
23$if(author.orcid)$
23$if(author.orcid)$
24<contrib-id contrib-id-type="orcid">$author.orcid$</contrib-id>
24<contrib-id contrib-id-type="orcid">$author.orcid$</contrib-id>
25$endif$
25$endif$
data/templates/default.latex+50−25
@@ -2,7 +2,7 @@
2\PassOptionsToPackage{unicode$for(hyperrefoptions)$,$hyperrefoptions$$endfor$}{hyperref}
2\PassOptionsToPackage{unicode$for(hyperrefoptions)$,$hyperrefoptions$$endfor$}{hyperref}
3\PassOptionsToPackage{hyphens}{url}
3\PassOptionsToPackage{hyphens}{url}
4$if(colorlinks)$
4$if(colorlinks)$
5−\PassOptionsToPackage{dvipsnames,svgnames*,x11names*}{xcolor}
5+\PassOptionsToPackage{dvipsnames,svgnames,x11names}{xcolor}
6$endif$
6$endif$
7$if(dir)$
7$if(dir)$
8$if(latex-dir-rtl)$
8$if(latex-dir-rtl)$
@@ -156,6 +156,25 @@ $if(CJKmainfont)$
156 \fi
156 \fi
157$endif$
157$endif$
158\fi
158\fi
159+$if(zero-width-non-joiner)$
160+%% Support for zero-width non-joiner characters.
161+\makeatletter
162+\def\zerowidthnonjoiner{%
163+ % Prevent ligatures and adjust kerning, but still support hyphenating.
164+ \texorpdfstring{%
165+ \textormath{\nobreak\discretionary{-}{}{\kern.03em}%
166+ \ifvmode\else\nobreak\hskip\z@skip\fi}{}%
167+ }{}%
168+}
169+\makeatother
170+\ifPDFTeX
171+ \DeclareUnicodeCharacter{200C}{\zerowidthnonjoiner}
172+\else
173+ \catcode`^^^^200c=\active
174+ \protected\def ^^^^200c{\zerowidthnonjoiner}
175+\fi
176+%% End of ZWNJ support
177+$endif$
159$if(beamer)$
178$if(beamer)$
160$if(theme)$
179$if(theme)$
161\usetheme[$for(themeoptions)$$themeoptions$$sep$,$endfor$]{$theme$}
180\usetheme[$for(themeoptions)$$themeoptions$$sep$,$endfor$]{$theme$}
@@ -325,6 +344,31 @@ $endif$
325$if(pagestyle)$
344$if(pagestyle)$
326\pagestyle{$pagestyle$}
345\pagestyle{$pagestyle$}
327$endif$
346$endif$
347+$if(csl-refs)$
348+\newlength{\cslhangindent}
349+\setlength{\cslhangindent}{1.5em}
350+\newlength{\csllabelwidth}
351+\setlength{\csllabelwidth}{3em}
352+\newlength{\cslentryspacingunit} % times entry-spacing
353+\setlength{\cslentryspacingunit}{\parskip}
354+\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing
355+ {% don't indent paragraphs
356+ \setlength{\parindent}{0pt}
357+ % turn on hanging indent if param 1 is 1
358+ \ifodd #1
359+ \let\oldpar\par
360+ \def\par{\hangindent=\cslhangindent\oldpar}
361+ \fi
362+ % set entry spacing
363+ \setlength{\parskip}{#2\cslentryspacingunit}
364+ }%
365+ {}
366+\usepackage{calc}
367+\newcommand{\CSLBlock}[1]{#1\hfill\break}
368+\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}}
369+\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break}
370+\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1}
371+$endif$
328$for(header-includes)$
372$for(header-includes)$
329$header-includes$
373$header-includes$
330$endfor$
374$endfor$
@@ -372,27 +416,8 @@ $for(bibliography)$
372\addbibresource{$bibliography$}
416\addbibresource{$bibliography$}
373$endfor$
417$endfor$
374$endif$
418$endif$
375−$if(csl-refs)$
419+$if(nocite-ids)$
376−\newlength{\cslhangindent}
420+\nocite{$for(nocite-ids)$$it$$sep$, $endfor$}
377−\setlength{\cslhangindent}{1.5em}
378−\newlength{\csllabelwidth}
379−\setlength{\csllabelwidth}{3em}
380−\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing
381− {% don't indent paragraphs
382− \setlength{\parindent}{0pt}
383− % turn on hanging indent if param 1 is 1
384− \ifodd #1 \everypar{\setlength{\hangindent}{\cslhangindent}}\ignorespaces\fi
385− % set entry spacing
386− \ifnum #2 > 0
387− \setlength{\parskip}{#2\baselineskip}
388− \fi
389− }%
390− {}
391−\usepackage{calc}
392−\newcommand{\CSLBlock}[1]{#1\hfill\break}
393−\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}}
394−\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break}
395−\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1}
396$endif$
421$endif$
397$if(csquotes)$
422$if(csquotes)$
398\usepackage{csquotes}
423\usepackage{csquotes}
@@ -469,12 +494,12 @@ $endif$
469}
494}
470$endif$
495$endif$
471$endif$
496$endif$
472−$if(lot)$
473−\listoftables
474−$endif$
475$if(lof)$
497$if(lof)$
476\listoffigures
498\listoffigures
477$endif$
499$endif$
500+$if(lot)$
501+\listoftables
502+$endif$
478$if(linestretch)$
503$if(linestretch)$
479\setstretch{$linestretch$}
504\setstretch{$linestretch$}
480$endif$
505$endif$
data/templates/default.revealjs+79−129
@@ -52,6 +52,9 @@ $endif$
52$for(author)$
52$for(author)$
53 <p class="author">$author$</p>
53 <p class="author">$author$</p>
54$endfor$
54$endfor$
55+$for(institute)$
56+ <p class="institute">$institute$</p>
57+$endfor$
55$if(date)$
58$if(date)$
56 <p class="date">$date$</p>
59 <p class="date">$date$</p>
57$endif$
60$endif$
@@ -59,7 +62,12 @@ $endif$
59$endif$
62$endif$
60$if(toc)$
63$if(toc)$
61<section id="$idprefix$TOC">
64<section id="$idprefix$TOC">
65+<nav role="doc-toc">
66+$if(toc-title)$
67+<h2 id="$idprefix$toc-title">$toc-title$</h2>
68+$endif$
62$table-of-contents$
69$table-of-contents$
70+</nav>
63</section>
71</section>
64$endif$
72$endif$
65
73
@@ -69,7 +77,7 @@ $body$
69
77
70 <script src="$revealjs-url$/dist/reveal.js"></script>
78 <script src="$revealjs-url$/dist/reveal.js"></script>
71
79
72− // reveal.js plugins
80+ <!-- reveal.js plugins -->
73 <script src="$revealjs-url$/plugin/notes/notes.js"></script>
81 <script src="$revealjs-url$/plugin/notes/notes.js"></script>
74 <script src="$revealjs-url$/plugin/search/search.js"></script>
82 <script src="$revealjs-url$/plugin/search/search.js"></script>
75 <script src="$revealjs-url$/plugin/zoom/zoom.js"></script>
83 <script src="$revealjs-url$/plugin/zoom/zoom.js"></script>
@@ -82,256 +90,198 @@ $endif$
82 // Full list of configuration options available at:
90 // Full list of configuration options available at:
83 // https://revealjs.com/config/
91 // https://revealjs.com/config/
84 Reveal.initialize({
92 Reveal.initialize({
85−$if(center)$
86− // Determines whether slide content should be vertically centered
87− center: $center$,
88−$endif$
89−$if(controls)$
90 // Display controls in the bottom right corner
93 // Display controls in the bottom right corner
91 controls: $controls$,
94 controls: $controls$,
92−$endif$
95+
93−$if(controlsTutorial)$
94 // Help the user learn the controls by providing hints, for example by
96 // Help the user learn the controls by providing hints, for example by
95 // bouncing the down arrow when they first encounter a vertical slide
97 // bouncing the down arrow when they first encounter a vertical slide
96 controlsTutorial: $controlsTutorial$,
98 controlsTutorial: $controlsTutorial$,
97−$endif$
99+
98−$if(controlsLayout)$
99 // Determines where controls appear, "edges" or "bottom-right"
100 // Determines where controls appear, "edges" or "bottom-right"
100 controlsLayout: '$controlsLayout$',
101 controlsLayout: '$controlsLayout$',
101−$endif$
102+
102−$if(controlsBackArrows)$
103 // Visibility rule for backwards navigation arrows; "faded", "hidden"
103 // Visibility rule for backwards navigation arrows; "faded", "hidden"
104 // or "visible"
104 // or "visible"
105 controlsBackArrows: '$controlsBackArrows$',
105 controlsBackArrows: '$controlsBackArrows$',
106−$endif$
106+
107−$if(progress)$
108 // Display a presentation progress bar
107 // Display a presentation progress bar
109 progress: $progress$,
108 progress: $progress$,
110−$endif$
109+
111−$if(slideNumber)$
112 // Display the page number of the current slide
110 // Display the page number of the current slide
113 slideNumber: $slideNumber$,
111 slideNumber: $slideNumber$,
114−$endif$
112+
115−$if(showSlideNumber)$
116 // 'all', 'print', or 'speaker'
113 // 'all', 'print', or 'speaker'
117 showSlideNumber: '$showSlideNumber$',
114 showSlideNumber: '$showSlideNumber$',
118−$endif$
115+
119−$if(hash)$
120 // Add the current slide number to the URL hash so that reloading the
116 // Add the current slide number to the URL hash so that reloading the
121 // page/copying the URL will return you to the same slide
117 // page/copying the URL will return you to the same slide
122 hash: $hash$,
118 hash: $hash$,
123−$endif$
119+
124−$if(hashOneBasedIndex)$
125 // Start with 1 for the hash rather than 0
120 // Start with 1 for the hash rather than 0
126 hashOneBasedIndex: $hashOneBasedIndex$,
121 hashOneBasedIndex: $hashOneBasedIndex$,
127−$endif$
122+
123+ // Flags if we should monitor the hash and change slides accordingly
124+ respondToHashChanges: $respondToHashChanges$,
125+
128 // Push each slide change to the browser history
126 // Push each slide change to the browser history
129− history: $if(history)$$history$$else$true$endif$,
127+ history: $history$,
130−$if(keyboard)$
128+
131 // Enable keyboard shortcuts for navigation
129 // Enable keyboard shortcuts for navigation
132 keyboard: $keyboard$,
130 keyboard: $keyboard$,
133−$endif$
131+
134−$if(overview)$
135 // Enable the slide overview mode
132 // Enable the slide overview mode
136 overview: $overview$,
133 overview: $overview$,
137−$endif$
134+
138−$if(center)$
135+ // Disables the default reveal.js slide layout (scaling and centering)
136+ // so that you can use custom CSS layout
137+ disableLayout: false,
138+
139 // Vertical centering of slides
139 // Vertical centering of slides
140 center: $center$,
140 center: $center$,
141−$endif$
141+
142−$if(touch)$
143 // Enables touch navigation on devices with touch input
142 // Enables touch navigation on devices with touch input
144 touch: $touch$,
143 touch: $touch$,
145−$endif$
144+
146−$if(loop)$
147 // Loop the presentation
145 // Loop the presentation
148 loop: $loop$,
146 loop: $loop$,
149−$endif$
147+
150−$if(rtl)$
151 // Change the presentation direction to be RTL
148 // Change the presentation direction to be RTL
152 rtl: $rtl$,
149 rtl: $rtl$,
153−$endif$
150+
154−$if(navigationMode)$
155 // see https://revealjs.com/vertical-slides/#navigation-mode
151 // see https://revealjs.com/vertical-slides/#navigation-mode
156 navigationMode: '$navigationMode$',
152 navigationMode: '$navigationMode$',
157−$endif$
153+
158−$if(shuffle)$
159 // Randomizes the order of slides each time the presentation loads
154 // Randomizes the order of slides each time the presentation loads
160 shuffle: $shuffle$,
155 shuffle: $shuffle$,
161−$endif$
156+
162−$if(fragments)$
163 // Turns fragments on and off globally
157 // Turns fragments on and off globally
164 fragments: $fragments$,
158 fragments: $fragments$,
165−$endif$
159+
166−$if(fragmentInURL)$
167 // Flags whether to include the current fragment in the URL,
160 // Flags whether to include the current fragment in the URL,
168 // so that reloading brings you to the same fragment position
161 // so that reloading brings you to the same fragment position
169 fragmentInURL: $fragmentInURL$,
162 fragmentInURL: $fragmentInURL$,
170−$endif$
163+
171−$if(embedded)$
172 // Flags if the presentation is running in an embedded mode,
164 // Flags if the presentation is running in an embedded mode,
173 // i.e. contained within a limited portion of the screen
165 // i.e. contained within a limited portion of the screen
174 embedded: $embedded$,
166 embedded: $embedded$,
175−$endif$
167+
176−$if(help)$
177 // Flags if we should show a help overlay when the questionmark
168 // Flags if we should show a help overlay when the questionmark
178 // key is pressed
169 // key is pressed
179 help: $help$,
170 help: $help$,
180−$endif$
171+
181−$if(pause)$
182− // Flags if it should be possible to pause the presentation (blackout)
172+ // Flags if it should be possible to pause the presentation (blackout)
183− pause: $pause$,
173+ pause: $pause$,
184−$endif$
174+
185−$if(showNotes)$
186 // Flags if speaker notes should be visible to all viewers
175 // Flags if speaker notes should be visible to all viewers
187 showNotes: $showNotes$,
176 showNotes: $showNotes$,
188−$endif$
177+
189−$if(autoPlayMedia)$
190− // Global override for autoplaying embedded media (video/audio/iframe)
178+ // Global override for autoplaying embedded media (null/true/false)
191− // - null: Media will only autoplay if data-autoplay is present
192− // - true: All media will autoplay, regardless of individual setting
193− // - false: No media will autoplay, regardless of individual setting
194 autoPlayMedia: $autoPlayMedia$,
179 autoPlayMedia: $autoPlayMedia$,
195−$endif$
180+
196−$if(preloadIframes)$
197− // Global override for preloading lazy-loaded iframes
181+ // Global override for preloading lazy-loaded iframes (null/true/false)
198− // - null: Iframes with data-src AND data-preload will be loaded when within
199− // the viewDistance, iframes with only data-src will be loaded when visible
200− // - true: All iframes with data-src will be loaded when within the viewDistance
201− // - false: All iframes with data-src will be loaded only when visible
202 preloadIframes: $preloadIframes$,
182 preloadIframes: $preloadIframes$,
203−$endif$
183+
204−$if(autoSlide)$
205 // Number of milliseconds between automatically proceeding to the
184 // Number of milliseconds between automatically proceeding to the
206 // next slide, disabled when set to 0, this value can be overwritten
185 // next slide, disabled when set to 0, this value can be overwritten
207 // by using a data-autoslide attribute on your slides
186 // by using a data-autoslide attribute on your slides
208 autoSlide: $autoSlide$,
187 autoSlide: $autoSlide$,
209−$endif$
188+
210−$if(autoSlideStoppable)$
211 // Stop auto-sliding after user input
189 // Stop auto-sliding after user input
212 autoSlideStoppable: $autoSlideStoppable$,
190 autoSlideStoppable: $autoSlideStoppable$,
213−$endif$
191+
214−$if(autoSlideMethod)$
215 // Use this method for navigation when auto-sliding
192 // Use this method for navigation when auto-sliding
216 autoSlideMethod: $autoSlideMethod$,
193 autoSlideMethod: $autoSlideMethod$,
217−$endif$
194+
218−$if(defaultTiming)$
219 // Specify the average time in seconds that you think you will spend
195 // Specify the average time in seconds that you think you will spend
220 // presenting each slide. This is used to show a pacing timer in the
196 // presenting each slide. This is used to show a pacing timer in the
221 // speaker view
197 // speaker view
222 defaultTiming: $defaultTiming$,
198 defaultTiming: $defaultTiming$,
223−$endif$
199+
224−$if(totalTime)$
225− // Specify the total time in seconds that is available to
226− // present. If this is set to a nonzero value, the pacing
227− // timer will work out the time available for each slide,
228− // instead of using the defaultTiming value
229− totalTime: $totalTime$,
230−$endif$
231−$if(minimumTimePerSlide)$
232− // Specify the minimum amount of time you want to allot to
233− // each slide, if using the totalTime calculation method. If
234− // the automated time allocation causes slide pacing to fall
235− // below this threshold, then you will see an alert in the
236− // speaker notes window
237− minimumTimePerSlide: $minimumTimePerSlide$,
238−$endif$
239−$if(mouseWheel)$
240 // Enable slide navigation via mouse wheel
200 // Enable slide navigation via mouse wheel
241 mouseWheel: $mouseWheel$,
201 mouseWheel: $mouseWheel$,
242−$endif$
202+
243−$if(rollingLinks)$
203+ // The display mode that will be used to show slides
244− // Apply a 3D roll to links on hover
204+ display: '$display$',
245− rollingLinks: $rollingLinks$,
205+
246−$endif$
247−$if(hideInactiveCursor)$
248 // Hide cursor if inactive
206 // Hide cursor if inactive
249 hideInactiveCursor: $hideInactiveCursor$,
207 hideInactiveCursor: $hideInactiveCursor$,
250−$endif$
208+
251−$if(hideCursorTime)$
252 // Time before the cursor is hidden (in ms)
209 // Time before the cursor is hidden (in ms)
253 hideCursorTime: $hideCursorTime$,
210 hideCursorTime: $hideCursorTime$,
254−$endif$
211+
255−$if(hideAddressBar)$
256− // Hides the address bar on mobile devices
257− hideAddressBar: $hideAddressBar$,
258−$endif$
259−$if(previewLinks)$
260 // Opens links in an iframe preview overlay
212 // Opens links in an iframe preview overlay
261 previewLinks: $previewLinks$,
213 previewLinks: $previewLinks$,
262−$endif$
214+
263−$if(transition)$
264− // Transition style
265− transition: '$transition$', // none/fade/slide/convex/concave/zoom
215+ // Transition style (none/fade/slide/convex/concave/zoom)
266−$endif$
216+ transition: '$transition$',
267−$if(transitionSpeed)$
217+
268− // Transition speed
218+ // Transition speed (default/fast/slow)
269− transitionSpeed: '$transitionSpeed$', // default/fast/slow
219+ transitionSpeed: '$transitionSpeed$',
270−$endif$
220+
271−$if(backgroundTransition)$
272 // Transition style for full page slide backgrounds
221 // Transition style for full page slide backgrounds
273− backgroundTransition: '$backgroundTransition$', // none/fade/slide/convex/concave/zoom
222+ // (none/fade/slide/convex/concave/zoom)
274−$endif$
223+ backgroundTransition: '$backgroundTransition$',
275−$if(viewDistance)$
224+
276 // Number of slides away from the current that are visible
225 // Number of slides away from the current that are visible
277 viewDistance: $viewDistance$,
226 viewDistance: $viewDistance$,
278−$endif$
227+
279−$if(mobileViewDistance)$
280 // Number of slides away from the current that are visible on mobile
228 // Number of slides away from the current that are visible on mobile
281 // devices. It is advisable to set this to a lower number than
229 // devices. It is advisable to set this to a lower number than
282 // viewDistance in order to save resources.
230 // viewDistance in order to save resources.
283 mobileViewDistance: $mobileViewDistance$,
231 mobileViewDistance: $mobileViewDistance$,
284−$endif$
285$if(parallaxBackgroundImage)$
232$if(parallaxBackgroundImage)$
233+
286 // Parallax background image
234 // Parallax background image
287 parallaxBackgroundImage: '$parallaxBackgroundImage$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
235 parallaxBackgroundImage: '$parallaxBackgroundImage$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
288$else$
236$else$
289$if(background-image)$
237$if(background-image)$
238+
290 // Parallax background image
239 // Parallax background image
291 parallaxBackgroundImage: '$background-image$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
240 parallaxBackgroundImage: '$background-image$', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
292$endif$
241$endif$
293$endif$
242$endif$
294$if(parallaxBackgroundSize)$
243$if(parallaxBackgroundSize)$
244+
295 // Parallax background size
245 // Parallax background size
296 parallaxBackgroundSize: '$parallaxBackgroundSize$', // CSS syntax, e.g. "2100px 900px"
246 parallaxBackgroundSize: '$parallaxBackgroundSize$', // CSS syntax, e.g. "2100px 900px"
297$endif$
247$endif$
298$if(parallaxBackgroundHorizontal)$
248$if(parallaxBackgroundHorizontal)$
249+
299 // Amount to move parallax background (horizontal and vertical) on slide change
250 // Amount to move parallax background (horizontal and vertical) on slide change
300 // Number, e.g. 100
251 // Number, e.g. 100
301 parallaxBackgroundHorizontal: $parallaxBackgroundHorizontal$,
252 parallaxBackgroundHorizontal: $parallaxBackgroundHorizontal$,
302$endif$
253$endif$
303$if(parallaxBackgroundVertical)$
254$if(parallaxBackgroundVertical)$
255+
304 parallaxBackgroundVertical: $parallaxBackgroundVertical$,
256 parallaxBackgroundVertical: $parallaxBackgroundVertical$,
305$endif$
257$endif$
306$if(width)$
258$if(width)$
259+
307 // The "normal" size of the presentation, aspect ratio will be preserved
260 // The "normal" size of the presentation, aspect ratio will be preserved
308 // when the presentation is scaled to fit different resolutions. Can be
261 // when the presentation is scaled to fit different resolutions. Can be
309 // specified using percentage units.
262 // specified using percentage units.
310 width: $width$,
263 width: $width$,
311$endif$
264$endif$
312$if(height)$
265$if(height)$
266+
313 height: $height$,
267 height: $height$,
314$endif$
268$endif$
315$if(margin)$
269$if(margin)$
270+
316 // Factor of the display size that should remain empty around the content
271 // Factor of the display size that should remain empty around the content
317 margin: $margin$,
272 margin: $margin$,
318$endif$
273$endif$
319$if(minScale)$
274$if(minScale)$
275+
320 // Bounds for smallest/largest possible scale to apply to content
276 // Bounds for smallest/largest possible scale to apply to content
321 minScale: $minScale$,
277 minScale: $minScale$,
322$endif$
278$endif$
323$if(maxScale)$
279$if(maxScale)$
280+
324 maxScale: $maxScale$,
281 maxScale: $maxScale$,
325$endif$
282$endif$
326−$if(zoomKey)$
327− // Modifier key used to click-zoom to part of the slide
328− zoomKey: '$zoomKey$',
329−$endif$
330−$if(display)$
331− // The display mode that will be used to show slides
332− display: '$display$',
333−$endif$
334$if(mathjax)$
283$if(mathjax)$
284+
335 math: {
285 math: {
336 mathjax: '$mathjaxurl$',
286 mathjax: '$mathjaxurl$',
337 config: 'TeX-AMS_HTML-full',
287 config: 'TeX-AMS_HTML-full',
data/templates/default.s5+3
@@ -61,6 +61,9 @@ $endif$
61$if(author)$
61$if(author)$
62 <h3 class="author">$for(author)$$author$$sep$<br/>$endfor$</h3>
62 <h3 class="author">$for(author)$$author$$sep$<br/>$endfor$</h3>
63$endif$
63$endif$
64+$if(institute)$
65+ <h3 class="institute">$for(institute)$$institute$$sep$<br/>$endfor$</h3>
66+$endif$
64$if(date)$
67$if(date)$
65 <h4 class="date">$date$</h4>
68 <h4 class="date">$date$</h4>
66$endif$
69$endif$
data/templates/default.slideous+7
@@ -63,9 +63,16 @@ $if(title)$
63$if(subtitle)$
63$if(subtitle)$
64 <h1 class="subtitle">$subtitle$</h1>
64 <h1 class="subtitle">$subtitle$</h1>
65$endif$
65$endif$
66+$if(author)$
66 <p class="author">
67 <p class="author">
67$for(author)$$author$$sep$<br/>$endfor$
68$for(author)$$author$$sep$<br/>$endfor$
68 </p>
69 </p>
70+$endif$
71+$if(institute)$
72+ <p class="institute">
73+$for(institute)$$institute$$sep$<br/>$endfor$
74+ </p>
75+$endif$
69$if(date)$
76$if(date)$
70 <p class="date">$date$</p>
77 <p class="date">$date$</p>
71$endif$
78$endif$
data/templates/default.slidy+5
@@ -52,6 +52,11 @@ $if(author)$
52$for(author)$$author$$sep$<br/>$endfor$
52$for(author)$$author$$sep$<br/>$endfor$
53 </p>
53 </p>
54$endif$
54$endif$
55+$if(institute)$
56+ <p class="institute">
57+$for(institute)$$institute$$sep$<br/>$endfor$
58+ </p>
59+$endif$
55$if(date)$
60$if(date)$
56 <p class="date">$date$</p>
61 <p class="date">$date$</p>
57$endif$
62$endif$
data/templates/styles.html+2−1
@@ -14,7 +14,7 @@ body {
14 padding-top: $if(margin-top)$$margin-top$$else$50px$endif$;
14 padding-top: $if(margin-top)$$margin-top$$else$50px$endif$;
15 padding-bottom: $if(margin-bottom)$$margin-bottom$$else$50px$endif$;
15 padding-bottom: $if(margin-bottom)$$margin-bottom$$else$50px$endif$;
16 hyphens: auto;
16 hyphens: auto;
17− word-wrap: break-word;
17+ overflow-wrap: break-word;
18 text-rendering: optimizeLegibility;
18 text-rendering: optimizeLegibility;
19 font-kerning: normal;
19 font-kerning: normal;
20}
20}
@@ -93,6 +93,7 @@ $endif$
93pre code {
93pre code {
94 padding: 0;
94 padding: 0;
95 overflow: visible;
95 overflow: visible;
96+ overflow-wrap: normal;
96}
97}
97.sourceCode {
98.sourceCode {
98 background-color: transparent;
99 background-color: transparent;
doc/epub.md+9−2
@@ -141,8 +141,15 @@ Pandoc has an EPUB3 writer. It renders LaTeX math into MathML, which
141EPUB3 readers are supposed to support (but unfortunately few do).
141EPUB3 readers are supposed to support (but unfortunately few do).
142
142
143Of course, this isn't much help if you want EPUB2 output (`pandoc -t epub2`)
143Of course, this isn't much help if you want EPUB2 output (`pandoc -t epub2`)
144−or target readers that don't support MathML. Then you should try using the
144+or target readers that don't support MathML. Then you have two options:
145+
146+1. Use the [`--webtex`](https://pandoc.org/MANUAL.html#option--webtex) option,
145−`--webtex` option, which will use a web service to convert the TeX to an image.
147+ which will use a web service to convert the TeX to an image.
148+2. Use the [`--gladtex`](https://pandoc.org/MANUAL.html#option--gladtex) option
149+ to convert maths into SVG images on your local machine.
150+
151+Both GladTeX and WebTeX add the LaTeX source of the formula as alternative text
152+of the image, increasing accessibility for blind users.
146
153
147[KindleGen]: https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211
154[KindleGen]: https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211
148[EPUB]: https://en.wikipedia.org/wiki/EPUB
155[EPUB]: https://en.wikipedia.org/wiki/EPUB
doc/faqs.mdnew file+128
@@ -0,0 +1,128 @@
1+% FAQs
2+
3+::: faqs
4+
5+## How can I convert a whole directory of files from Markdown to RTF?
6+
7+On linux or OSX:
8+
9+ for f in *.txt; do pandoc "$f" -s -o "${f%.txt}.rtf"; done
10+
11+
12+In Windows Powershell:
13+
14+ gci -r -i *.txt |foreach{$rtf=$_.directoryname+"\"+$_.basename+".rtf";pandoc -f markdown -s $_.name -o $rtf}
15+
16+## I used pandoc to convert a document to ICML (or OPML or RTF), and when I try to open it I'm told it's invalid. What have I done wrong?
17+
18+Be sure to use the `-s` or `--standalone` flag, or you just get a
19+fragment, not a full document with the required header:
20+
21+ pandoc -s -f markdown -t icml -o my.icml my.md
22+
23+## I get a blank document when I try to convert a markdown document in Chinese to PDF.
24+
25+By default, pandoc uses pdflatex to generate the PDF, and pdflatex
26+doesn't handle Chinese characters. But you can change the default to
27+use xelatex instead. You should also make sure you're using a font
28+with Chinese glyphs. For example:
29+
30+ pandoc -o c.pdf --pdf-engine=xelatex -V mainfont='Adobe Ming Std'
31+
32+## The Windows installer does a single user install, rather than installing pandoc for all users. How can I install pandoc for all users?
33+
34+Run the following command as admin:
35+
36+ msiexec /i pandoc-VERSION.msi ALLUSERS=1
37+
38+This will put pandoc in `C:\Program Files\Pandoc`.
39+You can install Pandoc to a different directory by setting APPLICATIONFOLDER parameter,
40+for example:
41+
42+ msiexec /i pandoc-1.11.1.msi ALLUSERS=1 APPLICATIONFOLDER="C:\Pandoc"
43+
44+## How do I change the margins in PDF output?
45+
46+The option
47+
48+ -V geometry:margin=1in
49+
50+will set the margins to one inch on each side. If you don't want uniform
51+margins, you can do something like
52+
53+ -V geometry:"top=2cm, bottom=1.5cm, left=1cm, right=1cm"
54+
55+Or
56+
57+ -V geometry:"left=3cm, width=10cm"
58+
59+For more options, see the documentation for the LaTeX [geometry
60+package](https://www.ctan.org/pkg/geometry).
61+
62+## How does pandoc compare to multimarkdown?
63+
64+Here is a [wiki
65+page](https://github.com/jgm/pandoc/wiki/Pandoc-vs-Multimarkdown)
66+comparing the two.
67+
68+## When I specify an image width of 50% and convert to LaTeX, pandoc sets the height to textheight and the aspect ratio isn't preserved. How can I prevent this?
69+
70+For example, if you convert an image with `{width="50%"}`, the LaTeX produced
71+will be `\includegraphics[width=0.5\textwidth,height=\textheight]`.
72+
73+This output presupposes the following code in pandoc's default latex
74+template:
75+
76+```
77+% Scale images if necessary, so that they will not overflow the page
78+% margins by default, and it is still possible to overwrite the defaults
79+% using explicit options in \includegraphics[width, height, ...]{}
80+\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
81+```
82+
83+If you don't have this in your custom template, you should
84+add it. If we didn't set the `height` explicitly in this way,
85+the image would not be resized correctly unless it was
86+being resized to smaller than its original size.
87+
88+## Pandoc sometimes uses too much memory. How can I limit the memory used by pandoc?
89+
90+`pandoc +RTS -M30m -RTS` will limit heap memory to 30MB.
91+When converting a document requires more than this, an out of
92+memory error will be issued.
93+
94+## When using `--include-in-header` with PDF or LaTeX output, how do I reference tex declarations coming after `$header-includes$` in the default template?
95+
96+For various reasons, the `$header-includes$` are not at the very
97+end of the LaTeX preamble. This poses a problem when the code
98+you are inserting depends on declarations in the preamble coming
99+after the `$header-includes$` location. For example, you might
100+want to reference the `\author` and `\title` metadata values
101+(set at the very bottom of the preamble) and present them in
102+margins. In that case you can wrap your code in `etoolbox`'s
103+`\AtEndPreamble`. The technique is demonstrated in [this
104+gist](https://gist.github.com/JohnLukeBentley/9dda6166b9ee5c4127afd2b8cd16b70a).
105+When using `\AtEndPreamble`, keep any `makeatletter` or
106+`makeatother` outside of the `\AtEndPreamble`, as shown in the
107+example.
108+
109+## How can I convert PDFs to other formats using pandoc?
110+
111+You can't. You can try opening the PDF in Word or Google Docs
112+and saving in a format from which pandoc can convert directly.
113+
114+## Do I really need to install a 1 GB TeX installation to produce a PDF using pandoc?
115+
116+No. You can get by with a relatively small TeX installation,
117+for example, by starting with MacTeX's Basic TeX distribution
118+and using the `tlmgr` tool to install a few packages required by pandoc
119+(see https://pandoc.org/MANUAL.html#creating-a-pdf).
120+
121+Or, you can produce PDFs via HTML and `wkhtmltopdf`,
122+or via groff ms and `pdfroff`. (These don't produce as nice
123+topography as TeX, particularly when it comes to math, but they
124+may be fine for many purposes.)
125+
126+
127+:::
128+
doc/filters.md+2
@@ -394,6 +394,8 @@ automatically assign it `Just` the target format or `Nothing`.
394
394
395We compile our script:
395We compile our script:
396
396
397+ # first, make sure pandoc-types is installed:
398+ cabal install --lib pandoc-types --package-env .
397 ghc --make handleRuby
399 ghc --make handleRuby
398
400
399Then run it:
401Then run it:
doc/jats.md+9
@@ -50,6 +50,13 @@ Metadata Values
50 list must also occur as the `id` of an affiliation listed in
50 list must also occur as the `id` of an affiliation listed in
51 the top-level `affiliation` list.
51 the top-level `affiliation` list.
52
52
53+ `equal-contrib`
54+ : boolean attribute used to mark authors who contributed
55+ equally to the work. The
56+ [`equal-contrib`][attr:equal-contrib] attribute is added
57+ to the author's [`<contrib>`] element if this is set to a
58+ truthy value.
59+
53 `cor-id`
60 `cor-id`
54 : identifier linking to the contributor's correspondence
61 : identifier linking to the contributor's correspondence
55 information. The info itself must be stored in as an item in
62 information. The info itself must be stored in as an item in
@@ -355,6 +362,7 @@ Required metadata values:
355[Ringgold]: https://ringgold.com/
362[Ringgold]: https://ringgold.com/
356[attr:content-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/content-type.html
363[attr:content-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/content-type.html
357[attr:date-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/date-type.html
364[attr:date-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/date-type.html
365+[attr:equal-contrib]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/equal-contrib.html
358[attr:fn-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/fn-type.html
366[attr:fn-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/fn-type.html
359[attr:institution-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/institution-id-type.html
367[attr:institution-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/institution-id-type.html
360[attr:iso-8601-date]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/iso-8601-date.html
368[attr:iso-8601-date]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/iso-8601-date.html
@@ -398,6 +406,7 @@ Required metadata values:
398[`<addr-line>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/addr-line.html
406[`<addr-line>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/addr-line.html
399[`<aff>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/aff.html
407[`<aff>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/aff.html
400[`<city>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/city.html
408[`<city>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/city.html
409+[`<contrib>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/contrib.html
401[`<country>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/country.html
410[`<country>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/country.html
402[`<institution-id>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-id.html
411[`<institution-id>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-id.html
403[`<institution-wrap>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-wrap.html
412[`<institution-wrap>`]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-wrap.html
doc/libraries.mdnew file+63
@@ -0,0 +1,63 @@
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+[unicode-collation]
19+: Proper Unicode collation (sorting).
20+
21+[doclayout]
22+: Combinators for laying out a textual document, with support
23+ for line wrapping, tabular layout, and more.
24+
25+[doctemplates]
26+: Supports pandoc's templates.
27+
28+[commonmark], [commonmark-extensions], and [commonmark-pandoc]
29+: Efficient, standards-compliant parser for commonmark and extensions.
30+
31+[ipynb]
32+: Representation of Jupyter notebooks and conversion to and
33+ from JSON.
34+
35+[zip-archive]
36+: A pure zip file creator and extractor, used by pandoc for
37+ docx, ODT, and EPUB.
38+
39+[rfc5051]
40+: Simple unicode collation (used for citation sorting).
41+
42+[emojis]
43+: Conversion between emoji characters and aliases.
44+
45+[jira-wiki-markup]
46+: Support for parsing Jira wiki syntax.
47+
48+[skylighting]: https://hackage.haskell.org/package/skylighting
49+[skylighting-core]: https://hackage.haskell.org/package/skylighting-core
50+[citeproc]: https://hackage.haskell.org/package/citeproc
51+[texmath]: https://hackage.haskell.org/package/texmath
52+[doclayout]: https://hackage.haskell.org/package/doclayout
53+[doctemplates]: https://hackage.haskell.org/package/doctemplates
54+[commonmark]: https://hackage.haskell.org/package/commonmark
55+[commonmark-extensions]: https://hackage.haskell.org/package/commonmark-extensions
56+[commonmark-pandoc]: https://hackage.haskell.org/package/commonmark-pandoc
57+[ipynb]: https://hackage.haskell.org/package/ipynb
58+[zip-archive]: https://hackage.haskell.org/package/zip-archive
59+[rfc5051]: https://hackage.haskell.org/package/rfc5051
60+[emojis]: https://hackage.haskell.org/package/emojis
61+[jira-wiki-markup]: https://hackage.haskell.org/package/jira-wiki-markup
62+[unicode-collation]: https://hackage.haskell.org/package/unicode-collation
63+
doc/lua-filters.md+2−2
@@ -109,7 +109,7 @@ element filtering function. In other words, filter entries will
109be called for each corresponding element in the document,
109be called for each corresponding element in the document,
110getting the respective element as input.
110getting the respective element as input.
111
111
112−The return of a filter function must one of the following:
112+The return of a filter function must be one of the following:
113
113
114- nil: this means that the object should remain unchanged.
114- nil: this means that the object should remain unchanged.
115- a pandoc object: this must be of the same type as the input
115- a pandoc object: this must be of the same type as the input
@@ -3205,7 +3205,7 @@ Usage:
3205
3205
3206# Module pandoc.List
3206# Module pandoc.List
3207
3207
3208−The this module defines pandoc's list type. It comes with useful
3208+This module defines pandoc's list type. It comes with useful
3209methods and convenience functions.
3209methods and convenience functions.
3210
3210
3211## Constructor
3211## Constructor
doc/org.md+16−2
@@ -25,8 +25,7 @@ The following export keywords are supported:
25- LANGUAGE: document language; included as plain-text metadata
25- LANGUAGE: document language; included as plain-text metadata
26 field `lang`. The value should be a [BCP47 language tag].
26 field `lang`. The value should be a [BCP47 language tag].
27
27
28−- SELECT_TAGS: tags which select a tree for export. Currently
28+- SELECT_TAGS: tags which select a tree for export.
29− *unsupported*.
30
29
31- EXCLUDE\_TAGS: tags which prevent a subtree from being
30- EXCLUDE\_TAGS: tags which prevent a subtree from being
32 exported. Fully supported.
31 exported. Fully supported.
@@ -37,6 +36,21 @@ The following export keywords are supported:
37 defaults to stdout unless a target has to be given as a command
36 defaults to stdout unless a target has to be given as a command
38 line option.
37 line option.
39
38
39+::: {.alert .alert-info}
40+Pandoc tries to be compatible with org-mode when exporting an org document. If
41+you find some behavior confusing, please do refer to org-mode
42+[Export-Settings](https://orgmode.org/manual/Export-Settings.html)
43+documentation. For example, a common confusion
44+([#3214](https://github.com/jgm/pandoc/issues/3214 "Problem with headers lower
45+then 3 in org-mode reader"), [#5169](https://github.com/jgm/pandoc/issues/5169
46+"org mode headings past level three converted to numbered outline list"),
47+[#6145](https://github.com/jgm/pandoc/issues/6145 "Headers 4 levels deep render
48+differently"), [#7236](https://github.com/jgm/pandoc/issues/7236 "In Org mode,
49+Header with level > 3 are not recognized as headers")) is treatment of headers
50+with level > 3 differently because org-mode sets `org-export-headline-levels`
51+(configurable with `#+OPTIONS: H:3`) to 3 by default.
52+:::
53+
40[BCP47 language tag]: https://tools.ietf.org/html/bcp47
54[BCP47 language tag]: https://tools.ietf.org/html/bcp47
41
55
42Format-specific options
56Format-specific options
doc/using-the-pandoc-api.md+25−12
@@ -14,7 +14,7 @@ and types is available at
14Pandoc is structured as a set of *readers*, which translate
14Pandoc is structured as a set of *readers*, which translate
15various input formats into an abstract syntax tree (the
15various input formats into an abstract syntax tree (the
16Pandoc AST) representing a structured document, and a set of
16Pandoc AST) representing a structured document, and a set of
17−*writers*, which render this AST into various input formats.
17+*writers*, which render this AST into various output formats.
18Pictorially:
18Pictorially:
19
19
20```
20```
@@ -90,8 +90,14 @@ Some notes:
90Let's look at the types of `readMarkdown` and `writeRST`:
90Let's look at the types of `readMarkdown` and `writeRST`:
91
91
92```haskell
92```haskell
93−readMarkdown :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
93+readMarkdown :: (PandocMonad m, ToSources a)
94+ => ReaderOptions
95+ -> a
96+ -> m Pandoc
94−writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text
97+writeRST :: PandocMonad m
98+ => WriterOptions
99+ -> Pandoc
100+ -> m Text
95```
101```
96
102
97The `PandocMonad m =>` part is a typeclass constraint.
103The `PandocMonad m =>` part is a typeclass constraint.
@@ -133,7 +139,7 @@ report :: PandocMonad m => LogMessage -> m ()
133-- | Fetch an image or other item from the local filesystem or the net.
139-- | Fetch an image or other item from the local filesystem or the net.
134-- Returns raw content and maybe mime type.
140-- Returns raw content and maybe mime type.
135fetchItem :: PandocMonad m
141fetchItem :: PandocMonad m
136− => String
142+ => Text
137 -> m (B.ByteString, Maybe MimeType)
143 -> m (B.ByteString, Maybe MimeType)
138
144
139-- Set the resource path searched by 'fetchItem'.
145-- Set the resource path searched by 'fetchItem'.
@@ -155,6 +161,13 @@ Note that `PandocIO` is an instance of `MonadIO`, so you can
155use `liftIO` to perform arbitrary IO operations inside a pandoc
161use `liftIO` to perform arbitrary IO operations inside a pandoc
156conversion chain.
162conversion chain.
157
163
164+`readMarkdown` is polymorphic in its second argument, which
165+can be any type that is an instance of the `ToSources`
166+typeclass. You can use `Text`, as in the example above.
167+But you can also use `[(FilePath, Text)]`, if the input comes
168+from multiple files and you want to track source positions
169+accurately.
170+
158# Options
171# Options
159
172
160The first argument of each reader or writer is for
173The first argument of each reader or writer is for
@@ -200,8 +213,8 @@ of the Monoid typeclass and can easily be concatenated:
200import Text.Pandoc.Builder
213import Text.Pandoc.Builder
201
214
202mydoc :: Pandoc
215mydoc :: Pandoc
203−mydoc = doc $ header 1 (text "Hello!")
216+mydoc = doc $ header 1 (text (T.pack "Hello!"))
204− <> para (emph (text "hello world") <> text ".")
217+ <> para (emph (text (T.pack "hello world")) <> text (T.pack "."))
205
218
206main :: IO ()
219main :: IO ()
207main = print mydoc
220main = print mydoc
@@ -248,16 +261,16 @@ import qualified Data.Text as T
248import Data.List (intersperse)
261import Data.List (intersperse)
249
262
250data Station = Station{
263data Station = Station{
251− address :: String
264+ address :: T.Text
252− , name :: String
265+ , name :: T.Text
253− , cardsAccepted :: [String]
266+ , cardsAccepted :: [T.Text]
254 } deriving Show
267 } deriving Show
255
268
256instance FromJSON Station where
269instance FromJSON Station where
257 parseJSON (Object v) = Station <$>
270 parseJSON (Object v) = Station <$>
258 v .: "street_address" <*>
271 v .: "street_address" <*>
259 v .: "station_name" <*>
272 v .: "station_name" <*>
260− (words <$> (v .:? "cards_accepted" .!= ""))
273+ (T.words <$> (v .:? "cards_accepted" .!= ""))
261 parseJSON _ = mzero
274 parseJSON _ = mzero
262
275
263createLetter :: [Station] -> Pandoc
276createLetter :: [Station] -> Pandoc
@@ -315,7 +328,7 @@ users to override the system defaults. If you want to disable
315this behavior, use `setUserDataDir Nothing`.
328this behavior, use `setUserDataDir Nothing`.
316
329
317To render a template, use `renderTemplate'`, which takes two
330To render a template, use `renderTemplate'`, which takes two
318−arguments, a template (String) and a context (any instance
331+arguments, a template (Text) and a context (any instance
319of ToJSON). If you want to create a context from the metadata
332of ToJSON). If you want to create a context from the metadata
320part of a Pandoc document, use `metaToJSON'` from
333part of a Pandoc document, use `metaToJSON'` from
321[Text.Pandoc.Writers.Shared]. If you also want to incorporate
334[Text.Pandoc.Writers.Shared]. If you also want to incorporate
@@ -413,7 +426,7 @@ concatenated together. Here's an example that returns a
413list of the URLs linked to in a document:
426list of the URLs linked to in a document:
414
427
415```haskell
428```haskell
416−listURLs :: Pandoc -> [String]
429+listURLs :: Pandoc -> [Text]
417listURLs = query urls
430listURLs = query urls
418 where urls (Link _ _ (src, _)) = [src]
431 where urls (Link _ _ (src, _)) = [src]
419 urls _ = []
432 urls _ = []
linux/make_artifacts.sh+19−7
@@ -2,13 +2,23 @@ set -e
2
2
3MACHINE=$(uname -m)
3MACHINE=$(uname -m)
4case "$MACHINE" in
4case "$MACHINE" in
5− x86_64) ARCHITECTURE=amd64;;
5+ x86_64) ARCHITECTURE=amd64;;
6− i686) ARCHITECTURE=i386;;
6+ i686) ARCHITECTURE=i386;;
7− i386) ARCHITECTURE=i386;;
7+ i386) ARCHITECTURE=i386;;
8+ aarch64) ARCHITECTURE=arm64;;
9+ *) ARCHITECTURE=unknown;;
8esac
10esac
9
11
10ARTIFACTS="${ARTIFACTS:-/artifacts}"
12ARTIFACTS="${ARTIFACTS:-/artifacts}"
11
13
14+# This is our sentinel that tells us when we're done.
15+rm -f $ARTIFACTS/DONE
16+
17+clean_up() {
18+ echo "All done!" > "$ARTIFACTS/DONE"
19+}
20+trap clean_up EXIT
21+
12# build binaries
22# build binaries
13
23
14cabal --version
24cabal --version
@@ -16,9 +26,9 @@ ghc --version
16
26
17cabal v2-update
27cabal v2-update
18cabal v2-clean
28cabal v2-clean
19−cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-split-sections -optc-Os -optl=-pthread' pandoc
29+cabal v2-configure --enable-tests -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc
20−cabal v2-build
30+cabal v2-build -j4
21−cabal v2-test -j1
31+cabal v2-test -j4
22for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f /artifacts/; done
32for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f /artifacts/; done
23
33
24# make deb
34# make deb
@@ -68,5 +78,7 @@ mv pandoc $TARGET/bin
68strip $TARGET/bin/pandoc
78strip $TARGET/bin/pandoc
69gzip -9 $TARGET/share/man/man1/pandoc.1
79gzip -9 $TARGET/share/man/man1/pandoc.1
70
80
71−tar cvzf $TARGET-linux-amd64.tar.gz $TARGET
81+tar cvzf $TARGET-linux-$ARCHITECTURE.tar.gz $TARGET
72rm -r $TARGET
82rm -r $TARGET
83+
84+exit 0
man/pandoc.1+286−61
@@ -1,7 +1,7 @@
1'\" t
1'\" t
2−.\" Automatically generated by Pandoc 2.11.4
2+.\" Automatically generated by Pandoc 2.14.0.1
3.\"
3.\"
4−.TH "Pandoc User\[cq]s Guide" "" "January 21, 2021" "pandoc 2.11.4" ""
4+.TH "Pandoc User\[cq]s Guide" "" "June 20, 2021" "pandoc 2.14.0.3" ""
5.hy
5.hy
6.SH NAME
6.SH NAME
7pandoc - general markup converter
7pandoc - general markup converter
@@ -172,13 +172,12 @@ You can then test it with \f[C]pdflatex test.tex\f[R].
172When using LaTeX, the following packages need to be available (they are
172When using LaTeX, the following packages need to be available (they are
173included with all recent versions of TeX Live): \f[C]amsfonts\f[R],
173included with all recent versions of TeX Live): \f[C]amsfonts\f[R],
174\f[C]amsmath\f[R], \f[C]lm\f[R], \f[C]unicode-math\f[R],
174\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
175+\f[C]iftex\f[R], \f[C]listings\f[R] (if the \f[C]--listings\f[R] option
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
176+is used), \f[C]fancyvrb\f[R], \f[C]longtable\f[R], \f[C]booktabs\f[R],
178−document contains images), \f[C]hyperref\f[R], \f[C]xcolor\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−\f[C]ulem\f[R], \f[C]geometry\f[R] (with the \f[C]geometry\f[R] variable
179+(with the \f[C]geometry\f[R] variable set), \f[C]setspace\f[R] (with
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]).
180+\f[C]linestretch\f[R]), and \f[C]babel\f[R] (with \f[C]lang\f[R]).
182The use of \f[C]xelatex\f[R] or \f[C]lualatex\f[R] as the PDF engine
181The use of \f[C]xelatex\f[R] or \f[C]lualatex\f[R] as the PDF engine
183requires \f[C]fontspec\f[R].
182requires \f[C]fontspec\f[R].
184\f[C]lualatex\f[R] uses \f[C]selnolig\f[R].
183\f[C]lualatex\f[R] uses \f[C]selnolig\f[R].
@@ -458,16 +457,15 @@ On *nix and macOS systems this will be the \f[C]pandoc\f[R] subdirectory
458of the XDG data directory (by default, \f[C]$HOME/.local/share\f[R],
457of the XDG data directory (by default, \f[C]$HOME/.local/share\f[R],
459overridable by setting the \f[C]XDG_DATA_HOME\f[R] environment
458overridable by setting the \f[C]XDG_DATA_HOME\f[R] environment
460variable).
459variable).
461−If that directory does not exist, \f[C]$HOME/.pandoc\f[R] will be used
460+If that directory does not exist and \f[C]$HOME/.pandoc\f[R] exists, it
462−(for backwards compatibility).
461+will be used (for backwards compatibility).
463−In Windows the default user data directory is
462+On Windows the default user data directory is
464\f[C]C:\[rs]Users\[rs]USERNAME\[rs]AppData\[rs]Roaming\[rs]pandoc\f[R].
463\f[C]C:\[rs]Users\[rs]USERNAME\[rs]AppData\[rs]Roaming\[rs]pandoc\f[R].
465You can find the default user data directory on your system by looking
464You can find the default user data directory on your system by looking
466at the output of \f[C]pandoc --version\f[R].
465at the output of \f[C]pandoc --version\f[R].
466+Data files placed in this directory (for example,
467−A \f[C]reference.odt\f[R], \f[C]reference.docx\f[R], \f[C]epub.css\f[R],
467+\f[C]reference.odt\f[R], \f[C]reference.docx\f[R], \f[C]epub.css\f[R],
468−\f[C]templates\f[R], \f[C]slidy\f[R], \f[C]slideous\f[R], or
469−\f[C]s5\f[R] directory placed in this directory will override
470−pandoc\[cq]s normal defaults.
468+\f[C]templates\f[R]) will override pandoc\[cq]s normal defaults.
471.TP
469.TP
472\f[B]\f[CB]-d\f[B]\f[R] \f[I]FILE\f[R], \f[B]\f[CB]--defaults=\f[B]\f[R]\f[I]FILE\f[R]
470\f[B]\f[CB]-d\f[B]\f[R] \f[I]FILE\f[R], \f[B]\f[CB]--defaults=\f[B]\f[R]\f[I]FILE\f[R]
473Specify a set of default option settings.
471Specify a set of default option settings.
@@ -718,11 +716,11 @@ Extract images and other media contained in or linked from the source
718document to the path \f[I]DIR\f[R], creating it if necessary, and adjust
716document to the path \f[I]DIR\f[R], creating it if necessary, and adjust
719the images references in the document so they point to the extracted
717the images references in the document so they point to the extracted
720files.
718files.
719+Media are downloaded, read from the file system, or extracted from a
721−If the source format is a binary container (docx, epub, or odt), the
720+binary container (e.g.\ docx), as needed.
722−media is extracted from the container and the original filenames are
721+The original file paths are used if they are relative paths not
723−used.
722+containing \f[C]..\f[R].
724−Otherwise the media is read from the file system or downloaded, and new
725−filenames are constructed based on SHA1 hashes of the contents.
723+Otherwise filenames are constructed from the SHA1 hash of the contents.
726.TP
724.TP
727\f[B]\f[CB]--abbreviations=\f[B]\f[R]\f[I]FILE\f[R]
725\f[B]\f[CB]--abbreviations=\f[B]\f[R]\f[I]FILE\f[R]
728Specifies a custom abbreviations file, with abbreviations one to a line.
726Specifies a custom abbreviations file, with abbreviations one to a line.
@@ -732,9 +730,9 @@ system default.
732To see the system default, use
730To see the system default, use
733\f[C]pandoc --print-default-data-file=abbreviations\f[R].
731\f[C]pandoc --print-default-data-file=abbreviations\f[R].
734The only use pandoc makes of this list is in the Markdown reader.
732The only use pandoc makes of this list is in the Markdown reader.
735−Strings ending in a period that are found in this list will be followed
733+Strings found in this list will be followed by a nonbreaking space, and
736−by a nonbreaking space, so that the period will not produce
737−sentence-ending space in formats like LaTeX.
734+the period will not produce sentence-ending space in formats like LaTeX.
735+The strings may not contain spaces.
738.SS General writer options
736.SS General writer options
739.TP
737.TP
740\f[B]\f[CB]-s\f[B]\f[R], \f[B]\f[CB]--standalone\f[B]\f[R]
738\f[B]\f[CB]-s\f[B]\f[R], \f[B]\f[CB]--standalone\f[B]\f[R]
@@ -925,6 +923,11 @@ Note that, if \f[C]--resource-path\f[R] is specified, the working
925directory must be explicitly listed or it will not be searched.
923directory must be explicitly listed or it will not be searched.
926For example: \f[C]--resource-path=.:test\f[R] will search the working
924For example: \f[C]--resource-path=.:test\f[R] will search the working
927directory and the \f[C]test\f[R] subdirectory, in that order.
925directory and the \f[C]test\f[R] subdirectory, in that order.
926+This option can be used repeatedly.
927+Search path components that come later on the command line will be
928+searched before those that come earlier, so
929+\f[C]--resource-path foo:bar --resource-path baz:bim\f[R] is equivalent
930+to \f[C]--resource-path baz:bim:foo:bar\f[R].
928.TP
931.TP
929\f[B]\f[CB]--request-header=\f[B]\f[R]\f[I]NAME\f[R]\f[B]\f[CB]:\f[B]\f[R]\f[I]VAL\f[R]
932\f[B]\f[CB]--request-header=\f[B]\f[R]\f[I]NAME\f[R]\f[B]\f[CB]:\f[B]\f[R]\f[I]VAL\f[R]
930Set the request header \f[I]NAME\f[R] to the value \f[I]VAL\f[R] when
933Set the request header \f[I]NAME\f[R] to the value \f[I]VAL\f[R] when
@@ -1501,16 +1504,16 @@ inserted.
1501.TP
1504.TP
1502\f[B]\f[CB]--gladtex\f[B]\f[R]
1505\f[B]\f[CB]--gladtex\f[B]\f[R]
1503Enclose TeX math in \f[C]<eq>\f[R] tags in HTML output.
1506Enclose TeX math in \f[C]<eq>\f[R] tags in HTML output.
1504−The resulting HTML can then be processed by GladTeX to produce images of
1507+The resulting HTML can then be processed by GladTeX to produce SVG
1505−the typeset formulas and an HTML file with links to these images.
1508+images of the typeset formulas and an HTML file with these images
1506−So, the procedure is:
1509+embedded.
1507.RS
1510.RS
1508.IP
1511.IP
1509.nf
1512.nf
1510\f[C]
1513\f[C]
1511pandoc -s --gladtex input.md -o myfile.htex
1514pandoc -s --gladtex input.md -o myfile.htex
1512−gladtex -d myfile-images myfile.htex
1515+gladtex -d image_dir myfile.htex
1513−# produces myfile.html and images in myfile-images
1516+# produces myfile.html and images in image_dir
1514\f[R]
1517\f[R]
1515.fi
1518.fi
1516.RE
1519.RE
@@ -1614,6 +1617,11 @@ T}@T{
1614PandocPDFError
1617PandocPDFError
1615T}
1618T}
1616T{
1619T{
1620+44
1621+T}@T{
1622+PandocXMLError
1623+T}
1624+T{
161747
162547
1618T}@T{
1626T}@T{
1619PandocPDFProgramNotFoundError
1627PandocPDFProgramNotFoundError
@@ -1674,6 +1682,11 @@ T}@T{
1674PandocIpynbDecodingError
1682PandocIpynbDecodingError
1675T}
1683T}
1676T{
1684T{
1685+94
1686+T}@T{
1687+PandocUnsupportedCharsetError
1688+T}
1689+T{
167797
169097
1678T}@T{
1691T}@T{
1679PandocCouldNotFindDataFileError
1692PandocCouldNotFindDataFileError
@@ -1773,8 +1786,11 @@ data-dir:
1773verbosity: INFO
1786verbosity: INFO
1774log-file: log.json
1787log-file: log.json
1775
1788
1776−# citeproc, natbib, or biblatex
1789+# citeproc, natbib, or biblatex. This only affects LaTeX
1790+# output. If you want to use citeproc to format citations,
1791+# you should also set \[aq]citeproc: true\[aq] (see above).
1777cite-method: citeproc
1792cite-method: citeproc
1793+
1778# part, chapter, section, or default:
1794# part, chapter, section, or default:
1779top-level-division: chapter
1795top-level-division: chapter
1780abbreviations:
1796abbreviations:
@@ -1875,6 +1891,38 @@ verbosity: INFO
1875\f[R]
1891\f[R]
1876.fi
1892.fi
1877.PP
1893.PP
1894+In fields that expect a file path (or list of file paths), the following
1895+syntax may be used to interpolate environment variables:
1896+.IP
1897+.nf
1898+\f[C]
1899+csl: ${HOME}/mycsldir/special.csl
1900+\f[R]
1901+.fi
1902+.PP
1903+\f[C]${USERDATA}\f[R] may also be used; this will always resolve to the
1904+user data directory that is current when the defaults file is parsed,
1905+regardless of the setting of the environment variable
1906+\f[C]USERDATA\f[R].
1907+.PP
1908+\f[C]${.}\f[R] will resolve to the directory containing the default file
1909+itself.
1910+This allows you to refer to resources contained in that directory:
1911+.IP
1912+.nf
1913+\f[C]
1914+epub-cover-image: ${.}/cover.jpg
1915+epub-metadata: ${.}/meta.xml
1916+resource-path:
1917+- . # the working directory from which pandoc is run
1918+- ${.}/images # the images subdirectory of the directory
1919+ # containing this defaults file
1920+\f[R]
1921+.fi
1922+.PP
1923+This environment variable interpolation syntax \f[I]only\f[R] works in
1924+fields that expect file paths.
1925+.PP
1878Default files can be placed in the \f[C]defaults\f[R] subdirectory of
1926Default files can be placed in the \f[C]defaults\f[R] subdirectory of
1879the user data directory and used from any directory.
1927the user data directory and used from any directory.
1880For example, one could create a file specifying defaults for writing
1928For example, one could create a file specifying defaults for writing
@@ -2134,9 +2182,9 @@ ${ styles.html() }
2134\f[R]
2182\f[R]
2135.fi
2183.fi
2136.PP
2184.PP
2137−(If a partial is not found in the directory of the template, it will
2185+(If a partial is not found in the directory of the template and the
2186+template path is given as a relative path, it will also be sought in the
2138−also be sought in the \f[C]templates\f[R] subdirectory of the user data
2187+\f[C]templates\f[R] subdirectory of the user data directory.)
2139−directory.)
2140.PP
2188.PP
2141Partials may optionally be applied to variables using a colon:
2189Partials may optionally be applied to variables using a colon:
2142.IP
2190.IP
@@ -2543,10 +2591,9 @@ YAML metadata or with \f[C]-M classoption=fleqn\f[R].
2543.SS Variables for HTML slides
2591.SS Variables for HTML slides
2544.PP
2592.PP
2545These affect HTML output when [producing slide shows with pandoc].
2593These affect HTML output when [producing slide shows with pandoc].
2546−.PP
2594+.TP
2547−All reveal.js configuration options are available as variables.
2595+\f[B]\f[CB]institute\f[B]\f[R]
2548−To turn off boolean flags that default to true in reveal.js, use
2596+author affiliations: can be a list when there are multiple authors
2549−\f[C]0\f[R].
2550.TP
2597.TP
2551\f[B]\f[CB]revealjs-url\f[B]\f[R]
2598\f[B]\f[CB]revealjs-url\f[B]\f[R]
2552base URL for reveal.js documents (defaults to
2599base URL for reveal.js documents (defaults to
@@ -2565,6 +2612,10 @@ base URL for Slideous documents (defaults to \f[C]slideous\f[R])
2565\f[B]\f[CB]title-slide-attributes\f[B]\f[R]
2612\f[B]\f[CB]title-slide-attributes\f[B]\f[R]
2566additional attributes for the title slide of reveal.js slide shows.
2613additional attributes for the title slide of reveal.js slide shows.
2567See background in reveal.js and beamer for an example.
2614See background in reveal.js and beamer for an example.
2615+.PP
2616+All reveal.js configuration options are available as variables.
2617+To turn off boolean flags that default to true in reveal.js, use
2618+\f[C]0\f[R].
2568.SS Variables for Beamer slides
2619.SS Variables for Beamer slides
2569.PP
2620.PP
2570These variables change the appearance of PDF slides using
2621These variables change the appearance of PDF slides using
@@ -3928,6 +3979,9 @@ begin with a space.
3928\f[R]
3979\f[R]
3929.fi
3980.fi
3930.PP
3981.PP
3982+Inline formatting (such as emphasis) is allowed in the content, but not
3983+block-level formatting (such as block quotes or lists).
3984+.PP
3931This syntax is borrowed from reStructuredText.
3985This syntax is borrowed from reStructuredText.
3932.SS Lists
3986.SS Lists
3933.SS Bullet lists
3987.SS Bullet lists
@@ -4221,7 +4275,7 @@ Term 2
4221Note that space between items in a definition list is required.
4275Note that space between items in a definition list is required.
4222(A variant that loosens this requirement, but disallows \[lq]lazy\[rq]
4276(A variant that loosens this requirement, but disallows \[lq]lazy\[rq]
4223hard wrapping, can be activated with \f[C]compact_definition_lists\f[R]:
4277hard wrapping, can be activated with \f[C]compact_definition_lists\f[R]:
4224−see Non-pandoc extensions, below.)
4278+see Non-default extensions, below.)
4225.SS Numbered example lists
4279.SS Numbered example lists
4226.SS Extension: \f[C]example_lists\f[R]
4280.SS Extension: \f[C]example_lists\f[R]
4227.PP
4281.PP
@@ -4742,7 +4796,9 @@ All of the metadata will appear in a single block at the beginning of
4742the document.
4796the document.
4743.PP
4797.PP
4744Note that YAML escaping rules must be followed.
4798Note that YAML escaping rules must be followed.
4745−Thus, for example, if a title contains a colon, it must be quoted.
4799+Thus, for example, if a title contains a colon, it must be quoted, and
4800+if it contains a backslash escape, then it must be ensured that it is
4801+not treated as a YAML escape sequence.
4746The pipe character (\f[C]|\f[R]) can be used to begin an indented block
4802The pipe character (\f[C]|\f[R]) can be used to begin an indented block
4747that will be interpreted literally, without need for escaping.
4803that will be interpreted literally, without need for escaping.
4748This form is necessary when the field contains blank lines or
4804This form is necessary when the field contains blank lines or
@@ -4764,6 +4820,13 @@ abstract: |
4764\f[R]
4820\f[R]
4765.fi
4821.fi
4766.PP
4822.PP
4823+The literal block after the \f[C]|\f[R] must be indented relative to the
4824+line containing the \f[C]|\f[R].
4825+If it is not, the YAML will be invalid and pandoc will not interpret it
4826+as metadata.
4827+For an overview of the complex rules governing YAML, see the Wikipedia
4828+entry on YAML syntax.
4829+.PP
4767Template variables will be set automatically from the metadata.
4830Template variables will be set automatically from the metadata.
4768Thus, for example, in writing HTML, the variable \f[C]abstract\f[R] will
4831Thus, for example, in writing HTML, the variable \f[C]abstract\f[R] will
4769be set to the HTML equivalent of the Markdown in the \f[C]abstract\f[R]
4832be set to the HTML equivalent of the Markdown in the \f[C]abstract\f[R]
@@ -4829,6 +4892,21 @@ header-includes:
4829 \[ga]\[ga]\[ga]
4892 \[ga]\[ga]\[ga]
4830\f[R]
4893\f[R]
4831.fi
4894.fi
4895+.PP
4896+Note: the \f[C]yaml_metadata_block\f[R] extension works with
4897+\f[C]commonmark\f[R] as well as \f[C]markdown\f[R] (and it is enabled by
4898+default in \f[C]gfm\f[R] and \f[C]commonmark_x\f[R]).
4899+However, in these formats the following restrictions apply:
4900+.IP \[bu] 2
4901+The YAML metadata block must occur at the beginning of the document (and
4902+there can be only one).
4903+If multiple files are given as arguments to pandoc, only the first can
4904+be a YAML metadata block.
4905+.IP \[bu] 2
4906+The leaf nodes of the YAML structure are parsed in isolation from each
4907+other and from the rest of the document.
4908+So, for example, you can\[cq]t use a reference link in these contexts if
4909+the link definition is somewhere else in the document.
4832.SS Backslash escapes
4910.SS Backslash escapes
4833.SS Extension: \f[C]all_symbols_escapable\f[R]
4911.SS Extension: \f[C]all_symbols_escapable\f[R]
4834.PP
4912.PP
@@ -5048,7 +5126,9 @@ delimiters.
5048.PP
5126.PP
5049For display math, use \f[C]$$\f[R] delimiters.
5127For display math, use \f[C]$$\f[R] delimiters.
5050(In this case, the delimiters may be separated from the formula by
5128(In this case, the delimiters may be separated from the formula by
5051−whitespace.)
5129+whitespace.
5130+However, there can be no blank lines betwen the opening and closing
5131+\f[C]$$\f[R] delimiters.)
5052.PP
5132.PP
5053TeX math will be printed in all output formats.
5133TeX math will be printed in all output formats.
5054How it is rendered depends on the output format:
5134How it is rendered depends on the output format:
@@ -5707,27 +5787,70 @@ Inline and regular footnotes may be mixed freely.
5707.SS Citation syntax
5787.SS Citation syntax
5708.SS Extension: \f[C]citations\f[R]
5788.SS Extension: \f[C]citations\f[R]
5709.PP
5789.PP
5710−Markdown citations go inside square brackets and are separated by
5790+To cite a bibliographic item with an identifier foo, use the syntax
5711−semicolons.
5791+\f[C]\[at]foo\f[R].
5712−Each citation must have a key, composed of `\[at]' + the citation
5792+Normal citations should be included in square brackets, with semicolons
5713−identifier from the database, and may optionally have a prefix, a
5793+separating distinct items:
5714−locator, and a suffix.
5715−The citation key must begin with a letter, digit, or \f[C]_\f[R], and
5716−may contain alphanumerics, \f[C]_\f[R], and internal punctuation
5717−characters (\f[C]:.#$%&-+?<>\[ti]/\f[R]).
5718−Here are some examples:
5719.IP
5794.IP
5720.nf
5795.nf
5721\f[C]
5796\f[C]
5722−Blah blah [see \[at]doe99, pp. 33-35; also \[at]smith04, chap. 1].
5797+Blah blah [\[at]doe99; \[at]smith2000; \[at]smith2004].
5723−
5798+\f[R]
5724−Blah blah [\[at]doe99, pp. 33-35, 38-39 and *passim*].
5799+.fi
5800+.PP
5801+How this is rendered depends on the citation style.
5802+In an author-date style, it might render as
5803+.IP
5804+.nf
5805+\f[C]
5806+Blah blah (Doe 1999, Smith 2000, 2004).
5807+\f[R]
5808+.fi
5809+.PP
5810+In a footnote style, it might render as
5811+.IP
5812+.nf
5813+\f[C]
5814+Blah blah.[\[ha]1]
5725
5815
5726−Blah blah [\[at]smith04; \[at]doe99].
5816+[\[ha]1]: John Doe, \[dq]Frogs,\[dq] *Journal of Amphibians* 44 (1999);
5817+Susan Smith, \[dq]Flies,\[dq] *Journal of Insects* (2000);
5818+Susan Smith, \[dq]Bees,\[dq] *Journal of Insects* (2004).
5727\f[R]
5819\f[R]
5728.fi
5820.fi
5729.PP
5821.PP
5822+See the CSL user documentation for more information about CSL styles and
5823+how they affect rendering.
5824+.PP
5825+Unless a citation key start with a letter, digit, or \f[C]_\f[R], and
5826+contains only alphanumerics and internal punctuation characters
5827+(\f[C]:.#$%&-+?<>\[ti]/\f[R]), it must be surrounded by curly braces,
5828+which are not considered part of the key.
5829+In \f[C]\[at]Foo_bar.baz.\f[R], the key is \f[C]Foo_bar.baz\f[R].
5830+The final period is not \f[I]internal\f[R] punctuation, so it is not
5831+included in the key.
5832+In \f[C]\[at]{Foo_bar.baz.}\f[R], the key is \f[C]Foo_bar.baz.\f[R],
5833+including the final period.
5834+The curly braces are recommended if you use URLs as keys:
5835+\f[C][\[at]{https://example.com/bib?name=foobar&date=2000}, p. 33]\f[R].
5836+.PP
5837+Citation items may optionally include a prefix, a locator, and a suffix.
5838+In
5839+.IP
5840+.nf
5841+\f[C]
5842+Blah blah [see \[at]doe99, pp. 33-35 and *passim*; \[at]smith04, chap. 1].
5843+\f[R]
5844+.fi
5845+.PP
5846+The first item (\f[C]doe99\f[R]) has prefix \f[C]see\f[R], locator
5847+\f[C]pp. 33-35\f[R], and suffix \f[C]and *passim*\f[R].
5848+The second item (\f[C]smith04\f[R]) has locator \f[C]chap. 1\f[R] and no
5849+prefix or suffix.
5850+.PP
5851+Pandoc uses some heuristics to separate the locator from the rest of the
5852+subject.
5730−\f[C]pandoc\f[R] detects locator terms in the CSL locale files.
5853+It is sensitive to the locator terms defined in the CSL locale files.
5731Either abbreviated or unabbreviated forms are accepted.
5854Either abbreviated or unabbreviated forms are accepted.
5732In the \f[C]en-US\f[R] locale, locator terms can be written in either
5855In the \f[C]en-US\f[R] locale, locator terms can be written in either
5733singular or plural forms, as \f[C]book\f[R],
5856singular or plural forms, as \f[C]book\f[R],
@@ -5749,14 +5872,15 @@ singular or plural forms, as \f[C]book\f[R],
5749\f[C]\[sc]\f[R]/\f[C]\[sc]\[sc]\f[R].
5872\f[C]\[sc]\f[R]/\f[C]\[sc]\[sc]\f[R].
5750If no locator term is used, \[lq]page\[rq] is assumed.
5873If no locator term is used, \[lq]page\[rq] is assumed.
5751.PP
5874.PP
5752−\f[C]pandoc\f[R] will use heuristics to distinguish the locator from the
5875+In complex cases, you can force something to be treated as a locator by
5753−suffix.
5876+enclosing it in curly braces or prevent parsing the suffix as locator by
5754−In complex cases, the locator can be enclosed in curly braces:
5877+prepending curly braces:
5755.IP
5878.IP
5756.nf
5879.nf
5757\f[C]
5880\f[C]
5758[\[at]smith{ii, A, D-Z}, with a suffix]
5881[\[at]smith{ii, A, D-Z}, with a suffix]
5759[\[at]smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
5882[\[at]smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
5883+[\[at]smith{}, 99 years later]
5760\f[R]
5884\f[R]
5761.fi
5885.fi
5762.PP
5886.PP
@@ -5770,7 +5894,8 @@ Smith says blah [-\[at]smith04].
5770\f[R]
5894\f[R]
5771.fi
5895.fi
5772.PP
5896.PP
5773−You can also write an in-text citation, as follows:
5897+You can also write an author-in-text citation, by omitting the square
5898+brackets:
5774.IP
5899.IP
5775.nf
5900.nf
5776\f[C]
5901\f[C]
@@ -5779,13 +5904,64 @@ You can also write an in-text citation, as follows:
5779\[at]smith04 [p. 33] says blah.
5904\[at]smith04 [p. 33] says blah.
5780\f[R]
5905\f[R]
5781.fi
5906.fi
5907+.PP
5908+This will cause the author\[cq]s name to be rendered, followed by the
5909+bibliographical details.
5910+Use this form when you want to make the citation the subject of a
5911+sentence.
5912+.PP
5913+When you are using a note style, it is usually better to let citeproc
5914+create the footnotes from citations rather than writing an explicit
5915+note.
5916+If you do write an explicit note that contains a citation, note that
5917+normal citations will be put in parentheses, while author-in-text
5918+citations will not.
5919+For this reason, it is sometimes preferable to use the author-in-text
5920+style inside notes when using a note style.
5782−.SS Non-pandoc extensions
5921+.SS Non-default extensions
5783.PP
5922.PP
5784The following Markdown syntax extensions are not enabled by default in
5923The following Markdown syntax extensions are not enabled by default in
5785pandoc, but may be enabled by adding \f[C]+EXTENSION\f[R] to the format
5924pandoc, but may be enabled by adding \f[C]+EXTENSION\f[R] to the format
5786name, where \f[C]EXTENSION\f[R] is the name of the extension.
5925name, where \f[C]EXTENSION\f[R] is the name of the extension.
5787Thus, for example, \f[C]markdown+hard_line_breaks\f[R] is Markdown with
5926Thus, for example, \f[C]markdown+hard_line_breaks\f[R] is Markdown with
5788hard line breaks.
5927hard line breaks.
5928+.SS Extension: \f[C]rebase_relative_paths\f[R]
5929+.PP
5930+Rewrite relative paths for Markdown links and images, depending on the
5931+path of the file containing the link or image link.
5932+For each link or image, pandoc will compute the directory of the
5933+containing file, relative to the working directory, and prepend the
5934+resulting path to the link or image path.
5935+.PP
5936+The use of this extension is best understood by example.
5937+Suppose you have a a subdirectory for each chapter of a book,
5938+\f[C]chap1\f[R], \f[C]chap2\f[R], \f[C]chap3\f[R].
5939+Each contains a file \f[C]text.md\f[R] and a number of images used in
5940+the chapter.
5941+You would like to have \f[C]![image](spider.jpg)\f[R] in
5942+\f[C]chap1/text.md\f[R] refer to \f[C]chap1/spider.jpg\f[R] and
5943+\f[C]![image](spider.jpg)\f[R] in \f[C]chap2/text.md\f[R] refer to
5944+\f[C]chap2/spider.jpg\f[R].
5945+To do this, use
5946+.IP
5947+.nf
5948+\f[C]
5949+pandoc chap*/*.md -f markdown+rebase_relative_paths
5950+\f[R]
5951+.fi
5952+.PP
5953+Without this extension, you would have to use
5954+\f[C]![image](chap1/spider.jpg)\f[R] in \f[C]chap1/text.md\f[R] and
5955+\f[C]![image](chap2/spider.jpg)\f[R] in \f[C]chap2/text.md\f[R].
5956+Links with relative paths will be rewritten in the same way as images.
5957+.PP
5958+Absolute paths and URLs are not changed.
5959+Neither are empty paths or paths consisting entirely of a fragment,
5960+e.g., \f[C]#foo\f[R].
5961+.PP
5962+Note that relative paths in reference links and images will be rewritten
5963+relative to the file containing the link reference definition, not the
5964+file containing the reference link or image itself, if these differ.
5789.SS Extension: \f[C]attributes\f[R]
5965.SS Extension: \f[C]attributes\f[R]
5790.PP
5966.PP
5791Allows attributes to be attached to any inline or block-level element.
5967Allows attributes to be attached to any inline or block-level element.
@@ -6415,9 +6591,29 @@ entries (for author-date and numerical styles only).
6415.TP
6591.TP
6416\f[B]\f[CB]lang\f[B]\f[R]
6592\f[B]\f[CB]lang\f[B]\f[R]
6417The \f[C]lang\f[R] field will affect how the style is localized, for
6593The \f[C]lang\f[R] field will affect how the style is localized, for
6418−example in the translation of labels and the use of quotation marks.
6594+example in the translation of labels, the use of quotation marks, and
6595+the way items are sorted.
6419(For backwards compatibility, \f[C]locale\f[R] may be used instead of
6596(For backwards compatibility, \f[C]locale\f[R] may be used instead of
6420\f[C]lang\f[R], but this use is deprecated.)
6597\f[C]lang\f[R], but this use is deprecated.)
6598+.RS
6599+.PP
6600+A BCP 47 language tag is expected: for example, \f[C]en\f[R],
6601+\f[C]de\f[R], \f[C]en-US\f[R], \f[C]fr-CA\f[R], \f[C]ug-Cyrl\f[R].
6602+The unicode extension syntax (after \f[C]-u-\f[R]) may be used to
6603+specify options for collation (sorting) more precisely.
6604+Here are some examples:
6605+.IP \[bu] 2
6606+\f[C]zh-u-co-pinyin\f[R] \[en] Chinese with the Pinyin collation.
6607+.IP \[bu] 2
6608+\f[C]es-u-co-trad\f[R] \[en] Spanish with the traditional collation
6609+(with \f[C]Ch\f[R] sorting after \f[C]C\f[R]).
6610+.IP \[bu] 2
6611+\f[C]fr-u-kb\f[R] \[en] French with \[lq]backwards\[rq] accent sorting
6612+(with \f[C]cot\['e]\f[R] sorting after \f[C]c\[^o]te\f[R]).
6613+.IP \[bu] 2
6614+\f[C]en-US-u-kf-upper\f[R] \[en] English with uppercase letters sorting
6615+before lower (default is lower before upper).
6616+.RE
6421.TP
6617.TP
6422\f[B]\f[CB]notes-after-punctuation\f[B]\f[R]
6618\f[B]\f[CB]notes-after-punctuation\f[B]\f[R]
6423If true (the default), pandoc will put footnote citations after
6619If true (the default), pandoc will put footnote citations after
@@ -6538,6 +6734,11 @@ A heading at the slide level always starts a new slide.
6538.IP \[bu] 2
6734.IP \[bu] 2
6539Headings \f[I]below\f[R] the slide level in the hierarchy create
6735Headings \f[I]below\f[R] the slide level in the hierarchy create
6540headings \f[I]within\f[R] a slide.
6736headings \f[I]within\f[R] a slide.
6737+(In beamer, a \[lq]block\[rq] will be created.
6738+If the heading has the class \f[C]example\f[R], an
6739+\f[C]exampleblock\f[R] environment will be used; if it has the class
6740+\f[C]alert\f[R], an \f[C]alertblock\f[R] will be used; otherwise a
6741+regular \f[C]block\f[R] will be used.)
6541.IP \[bu] 2
6742.IP \[bu] 2
6542Headings \f[I]above\f[R] the slide level in the hierarchy create
6743Headings \f[I]above\f[R] the slide level in the hierarchy create
6543\[lq]title slides,\[rq] which just contain the section title and help to
6744\[lq]title slides,\[rq] which just contain the section title and help to
@@ -6938,6 +7139,16 @@ A string value.
6938\f[B]\f[CB]rights\f[B]\f[R]
7139\f[B]\f[CB]rights\f[B]\f[R]
6939A string value.
7140A string value.
6940.TP
7141.TP
7142+\f[B]\f[CB]belongs-to-collection\f[B]\f[R]
7143+A string value.
7144+identifies the name of a collection to which the EPUB Publication
7145+belongs.
7146+.TP
7147+\f[B]\f[CB]group-position\f[B]\f[R]
7148+The \f[C]group-position\f[R] field indicates the numeric position in
7149+which the EPUB Publication belongs relative to other works belonging to
7150+the same \f[C]belongs-to-collection\f[R] field.
7151+.TP
6941\f[B]\f[CB]cover-image\f[B]\f[R]
7152\f[B]\f[CB]cover-image\f[B]\f[R]
6942A string value (path to cover image).
7153A string value (path to cover image).
6943.TP
7154.TP
@@ -7453,6 +7664,20 @@ need to specify a template manually using \f[C]--template\f[R] or add a
7453new default template with the name
7664new default template with the name
7454\f[C]default.NAME_OF_CUSTOM_WRITER.lua\f[R] to the \f[C]templates\f[R]
7665\f[C]default.NAME_OF_CUSTOM_WRITER.lua\f[R] to the \f[C]templates\f[R]
7455subdirectory of your user data directory (see Templates).
7666subdirectory of your user data directory (see Templates).
7667+.SH REPRODUCIBLE BUILDS
7668+.PP
7669+Some of the document formats pandoc targets (such as EPUB, docx, and
7670+ODT) include build timestamps in the generated document.
7671+That means that the files generated on successive builds will differ,
7672+even if the source does not.
7673+To avoid this, set the \f[C]SOURCE_DATE_EPOCH\f[R] environment variable,
7674+and the timestamp will be taken from it instead of the current time.
7675+\f[C]SOURCE_DATE_EPOCH\f[R] should contain an integer unix timestamp
7676+(specifying the number of second since midnight UTC January 1, 1970).
7677+.PP
7678+Some document formats also include a unique identifier.
7679+For EPUB, this can be set explicitly by setting the \f[C]identifier\f[R]
7680+metadata field (see EPUB Metadata, above).
7456.SH A NOTE ON SECURITY
7681.SH A NOTE ON SECURITY
7457.PP
7682.PP
7458If you use pandoc to convert user-contributed content in a web
7683If you use pandoc to convert user-contributed content in a web
@@ -7483,7 +7708,7 @@ The HTML generated by pandoc is not guaranteed to be safe.
7483If \f[C]raw_html\f[R] is enabled for the Markdown input, users can
7708If \f[C]raw_html\f[R] is enabled for the Markdown input, users can
7484inject arbitrary HTML.
7709inject arbitrary HTML.
7485Even if \f[C]raw_html\f[R] is disabled, users can include dangerous
7710Even if \f[C]raw_html\f[R] is disabled, users can include dangerous
7486−content in attributes for headings, spans, and code blocks.
7711+content in URLs and attributes.
7487To be safe, you should run all the generated HTML through an HTML
7712To be safe, you should run all the generated HTML through an HTML
7488sanitizer.
7713sanitizer.
7489.SH AUTHORS
7714.SH AUTHORS
pandoc.cabal+51−36
@@ -1,6 +1,6 @@
1cabal-version: 2.2
1cabal-version: 2.2
2name: pandoc
2name: pandoc
3−version: 2.11.4
3+version: 2.14.0.3
4build-type: Simple
4build-type: Simple
5license: GPL-2.0-or-later
5license: GPL-2.0-or-later
6license-file: COPYING.md
6license-file: COPYING.md
@@ -12,7 +12,7 @@ stability: alpha
12homepage: https://pandoc.org
12homepage: https://pandoc.org
13category: Text
13category: Text
14tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,
14tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,
15− GHC == 8.8.4, GHC == 8.10.2
15+ GHC == 8.8.4, GHC == 8.10.2, GHC == 9.0.1
16synopsis: Conversion between markup formats
16synopsis: Conversion between markup formats
17description: Pandoc is a Haskell library for converting from one markup
17description: Pandoc is a Haskell library for converting from one markup
18 format to another, and a command-line tool that uses
18 format to another, and a command-line tool that uses
@@ -208,12 +208,16 @@ extra-source-files:
208 test/command/*.md
208 test/command/*.md
209 test/command/*.csl
209 test/command/*.csl
210 test/command/biblio.bib
210 test/command/biblio.bib
211− test/command/biblatex-examples.bib
211+ test/command/averroes.bib
212 test/command/A.txt
212 test/command/A.txt
213 test/command/B.txt
213 test/command/B.txt
214 test/command/C.txt
214 test/command/C.txt
215 test/command/D.txt
215 test/command/D.txt
216 test/command/01.csv
216 test/command/01.csv
217+ test/command/chap1/spider.png
218+ test/command/chap2/spider.png
219+ test/command/chap1/text.md
220+ test/command/chap2/text.md
217 test/command/defaults1.yaml
221 test/command/defaults1.yaml
218 test/command/defaults2.yaml
222 test/command/defaults2.yaml
219 test/command/defaults3.yaml
223 test/command/defaults3.yaml
@@ -398,8 +402,7 @@ flag trypandoc
398
402
399common common-options
403common common-options
400 default-language: Haskell2010
404 default-language: Haskell2010
401− build-depends: base >= 4.9 && < 5,
405+ build-depends: base >= 4.9 && < 5
402− text >= 1.1.1.0 && < 1.3
403 ghc-options: -Wall -fno-warn-unused-do-bind
406 ghc-options: -Wall -fno-warn-unused-do-bind
404 -Wincomplete-record-updates
407 -Wincomplete-record-updates
405 -Wnoncanonical-monad-instances
408 -Wnoncanonical-monad-instances
@@ -427,7 +430,7 @@ common common-options
427common common-executable
430common common-executable
428 import: common-options
431 import: common-options
429 build-depends: pandoc
432 build-depends: pandoc
430− ghc-options: -rtsopts -with-rtsopts=-K16m -threaded
433+ ghc-options: -rtsopts -with-rtsopts=-A8m -threaded
431
434
432
435
433library
436library
@@ -439,29 +442,30 @@ library
439 SHA >= 1.6 && < 1.7,
442 SHA >= 1.6 && < 1.7,
440 aeson >= 0.7 && < 1.6,
443 aeson >= 0.7 && < 1.6,
441 aeson-pretty >= 0.8.5 && < 0.9,
444 aeson-pretty >= 0.8.5 && < 0.9,
445+ array >= 0.5 && < 0.6,
442− attoparsec >= 0.12 && < 0.14,
446+ attoparsec >= 0.12 && < 0.15,
443 base64-bytestring >= 0.1 && < 1.3,
447 base64-bytestring >= 0.1 && < 1.3,
444 binary >= 0.7 && < 0.11,
448 binary >= 0.7 && < 0.11,
445 blaze-html >= 0.9 && < 0.10,
449 blaze-html >= 0.9 && < 0.10,
446 blaze-markup >= 0.8 && < 0.9,
450 blaze-markup >= 0.8 && < 0.9,
447 bytestring >= 0.9 && < 0.12,
451 bytestring >= 0.9 && < 0.12,
448 case-insensitive >= 1.2 && < 1.3,
452 case-insensitive >= 1.2 && < 1.3,
449− citeproc >= 0.3.0.7 && < 0.4,
453+ citeproc >= 0.4.0.1 && < 0.4.1,
450− commonmark >= 0.1.1.3 && < 0.2,
454+ commonmark >= 0.2.1 && < 0.3,
451− commonmark-extensions >= 0.2.0.4 && < 0.3,
455+ commonmark-extensions >= 0.2.1.2 && < 0.3,
452− commonmark-pandoc >= 0.2 && < 0.3,
456+ commonmark-pandoc >= 0.2.1.1 && < 0.3,
453 connection >= 0.3.1,
457 connection >= 0.3.1,
454 containers >= 0.4.2.1 && < 0.7,
458 containers >= 0.4.2.1 && < 0.7,
455 data-default >= 0.4 && < 0.8,
459 data-default >= 0.4 && < 0.8,
456 deepseq >= 1.3 && < 1.5,
460 deepseq >= 1.3 && < 1.5,
457 directory >= 1.2.3 && < 1.4,
461 directory >= 1.2.3 && < 1.4,
458− doclayout >= 0.3 && < 0.4,
462+ doclayout >= 0.3.0.1 && < 0.4,
459 doctemplates >= 0.9 && < 0.10,
463 doctemplates >= 0.9 && < 0.10,
460 emojis >= 0.1 && < 0.2,
464 emojis >= 0.1 && < 0.2,
461 exceptions >= 0.8 && < 0.11,
465 exceptions >= 0.8 && < 0.11,
462 file-embed >= 0.0 && < 0.1,
466 file-embed >= 0.0 && < 0.1,
463 filepath >= 1.1 && < 1.5,
467 filepath >= 1.1 && < 1.5,
464− haddock-library >= 1.8 && < 1.10,
468+ haddock-library >= 1.10 && < 1.11,
465 hslua >= 1.1 && < 1.4,
469 hslua >= 1.1 && < 1.4,
466 hslua-module-path >= 0.1.0 && < 0.2.0,
470 hslua-module-path >= 0.1.0 && < 0.2.0,
467 hslua-module-system >= 0.2 && < 0.3,
471 hslua-module-system >= 0.2 && < 0.3,
@@ -470,30 +474,31 @@ library
470 http-client-tls >= 0.2.4 && < 0.4,
474 http-client-tls >= 0.2.4 && < 0.4,
471 http-types >= 0.8 && < 0.13,
475 http-types >= 0.8 && < 0.13,
472 ipynb >= 0.1 && < 0.2,
476 ipynb >= 0.1 && < 0.2,
473− jira-wiki-markup >= 1.3.3 && < 1.4,
477+ jira-wiki-markup >= 1.4 && < 1.5,
474 mtl >= 2.2 && < 2.3,
478 mtl >= 2.2 && < 2.3,
475 network >= 2.6,
479 network >= 2.6,
476− network-uri >= 2.6 && < 2.7,
480+ network-uri >= 2.6 && < 2.8,
477 pandoc-types >= 1.22 && < 1.23,
481 pandoc-types >= 1.22 && < 1.23,
478 parsec >= 3.1 && < 3.2,
482 parsec >= 3.1 && < 3.2,
479 process >= 1.2.3 && < 1.7,
483 process >= 1.2.3 && < 1.7,
480− random >= 1 && < 1.2,
484+ random >= 1 && < 1.3,
481− safe >= 0.3 && < 0.4,
485+ safe >= 0.3.18 && < 0.4,
482 scientific >= 0.3 && < 0.4,
486 scientific >= 0.3 && < 0.4,
483− skylighting >= 0.10.4 && < 0.11,
487+ skylighting >= 0.10.5.2 && < 0.10.6,
484− skylighting-core >= 0.10.4 && < 0.11,
488+ skylighting-core >= 0.10.5.2 && < 0.10.6,
485 split >= 0.2 && < 0.3,
489 split >= 0.2 && < 0.3,
486 syb >= 0.1 && < 0.8,
490 syb >= 0.1 && < 0.8,
487 tagsoup >= 0.14.6 && < 0.15,
491 tagsoup >= 0.14.6 && < 0.15,
488 temporary >= 1.1 && < 1.4,
492 temporary >= 1.1 && < 1.4,
489− texmath >= 0.12.1 && < 0.13,
493+ texmath >= 0.12.3 && < 0.12.4,
490 text >= 1.1.1.0 && < 1.3,
494 text >= 1.1.1.0 && < 1.3,
491 text-conversions >= 0.3 && < 0.4,
495 text-conversions >= 0.3 && < 0.4,
492− time >= 1.5 && < 1.10,
496+ time >= 1.5 && < 1.12,
493 unicode-transforms >= 0.3 && < 0.4,
497 unicode-transforms >= 0.3 && < 0.4,
494 unordered-containers >= 0.2 && < 0.3,
498 unordered-containers >= 0.2 && < 0.3,
495 xml >= 1.3.12 && < 1.4,
499 xml >= 1.3.12 && < 1.4,
496− xml-conduit >= 1.7 && < 1.10,
500+ xml-conduit >= 1.9.1.1 && < 1.10,
501+ unicode-collation >= 0.1.1 && < 0.2,
497 zip-archive >= 0.2.3.4 && < 0.5,
502 zip-archive >= 0.2.3.4 && < 0.5,
498 zlib >= 0.5 && < 0.7
503 zlib >= 0.5 && < 0.7
499 if os(windows) && arch(i386)
504 if os(windows) && arch(i386)
@@ -512,6 +517,7 @@ library
512 Text.Pandoc.Options,
517 Text.Pandoc.Options,
513 Text.Pandoc.Extensions,
518 Text.Pandoc.Extensions,
514 Text.Pandoc.Shared,
519 Text.Pandoc.Shared,
520+ Text.Pandoc.Sources,
515 Text.Pandoc.MediaBag,
521 Text.Pandoc.MediaBag,
516 Text.Pandoc.Error,
522 Text.Pandoc.Error,
517 Text.Pandoc.Filter,
523 Text.Pandoc.Filter,
@@ -601,7 +607,6 @@ library
601 Text.Pandoc.Asciify,
607 Text.Pandoc.Asciify,
602 Text.Pandoc.Emoji,
608 Text.Pandoc.Emoji,
603 Text.Pandoc.ImageSize,
609 Text.Pandoc.ImageSize,
604− Text.Pandoc.BCP47,
605 Text.Pandoc.Class,
610 Text.Pandoc.Class,
606 Text.Pandoc.Citeproc
611 Text.Pandoc.Citeproc
607 other-modules: Text.Pandoc.App.CommandLineOptions,
612 other-modules: Text.Pandoc.App.CommandLineOptions,
@@ -626,15 +631,15 @@ library
626 Text.Pandoc.Readers.HTML.Table,
631 Text.Pandoc.Readers.HTML.Table,
627 Text.Pandoc.Readers.HTML.TagCategories,
632 Text.Pandoc.Readers.HTML.TagCategories,
628 Text.Pandoc.Readers.HTML.Types,
633 Text.Pandoc.Readers.HTML.Types,
629− Text.Pandoc.Readers.LaTeX.Types,
634+ Text.Pandoc.Readers.LaTeX.Inline,
630− Text.Pandoc.Readers.LaTeX.Parsing,
631− Text.Pandoc.Readers.LaTeX.Lang,
632− Text.Pandoc.Readers.LaTeX.SIunitx,
633− Text.Pandoc.Readers.LaTeX.Accent,
634 Text.Pandoc.Readers.LaTeX.Citation,
635 Text.Pandoc.Readers.LaTeX.Citation,
636+ Text.Pandoc.Readers.LaTeX.Lang,
637+ Text.Pandoc.Readers.LaTeX.Macro,
635 Text.Pandoc.Readers.LaTeX.Math,
638 Text.Pandoc.Readers.LaTeX.Math,
639+ Text.Pandoc.Readers.LaTeX.Parsing,
640+ Text.Pandoc.Readers.LaTeX.SIunitx,
636 Text.Pandoc.Readers.LaTeX.Table,
641 Text.Pandoc.Readers.LaTeX.Table,
637− Text.Pandoc.Readers.LaTeX.Macro,
642+ Text.Pandoc.Readers.LaTeX.Types,
638 Text.Pandoc.Readers.Odt.Base,
643 Text.Pandoc.Readers.Odt.Base,
639 Text.Pandoc.Readers.Odt.Namespaces,
644 Text.Pandoc.Readers.Odt.Namespaces,
640 Text.Pandoc.Readers.Odt.StyleReader,
645 Text.Pandoc.Readers.Odt.StyleReader,
@@ -658,13 +663,21 @@ library
658 Text.Pandoc.Readers.Metadata,
663 Text.Pandoc.Readers.Metadata,
659 Text.Pandoc.Readers.Roff,
664 Text.Pandoc.Readers.Roff,
660 Text.Pandoc.Writers.Docx.StyleMap,
665 Text.Pandoc.Writers.Docx.StyleMap,
666+ Text.Pandoc.Writers.Docx.Table,
667+ Text.Pandoc.Writers.Docx.Types,
668+ Text.Pandoc.Writers.GridTable
661 Text.Pandoc.Writers.JATS.References,
669 Text.Pandoc.Writers.JATS.References,
662 Text.Pandoc.Writers.JATS.Table,
670 Text.Pandoc.Writers.JATS.Table,
663 Text.Pandoc.Writers.JATS.Types,
671 Text.Pandoc.Writers.JATS.Types,
664 Text.Pandoc.Writers.LaTeX.Caption,
672 Text.Pandoc.Writers.LaTeX.Caption,
665 Text.Pandoc.Writers.LaTeX.Notes,
673 Text.Pandoc.Writers.LaTeX.Notes,
666 Text.Pandoc.Writers.LaTeX.Table,
674 Text.Pandoc.Writers.LaTeX.Table,
675+ Text.Pandoc.Writers.LaTeX.Lang,
667 Text.Pandoc.Writers.LaTeX.Types,
676 Text.Pandoc.Writers.LaTeX.Types,
677+ Text.Pandoc.Writers.LaTeX.Citation,
678+ Text.Pandoc.Writers.LaTeX.Util,
679+ Text.Pandoc.Writers.Markdown.Types,
680+ Text.Pandoc.Writers.Markdown.Inline,
668 Text.Pandoc.Writers.Roff,
681 Text.Pandoc.Writers.Roff,
669 Text.Pandoc.Writers.Powerpoint.Presentation,
682 Text.Pandoc.Writers.Powerpoint.Presentation,
670 Text.Pandoc.Writers.Powerpoint.Output,
683 Text.Pandoc.Writers.Powerpoint.Output,
@@ -725,7 +738,7 @@ executable trypandoc
725 main-is: trypandoc.hs
738 main-is: trypandoc.hs
726 hs-source-dirs: trypandoc
739 hs-source-dirs: trypandoc
727 if flag(trypandoc)
740 if flag(trypandoc)
728− build-depends: aeson, http-types, wai >= 0.3, wai-extra >= 3.0.24
741+ build-depends: aeson, http-types, text, wai >= 0.3, wai-extra >= 3.0.24
729 buildable: True
742 buildable: True
730 else
743 else
731 buildable: False
744 buildable: False
@@ -739,13 +752,11 @@ test-suite test-pandoc
739 Diff >= 0.2 && < 0.5,
752 Diff >= 0.2 && < 0.5,
740 Glob >= 0.7 && < 0.11,
753 Glob >= 0.7 && < 0.11,
741 QuickCheck >= 2.4 && < 2.15,
754 QuickCheck >= 2.4 && < 2.15,
742− base64-bytestring >= 0.1 && < 1.3,
743 bytestring >= 0.9 && < 0.12,
755 bytestring >= 0.9 && < 0.12,
744 containers >= 0.4.2.1 && < 0.7,
756 containers >= 0.4.2.1 && < 0.7,
745 directory >= 1.2.3 && < 1.4,
757 directory >= 1.2.3 && < 1.4,
746 doctemplates >= 0.9 && < 0.10,
758 doctemplates >= 0.9 && < 0.10,
747 exceptions >= 0.8 && < 0.11,
759 exceptions >= 0.8 && < 0.11,
748− executable-path >= 0.0 && < 0.1,
749 filepath >= 1.1 && < 1.5,
760 filepath >= 1.1 && < 1.5,
750 hslua >= 1.1 && < 1.4,
761 hslua >= 1.1 && < 1.4,
751 mtl >= 2.2 && < 2.3,
762 mtl >= 2.2 && < 2.3,
@@ -756,8 +767,8 @@ test-suite test-pandoc
756 tasty-hunit >= 0.9 && < 0.11,
767 tasty-hunit >= 0.9 && < 0.11,
757 tasty-lua >= 0.2 && < 0.3,
768 tasty-lua >= 0.2 && < 0.3,
758 tasty-quickcheck >= 0.8 && < 0.11,
769 tasty-quickcheck >= 0.8 && < 0.11,
759− temporary >= 1.1 && < 1.4,
770+ text >= 1.1.1.0 && < 1.3,
760− time >= 1.5 && < 1.10,
771+ time >= 1.5 && < 1.12,
761 xml >= 1.3.12 && < 1.4,
772 xml >= 1.3.12 && < 1.4,
762 zip-archive >= 0.2.3.4 && < 0.5
773 zip-archive >= 0.2.3.4 && < 0.5
763 other-modules: Tests.Old
774 other-modules: Tests.Old
@@ -823,7 +834,11 @@ benchmark benchmark-pandoc
823 hs-source-dirs: benchmark
834 hs-source-dirs: benchmark
824 build-depends: bytestring,
835 build-depends: bytestring,
825 containers,
836 containers,
826− tasty,
837+ -- gauge >= 0.2 && < 0.3,
827 tasty-bench >= 0.2 && <= 0.3,
838 tasty-bench >= 0.2 && <= 0.3,
828 mtl >= 2.2 && < 2.3,
839 mtl >= 2.2 && < 2.3,
840+ text >= 1.1.1.0 && < 1.3,
829− time
841+ time,
842+ deepseq
843+ -- we increase heap size to avoid benchmarking garbage collection:
844+ ghc-options: -rtsopts -with-rtsopts=-A8m -threaded
src/Text/Pandoc/App.hs+40−21
@@ -47,20 +47,21 @@ import System.FilePath ( takeBaseName, takeExtension )
47import System.IO (nativeNewline, stdout)
47import System.IO (nativeNewline, stdout)
48import qualified System.IO as IO (Newline (..))
48import qualified System.IO as IO (Newline (..))
49import Text.Pandoc
49import Text.Pandoc
50+import Text.Pandoc.Builder (setMeta)
50import Text.Pandoc.MIME (getCharset)
51import Text.Pandoc.MIME (getCharset)
51import Text.Pandoc.App.FormatHeuristics (formatFromFilePaths)
52import Text.Pandoc.App.FormatHeuristics (formatFromFilePaths)
52import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts,
53import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts,
53− IpynbOutput (..) )
54+ IpynbOutput (..))
54import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs,
55import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs,
55 options)
56 options)
56import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings)
57import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings)
57−import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
58+import Text.Collate.Lang (Lang (..), parseLang)
58import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters)
59import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters)
59import Text.Pandoc.PDF (makePDF)
60import Text.Pandoc.PDF (makePDF)
60import Text.Pandoc.SelfContained (makeSelfContained)
61import Text.Pandoc.SelfContained (makeSelfContained)
61import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs,
62import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs,
62 headerShift, isURI, tabFilter, uriPathToPath, filterIpynbOutput,
63 headerShift, isURI, tabFilter, uriPathToPath, filterIpynbOutput,
63− defaultUserDataDirs, tshow, findM)
64+ defaultUserDataDir, tshow)
64import Text.Pandoc.Writers.Shared (lookupMetaString)
65import Text.Pandoc.Writers.Shared (lookupMetaString)
65import Text.Pandoc.Readers.Markdown (yamlToMeta)
66import Text.Pandoc.Readers.Markdown (yamlToMeta)
66import qualified Text.Pandoc.UTF8 as UTF8
67import qualified Text.Pandoc.UTF8 as UTF8
@@ -71,6 +72,15 @@ import System.Posix.Terminal (queryTerminal)
71
72
72convertWithOpts :: Opt -> IO ()
73convertWithOpts :: Opt -> IO ()
73convertWithOpts opts = do
74convertWithOpts 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+
74 let outputFile = fromMaybe "-" (optOutputFile opts)
84 let outputFile = fromMaybe "-" (optOutputFile opts)
75 let filters = optFilters opts
85 let filters = optFilters opts
76 let verbosity = optVerbosity opts
86 let verbosity = optVerbosity opts
@@ -85,12 +95,6 @@ convertWithOpts opts = do
85 Just xs | not (optIgnoreArgs opts) -> xs
95 Just xs | not (optIgnoreArgs opts) -> xs
86 _ -> ["-"]
96 _ -> ["-"]
87
97
88− datadir <- case optDataDir opts of
89− Nothing -> do
90− ds <- defaultUserDataDirs
91− findM doesDirectoryExist ds
92− Just _ -> return $ optDataDir opts
93−
94 let runIO' :: PandocIO a -> IO a
98 let runIO' :: PandocIO a -> IO a
95 runIO' f = do
99 runIO' f = do
96 (res, reports) <- runIOorExplode $ do
100 (res, reports) <- runIOorExplode $ do
@@ -156,9 +160,11 @@ convertWithOpts opts = do
156 else optTabStop opts)
160 else optTabStop opts)
157
161
158
162
159− let readSources :: [FilePath] -> PandocIO Text
163+ let readSources :: [FilePath] -> PandocIO [(FilePath, Text)]
160− readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$>
164+ readSources srcs =
165+ mapM (\fp -> do
161− mapM readSource srcs
166+ t <- readSource fp
167+ return (if fp == "-" then "" else fp, convertTabs t)) srcs
162
168
163
169
164 outputSettings <- optToOutputSettings opts
170 outputSettings <- optToOutputSettings opts
@@ -196,8 +202,8 @@ convertWithOpts opts = do
196 Just f -> readFileStrict f
202 Just f -> readFileStrict f
197
203
198 case lookupMetaString "lang" (optMetadata opts) of
204 case lookupMetaString "lang" (optMetadata opts) of
199− "" -> setTranslations $ Lang "en" "" "US" []
205+ "" -> setTranslations $ Lang "en" Nothing (Just "US") [] [] []
200− l -> case parseBCP47 l of
206+ l -> case parseLang l of
201 Left _ -> report $ InvalidLang l
207 Left _ -> report $ InvalidLang l
202 Right l' -> setTranslations l'
208 Right l' -> setTranslations l'
203
209
@@ -251,13 +257,17 @@ convertWithOpts opts = do
251 let sourceToDoc :: [FilePath] -> PandocIO Pandoc
257 let sourceToDoc :: [FilePath] -> PandocIO Pandoc
252 sourceToDoc sources' =
258 sourceToDoc sources' =
253 case reader of
259 case reader of
254− TextReader r
260+ TextReader r
255− | optFileScope opts || readerNameBase == "json" ->
261+ | readerNameBase == "json" ->
262+ mconcat <$> mapM (readSource >=> r readerOpts) sources'
263+ | optFileScope opts ->
264+ -- Read source and convert tabs (see #6709)
265+ let readSource' = fmap convertTabs . readSource
256− mconcat <$> mapM (readSource >=> r readerOpts) sources'
266+ in mconcat <$> mapM (readSource' >=> r readerOpts) sources'
257− | otherwise ->
267+ | otherwise ->
258− readSources sources' >>= r readerOpts
268+ readSources sources' >>= r readerOpts
259− ByteStringReader r ->
269+ ByteStringReader r ->
260− mconcat <$> mapM (readFile' >=> r readerOpts) sources'
270+ mconcat <$> mapM (readFile' >=> r readerOpts) sources'
261
271
262
272
263 when (readerNameBase == "markdown_github" ||
273 when (readerNameBase == "markdown_github" ||
@@ -275,12 +285,21 @@ convertWithOpts opts = do
275 report $ Deprecated "pandoc-citeproc filter"
285 report $ Deprecated "pandoc-citeproc filter"
276 "Use --citeproc instead."
286 "Use --citeproc instead."
277
287
288+ let cslMetadata =
289+ maybe id (setMeta "csl") (optCSL opts) .
290+ (case optBibliography opts of
291+ [] -> id
292+ xs -> setMeta "bibliography" xs) .
293+ maybe id (setMeta "citation-abbreviations")
294+ (optCitationAbbreviations opts) $ mempty
295+
278 doc <- sourceToDoc sources >>=
296 doc <- sourceToDoc sources >>=
279 ( (if isJust (optExtractMedia opts)
297 ( (if isJust (optExtractMedia opts)
280 then fillMediaBag
298 then fillMediaBag
281 else return)
299 else return)
282 >=> return . adjustMetadata (metadataFromFile <>)
300 >=> return . adjustMetadata (metadataFromFile <>)
283 >=> return . adjustMetadata (<> optMetadata opts)
301 >=> return . adjustMetadata (<> optMetadata opts)
302+ >=> return . adjustMetadata (<> cslMetadata)
284 >=> applyTransforms transforms
303 >=> applyTransforms transforms
285 >=> applyFilters readerOpts filters [T.unpack format]
304 >=> applyFilters readerOpts filters [T.unpack format]
286 >=> maybe return extractMedia (optExtractMedia opts)
305 >=> maybe return extractMedia (optExtractMedia opts)
src/Text/Pandoc/App/CommandLineOptions.hs+30−8
@@ -31,7 +31,7 @@ import Data.Aeson.Encode.Pretty (encodePretty', Config(..), keyOrder,
31 defConfig, Indent(..), NumberFormat(..))
31 defConfig, Indent(..), NumberFormat(..))
32import Data.Bifunctor (second)
32import Data.Bifunctor (second)
33import Data.Char (toLower)
33import Data.Char (toLower)
34−import Data.List (intercalate, sort)
34+import Data.List (intercalate, sort, foldl')
35#ifdef _WINDOWS
35#ifdef _WINDOWS
36#if MIN_VERSION_base(4,12,0)
36#if MIN_VERSION_base(4,12,0)
37import Data.List (isPrefixOf)
37import Data.List (isPrefixOf)
@@ -48,12 +48,13 @@ import System.FilePath
48import System.IO (stdout)
48import System.IO (stdout)
49import Text.DocTemplates (Context (..), ToContext (toVal), Val (..))
49import Text.DocTemplates (Context (..), ToContext (toVal), Val (..))
50import Text.Pandoc
50import Text.Pandoc
51+import Text.Pandoc.Builder (setMeta)
51import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), IpynbOutput (..),
52import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), IpynbOutput (..),
52− DefaultsState (..), addMeta, applyDefaults,
53+ DefaultsState (..), applyDefaults,
53 fullDefaultsPath)
54 fullDefaultsPath)
54import Text.Pandoc.Filter (Filter (..))
55import Text.Pandoc.Filter (Filter (..))
55import Text.Pandoc.Highlighting (highlightingStyles)
56import Text.Pandoc.Highlighting (highlightingStyles)
56−import Text.Pandoc.Shared (ordNub, elemText, safeStrRead, defaultUserDataDirs)
57+import Text.Pandoc.Shared (ordNub, elemText, safeStrRead, defaultUserDataDir)
57import Text.Printf
58import Text.Printf
58
59
59#ifdef EMBED_DATA_FILES
60#ifdef EMBED_DATA_FILES
@@ -92,7 +93,7 @@ parseOptionsFromArgs options' defaults prg rawArgs = do
92 ("Try " ++ prg ++ " --help for more information.")
93 ("Try " ++ prg ++ " --help for more information.")
93
94
94 -- thread option data structure through all supplied option actions
95 -- thread option data structure through all supplied option actions
95− opts <- foldl (>>=) (return defaults) actions
96+ opts <- foldl' (>>=) (return defaults) actions
96 let mbArgs = case args of
97 let mbArgs = case args of
97 [] -> Nothing
98 [] -> Nothing
98 xs -> Just xs
99 xs -> Just xs
@@ -287,7 +288,8 @@ options =
287 , Option "" ["resource-path"]
288 , Option "" ["resource-path"]
288 (ReqArg
289 (ReqArg
289 (\arg opt -> return opt { optResourcePath =
290 (\arg opt -> return opt { optResourcePath =
290− splitSearchPath arg })
291+ splitSearchPath arg ++
292+ optResourcePath opt })
291 "SEARCHPATH")
293 "SEARCHPATH")
292 "" -- "Paths to search for images and other resources"
294 "" -- "Paths to search for images and other resources"
293
295
@@ -939,13 +941,12 @@ options =
939 (NoArg
941 (NoArg
940 (\_ -> do
942 (\_ -> do
941 prg <- getProgName
943 prg <- getProgName
942− defaultDatadirs <- defaultUserDataDirs
944+ defaultDatadir <- defaultUserDataDir
943 UTF8.hPutStrLn stdout
945 UTF8.hPutStrLn stdout
944 $ T.pack
946 $ T.pack
945 $ prg ++ " " ++ T.unpack pandocVersion ++
947 $ prg ++ " " ++ T.unpack pandocVersion ++
946 compileInfo ++
948 compileInfo ++
947− "\nUser data directory: " ++
949+ "\nUser data directory: " ++ defaultDatadir ++
948− intercalate " or " defaultDatadirs ++
949 ('\n':copyrightMessage)
950 ('\n':copyrightMessage)
950 exitSuccess ))
951 exitSuccess ))
951 "" -- "Print version"
952 "" -- "Print version"
@@ -1053,6 +1054,27 @@ setVariable key val (Context ctx) = Context $ M.alter go key ctx
1053 go (Just (ListVal xs)) = Just $ ListVal $ xs ++ [toVal val]
1054 go (Just (ListVal xs)) = Just $ ListVal $ xs ++ [toVal val]
1054 go (Just x) = Just $ ListVal [x, toVal val]
1055 go (Just x) = Just $ ListVal [x, toVal val]
1055
1056
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+
1056-- On Windows with ghc 8.6+, we need to rewrite paths
1078-- On Windows with ghc 8.6+, we need to rewrite paths
1057-- beginning with \\ to \\?\UNC\. -- See #5127.
1079-- beginning with \\ to \\?\UNC\. -- See #5127.
1058normalizePath :: FilePath -> FilePath
1080normalizePath :: FilePath -> FilePath
src/Text/Pandoc/App/FormatHeuristics.hs+13−7
@@ -15,18 +15,24 @@ module Text.Pandoc.App.FormatHeuristics
15 ) where
15 ) where
16
16
17import Data.Char (toLower)
17import Data.Char (toLower)
18+import Data.Foldable (asum)
18import Data.Text (Text)
19import Data.Text (Text)
19import System.FilePath (takeExtension)
20import System.FilePath (takeExtension)
20
21
21−-- Determine default format based on file extensions.
22+-- | Determines default format based on file extensions; uses the format
23+-- of the first extension that's associated with a format.
24+--
25+-- Examples:
26+--
27+-- > formatFromFilePaths ["text.unknown", "no-extension"]
28+-- Nothing
29+--
30+-- > formatFromFilePaths ["my.md", "other.rst"]
31+-- Just "markdown"
22formatFromFilePaths :: [FilePath] -> Maybe Text
32formatFromFilePaths :: [FilePath] -> Maybe Text
23−formatFromFilePaths [] = Nothing
33+formatFromFilePaths = asum . map formatFromFilePath
24−formatFromFilePaths (x:xs) =
25− case formatFromFilePath x of
26− Just f -> Just f
27− Nothing -> formatFromFilePaths xs
28
34
29−-- Determine format based on file extension
35+-- | Determines format based on file extension.
30formatFromFilePath :: FilePath -> Maybe Text
36formatFromFilePath :: FilePath -> Maybe Text
31formatFromFilePath x =
37formatFromFilePath x =
32 case takeExtension (map toLower x) of
38 case takeExtension (map toLower x) of
src/Text/Pandoc/App/Opt.hs+145−57
@@ -22,28 +22,29 @@ module Text.Pandoc.App.Opt (
22 , IpynbOutput (..)
22 , IpynbOutput (..)
23 , DefaultsState (..)
23 , DefaultsState (..)
24 , defaultOpts
24 , defaultOpts
25− , addMeta
26 , applyDefaults
25 , applyDefaults
27 , fullDefaultsPath
26 , fullDefaultsPath
28 ) where
27 ) where
29import Control.Monad.Except (MonadIO, liftIO, throwError, (>=>), foldM)
28import Control.Monad.Except (MonadIO, liftIO, throwError, (>=>), foldM)
30import Control.Monad.State.Strict (StateT, modify, gets)
29import 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 )
32import Data.Char (isLower, toLower)
32import Data.Char (isLower, toLower)
33import Data.Maybe (fromMaybe)
33import Data.Maybe (fromMaybe)
34import GHC.Generics hiding (Meta)
34import GHC.Generics hiding (Meta)
35−import Text.Pandoc.Builder (setMeta)
36import Text.Pandoc.Filter (Filter (..))
35import Text.Pandoc.Filter (Filter (..))
37−import Text.Pandoc.Logging (Verbosity (WARNING))
36+import Text.Pandoc.Logging (Verbosity (WARNING), LogMessage(..))
38import Text.Pandoc.Options (TopLevelDivision (TopLevelDefault),
37import Text.Pandoc.Options (TopLevelDivision (TopLevelDefault),
39 TrackChanges (AcceptChanges),
38 TrackChanges (AcceptChanges),
40 WrapOption (WrapAuto), HTMLMathMethod (PlainMath),
39 WrapOption (WrapAuto), HTMLMathMethod (PlainMath),
41 ReferenceLocation (EndOfDocument),
40 ReferenceLocation (EndOfDocument),
42 ObfuscationMethod (NoObfuscation),
41 ObfuscationMethod (NoObfuscation),
43 CiteMethod (Citeproc))
42 CiteMethod (Citeproc))
44−import Text.Pandoc.Class (readFileLazy, fileExists, setVerbosity, PandocMonad)
43+import Text.Pandoc.Class (readFileLazy, fileExists, setVerbosity, report,
44+ PandocMonad(lookupEnv), getUserDataDir)
45import Text.Pandoc.Error (PandocError (PandocParseError, PandocSomeError))
45import 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)
47import qualified Text.Pandoc.Parsing as P
48import qualified Text.Pandoc.Parsing as P
48import Text.Pandoc.Readers.Metadata (yamlMap)
49import Text.Pandoc.Readers.Metadata (yamlMap)
49import Text.Pandoc.Class.PandocPure
50import Text.Pandoc.Class.PandocPure
@@ -52,7 +53,7 @@ import Data.Text (Text, unpack)
52import Data.Default (def)
53import Data.Default (def)
53import qualified Data.Text as T
54import qualified Data.Text as T
54import qualified Data.Map as M
55import qualified Data.Map as M
55−import Text.Pandoc.Definition (Meta(..), MetaValue(..), lookupMeta)
56+import Text.Pandoc.Definition (Meta(..), MetaValue(..))
56import Data.Aeson (defaultOptions, Options(..))
57import Data.Aeson (defaultOptions, Options(..))
57import Data.Aeson.TH (deriveJSON)
58import Data.Aeson.TH (deriveJSON)
58import Control.Applicative ((<|>))
59import Control.Applicative ((<|>))
@@ -156,6 +157,9 @@ data Opt = Opt
156 , optNoCheckCertificate :: Bool -- ^ Disable certificate validation
157 , optNoCheckCertificate :: Bool -- ^ Disable certificate validation
157 , optEol :: LineEnding -- ^ Style of line-endings to use
158 , optEol :: LineEnding -- ^ Style of line-endings to use
158 , optStripComments :: Bool -- ^ Skip HTML comments
159 , optStripComments :: Bool -- ^ Skip HTML comments
160+ , optCSL :: Maybe FilePath -- ^ CSL stylesheet
161+ , optBibliography :: [FilePath] -- ^ Bibliography files
162+ , optCitationAbbreviations :: Maybe FilePath -- ^ Citation abbreviations
159 } deriving (Generic, Show)
163 } deriving (Generic, Show)
160
164
161instance FromYAML (Opt -> Opt) where
165instance FromYAML (Opt -> Opt) where
@@ -175,17 +179,128 @@ instance (PandocMonad m, MonadIO m)
175 dataDir <- case M.lookup "data-dir" opts of
179 dataDir <- case M.lookup "data-dir" opts of
176 Nothing -> return Nothing
180 Nothing -> return Nothing
177 Just v -> Just . unpack <$> parseYAML v
181 Just v -> Just . unpack <$> parseYAML v
178− f <- parseOptions $ M.toList m
182+ f <- parseOptions (M.toList m)
179 case M.lookup "defaults" opts of
183 case M.lookup "defaults" opts of
180 Just v -> do
184 Just v -> do
181 g <- parseDefaults v dataDir
185 g <- parseDefaults v dataDir
182− return $ g >=> f
186+ return $ g >=> f >=> resolveVarsInOpt
183− Nothing -> return f
187+ Nothing -> return $ f >=> resolveVarsInOpt
184 where
188 where
185 toText (Scalar _ (SStr s)) = s
189 toText (Scalar _ (SStr s)) = s
186 toText _ = ""
190 toText _ = ""
187 parseYAML n = failAtNode n "Expected a mapping"
191 parseYAML n = failAtNode n "Expected a mapping"
188
192
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+
189parseDefaults :: (PandocMonad m, MonadIO m)
304parseDefaults :: (PandocMonad m, MonadIO m)
190 => Node Pos
305 => Node Pos
191 -> Maybe FilePath
306 -> Maybe FilePath
@@ -428,26 +543,18 @@ doOpt (k',v) = do
428 (parseYAML v >>= \x -> return (\o -> o{ optCss = optCss o <>
543 (parseYAML v >>= \x -> return (\o -> o{ optCss = optCss o <>
429 [unpack x] }))
544 [unpack x] }))
430 "bibliography" ->
545 "bibliography" ->
431− do let addItem x o = o{ optMetadata =
546+ (parseYAML v >>= \x -> return (\o ->
432− addMeta "bibliography" (T.unpack x)
547+ o{ optBibliography = optBibliography o <>
433− (optMetadata o) }
548+ map unpack x }))
549+ <|>
434− (parseYAML v >>= \(xs :: [Text]) -> return $ \o ->
550+ (parseYAML v >>= \x -> return (\o ->
435− foldr addItem o xs)
551+ o{ optBibliography = optBibliography o <>
436− <|>
552+ [unpack x] }))
437− (parseYAML v >>= \(x :: Text) -> return $ \o -> addItem x o)
438 "csl" ->
553 "csl" ->
439− do let addItem x o = o{ optMetadata =
554+ parseYAML v >>= \x -> return (\o -> o{ optCSL = unpack <$> x })
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)
446 "citation-abbreviations" ->
555 "citation-abbreviations" ->
447− parseYAML v >>= \x ->
556+ parseYAML v >>= \x -> return (\o -> o{ optCitationAbbreviations =
448− return (\o -> o{ optMetadata =
557+ unpack <$> x })
449− addMeta "citation-abbreviations" (T.unpack x)
450− (optMetadata o) })
451 "ipynb-output" ->
558 "ipynb-output" ->
452 parseYAML v >>= \x -> return (\o -> o{ optIpynbOutput = x })
559 parseYAML v >>= \x -> return (\o -> o{ optIpynbOutput = x })
453 "include-before-body" ->
560 "include-before-body" ->
@@ -476,7 +583,8 @@ doOpt (k',v) = do
476 optIncludeInHeader o <> [unpack x] }))
583 optIncludeInHeader o <> [unpack x] }))
477 "resource-path" ->
584 "resource-path" ->
478 parseYAML v >>= \x ->
585 parseYAML v >>= \x ->
479− return (\o -> o{ optResourcePath = map unpack x })
586+ return (\o -> o{ optResourcePath = map unpack x <>
587+ optResourcePath o })
480 "request-headers" ->
588 "request-headers" ->
481 parseYAML v >>= \x ->
589 parseYAML v >>= \x ->
482 return (\o -> o{ optRequestHeaders = x })
590 return (\o -> o{ optRequestHeaders = x })
@@ -562,6 +670,9 @@ defaultOpts = Opt
562 , optNoCheckCertificate = False
670 , optNoCheckCertificate = False
563 , optEol = Native
671 , optEol = Native
564 , optStripComments = False
672 , optStripComments = False
673+ , optCSL = Nothing
674+ , optBibliography = []
675+ , optCitationAbbreviations = Nothing
565 }
676 }
566
677
567parseStringKey :: Node Pos -> Parser Text
678parseStringKey :: Node Pos -> Parser Text
@@ -575,31 +686,11 @@ yamlToMeta (Mapping _ _ m) =
575 either (fail . show) return $ runEverything (yamlMap pMetaString m)
686 either (fail . show) return $ runEverything (yamlMap pMetaString m)
576 where
687 where
577 pMetaString = pure . MetaString <$> P.manyChar P.anyChar
688 pMetaString = pure . MetaString <$> P.manyChar P.anyChar
689+ runEverything p =
578− runEverything p = runPure (P.readWithM p def "")
690+ runPure (P.readWithM p (def :: P.ParserState) ("" :: Text))
579 >>= fmap (Meta . flip P.runF def)
691 >>= fmap (Meta . flip P.runF def)
580yamlToMeta _ = return mempty
692yamlToMeta _ = return mempty
581
693
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−
603-- | Apply defaults from --defaults file.
694-- | Apply defaults from --defaults file.
604applyDefaults :: (PandocMonad m, MonadIO m)
695applyDefaults :: (PandocMonad m, MonadIO m)
605 => Opt
696 => Opt
@@ -625,12 +716,9 @@ fullDefaultsPath dataDir file = do
625 let fp = if null (takeExtension file)
716 let fp = if null (takeExtension file)
626 then addExtension file "yaml"
717 then addExtension file "yaml"
627 else file
718 else file
628− dataDirs <- liftIO defaultUserDataDirs
719+ defaultDataDir <- liftIO defaultUserDataDir
629− let fps = fp : case dataDir of
720+ let defaultFp = fromMaybe defaultDataDir dataDir </> "defaults" </> fp
630− Nothing -> map (</> ("defaults" </> fp))
631− dataDirs
632− Just dd -> [dd </> "defaults" </> fp]
633− fromMaybe fp <$> findM fileExists fps
721+ fromMaybe fp <$> findM fileExists [fp, defaultFp]
634
722
635-- | In a list of lists, append another list in front of every list which
723-- | In a list of lists, append another list in front of every list which
636-- starts with specific element.
724-- starts with specific element.
src/Text/Pandoc/Asciify.hs+13−390
@@ -10,396 +10,19 @@
10Function to convert accented latin letters to their unaccented
10Function to convert accented latin letters to their unaccented
11ascii equivalents (used in constructing HTML identifiers).
11ascii equivalents (used in constructing HTML identifiers).
12-}
12-}
13−module Text.Pandoc.Asciify (toAsciiChar)
13+module Text.Pandoc.Asciify (toAsciiChar, toAsciiText)
14where
14where
15−import Data.Char (isAscii)
15+import Data.Char (isAscii, isMark)
16−import qualified Data.Map as M
16+import qualified Data.Text.Normalize as TN
17+import Data.Text (Text)
18+import qualified Data.Text as T
17
19
18−toAsciiChar :: Char -> Maybe Char
20+toAsciiText :: Text -> Text
19−toAsciiChar c | isAscii c = Just c
21+toAsciiText = T.filter isAscii . TN.normalize (TN.NFD)
20− | otherwise = M.lookup c asciiMap
21
22
22−asciiMap :: M.Map Char Char
23+toAsciiChar :: Char -> Maybe Char
23−asciiMap = M.fromList
24+toAsciiChar c = case T.unpack (TN.normalize TN.NFD (T.singleton c)) of
24− [('\192','A')
25+ (x:xs) | isAscii x
25− ,('\193','A')
26+ , all isMark xs
26− ,('\194','A')
27+ -> Just x
27− ,('\195','A')
28+ _ -> Nothing
28− ,('\196','A')
29− ,('\197','A')
30− ,('\199','C')
31− ,('\200','E')
32− ,('\201','E')
33− ,('\202','E')
34− ,('\203','E')
35− ,('\204','I')
36− ,('\205','I')
37− ,('\206','I')
38− ,('\207','I')
39− ,('\209','N')
40− ,('\210','O')
41− ,('\211','O')
42− ,('\212','O')
43− ,('\213','O')
44− ,('\214','O')
45− ,('\217','U')
46− ,('\218','U')
47− ,('\219','U')
48− ,('\220','U')
49− ,('\221','Y')
50− ,('\224','a')
51− ,('\225','a')
52− ,('\226','a')
53− ,('\227','a')
54− ,('\228','a')
55− ,('\229','a')
56− ,('\231','c')
57− ,('\232','e')
58− ,('\233','e')
59− ,('\234','e')
60− ,('\235','e')
61− ,('\236','i')
62− ,('\237','i')
63− ,('\238','i')
64− ,('\239','i')
65− ,('\241','n')
66− ,('\242','o')
67− ,('\243','o')
68− ,('\244','o')
69− ,('\245','o')
70− ,('\246','o')
71− ,('\249','u')
72− ,('\250','u')
73− ,('\251','u')
74− ,('\252','u')
75− ,('\253','y')
76− ,('\255','y')
77− ,('\256','A')
78− ,('\257','a')
79− ,('\258','A')
80− ,('\259','a')
81− ,('\260','A')
82− ,('\261','a')
83− ,('\262','C')
84− ,('\263','c')
85− ,('\264','C')
86− ,('\265','c')
87− ,('\266','C')
88− ,('\267','c')
89− ,('\268','C')
90− ,('\269','c')
91− ,('\270','D')
92− ,('\271','d')
93− ,('\274','E')
94− ,('\275','e')
95− ,('\276','E')
96− ,('\277','e')
97− ,('\278','E')
98− ,('\279','e')
99− ,('\280','E')
100− ,('\281','e')
101− ,('\282','E')
102− ,('\283','e')
103− ,('\284','G')
104− ,('\285','g')
105− ,('\286','G')
106− ,('\287','g')
107− ,('\288','G')
108− ,('\289','g')
109− ,('\290','G')
110− ,('\291','g')
111− ,('\292','H')
112− ,('\293','h')
113− ,('\296','I')
114− ,('\297','i')
115− ,('\298','I')
116− ,('\299','i')
117− ,('\300','I')
118− ,('\301','i')
119− ,('\302','I')
120− ,('\303','i')
121− ,('\304','I')
122− ,('\305','i')
123− ,('\308','J')
124− ,('\309','j')
125− ,('\310','K')
126− ,('\311','k')
127− ,('\313','L')
128− ,('\314','l')
129− ,('\315','L')
130− ,('\316','l')
131− ,('\317','L')
132− ,('\318','l')
133− ,('\323','N')
134− ,('\324','n')
135− ,('\325','N')
136− ,('\326','n')
137− ,('\327','N')
138− ,('\328','n')
139− ,('\332','O')
140− ,('\333','o')
141− ,('\334','O')
142− ,('\335','o')
143− ,('\336','O')
144− ,('\337','o')
145− ,('\340','R')
146− ,('\341','r')
147− ,('\342','R')
148− ,('\343','r')
149− ,('\344','R')
150− ,('\345','r')
151− ,('\346','S')
152− ,('\347','s')
153− ,('\348','S')
154− ,('\349','s')
155− ,('\350','S')
156− ,('\351','s')
157− ,('\352','S')
158− ,('\353','s')
159− ,('\354','T')
160− ,('\355','t')
161− ,('\356','T')
162− ,('\357','t')
163− ,('\360','U')
164− ,('\361','u')
165− ,('\362','U')
166− ,('\363','u')
167− ,('\364','U')
168− ,('\365','u')
169− ,('\366','U')
170− ,('\367','u')
171− ,('\368','U')
172− ,('\369','u')
173− ,('\370','U')
174− ,('\371','u')
175− ,('\372','W')
176− ,('\373','w')
177− ,('\374','Y')
178− ,('\375','y')
179− ,('\376','Y')
180− ,('\377','Z')
181− ,('\378','z')
182− ,('\379','Z')
183− ,('\380','z')
184− ,('\381','Z')
185− ,('\382','z')
186− ,('\416','O')
187− ,('\417','o')
188− ,('\431','U')
189− ,('\432','u')
190− ,('\461','A')
191− ,('\462','a')
192− ,('\463','I')
193− ,('\464','i')
194− ,('\465','O')
195− ,('\466','o')
196− ,('\467','U')
197− ,('\468','u')
198− ,('\486','G')
199− ,('\487','g')
200− ,('\488','K')
201− ,('\489','k')
202− ,('\490','O')
203− ,('\491','o')
204− ,('\496','j')
205− ,('\500','G')
206− ,('\501','g')
207− ,('\504','N')
208− ,('\505','n')
209− ,('\512','A')
210− ,('\513','a')
211− ,('\514','A')
212− ,('\515','a')
213− ,('\516','E')
214− ,('\517','e')
215− ,('\518','E')
216− ,('\519','e')
217− ,('\520','I')
218− ,('\521','i')
219− ,('\522','I')
220− ,('\523','i')
221− ,('\524','O')
222− ,('\525','o')
223− ,('\526','O')
224− ,('\527','o')
225− ,('\528','R')
226− ,('\529','r')
227− ,('\530','R')
228− ,('\531','r')
229− ,('\532','U')
230− ,('\533','u')
231− ,('\534','U')
232− ,('\535','u')
233− ,('\536','S')
234− ,('\537','s')
235− ,('\538','T')
236− ,('\539','t')
237− ,('\542','H')
238− ,('\543','h')
239− ,('\550','A')
240− ,('\551','a')
241− ,('\552','E')
242− ,('\553','e')
243− ,('\558','O')
244− ,('\559','o')
245− ,('\562','Y')
246− ,('\563','y')
247− ,('\894',';')
248− ,('\7680','A')
249− ,('\7681','a')
250− ,('\7682','B')
251− ,('\7683','b')
252− ,('\7684','B')
253− ,('\7685','b')
254− ,('\7686','B')
255− ,('\7687','b')
256− ,('\7690','D')
257− ,('\7691','d')
258− ,('\7692','D')
259− ,('\7693','d')
260− ,('\7694','D')
261− ,('\7695','d')
262− ,('\7696','D')
263− ,('\7697','d')
264− ,('\7698','D')
265− ,('\7699','d')
266− ,('\7704','E')
267− ,('\7705','e')
268− ,('\7706','E')
269− ,('\7707','e')
270− ,('\7710','F')
271− ,('\7711','f')
272− ,('\7712','G')
273− ,('\7713','g')
274− ,('\7714','H')
275− ,('\7715','h')
276− ,('\7716','H')
277− ,('\7717','h')
278− ,('\7718','H')
279− ,('\7719','h')
280− ,('\7720','H')
281− ,('\7721','h')
282− ,('\7722','H')
283− ,('\7723','h')
284− ,('\7724','I')
285− ,('\7725','i')
286− ,('\7728','K')
287− ,('\7729','k')
288− ,('\7730','K')
289− ,('\7731','k')
290− ,('\7732','K')
291− ,('\7733','k')
292− ,('\7734','L')
293− ,('\7735','l')
294− ,('\7738','L')
295− ,('\7739','l')
296− ,('\7740','L')
297− ,('\7741','l')
298− ,('\7742','M')
299− ,('\7743','m')
300− ,('\7744','M')
301− ,('\7745','m')
302− ,('\7746','M')
303− ,('\7747','m')
304− ,('\7748','N')
305− ,('\7749','n')
306− ,('\7750','N')
307− ,('\7751','n')
308− ,('\7752','N')
309− ,('\7753','n')
310− ,('\7754','N')
311− ,('\7755','n')
312− ,('\7764','P')
313− ,('\7765','p')
314− ,('\7766','P')
315− ,('\7767','p')
316− ,('\7768','R')
317− ,('\7769','r')
318− ,('\7770','R')
319− ,('\7771','r')
320− ,('\7774','R')
321− ,('\7775','r')
322− ,('\7776','S')
323− ,('\7777','s')
324− ,('\7778','S')
325− ,('\7779','s')
326− ,('\7786','T')
327− ,('\7787','t')
328− ,('\7788','T')
329− ,('\7789','t')
330− ,('\7790','T')
331− ,('\7791','t')
332− ,('\7792','T')
333− ,('\7793','t')
334− ,('\7794','U')
335− ,('\7795','u')
336− ,('\7796','U')
337− ,('\7797','u')
338− ,('\7798','U')
339− ,('\7799','u')
340− ,('\7804','V')
341− ,('\7805','v')
342− ,('\7806','V')
343− ,('\7807','v')
344− ,('\7808','W')
345− ,('\7809','w')
346− ,('\7810','W')
347− ,('\7811','w')
348− ,('\7812','W')
349− ,('\7813','w')
350− ,('\7814','W')
351− ,('\7815','w')
352− ,('\7816','W')
353− ,('\7817','w')
354− ,('\7818','X')
355− ,('\7819','x')
356− ,('\7820','X')
357− ,('\7821','x')
358− ,('\7822','Y')
359− ,('\7823','y')
360− ,('\7824','Z')
361− ,('\7825','z')
362− ,('\7826','Z')
363− ,('\7827','z')
364− ,('\7828','Z')
365− ,('\7829','z')
366− ,('\7830','h')
367− ,('\7831','t')
368− ,('\7832','w')
369− ,('\7833','y')
370− ,('\7840','A')
371− ,('\7841','a')
372− ,('\7842','A')
373− ,('\7843','a')
374− ,('\7864','E')
375− ,('\7865','e')
376− ,('\7866','E')
377− ,('\7867','e')
378− ,('\7868','E')
379− ,('\7869','e')
380− ,('\7880','I')
381− ,('\7881','i')
382− ,('\7882','I')
383− ,('\7883','i')
384− ,('\7884','O')
385− ,('\7885','o')
386− ,('\7886','O')
387− ,('\7887','o')
388− ,('\7908','U')
389− ,('\7909','u')
390− ,('\7910','U')
391− ,('\7911','u')
392− ,('\7922','Y')
393− ,('\7923','y')
394− ,('\7924','Y')
395− ,('\7925','y')
396− ,('\7926','Y')
397− ,('\7927','y')
398− ,('\7928','Y')
399− ,('\7929','y')
400− ,('\8175','`')
401− ,('\8490','K')
402− ,('\8800','=')
403− ,('\8814','<')
404− ,('\8815','>')
405− ]
src/Text/Pandoc/BCP47.hsdeleted−112
@@ -1,112 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{- |
3− Module : Text.Pandoc.BCP47
4− Copyright : Copyright (C) 2017-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−Functions for parsing and rendering BCP47 language identifiers.
12−-}
13−module Text.Pandoc.BCP47 (
14− getLang
15− , parseBCP47
16− , Lang(..)
17− , renderLang
18− )
19−where
20−import Control.Monad (guard)
21−import Data.Char (isAlphaNum, isAscii, isLetter, isLower, isUpper)
22−import Text.Pandoc.Definition
23−import Text.Pandoc.Options
24−import Text.DocTemplates (FromContext(..))
25−import qualified Data.Text as T
26−import qualified Text.Parsec as P
27−
28−-- | Represents BCP 47 language/country code.
29−data Lang = Lang{ langLanguage :: T.Text
30− , langScript :: T.Text
31− , langRegion :: T.Text
32− , langVariants :: [T.Text] }
33− deriving (Eq, Ord, Show)
34−
35−-- | Render a Lang as BCP 47.
36−renderLang :: Lang -> T.Text
37−renderLang lang = T.intercalate "-" (langLanguage lang : filter (not . T.null)
38− ([langScript lang, langRegion lang] ++ langVariants lang))
39−
40−-- | Get the contents of the `lang` metadata field or variable.
41−getLang :: WriterOptions -> Meta -> Maybe T.Text
42−getLang opts meta =
43− case lookupContext "lang" (writerVariables opts) of
44− Just s -> Just s
45− _ ->
46− case lookupMeta "lang" meta of
47− Just (MetaBlocks [Para [Str s]]) -> Just s
48− Just (MetaBlocks [Plain [Str s]]) -> Just s
49− Just (MetaInlines [Str s]) -> Just s
50− Just (MetaString s) -> Just s
51− _ -> Nothing
52−
53−-- | Parse a BCP 47 string as a Lang. Currently we parse
54−-- extensions and private-use fields as "variants," even
55−-- though officially they aren't.
56−parseBCP47 :: T.Text -> Either T.Text Lang
57−parseBCP47 lang =
58− case P.parse bcp47 "lang" lang of
59− Right r -> Right r
60− Left e -> Left $ T.pack $ show e
61− where bcp47 = do
62− language <- pLanguage
63− script <- P.option "" pScript
64− region <- P.option "" pRegion
65− variants <- P.many (pVariant P.<|> pExtension P.<|> pPrivateUse)
66− P.eof
67− return Lang{ langLanguage = language
68− , langScript = script
69− , langRegion = region
70− , langVariants = variants }
71− asciiLetter = P.satisfy (\c -> isAscii c && isLetter c)
72− pLanguage = do
73− cs <- P.many1 asciiLetter
74− let lcs = length cs
75− guard $ lcs == 2 || lcs == 3
76− return $ T.toLower $ T.pack cs
77− pScript = P.try $ do
78− P.char '-'
79− x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c)
80− xs <- P.count 3
81− (P.satisfy (\c -> isAscii c && isLetter c && isLower c))
82− return $ T.toLower $ T.pack (x:xs)
83− pRegion = P.try $ do
84− P.char '-'
85− cs <- P.many1 asciiLetter
86− let lcs = length cs
87− guard $ lcs == 2 || lcs == 3
88− return $ T.toUpper $ T.pack cs
89− pVariant = P.try $ do
90− P.char '-'
91− ds <- P.option "" (P.count 1 P.digit)
92− cs <- P.many1 asciiLetter
93− let var = ds ++ cs
94− lv = length var
95− guard $ if null ds
96− then lv >= 5 && lv <= 8
97− else lv == 4
98− return $ T.toLower $ T.pack var
99− pExtension = P.try $ do
100− P.char '-'
101− cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c)
102− let lcs = length cs
103− guard $ lcs >= 2 && lcs <= 8
104− return $ T.toLower $ T.pack cs
105− pPrivateUse = P.try $ do
106− P.char '-'
107− P.char 'x'
108− P.char '-'
109− cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c)
110− guard $ not (null cs) && length cs <= 8
111− let var = "x-" ++ cs
112− return $ T.toLower $ T.pack var
src/Text/Pandoc/Citeproc.hs+75−56
@@ -18,7 +18,6 @@ import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences)
18import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..))
18import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..))
19import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText)
19import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText)
20import Text.Pandoc.Readers.Markdown (yamlToRefs)
20import Text.Pandoc.Readers.Markdown (yamlToRefs)
21−import qualified Text.Pandoc.BCP47 as BCP47
22import Text.Pandoc.Builder (Inlines, Many(..), deleteMeta, setMeta)
21import Text.Pandoc.Builder (Inlines, Many(..), deleteMeta, setMeta)
23import qualified Text.Pandoc.Builder as B
22import qualified Text.Pandoc.Builder as B
24import Text.Pandoc.Definition as Pandoc
23import Text.Pandoc.Definition as Pandoc
@@ -28,7 +27,7 @@ import Text.Pandoc.Error (PandocError(..))
28import Text.Pandoc.Extensions (pandocExtensions)
27import Text.Pandoc.Extensions (pandocExtensions)
29import Text.Pandoc.Logging (LogMessage(..))
28import Text.Pandoc.Logging (LogMessage(..))
30import Text.Pandoc.Options (ReaderOptions(..))
29import Text.Pandoc.Options (ReaderOptions(..))
31−import Text.Pandoc.Shared (stringify, ordNub, blocksToInlines, tshow)
30+import Text.Pandoc.Shared (stringify, ordNub, tshow)
32import qualified Text.Pandoc.UTF8 as UTF8
31import qualified Text.Pandoc.UTF8 as UTF8
33import Text.Pandoc.Walk (query, walk, walkM)
32import Text.Pandoc.Walk (query, walk, walkM)
34import Control.Applicative ((<|>))
33import Control.Applicative ((<|>))
@@ -49,7 +48,6 @@ import Data.Text (Text)
49import qualified Data.Text as T
48import qualified Data.Text as T
50import System.FilePath (takeExtension)
49import System.FilePath (takeExtension)
51import Safe (lastMay, initSafe)
50import Safe (lastMay, initSafe)
52−-- import Debug.Trace as Trace (trace, traceShowId)
53
51
54
52
55processCitations :: PandocMonad m => Pandoc -> m Pandoc
53processCitations :: PandocMonad m => Pandoc -> m Pandoc
@@ -76,8 +74,7 @@ processCitations (Pandoc meta bs) = do
76
74
77 let linkCites = maybe False truish $ lookupMeta "link-citations" meta
75 let linkCites = maybe False truish $ lookupMeta "link-citations" meta
78 let opts = defaultCiteprocOptions{ linkCitations = linkCites }
76 let opts = defaultCiteprocOptions{ linkCitations = linkCites }
79− let result = Citeproc.citeproc opts style (localeLanguage locale)
77+ let result = Citeproc.citeproc opts style mblang refs citations
80− refs citations
81 mapM_ (report . CiteprocWarning) (resultWarnings result)
78 mapM_ (report . CiteprocWarning) (resultWarnings result)
82 let sopts = styleOptions style
79 let sopts = styleOptions style
83 let classes = "references" : -- TODO remove this or keep for compatibility?
80 let classes = "references" : -- TODO remove this or keep for compatibility?
@@ -91,24 +88,21 @@ processCitations (Pandoc meta bs) = do
91 _ -> id) $ []
88 _ -> id) $ []
92 let bibs = mconcat $ map (\(ident, out) ->
89 let bibs = mconcat $ map (\(ident, out) ->
93 B.divWith ("ref-" <> ident,["csl-entry"],[]) . B.para .
90 B.divWith ("ref-" <> ident,["csl-entry"],[]) . B.para .
94− walk (convertQuotes locale) . insertSpace $ out)
91+ walk (convertQuotes locale) .
92+ insertSpace $ out)
95 (resultBibliography result)
93 (resultBibliography result)
96− let moveNotes = maybe True truish $
94+ let moveNotes = styleIsNoteStyle sopts &&
97− lookupMeta "notes-after-punctuation" meta
95+ maybe True truish (lookupMeta "notes-after-punctuation" meta)
98− let cits = map (walk fixLinks . walk (convertQuotes locale)) $
96+ let cits = map (walk (convertQuotes locale)) $
99 resultCitations result
97 resultCitations result
100
98
101− let fixQuotes = case localePunctuationInQuote locale of
102− Just True ->
103− B.toList . movePunctuationInsideQuotes . B.fromList
104− _ -> id
105−
106 let metanocites = lookupMeta "nocite" meta
99 let metanocites = lookupMeta "nocite" meta
107 let Pandoc meta'' bs' =
100 let Pandoc meta'' bs' =
108 maybe id (setMeta "nocite") metanocites .
101 maybe id (setMeta "nocite") metanocites .
109− walk (map capitalizeNoteCitation .
110− fixQuotes . mvPunct moveNotes locale) .
102+ walk (mvPunct moveNotes locale) .
103+ (if styleIsNoteStyle sopts
111− walk deNote .
104+ then walk addNote . walk deNote
105+ else id) .
112 evalState (walkM insertResolvedCitations $ Pandoc meta' bs)
106 evalState (walkM insertResolvedCitations $ Pandoc meta' bs)
113 $ cits
107 $ cits
114 return $ Pandoc meta''
108 return $ Pandoc meta''
@@ -197,7 +191,9 @@ getReferences mblocale (Pandoc meta bs) = do
197 then const True
191 then const True
198 else (`Set.member` citeIds)
192 else (`Set.member` citeIds)
199 let inlineRefs = case lookupMeta "references" meta of
193 let inlineRefs = case lookupMeta "references" meta of
194+ Just (MetaList rs) ->
195+ filter (idpred . unItemId . referenceId)
200− Just (MetaList rs) -> mapMaybe metaValueToReference rs
196+ $ mapMaybe metaValueToReference rs
201 _ -> []
197 _ -> []
202 externalRefs <- case lookupMeta "bibliography" meta of
198 externalRefs <- case lookupMeta "bibliography" meta of
203 Just (MetaList xs) ->
199 Just (MetaList xs) ->
@@ -294,7 +290,7 @@ insertResolvedCitations (Cite cs ils) = do
294 [] -> return (Cite cs ils)
290 [] -> return (Cite cs ils)
295 (x:xs) -> do
291 (x:xs) -> do
296 put xs
292 put xs
297− return $ Cite cs (B.toList x)
293+ return $ Cite cs (walk fixLinks $ B.toList x)
298insertResolvedCitations x = return x
294insertResolvedCitations x = return x
299
295
300getCitations :: Locale
296getCitations :: Locale
@@ -376,7 +372,6 @@ formatFromExtension fp = case dropWhile (== '.') $ takeExtension fp of
376
372
377
373
378isNote :: Inline -> Bool
374isNote :: Inline -> Bool
379−isNote (Note _) = True
380isNote (Cite _ [Note _]) = True
375isNote (Cite _ [Note _]) = True
381 -- the following allows citation styles that are "in-text" but use superscript
376 -- the following allows citation styles that are "in-text" but use superscript
382 -- references to be treated as if they are "notes" for the purposes of moving
377 -- references to be treated as if they are "notes" for the purposes of moving
@@ -389,6 +384,12 @@ isSpacy Space = True
389isSpacy SoftBreak = True
384isSpacy SoftBreak = True
390isSpacy _ = False
385isSpacy _ = False
391
386
387+movePunctInsideQuotes :: Locale -> [Inline] -> [Inline]
388+movePunctInsideQuotes locale
389+ | localePunctuationInQuote locale == Just True
390+ = B.toList . movePunctuationInsideQuotes . B.fromList
391+ | otherwise
392+ = id
392
393
393mvPunct :: Bool -> Locale -> [Inline] -> [Inline]
394mvPunct :: Bool -> Locale -> [Inline] -> [Inline]
394mvPunct moveNotes locale (x : xs)
395mvPunct moveNotes locale (x : xs)
@@ -401,7 +402,8 @@ mvPunct moveNotes locale (q : s : x : ys)
401 in if moveNotes
402 in if moveNotes
402 then if T.null spunct
403 then if T.null spunct
403 then q : x : mvPunct moveNotes locale ys
404 then q : x : mvPunct moveNotes locale ys
405+ else movePunctInsideQuotes locale
404− else q : Str spunct : x : mvPunct moveNotes locale
406+ [q , Str spunct , x] ++ mvPunct moveNotes locale
405 (B.toList
407 (B.toList
406 (dropTextWhile isPunctuation (B.fromList ys)))
408 (dropTextWhile isPunctuation (B.fromList ys)))
407 else q : x : mvPunct moveNotes locale ys
409 else q : x : mvPunct moveNotes locale ys
@@ -413,9 +415,10 @@ mvPunct moveNotes locale (Cite cs ils : ys)
413 , moveNotes
415 , moveNotes
414 = let s = stringify ys
416 = let s = stringify ys
415 spunct = T.takeWhile isPunctuation s
417 spunct = T.takeWhile isPunctuation s
418+ in Cite cs (movePunctInsideQuotes locale $
416− in Cite cs (init ils
419+ init ils
417− ++ [Str spunct | not (endWithPunct False (init ils))]
420+ ++ [Str spunct | not (endWithPunct False (init ils))]
418− ++ [last ils]) :
421+ ++ [last ils]) :
419 mvPunct moveNotes locale
422 mvPunct moveNotes locale
420 (B.toList (dropTextWhile isPunctuation (B.fromList ys)))
423 (B.toList (dropTextWhile isPunctuation (B.fromList ys)))
421mvPunct moveNotes locale (s : x : ys) | isSpacy s, isNote x =
424mvPunct moveNotes locale (s : x : ys) | isSpacy s, isNote x =
@@ -431,7 +434,7 @@ mvPunct _ _ [] = []
431-- move https://doi.org etc. prefix inside link text (#6723):
434-- move https://doi.org etc. prefix inside link text (#6723):
432fixLinks :: [Inline] -> [Inline]
435fixLinks :: [Inline] -> [Inline]
433fixLinks (Str t : Link attr [Str u1] (u2,tit) : xs)
436fixLinks (Str t : Link attr [Str u1] (u2,tit) : xs)
434− | t <> u1 == u2
437+ | u2 == t <> u1
435 = Link attr [Str (t <> u1)] (u2,tit) : fixLinks xs
438 = Link attr [Str (t <> u1)] (u2,tit) : fixLinks xs
436fixLinks (x:xs) = x : fixLinks xs
439fixLinks (x:xs) = x : fixLinks xs
437fixLinks [] = []
440fixLinks [] = []
@@ -501,7 +504,8 @@ insertRefs refkvs refclasses meta refs bs =
501 put True
504 put True
502 -- refHeader isn't used if you have an explicit references div
505 -- refHeader isn't used if you have an explicit references div
503 let cs' = ordNub $ cs ++ refclasses
506 let cs' = ordNub $ cs ++ refclasses
507+ let kvs' = ordNub $ kvs ++ refkvs
504− return $ Div ("refs",cs',kvs) (xs ++ refs)
508+ return $ Div ("refs",cs',kvs') (xs ++ refs)
505 go x = return x
509 go x = return x
506
510
507refTitle :: Meta -> Maybe [Inline]
511refTitle :: Meta -> Maybe [Inline]
@@ -560,42 +564,62 @@ extractText (FancyVal x) = toText x
560extractText (NumVal n) = T.pack (show n)
564extractText (NumVal n) = T.pack (show n)
561extractText _ = mempty
565extractText _ = mempty
562
566
567+-- Here we take the Spans with class csl-note that are left
568+-- after deNote has removed nested ones, and convert them
569+-- into real notes.
563−capitalizeNoteCitation :: Inline -> Inline
570+addNote :: Inline -> Inline
564−capitalizeNoteCitation (Cite cs [Note [Para ils]]) =
571+addNote (Span ("",["csl-note"],[]) ils) =
565− Cite cs
572+ Note [Para $
566− [Note [Para $ B.toList $ addTextCase Nothing CapitalizeFirst
573+ B.toList . addTextCase Nothing CapitalizeFirst . B.fromList $ ils]
567− $ B.fromList ils]]
574+addNote x = x
568−capitalizeNoteCitation x = x
575+
569−
576+-- Here we handle citation notes that occur inside footnotes
577+-- or other citation notes, in a note style. We don't want
578+-- notes inside notes, so we convert these to parenthesized
579+-- or comma-separated citations.
570−deNote :: [Inline] -> [Inline]
580+deNote :: Inline -> Inline
571−deNote [] = []
581+deNote (Note bs) =
572−deNote (Note bs:rest) =
582+ case bs of
573− Note (walk go bs) : deNote rest
583+ [Para (cit@(Cite (c:_) _) : ils)]
584+ | citationMode c /= AuthorInText ->
585+ -- if citation is first in note, no need to parenthesize.
586+ Note [Para (walk removeNotes $ cit : walk addParens ils)]
587+ _ -> Note (walk removeNotes . walk addParens $ bs)
588+
574 where
589 where
575− go [] = []
590+ addParens [] = []
576− go (Cite (c:cs) ils : zs)
591+ addParens (Cite (c:cs) ils : zs)
577 | citationMode c == AuthorInText
592 | citationMode c == AuthorInText
578− = Cite (c:cs) (concatMap (noteAfterComma (needsPeriod zs)) ils) : go zs
593+ = Cite (c:cs) (concatMap (noteAfterComma (needsPeriod zs)) ils) :
594+ addParens zs
579 | otherwise
595 | otherwise
580− = Cite (c:cs) (concatMap noteInParens ils) : go zs
596+ = Cite (c:cs) (concatMap noteInParens ils) : addParens zs
581− go (x:xs) = x : go xs
597+ addParens (x:xs) = x : addParens xs
598+
599+ removeNotes (Span ("",["csl-note"],[]) ils) = Span ("",[],[]) ils
600+ removeNotes x = x
601+
582 needsPeriod [] = True
602 needsPeriod [] = True
583 needsPeriod (Str t:_) = case T.uncons t of
603 needsPeriod (Str t:_) = case T.uncons t of
584 Nothing -> False
604 Nothing -> False
585 Just (c,_) -> isUpper c
605 Just (c,_) -> isUpper c
586 needsPeriod (Space:zs) = needsPeriod zs
606 needsPeriod (Space:zs) = needsPeriod zs
587 needsPeriod _ = False
607 needsPeriod _ = False
608+
588− noteInParens (Note bs')
609+ noteInParens (Span ("",["csl-note"],[]) ils)
589 = Space : Str "(" :
610 = Space : Str "(" :
590− removeFinalPeriod (blocksToInlines bs') ++ [Str ")"]
611+ removeFinalPeriod ils ++ [Str ")"]
591 noteInParens x = [x]
612 noteInParens x = [x]
613+
592− noteAfterComma needsPer (Note bs')
614+ noteAfterComma needsPer (Span ("",["csl-note"],[]) ils)
615+ | not (null ils)
593 = Str "," : Space :
616 = Str "," : Space :
594− (if needsPer
617+ if needsPer
595− then id
618+ then ils
596− else removeFinalPeriod) (blocksToInlines bs')
619+ else removeFinalPeriod ils
597 noteAfterComma _ x = [x]
620 noteAfterComma _ x = [x]
621+
598−deNote (x:xs) = x : deNote xs
622+deNote x = x
599
623
600-- Note: we can't use dropTextWhileEnd indiscriminately,
624-- Note: we can't use dropTextWhileEnd indiscriminately,
601-- because this would remove the final period on abbreviations like Ibid.
625-- because this would remove the final period on abbreviations like Ibid.
@@ -630,13 +654,8 @@ removeFinalPeriod ils =
630
654
631bcp47LangToIETF :: PandocMonad m => Text -> m (Maybe Lang)
655bcp47LangToIETF :: PandocMonad m => Text -> m (Maybe Lang)
632bcp47LangToIETF bcplang =
656bcp47LangToIETF bcplang =
633− case BCP47.parseBCP47 bcplang of
657+ case parseLang bcplang of
634 Left _ -> do
658 Left _ -> do
635 report $ InvalidLang bcplang
659 report $ InvalidLang bcplang
636 return Nothing
660 return Nothing
637− Right lang ->
638− return $ Just
661+ Right lang -> return $ Just lang
639− $ Lang (BCP47.langLanguage lang)
640− (if T.null (BCP47.langRegion lang)
641− then Nothing
642− else Just (BCP47.langRegion lang))
src/Text/Pandoc/Citeproc/BibTeX.hs+25−15
@@ -59,10 +59,11 @@ data Variant = Bibtex | Biblatex
59 deriving (Show, Eq, Ord)
59 deriving (Show, Eq, Ord)
60
60
61-- | Parse BibTeX or BibLaTeX into a list of 'Reference's.
61-- | Parse BibTeX or BibLaTeX into a list of 'Reference's.
62+readBibtexString :: ToSources a
62−readBibtexString :: Variant -- ^ bibtex or biblatex
63+ => Variant -- ^ bibtex or biblatex
63 -> Locale -- ^ Locale
64 -> Locale -- ^ Locale
64 -> (Text -> Bool) -- ^ Filter on citation ids
65 -> (Text -> Bool) -- ^ Filter on citation ids
65− -> Text -- ^ bibtex/biblatex text
66+ -> a -- ^ bibtex/biblatex text
66 -> Either ParseError [Reference Inlines]
67 -> Either ParseError [Reference Inlines]
67readBibtexString variant locale idpred contents = do
68readBibtexString variant locale idpred contents = do
68 case runParser (((resolveCrossRefs variant <$> bibEntries) <* eof) >>=
69 case runParser (((resolveCrossRefs variant <$> bibEntries) <* eof) >>=
@@ -70,9 +71,11 @@ readBibtexString variant locale idpred contents = do
70 filter (\item -> idpred (identifier item) &&
71 filter (\item -> idpred (identifier item) &&
71 entryType item /= "xdata"))
72 entryType item /= "xdata"))
72 (fromMaybe defaultLang $ localeLanguage locale, Map.empty)
73 (fromMaybe defaultLang $ localeLanguage locale, Map.empty)
73− "" contents of
74+ (initialSourceName sources) sources of
74 Left err -> Left err
75 Left err -> Left err
75 Right xs -> return xs
76 Right xs -> return xs
77+ where
78+ sources = toSources contents
76
79
77-- | Write BibTeX or BibLaTeX given given a 'Reference'.
80-- | Write BibTeX or BibLaTeX given given a 'Reference'.
78writeBibtexString :: WriterOptions -- ^ options (for writing LaTex)
81writeBibtexString :: WriterOptions -- ^ options (for writing LaTex)
@@ -155,6 +158,7 @@ writeBibtexString opts variant mblang ref =
155 , "langid"
158 , "langid"
156 , "abstract"
159 , "abstract"
157 , "keywords"
160 , "keywords"
161+ , "annote"
158 ]
162 ]
159 Bibtex ->
163 Bibtex ->
160 [ "author"
164 [ "author"
@@ -174,6 +178,7 @@ writeBibtexString opts variant mblang ref =
174 , "address"
178 , "address"
175 , "type"
179 , "type"
176 , "note"
180 , "note"
181+ , "annote"
177 ]
182 ]
178
183
179 valToInlines (TextVal t) = B.text t
184 valToInlines (TextVal t) = B.text t
@@ -205,10 +210,13 @@ writeBibtexString opts variant mblang ref =
205 [ (", " <>) <$> nameGiven name,
210 [ (", " <>) <$> nameGiven name,
206 nameDroppingParticle name ]
211 nameDroppingParticle name ]
207
212
208− mblang' = (parseLang <$> getVariableAsText "language") <|> mblang
213+ mblang' = case getVariableAsText "language" of
214+ Just l -> either (const Nothing) Just $ parseLang l
215+ Nothing -> mblang
209
216
210 titlecase = case mblang' of
217 titlecase = case mblang' of
218+ Just lang | langLanguage lang == "en"
211− Just (Lang "en" _) -> titlecase'
219+ -> titlecase'
212 Nothing -> titlecase'
220 Nothing -> titlecase'
213 _ ->
221 _ ->
214 case variant of
222 case variant of
@@ -331,12 +339,12 @@ writeBibtexString opts variant mblang ref =
331 renderFields = mconcat . intersperse ("," <> cr) . mapMaybe renderField
339 renderFields = mconcat . intersperse ("," <> cr) . mapMaybe renderField
332
340
333defaultLang :: Lang
341defaultLang :: Lang
334−defaultLang = Lang "en" (Just "US")
342+defaultLang = Lang "en" Nothing (Just "US") [] [] []
335
343
336-- a map of bibtex "string" macros
344-- a map of bibtex "string" macros
337type StringMap = Map.Map Text Text
345type StringMap = Map.Map Text Text
338
346
339−type BibParser = Parser Text (Lang, StringMap)
347+type BibParser = Parser Sources (Lang, StringMap)
340
348
341data Item = Item{ identifier :: Text
349data Item = Item{ identifier :: Text
342 , sourcePos :: SourcePos
350 , sourcePos :: SourcePos
@@ -351,9 +359,7 @@ itemToReference locale variant item = do
351 bib item $ do
359 bib item $ do
352 let lang = fromMaybe defaultLang $ localeLanguage locale
360 let lang = fromMaybe defaultLang $ localeLanguage locale
353 modify $ \st -> st{ localeLang = lang,
361 modify $ \st -> st{ localeLang = lang,
354− untitlecase = case lang of
362+ untitlecase = langLanguage lang == "en" }
355− (Lang "en" _) -> True
356− _ -> False }
357
363
358 id' <- asks identifier
364 id' <- asks identifier
359 otherIds <- (Just <$> getRawField "ids")
365 otherIds <- (Just <$> getRawField "ids")
@@ -711,7 +717,7 @@ itemToReference locale variant item = do
711
717
712
718
713bib :: Item -> Bib a -> BibParser a
719bib :: Item -> Bib a -> BibParser a
714−bib entry m = fst <$> evalRWST m entry (BibState True (Lang "en" (Just "US")))
720+bib entry m = fst <$> evalRWST m entry (BibState True defaultLang)
715
721
716resolveCrossRefs :: Variant -> [Item] -> [Item]
722resolveCrossRefs :: Variant -> [Item] -> [Item]
717resolveCrossRefs variant entries =
723resolveCrossRefs variant entries =
@@ -803,7 +809,7 @@ bibEntries = do
803 (bibComment <|> bibPreamble <|> bibString))
809 (bibComment <|> bibPreamble <|> bibString))
804
810
805bibSkip :: BibParser ()
811bibSkip :: BibParser ()
806−bibSkip = () <$ take1WhileP (/='@')
812+bibSkip = skipMany1 (satisfy (/='@'))
807
813
808bibComment :: BibParser ()
814bibComment :: BibParser ()
809bibComment = do
815bibComment = do
@@ -828,6 +834,9 @@ bibString = do
828 updateState (\(l,m) -> (l, Map.insert k v m))
834 updateState (\(l,m) -> (l, Map.insert k v m))
829 return ()
835 return ()
830
836
837+take1WhileP :: Monad m => (Char -> Bool) -> ParserT Sources u m Text
838+take1WhileP f = T.pack <$> many1 (satisfy f)
839+
831inBraces :: BibParser Text
840inBraces :: BibParser Text
832inBraces = do
841inBraces = do
833 char '{'
842 char '{'
@@ -859,7 +868,7 @@ fieldName = resolveAlias . T.toLower
859
868
860isBibtexKeyChar :: Char -> Bool
869isBibtexKeyChar :: Char -> Bool
861isBibtexKeyChar c =
870isBibtexKeyChar c =
862− isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]*&" :: [Char])
871+ isAlphaNum c || c `elem` (".:;?!`'()$/*@_+=-[]*&" :: [Char])
863
872
864bibItem :: BibParser Item
873bibItem :: BibParser Item
865bibItem = do
874bibItem = do
@@ -1456,8 +1465,9 @@ resolveKey lang ils = Walk.walk go ils
1456 go x = x
1465 go x = x
1457
1466
1458resolveKey' :: Lang -> Text -> Text
1467resolveKey' :: Lang -> Text -> Text
1459−resolveKey' lang@(Lang l _) k =
1468+resolveKey' lang k =
1460− case Map.lookup l biblatexStringMap >>= Map.lookup (T.toLower k) of
1469+ case Map.lookup (langLanguage lang) biblatexStringMap >>=
1470+ Map.lookup (T.toLower k) of
1461 Nothing -> k
1471 Nothing -> k
1462 Just (x, _) -> either (const k) stringify $ parseLaTeX lang x
1472 Just (x, _) -> either (const k) stringify $ parseLaTeX lang x
1463
1473
src/Text/Pandoc/Citeproc/Data.hs+7−6
@@ -10,7 +10,7 @@ import qualified Data.Text.Encoding as TE
10import qualified Data.Text as T
10import qualified Data.Text as T
11import Data.Text (Text)
11import Data.Text (Text)
12import Text.Pandoc.Citeproc.Util (toIETF)
12import Text.Pandoc.Citeproc.Util (toIETF)
13−import Citeproc (Lang(..), parseLang)
13+import Text.Collate.Lang (Lang(..), parseLang)
14
14
15biblatexLocalizations :: [(FilePath, ByteString)]
15biblatexLocalizations :: [(FilePath, ByteString)]
16biblatexLocalizations = $(embedDir "citeproc/biblatex-localization")
16biblatexLocalizations = $(embedDir "citeproc/biblatex-localization")
@@ -21,11 +21,12 @@ biblatexStringMap :: M.Map Text (M.Map Text (Text, Text))
21biblatexStringMap = foldr go mempty biblatexLocalizations
21biblatexStringMap = foldr go mempty biblatexLocalizations
22 where
22 where
23 go (fp, bs) =
23 go (fp, bs) =
24− let Lang lang _ = parseLang (toIETF $ T.takeWhile (/= '.') $ T.pack fp)
25− ls = T.lines $ TE.decodeUtf8 bs
24+ let ls = T.lines $ TE.decodeUtf8 bs
25+ in case parseLang (toIETF $ T.takeWhile (/= '.') $ T.pack fp) of
26− in if length ls > 4
26+ Right lang | length ls > 4
27+ -> M.insert (langLanguage lang)
27− then M.insert lang (toStringMap $ map (T.splitOn "|") ls)
28+ (toStringMap $ map (T.splitOn "|") ls)
28− else id
29+ _ -> id
29 toStringMap = foldr go' mempty
30 toStringMap = foldr go' mempty
30 go' [term, x, y] = M.insert term (x, y)
31 go' [term, x, y] = M.insert term (x, y)
31 go' _ = id
32 go' _ = id
src/Text/Pandoc/Citeproc/Locator.hs+20−4
@@ -7,6 +7,7 @@ where
7import Citeproc.Types
7import Citeproc.Types
8import Data.Text (Text)
8import Data.Text (Text)
9import qualified Data.Text as T
9import qualified Data.Text as T
10+import Data.List (foldl')
10import Text.Parsec
11import Text.Parsec
11import Text.Pandoc.Definition
12import Text.Pandoc.Definition
12import Text.Pandoc.Parsing (romanNumeral)
13import Text.Pandoc.Parsing (romanNumeral)
@@ -19,7 +20,7 @@ parseLocator :: Locale -> [Inline] -> (Maybe (Text, Text), [Inline])
19parseLocator locale inp =
20parseLocator locale inp =
20 case parse (pLocatorWords (toLocatorMap locale)) "suffix" $ splitInp inp of
21 case parse (pLocatorWords (toLocatorMap locale)) "suffix" $ splitInp inp of
21 Right r -> r
22 Right r -> r
22− Left _ -> (Nothing, inp)
23+ Left _ -> (Nothing, maybeAddComma inp)
23
24
24splitInp :: [Inline] -> [Inline]
25splitInp :: [Inline] -> [Inline]
25splitInp = splitStrWhen (\c -> isSpace c || (isPunctuation c && c /= ':'))
26splitInp = splitStrWhen (\c -> isSpace c || (isPunctuation c && c /= ':'))
@@ -41,9 +42,17 @@ pLocatorWords locMap = do
41 -- i.e. the first one will be " 9"
42 -- i.e. the first one will be " 9"
42 return $
43 return $
43 if T.null la && T.null lo
44 if T.null la && T.null lo
44− then (Nothing, s)
45+ then (Nothing, maybeAddComma s)
45 else (Just (la, T.strip lo), s)
46 else (Just (la, T.strip lo), s)
46
47
48+maybeAddComma :: [Inline] -> [Inline]
49+maybeAddComma [] = []
50+maybeAddComma ils@(Space : _) = ils
51+maybeAddComma ils@(Str t : _)
52+ | Just (c, _) <- T.uncons t
53+ , isPunctuation c = ils
54+maybeAddComma ils = Str "," : Space : ils
55+
47pLocatorDelimited :: LocatorMap -> LocatorParser (Text, Text)
56pLocatorDelimited :: LocatorMap -> LocatorParser (Text, Text)
48pLocatorDelimited locMap = try $ do
57pLocatorDelimited locMap = try $ do
49 _ <- pMatchChar "{" (== '{')
58 _ <- pMatchChar "{" (== '{')
@@ -96,7 +105,7 @@ pLocatorLabel' locMap lim = go ""
96 t <- anyToken
105 t <- anyToken
97 ts <- manyTill anyToken (try $ lookAhead lim)
106 ts <- manyTill anyToken (try $ lookAhead lim)
98 let s = acc <> stringify (t:ts)
107 let s = acc <> stringify (t:ts)
99− case M.lookup (T.strip s) locMap of
108+ case M.lookup (T.toCaseFold $ T.strip s) locMap of
100 -- try to find a longer one, or return this one
109 -- try to find a longer one, or return this one
101 Just l -> go s <|> return (l, False)
110 Just l -> go s <|> return (l, False)
102 Nothing -> go s
111 Nothing -> go s
@@ -139,7 +148,7 @@ pBalancedBraces braces p = try $ do
139 where
148 where
140 except = notFollowedBy pBraces >> p
149 except = notFollowedBy pBraces >> p
141 -- outer and inner
150 -- outer and inner
142− surround = foldl (\a (open, close) -> sur open close except <|> a)
151+ surround = foldl' (\a (open, close) -> sur open close except <|> a)
143 except
152 except
144 braces
153 braces
145
154
@@ -180,6 +189,7 @@ pPageUnit = roman <|> plainUnit
180 plainUnit = do
189 plainUnit = do
181 ts <- many1 (notFollowedBy pSpace >>
190 ts <- many1 (notFollowedBy pSpace >>
182 notFollowedBy pLocatorPunct >>
191 notFollowedBy pLocatorPunct >>
192+ notFollowedBy pMath >>
183 anyToken)
193 anyToken)
184 let s = stringify ts
194 let s = stringify ts
185 -- otherwise look for actual digits or -s
195 -- otherwise look for actual digits or -s
@@ -210,6 +220,12 @@ pMatchChar msg f = satisfyTok f' <?> msg
210pSpace :: LocatorParser Inline
220pSpace :: LocatorParser Inline
211pSpace = satisfyTok (\t -> isSpacey t || t == Str "\160") <?> "space"
221pSpace = satisfyTok (\t -> isSpacey t || t == Str "\160") <?> "space"
212
222
223+pMath :: LocatorParser Inline
224+pMath = satisfyTok isMath
225+ where
226+ isMath (Math{}) = True
227+ isMath _ = False
228+
213satisfyTok :: (Inline -> Bool) -> LocatorParser Inline
229satisfyTok :: (Inline -> Bool) -> LocatorParser Inline
214satisfyTok f = tokenPrim show (\sp _ _ -> sp) (\tok -> if f tok
230satisfyTok f = tokenPrim show (\sp _ _ -> sp) (\tok -> if f tok
215 then Just tok
231 then Just tok
src/Text/Pandoc/Class/CommonState.hs+1−1
@@ -19,7 +19,7 @@ where
19
19
20import Data.Default (Default (def))
20import Data.Default (Default (def))
21import Data.Text (Text)
21import Data.Text (Text)
22−import Text.Pandoc.BCP47 (Lang)
22+import Text.Collate.Lang (Lang)
23import Text.Pandoc.MediaBag (MediaBag)
23import Text.Pandoc.MediaBag (MediaBag)
24import Text.Pandoc.Logging (LogMessage, Verbosity (WARNING))
24import Text.Pandoc.Logging (LogMessage, Verbosity (WARNING))
25import Text.Pandoc.Translations (Translations)
25import Text.Pandoc.Translations (Translations)
src/Text/Pandoc/Class/IO.hs+21−22
@@ -62,7 +62,7 @@ import Text.Pandoc.Definition (Pandoc, Inline (Image))
62import Text.Pandoc.Error (PandocError (..))
62import Text.Pandoc.Error (PandocError (..))
63import Text.Pandoc.Logging (LogMessage (..), messageVerbosity, showLogMessage)
63import Text.Pandoc.Logging (LogMessage (..), messageVerbosity, showLogMessage)
64import Text.Pandoc.MIME (MimeType)
64import Text.Pandoc.MIME (MimeType)
65−import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory)
65+import Text.Pandoc.MediaBag (MediaBag, MediaItem(..), lookupMedia, mediaItems)
66import Text.Pandoc.Walk (walk)
66import Text.Pandoc.Walk (walk)
67import qualified Control.Exception as E
67import qualified Control.Exception as E
68import qualified Data.ByteString as B
68import qualified Data.ByteString as B
@@ -200,33 +200,32 @@ alertIndent (l:ls) = do
200extractMedia :: (PandocMonad m, MonadIO m) => FilePath -> Pandoc -> m Pandoc
200extractMedia :: (PandocMonad m, MonadIO m) => FilePath -> Pandoc -> m Pandoc
201extractMedia dir d = do
201extractMedia dir d = do
202 media <- getMediaBag
202 media <- getMediaBag
203− case [fp | (fp, _, _) <- mediaDirectory media] of
203+ let items = mediaItems media
204+ if null items
204− [] -> return d
205+ then return d
205− fps -> do
206+ else do
206− mapM_ (writeMedia dir media) fps
207+ mapM_ (writeMedia dir) items
207− return $ walk (adjustImagePath dir fps) d
208+ return $ walk (adjustImagePath dir media) d
208
209
209-- | Write the contents of a media bag to a path.
210-- | Write the contents of a media bag to a path.
210writeMedia :: (PandocMonad m, MonadIO m)
211writeMedia :: (PandocMonad m, MonadIO m)
211− => FilePath -> MediaBag -> FilePath
212+ => FilePath
213+ -> (FilePath, MimeType, BL.ByteString)
212 -> m ()
214 -> m ()
213−writeMedia dir mediabag subpath = do
215+writeMedia dir (fp, _mt, bs) = do
214− -- we join and split to convert a/b/c to a\b\c on Windows;
216+ -- we normalize to get proper path separators for the platform
215− -- in zip containers all paths use /
216− let fullpath = dir </> unEscapeString (normalise subpath)
217+ let fullpath = normalise $ dir </> fp
217− let mbcontents = lookupMedia subpath mediabag
218− case mbcontents of
219− Nothing -> throwError $ PandocResourceNotFound $ pack subpath
220− Just (_, bs) -> do
221− report $ Extracting $ pack fullpath
222− liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath)
218+ liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath)
223− logIOError $ BL.writeFile fullpath bs
219+ logIOError $ BL.writeFile fullpath bs
224
220
225-- | If the given Inline element is an image with a @src@ path equal to
221-- | If the given Inline element is an image with a @src@ path equal to
226-- one in the list of @paths@, then prepends @dir@ to the image source;
222-- one in the list of @paths@, then prepends @dir@ to the image source;
227-- returns the element unchanged otherwise.
223-- returns the element unchanged otherwise.
228−adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
224+adjustImagePath :: FilePath -> MediaBag -> Inline -> Inline
229−adjustImagePath dir paths (Image attr lab (src, tit))
225+adjustImagePath dir mediabag (Image attr lab (src, tit)) =
230− | unpack src `elem` paths
226+ case lookupMedia (T.unpack src) mediabag of
227+ Nothing -> Image attr lab (src, tit)
228+ Just item ->
229+ let fullpath = dir </> mediaPath item
231− = Image attr lab (pack (normalise $ dir </> unpack src), tit)
230+ in Image attr lab (T.pack fullpath, tit)
232adjustImagePath _ _ x = x
231adjustImagePath _ _ x = x
src/Text/Pandoc/Class/PandocMonad.hs+24−34
@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TupleSections #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
@@ -37,7 +38,6 @@ module Text.Pandoc.Class.PandocMonad
37 , setUserDataDir
38 , setUserDataDir
38 , getUserDataDir
39 , getUserDataDir
39 , fetchItem
40 , fetchItem
40− , fetchMediaResource
41 , getInputFiles
41 , getInputFiles
42 , setInputFiles
42 , setInputFiles
43 , getOutputFile
43 , getOutputFile
@@ -57,8 +57,7 @@ module Text.Pandoc.Class.PandocMonad
57import Codec.Archive.Zip
57import Codec.Archive.Zip
58import Control.Monad.Except (MonadError (catchError, throwError),
58import Control.Monad.Except (MonadError (catchError, throwError),
59 MonadTrans, lift, when)
59 MonadTrans, lift, when)
60−import Data.Digest.Pure.SHA (sha1, showDigest)
60+import Data.List (foldl')
61−import Data.Maybe (fromMaybe)
62import Data.Time (UTCTime)
61import Data.Time (UTCTime)
63import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds,
62import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds,
64 posixSecondsToUTCTime)
63 posixSecondsToUTCTime)
@@ -66,16 +65,16 @@ import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime)
66import Network.URI ( escapeURIString, nonStrictRelativeTo,
65import Network.URI ( escapeURIString, nonStrictRelativeTo,
67 unEscapeString, parseURIReference, isAllowedInURI,
66 unEscapeString, parseURIReference, isAllowedInURI,
68 parseURI, URI(..) )
67 parseURI, URI(..) )
69−import System.FilePath ((</>), (<.>), takeExtension, dropExtension,
68+import System.FilePath ((</>), takeExtension, dropExtension,
70− isRelative, splitDirectories)
69+ isRelative, splitDirectories, makeRelative)
71import System.Random (StdGen)
70import System.Random (StdGen)
72−import Text.Pandoc.BCP47 (Lang(..), parseBCP47, renderLang)
71+import Text.Collate.Lang (Lang(..), parseLang, renderLang)
73import Text.Pandoc.Class.CommonState (CommonState (..))
72import Text.Pandoc.Class.CommonState (CommonState (..))
74import Text.Pandoc.Definition
73import Text.Pandoc.Definition
75import Text.Pandoc.Error
74import Text.Pandoc.Error
76import Text.Pandoc.Logging
75import Text.Pandoc.Logging
77−import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType)
76+import Text.Pandoc.MIME (MimeType, getMimeType)
78−import Text.Pandoc.MediaBag (MediaBag, lookupMedia)
77+import Text.Pandoc.MediaBag (MediaBag, lookupMedia, MediaItem(..))
79import Text.Pandoc.Shared (uriPathToPath, safeRead)
78import Text.Pandoc.Shared (uriPathToPath, safeRead)
80import Text.Pandoc.Translations (Term(..), Translations, lookupTerm,
79import Text.Pandoc.Translations (Term(..), Translations, lookupTerm,
81 readTranslations)
80 readTranslations)
@@ -284,7 +283,7 @@ readFileFromDirs (d:ds) f = catchError
284toLang :: PandocMonad m => Maybe T.Text -> m (Maybe Lang)
283toLang :: PandocMonad m => Maybe T.Text -> m (Maybe Lang)
285toLang Nothing = return Nothing
284toLang Nothing = return Nothing
286toLang (Just s) =
285toLang (Just s) =
287− case parseBCP47 s of
286+ case parseLang s of
288 Left _ -> do
287 Left _ -> do
289 report $ InvalidLang s
288 report $ InvalidLang s
290 return Nothing
289 return Nothing
@@ -375,7 +374,8 @@ fetchItem :: PandocMonad m
375fetchItem s = do
374fetchItem s = do
376 mediabag <- getMediaBag
375 mediabag <- getMediaBag
377 case lookupMedia (T.unpack s) mediabag of
376 case lookupMedia (T.unpack s) mediabag of
378− Just (mime, bs) -> return (BL.toStrict bs, Just mime)
377+ Just item -> return (BL.toStrict (mediaContents item),
378+ Just (mediaMimeType item))
379 Nothing -> downloadOrRead s
379 Nothing -> downloadOrRead s
380
380
381-- | Returns the content and, if available, the MIME type of a resource.
381-- | Returns the content and, if available, the MIME type of a resource.
@@ -410,9 +410,10 @@ downloadOrRead s = do
410 _ -> readLocalFile fp -- get from local file system
410 _ -> readLocalFile fp -- get from local file system
411 where readLocalFile f = do
411 where readLocalFile f = do
412 resourcePath <- getResourcePath
412 resourcePath <- getResourcePath
413− cont <- if isRelative f
413+ (fp', cont) <- if isRelative f
414− then withPaths resourcePath readFileStrict f
414+ then withPaths resourcePath readFileStrict f
415− else readFileStrict f
415+ else (f,) <$> readFileStrict f
416+ report $ LoadedResource f (makeRelative "." fp')
416 return (cont, mime)
417 return (cont, mime)
417 httpcolon = URI{ uriScheme = "http:",
418 httpcolon = URI{ uriScheme = "http:",
418 uriAuthority = Nothing,
419 uriAuthority = Nothing,
@@ -612,7 +613,7 @@ checkExistence fn = do
612-- | Canonicalizes a file path by removing redundant @.@ and @..@.
613-- | Canonicalizes a file path by removing redundant @.@ and @..@.
613makeCanonical :: FilePath -> FilePath
614makeCanonical :: FilePath -> FilePath
614makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
615makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
615− where transformPathParts = reverse . foldl go []
616+ where transformPathParts = reverse . foldl' go []
616 go as "." = as
617 go as "." = as
617 go (_:as) ".." = as
618 go (_:as) ".." = as
618 go as x = x : as
619 go as x = x : as
@@ -622,25 +623,13 @@ makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
622-- that filepath. Returns the result of the first successful execution
623-- that filepath. Returns the result of the first successful execution
623-- of the action, or throws a @PandocResourceNotFound@ exception if the
624-- of the action, or throws a @PandocResourceNotFound@ exception if the
624-- action errors for all filepaths.
625-- action errors for all filepaths.
626+withPaths :: PandocMonad m
625−withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a
627+ => [FilePath] -> (FilePath -> m a) -> FilePath -> m (FilePath, a)
626withPaths [] _ fp = throwError $ PandocResourceNotFound $ T.pack fp
628withPaths [] _ fp = throwError $ PandocResourceNotFound $ T.pack fp
627withPaths (p:ps) action fp =
629withPaths (p:ps) action fp =
628− catchError (action (p </> fp))
630+ catchError ((p </> fp,) <$> action (p </> fp))
629 (\_ -> withPaths ps action fp)
631 (\_ -> withPaths ps action fp)
630
632
631−-- | Fetch local or remote resource (like an image) and provide data suitable
632−-- for adding it to the MediaBag.
633−fetchMediaResource :: PandocMonad m
634− => T.Text -> m (FilePath, Maybe MimeType, BL.ByteString)
635−fetchMediaResource src = do
636− (bs, mt) <- downloadOrRead src
637− let ext = fromMaybe (T.pack $ takeExtension $ T.unpack src)
638− (mt >>= extensionFromMimeType)
639− let bs' = BL.fromChunks [bs]
640− let basename = showDigest $ sha1 bs'
641− let fname = basename <.> T.unpack ext
642− return (fname, mt, bs')
643−
644-- | Traverse tree, filling media bag for any images that
633-- | Traverse tree, filling media bag for any images that
645-- aren't already in the media bag.
634-- aren't already in the media bag.
646fillMediaBag :: PandocMonad m => Pandoc -> m Pandoc
635fillMediaBag :: PandocMonad m => Pandoc -> m Pandoc
@@ -648,12 +637,13 @@ fillMediaBag d = walkM handleImage d
648 where handleImage :: PandocMonad m => Inline -> m Inline
637 where handleImage :: PandocMonad m => Inline -> m Inline
649 handleImage (Image attr lab (src, tit)) = catchError
638 handleImage (Image attr lab (src, tit)) = catchError
650 (do mediabag <- getMediaBag
639 (do mediabag <- getMediaBag
651− case lookupMedia (T.unpack src) mediabag of
640+ let fp = T.unpack src
641+ case lookupMedia fp mediabag of
652− Just (_, _) -> return $ Image attr lab (src, tit)
642+ Just _ -> return ()
653 Nothing -> do
643 Nothing -> do
654− (fname, mt, bs) <- fetchMediaResource src
644+ (bs, mt) <- fetchItem src
655− insertMedia fname mt bs
645+ insertMedia fp mt (BL.fromStrict bs)
656− return $ Image attr lab (T.pack fname, tit))
646+ return $ Image attr lab (src, tit))
657 (\e ->
647 (\e ->
658 case e of
648 case e of
659 PandocResourceNotFound _ -> do
649 PandocResourceNotFound _ -> do
src/Text/Pandoc/Error.hs+23−16
@@ -23,26 +23,27 @@ import Control.Exception (Exception, displayException)
23import Data.Typeable (Typeable)
23import Data.Typeable (Typeable)
24import Data.Word (Word8)
24import Data.Word (Word8)
25import Data.Text (Text)
25import Data.Text (Text)
26+import Data.List (sortOn)
26import qualified Data.Text as T
27import qualified Data.Text as T
28+import Data.Ord (Down(..))
27import GHC.Generics (Generic)
29import GHC.Generics (Generic)
28import Network.HTTP.Client (HttpException)
30import Network.HTTP.Client (HttpException)
29import System.Exit (ExitCode (..), exitWith)
31import System.Exit (ExitCode (..), exitWith)
30import System.IO (stderr)
32import System.IO (stderr)
31import qualified Text.Pandoc.UTF8 as UTF8
33import qualified Text.Pandoc.UTF8 as UTF8
34+import Text.Pandoc.Sources (Sources(..))
32import Text.Printf (printf)
35import Text.Printf (printf)
33import Text.Parsec.Error
36import Text.Parsec.Error
34import Text.Parsec.Pos hiding (Line)
37import Text.Parsec.Pos hiding (Line)
35import Text.Pandoc.Shared (tshow)
38import Text.Pandoc.Shared (tshow)
36import Citeproc (CiteprocError, prettyCiteprocError)
39import Citeproc (CiteprocError, prettyCiteprocError)
37
40
38−type Input = Text
39−
40data PandocError = PandocIOError Text IOError
41data PandocError = PandocIOError Text IOError
41 | PandocHttpError Text HttpException
42 | PandocHttpError Text HttpException
42 | PandocShouldNeverHappenError Text
43 | PandocShouldNeverHappenError Text
43 | PandocSomeError Text
44 | PandocSomeError Text
44 | PandocParseError Text
45 | PandocParseError Text
45− | PandocParsecError Input ParseError
46+ | PandocParsecError Sources ParseError
46 | PandocMakePDFError Text
47 | PandocMakePDFError Text
47 | PandocOptionError Text
48 | PandocOptionError Text
48 | PandocSyntaxMapError Text
49 | PandocSyntaxMapError Text
@@ -81,22 +82,28 @@ renderError e =
81 "Please report this to pandoc's developers: " <> s
82 "Please report this to pandoc's developers: " <> s
82 PandocSomeError s -> s
83 PandocSomeError s -> s
83 PandocParseError s -> s
84 PandocParseError s -> s
84− PandocParsecError input err' ->
85+ PandocParsecError (Sources inputs) err' ->
85 let errPos = errorPos err'
86 let errPos = errorPos err'
86 errLine = sourceLine errPos
87 errLine = sourceLine errPos
87 errColumn = sourceColumn errPos
88 errColumn = sourceColumn errPos
89+ errFile = sourceName errPos
90+ errorInFile =
91+ case sortOn (Down . sourceLine . fst)
92+ [ (pos,t)
93+ | (pos,t) <- inputs
94+ , sourceName pos == errFile
95+ , sourceLine pos <= errLine
96+ ] of
97+ [] -> ""
98+ ((pos,txt):_) ->
88− ls = T.lines input <> [""]
99+ let ls = T.lines txt <> [""]
100+ ln = (errLine - sourceLine pos) + 1
89− errorInFile = if length ls > errLine - 1
101+ in if length ls > ln && ln >= 1
90− then T.concat ["\n", ls !! (errLine - 1)
102+ then T.concat ["\n", ls !! (ln - 1)
91− ,"\n", T.replicate (errColumn - 1) " "
103+ ,"\n", T.replicate (errColumn - 1) " "
92− ,"^"]
104+ ,"^"]
93− else ""
105+ else ""
94− in "\nError at " <> tshow err' <>
106+ in "Error at " <> tshow err' <> errorInFile
95− -- if error comes from a chunk or included file,
96− -- then we won't get the right text this way:
97− if sourceName errPos == "source"
98− then errorInFile
99− else ""
100 PandocMakePDFError s -> s
107 PandocMakePDFError s -> s
101 PandocOptionError s -> s
108 PandocOptionError s -> s
102 PandocSyntaxMapError s -> s
109 PandocSyntaxMapError s -> s
src/Text/Pandoc/Extensions.hs+9−1
@@ -34,6 +34,7 @@ module Text.Pandoc.Extensions ( Extension(..)
34where
34where
35import Data.Bits (clearBit, setBit, testBit, (.|.))
35import Data.Bits (clearBit, setBit, testBit, (.|.))
36import Data.Data (Data)
36import Data.Data (Data)
37+import Data.List (foldl')
37import qualified Data.Text as T
38import qualified Data.Text as T
38import Data.Typeable (Typeable)
39import Data.Typeable (Typeable)
39import GHC.Generics (Generic)
40import GHC.Generics (Generic)
@@ -135,6 +136,8 @@ data Extension =
135 | Ext_raw_html -- ^ Allow raw HTML
136 | Ext_raw_html -- ^ Allow raw HTML
136 | Ext_raw_tex -- ^ Allow raw TeX (other than math)
137 | Ext_raw_tex -- ^ Allow raw TeX (other than math)
137 | Ext_raw_markdown -- ^ Parse markdown in ipynb as raw markdown
138 | Ext_raw_markdown -- ^ Parse markdown in ipynb as raw markdown
139+ | Ext_rebase_relative_paths -- ^ Rebase relative image and link paths,
140+ -- relative to directory of containing file
138 | Ext_shortcut_reference_links -- ^ Shortcut reference links
141 | Ext_shortcut_reference_links -- ^ Shortcut reference links
139 | Ext_simple_tables -- ^ Pandoc-style simple tables
142 | Ext_simple_tables -- ^ Pandoc-style simple tables
140 | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes
143 | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes
@@ -353,6 +356,7 @@ getDefaultExtensions "gfm" = extensionsFromList
353 , Ext_strikeout
356 , Ext_strikeout
354 , Ext_task_lists
357 , Ext_task_lists
355 , Ext_emoji
358 , Ext_emoji
359+ , Ext_yaml_metadata_block
356 ]
360 ]
357getDefaultExtensions "commonmark" = extensionsFromList
361getDefaultExtensions "commonmark" = extensionsFromList
358 [Ext_raw_html]
362 [Ext_raw_html]
@@ -378,6 +382,7 @@ getDefaultExtensions "commonmark_x" = extensionsFromList
378 , Ext_raw_attribute
382 , Ext_raw_attribute
379 , Ext_implicit_header_references
383 , Ext_implicit_header_references
380 , Ext_attributes
384 , Ext_attributes
385+ , Ext_yaml_metadata_block
381 ]
386 ]
382getDefaultExtensions "org" = extensionsFromList
387getDefaultExtensions "org" = extensionsFromList
383 [Ext_citations,
388 [Ext_citations,
@@ -459,6 +464,7 @@ getAllExtensions f = universalExtensions <> getAll f
459 , Ext_gutenberg
464 , Ext_gutenberg
460 , Ext_smart
465 , Ext_smart
461 , Ext_literate_haskell
466 , Ext_literate_haskell
467+ , Ext_rebase_relative_paths
462 ]
468 ]
463 getAll "markdown_strict" = allMarkdownExtensions
469 getAll "markdown_strict" = allMarkdownExtensions
464 getAll "markdown_phpextra" = allMarkdownExtensions
470 getAll "markdown_phpextra" = allMarkdownExtensions
@@ -510,6 +516,8 @@ getAllExtensions f = universalExtensions <> getAll f
510 , Ext_implicit_header_references
516 , Ext_implicit_header_references
511 , Ext_attributes
517 , Ext_attributes
512 , Ext_sourcepos
518 , Ext_sourcepos
519+ , Ext_yaml_metadata_block
520+ , Ext_rebase_relative_paths
513 ]
521 ]
514 getAll "commonmark_x" = getAll "commonmark"
522 getAll "commonmark_x" = getAll "commonmark"
515 getAll "org" = autoIdExtensions <>
523 getAll "org" = autoIdExtensions <>
@@ -593,7 +601,7 @@ parseFormatSpec :: T.Text
593parseFormatSpec = parse formatSpec ""
601parseFormatSpec = parse formatSpec ""
594 where formatSpec = do
602 where formatSpec = do
595 name <- formatName
603 name <- formatName
596− (extsToEnable, extsToDisable) <- foldl (flip ($)) ([],[]) <$>
604+ (extsToEnable, extsToDisable) <- foldl' (flip ($)) ([],[]) <$>
597 many extMod
605 many extMod
598 return (T.pack name, reverse extsToEnable, reverse extsToDisable)
606 return (T.pack name, reverse extsToEnable, reverse extsToDisable)
599 formatName = many1 $ noneOf "-+"
607 formatName = many1 $ noneOf "-+"
src/Text/Pandoc/Highlighting.hs+5−5
@@ -52,12 +52,12 @@ highlightingStyles =
52 ("breezedark", breezeDark),
52 ("breezedark", breezeDark),
53 ("haddock", haddock)]
53 ("haddock", haddock)]
54
54
55−languages :: [T.Text]
55+languages :: SyntaxMap -> [T.Text]
56−languages = [T.toLower (sName s) | s <- M.elems defaultSyntaxMap]
56+languages syntaxmap = [T.toLower (sName s) | s <- M.elems syntaxmap]
57
57
58−languagesByExtension :: T.Text -> [T.Text]
58+languagesByExtension :: SyntaxMap -> T.Text -> [T.Text]
59−languagesByExtension ext =
59+languagesByExtension syntaxmap ext =
60− [T.toLower (sName s) | s <- syntaxesByExtension defaultSyntaxMap (T.unpack ext)]
60+ [T.toLower (sName s) | s <- syntaxesByExtension syntaxmap (T.unpack ext)]
61
61
62highlight :: SyntaxMap
62highlight :: SyntaxMap
63 -> (FormatOptions -> [SourceLine] -> a) -- ^ Formatter
63 -> (FormatOptions -> [SourceLine] -> a) -- ^ Formatter
src/Text/Pandoc/ImageSize.hs+5−1
@@ -57,7 +57,8 @@ import Codec.Picture (decodeImageWithMetadata)
57-- quick and dirty functions to get image sizes
57-- quick and dirty functions to get image sizes
58-- algorithms borrowed from wwwis.pl
58-- algorithms borrowed from wwwis.pl
59
59
60−data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf deriving Show
60+data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf | Tiff
61+ deriving Show
61data Direction = Width | Height
62data Direction = Width | Height
62instance Show Direction where
63instance Show Direction where
63 show Width = "width"
64 show Width = "width"
@@ -100,6 +101,8 @@ imageType :: ByteString -> Maybe ImageType
100imageType img = case B.take 4 img of
101imageType img = case B.take 4 img of
101 "\x89\x50\x4e\x47" -> return Png
102 "\x89\x50\x4e\x47" -> return Png
102 "\x47\x49\x46\x38" -> return Gif
103 "\x47\x49\x46\x38" -> return Gif
104+ "\x49\x49\x2a\x00" -> return Tiff
105+ "\x4D\x4D\x00\x2a" -> return Tiff
103 "\xff\xd8\xff\xe0" -> return Jpeg -- JFIF
106 "\xff\xd8\xff\xe0" -> return Jpeg -- JFIF
104 "\xff\xd8\xff\xe1" -> return Jpeg -- Exif
107 "\xff\xd8\xff\xe1" -> return Jpeg -- Exif
105 "%PDF" -> return Pdf
108 "%PDF" -> return Pdf
@@ -124,6 +127,7 @@ imageSize opts img = checkDpi <$>
124 Just Png -> getSize img
127 Just Png -> getSize img
125 Just Gif -> getSize img
128 Just Gif -> getSize img
126 Just Jpeg -> getSize img
129 Just Jpeg -> getSize img
130+ Just Tiff -> getSize img
127 Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img
131 Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img
128 Just Eps -> mbToEither "could not determine EPS size" $ epsSize img
132 Just Eps -> mbToEither "could not determine EPS size" $ epsSize img
129 Just Pdf -> mbToEither "could not determine PDF size" $ pdfSize img
133 Just Pdf -> mbToEither "could not determine PDF size" $ pdfSize img
src/Text/Pandoc/Logging.hs+32−9
@@ -36,6 +36,7 @@ import Data.Typeable (Typeable)
36import GHC.Generics (Generic)
36import GHC.Generics (Generic)
37import Text.Pandoc.Definition
37import Text.Pandoc.Definition
38import Text.Parsec.Pos
38import Text.Parsec.Pos
39+import Text.Pandoc.Shared (tshow)
39
40
40-- | Verbosity level.
41-- | Verbosity level.
41data Verbosity = ERROR | WARNING | INFO
42data Verbosity = ERROR | WARNING | INFO
@@ -84,6 +85,7 @@ data LogMessage =
84 | CouldNotParseCSS Text
85 | CouldNotParseCSS Text
85 | Fetching Text
86 | Fetching Text
86 | Extracting Text
87 | Extracting Text
88+ | LoadedResource FilePath FilePath
87 | NoTitleElement Text
89 | NoTitleElement Text
88 | NoLangSpecified
90 | NoLangSpecified
89 | InvalidLang Text
91 | InvalidLang Text
@@ -100,6 +102,8 @@ data LogMessage =
100 | FilterCompleted FilePath Integer
102 | FilterCompleted FilePath Integer
101 | CiteprocWarning Text
103 | CiteprocWarning Text
102 | ATXHeadingInLHS Int Text
104 | ATXHeadingInLHS Int Text
105+ | EnvironmentVariableUndefined Text
106+ | DuplicateAttribute Text Text
103 deriving (Show, Eq, Data, Ord, Typeable, Generic)
107 deriving (Show, Eq, Data, Ord, Typeable, Generic)
104
108
105instance ToJSON LogMessage where
109instance ToJSON LogMessage where
@@ -192,6 +196,9 @@ instance ToJSON LogMessage where
192 ["path" .= fp]
196 ["path" .= fp]
193 Extracting fp ->
197 Extracting fp ->
194 ["path" .= fp]
198 ["path" .= fp]
199+ LoadedResource orig found ->
200+ ["for" .= orig
201+ ,"from" .= found]
195 NoTitleElement fallback ->
202 NoTitleElement fallback ->
196 ["fallback" .= fallback]
203 ["fallback" .= fallback]
197 NoLangSpecified -> []
204 NoLangSpecified -> []
@@ -229,13 +236,20 @@ instance ToJSON LogMessage where
229 ATXHeadingInLHS lvl contents ->
236 ATXHeadingInLHS lvl contents ->
230 ["level" .= lvl
237 ["level" .= lvl
231 ,"contents" .= contents]
238 ,"contents" .= contents]
239+ EnvironmentVariableUndefined var ->
240+ ["variable" .= var ]
241+ DuplicateAttribute attr val ->
242+ ["attribute" .= attr
243+ ,"value" .= val]
232
244
233showPos :: SourcePos -> Text
245showPos :: SourcePos -> Text
234showPos pos = Text.pack $ sn ++ "line " ++
246showPos pos = Text.pack $ sn ++ "line " ++
235 show (sourceLine pos) ++ " column " ++ show (sourceColumn pos)
247 show (sourceLine pos) ++ " column " ++ show (sourceColumn pos)
236− where sn = if sourceName pos == "source" || sourceName pos == ""
248+ where
249+ sn' = sourceName pos
250+ sn = if sn' == "source" || sn' == "" || sn' == "-"
237− then ""
251+ then ""
238− else sourceName pos ++ " "
252+ else sn' ++ " "
239
253
240encodeLogMessages :: [LogMessage] -> BL.ByteString
254encodeLogMessages :: [LogMessage] -> BL.ByteString
241encodeLogMessages ms =
255encodeLogMessages ms =
@@ -268,7 +282,7 @@ showLogMessage msg =
268 ParsingUnescaped s pos ->
282 ParsingUnescaped s pos ->
269 "Parsing unescaped '" <> s <> "' at " <> showPos pos
283 "Parsing unescaped '" <> s <> "' at " <> showPos pos
270 CouldNotLoadIncludeFile fp pos ->
284 CouldNotLoadIncludeFile fp pos ->
271− "Could not load include file '" <> fp <> "' at " <> showPos pos
285+ "Could not load include file " <> fp <> " at " <> showPos pos
272 MacroAlreadyDefined name pos ->
286 MacroAlreadyDefined name pos ->
273 "Macro '" <> name <> "' already defined, ignoring at " <> showPos pos
287 "Macro '" <> name <> "' already defined, ignoring at " <> showPos pos
274 InlineNotRendered il ->
288 InlineNotRendered il ->
@@ -280,18 +294,18 @@ showLogMessage msg =
280 IgnoredIOError s ->
294 IgnoredIOError s ->
281 "IO Error (ignored): " <> s
295 "IO Error (ignored): " <> s
282 CouldNotFetchResource fp s ->
296 CouldNotFetchResource fp s ->
283− "Could not fetch resource '" <> fp <> "'" <>
297+ "Could not fetch resource " <> fp <>
284 if Text.null s then "" else ": " <> s
298 if Text.null s then "" else ": " <> s
285 CouldNotDetermineImageSize fp s ->
299 CouldNotDetermineImageSize fp s ->
286− "Could not determine image size for '" <> fp <> "'" <>
300+ "Could not determine image size for " <> fp <>
287 if Text.null s then "" else ": " <> s
301 if Text.null s then "" else ": " <> s
288 CouldNotConvertImage fp s ->
302 CouldNotConvertImage fp s ->
289− "Could not convert image '" <> fp <> "'" <>
303+ "Could not convert image " <> fp <>
290 if Text.null s then "" else ": " <> s
304 if Text.null s then "" else ": " <> s
291 CouldNotDetermineMimeType fp ->
305 CouldNotDetermineMimeType fp ->
292− "Could not determine mime type for '" <> fp <> "'"
306+ "Could not determine mime type for " <> fp
293 CouldNotConvertTeXMath s m ->
307 CouldNotConvertTeXMath s m ->
294− "Could not convert TeX math '" <> s <> "', rendering as TeX" <>
308+ "Could not convert TeX math " <> s <> ", rendering as TeX" <>
295 if Text.null m then "" else ":\n" <> m
309 if Text.null m then "" else ":\n" <> m
296 CouldNotParseCSS m ->
310 CouldNotParseCSS m ->
297 "Could not parse CSS" <> if Text.null m then "" else ":\n" <> m
311 "Could not parse CSS" <> if Text.null m then "" else ":\n" <> m
@@ -299,6 +313,8 @@ showLogMessage msg =
299 "Fetching " <> fp <> "..."
313 "Fetching " <> fp <> "..."
300 Extracting fp ->
314 Extracting fp ->
301 "Extracting " <> fp <> "..."
315 "Extracting " <> fp <> "..."
316+ LoadedResource orig found ->
317+ "Loaded " <> Text.pack orig <> " from " <> Text.pack found
302 NoTitleElement fallback ->
318 NoTitleElement fallback ->
303 "This document format requires a nonempty <title> element.\n" <>
319 "This document format requires a nonempty <title> element.\n" <>
304 "Defaulting to '" <> fallback <> "' as the title.\n" <>
320 "Defaulting to '" <> fallback <> "' as the title.\n" <>
@@ -345,6 +361,10 @@ showLogMessage msg =
345 if lvl < 3
361 if lvl < 3
346 then " Consider using --markdown-headings=setext."
362 then " Consider using --markdown-headings=setext."
347 else ""
363 else ""
364+ EnvironmentVariableUndefined var ->
365+ "Undefined environment variable " <> var <> " in defaults file."
366+ DuplicateAttribute attr val ->
367+ "Ignoring duplicate attribute " <> attr <> "=" <> tshow val <> "."
348
368
349messageVerbosity :: LogMessage -> Verbosity
369messageVerbosity :: LogMessage -> Verbosity
350messageVerbosity msg =
370messageVerbosity msg =
@@ -375,6 +395,7 @@ messageVerbosity msg =
375 CouldNotParseCSS{} -> WARNING
395 CouldNotParseCSS{} -> WARNING
376 Fetching{} -> INFO
396 Fetching{} -> INFO
377 Extracting{} -> INFO
397 Extracting{} -> INFO
398+ LoadedResource{} -> INFO
378 NoTitleElement{} -> WARNING
399 NoTitleElement{} -> WARNING
379 NoLangSpecified -> INFO
400 NoLangSpecified -> INFO
380 InvalidLang{} -> WARNING
401 InvalidLang{} -> WARNING
@@ -391,3 +412,5 @@ messageVerbosity msg =
391 FilterCompleted{} -> INFO
412 FilterCompleted{} -> INFO
392 CiteprocWarning{} -> WARNING
413 CiteprocWarning{} -> WARNING
393 ATXHeadingInLHS{} -> WARNING
414 ATXHeadingInLHS{} -> WARNING
415+ EnvironmentVariableUndefined{}-> WARNING
416+ DuplicateAttribute{} -> WARNING
src/Text/Pandoc/Lua/Filter.hs+4−1
@@ -13,7 +13,9 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction
13 , LuaFilter
13 , LuaFilter
14 , runFilterFile
14 , runFilterFile
15 , walkInlines
15 , walkInlines
16+ , walkInlineLists
16 , walkBlocks
17 , walkBlocks
18+ , walkBlockLists
17 , module Text.Pandoc.Lua.Walk
19 , module Text.Pandoc.Lua.Walk
18 ) where
20 ) where
19import Control.Applicative ((<|>))
21import Control.Applicative ((<|>))
@@ -22,6 +24,7 @@ import Control.Monad.Catch (finally, try)
22import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
24import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
23 showConstr, toConstr, tyconUQname)
25 showConstr, toConstr, tyconUQname)
24import Data.Foldable (foldrM)
26import Data.Foldable (foldrM)
27+import Data.List (foldl')
25import Data.Map (Map)
28import Data.Map (Map)
26import Data.Maybe (fromMaybe)
29import Data.Maybe (fromMaybe)
27import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
30import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
@@ -204,7 +207,7 @@ walkMeta lf (Pandoc m bs) = do
204
207
205walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
208walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
206walkPandoc (LuaFilter fnMap) =
209walkPandoc (LuaFilter fnMap) =
207− case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
210+ case foldl' mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
208 Just fn -> \x -> runFilterFunction fn x *> singleElement x
211 Just fn -> \x -> runFilterFunction fn x *> singleElement x
209 Nothing -> return
212 Nothing -> return
210
213
src/Text/Pandoc/Lua/Module/MediaBag.hs+3−3
@@ -73,9 +73,9 @@ lookup fp = do
73 res <- MB.lookupMedia fp <$> getMediaBag
73 res <- MB.lookupMedia fp <$> getMediaBag
74 liftPandocLua $ case res of
74 liftPandocLua $ case res of
75 Nothing -> 1 <$ Lua.pushnil
75 Nothing -> 1 <$ Lua.pushnil
76− Just (mimeType, contents) -> do
76+ Just item -> do
77− Lua.push mimeType
77+ Lua.push $ MB.mediaMimeType item
78− Lua.push contents
78+ Lua.push $ MB.mediaContents item
79 return 2
79 return 2
80
80
81list :: PandocLua NumResults
81list :: PandocLua NumResults
src/Text/Pandoc/Lua/Module/Pandoc.hs+8−3
@@ -23,8 +23,10 @@ import Foreign.Lua (Lua, NumResults, Optional, Peekable, Pushable)
23import System.Exit (ExitCode (..))
23import System.Exit (ExitCode (..))
24import Text.Pandoc.Class.PandocIO (runIO)
24import Text.Pandoc.Class.PandocIO (runIO)
25import Text.Pandoc.Definition (Block, Inline)
25import Text.Pandoc.Definition (Block, Inline)
26−import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..))
26+import Text.Pandoc.Lua.Filter (LuaFilter, SingletonsList (..), walkInlines,
27+ walkInlineLists, walkBlocks, walkBlockLists)
27import Text.Pandoc.Lua.Marshaling ()
28import Text.Pandoc.Lua.Marshaling ()
29+import Text.Pandoc.Lua.Marshaling.List (List (..))
28import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
30import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
29 loadDefaultModule)
31 loadDefaultModule)
30import Text.Pandoc.Walk (Walkable)
32import Text.Pandoc.Walk (Walkable)
@@ -51,9 +53,12 @@ pushModule = do
51 return 1
53 return 1
52
54
53walkElement :: (Walkable (SingletonsList Inline) a,
55walkElement :: (Walkable (SingletonsList Inline) a,
54− Walkable (SingletonsList Block) a)
56+ Walkable (SingletonsList Block) a,
57+ Walkable (List Inline) a,
58+ Walkable (List Block) a)
55 => a -> LuaFilter -> PandocLua a
59 => a -> LuaFilter -> PandocLua a
56−walkElement x f = liftPandocLua $ walkInlines f x >>= walkBlocks f
60+walkElement x f = liftPandocLua $
61+ walkInlines f x >>= walkInlineLists f >>= walkBlocks f >>= walkBlockLists f
57
62
58walk_inline :: Inline -> LuaFilter -> PandocLua Inline
63walk_inline :: Inline -> LuaFilter -> PandocLua Inline
59walk_inline = walkElement
64walk_inline = walkElement
src/Text/Pandoc/Lua/Module/Utils.hs+1−1
@@ -146,7 +146,7 @@ from_simple_table (SimpleTable capt aligns widths head' body) = do
146 nullAttr
146 nullAttr
147 (Caption Nothing [Plain capt])
147 (Caption Nothing [Plain capt])
148 (zipWith (\a w -> (a, toColWidth w)) aligns widths)
148 (zipWith (\a w -> (a, toColWidth w)) aligns widths)
149− (TableHead nullAttr [blockListToRow head'])
149+ (TableHead nullAttr [blockListToRow head' | not (null head') ])
150 [TableBody nullAttr 0 [] $ map blockListToRow body]
150 [TableBody nullAttr 0 [] $ map blockListToRow body]
151 (TableFoot nullAttr [])
151 (TableFoot nullAttr [])
152 return (NumResults 1)
152 return (NumResults 1)
src/Text/Pandoc/MIME.hs+10
@@ -43,6 +43,16 @@ getMimeTypeDef :: FilePath -> MimeType
43getMimeTypeDef = fromMaybe "application/octet-stream" . getMimeType
43getMimeTypeDef = fromMaybe "application/octet-stream" . getMimeType
44
44
45extensionFromMimeType :: MimeType -> Maybe T.Text
45extensionFromMimeType :: MimeType -> Maybe T.Text
46+-- few special cases, where there are multiple options:
47+extensionFromMimeType "text/plain" = Just "txt"
48+extensionFromMimeType "video/quicktime" = Just "mov"
49+extensionFromMimeType "video/mpeg" = Just "mpeg"
50+extensionFromMimeType "video/dv" = Just "dv"
51+extensionFromMimeType "image/vnd.djvu" = Just "djvu"
52+extensionFromMimeType "image/tiff" = Just "tiff"
53+extensionFromMimeType "image/jpeg" = Just "jpg"
54+extensionFromMimeType "application/xml" = Just "xml"
55+extensionFromMimeType "application/ogg" = Just "ogg"
46extensionFromMimeType mimetype =
56extensionFromMimeType mimetype =
47 M.lookup (T.takeWhile (/=';') mimetype) reverseMimeTypes
57 M.lookup (T.takeWhile (/=';') mimetype) reverseMimeTypes
48 -- note: we just look up the basic mime type, dropping the content-encoding etc.
58 -- note: we just look up the basic mime type, dropping the content-encoding etc.
src/Text/Pandoc/MediaBag.hs+45−14
@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{- |
5{- |
@@ -14,6 +15,7 @@ Definition of a MediaBag object to hold binary resources, and an
14interface for interacting with it.
15interface for interacting with it.
15-}
16-}
16module Text.Pandoc.MediaBag (
17module Text.Pandoc.MediaBag (
18+ MediaItem(..),
17 MediaBag,
19 MediaBag,
18 deleteMedia,
20 deleteMedia,
19 lookupMedia,
21 lookupMedia,
@@ -24,29 +26,43 @@ module Text.Pandoc.MediaBag (
24import qualified Data.ByteString.Lazy as BL
26import qualified Data.ByteString.Lazy as BL
25import Data.Data (Data)
27import Data.Data (Data)
26import qualified Data.Map as M
28import qualified Data.Map as M
27−import Data.Maybe (fromMaybe)
29+import Data.Maybe (fromMaybe, isNothing)
28import Data.Typeable (Typeable)
30import Data.Typeable (Typeable)
29import System.FilePath
31import System.FilePath
30−import qualified System.FilePath.Posix as Posix
31−import Text.Pandoc.MIME (MimeType, getMimeTypeDef)
32+import Text.Pandoc.MIME (MimeType, getMimeTypeDef, extensionFromMimeType)
33+import Data.Text (Text)
34+import qualified Data.Text as T
35+import Data.Digest.Pure.SHA (sha1, showDigest)
36+import Network.URI (URI (..), parseURI)
37+
38+data MediaItem =
39+ MediaItem
40+ { mediaMimeType :: MimeType
41+ , mediaPath :: FilePath
42+ , mediaContents :: BL.ByteString
43+ } deriving (Eq, Ord, Show, Data, Typeable)
32
44
33-- | A container for a collection of binary resources, with names and
45-- | A container for a collection of binary resources, with names and
34-- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty'
46-- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty'
35-- can be used for an empty 'MediaBag', and '<>' can be used to append
47-- can be used for an empty 'MediaBag', and '<>' can be used to append
36-- two 'MediaBag's.
48-- two 'MediaBag's.
37−newtype MediaBag = MediaBag (M.Map [FilePath] (MimeType, BL.ByteString))
49+newtype MediaBag = MediaBag (M.Map Text MediaItem)
38 deriving (Semigroup, Monoid, Data, Typeable)
50 deriving (Semigroup, Monoid, Data, Typeable)
39
51
40instance Show MediaBag where
52instance Show MediaBag where
41 show bag = "MediaBag " ++ show (mediaDirectory bag)
53 show bag = "MediaBag " ++ show (mediaDirectory bag)
42
54
55+-- | We represent paths with /, in normalized form.
56+canonicalize :: FilePath -> Text
57+canonicalize = T.replace "\\" "/" . T.pack . normalise
58+
43-- | Delete a media item from a 'MediaBag', or do nothing if no item corresponds
59-- | Delete a media item from a 'MediaBag', or do nothing if no item corresponds
44-- to the given path.
60-- to the given path.
45deleteMedia :: FilePath -- ^ relative path and canonical name of resource
61deleteMedia :: FilePath -- ^ relative path and canonical name of resource
46 -> MediaBag
62 -> MediaBag
47 -> MediaBag
63 -> MediaBag
48deleteMedia fp (MediaBag mediamap) =
64deleteMedia fp (MediaBag mediamap) =
49− MediaBag $ M.delete (splitDirectories fp) mediamap
65+ MediaBag $ M.delete (canonicalize fp) mediamap
50
66
51-- | Insert a media item into a 'MediaBag', replacing any existing
67-- | Insert a media item into a 'MediaBag', replacing any existing
52-- value with the same name.
68-- value with the same name.
@@ -56,26 +72,41 @@ insertMedia :: FilePath -- ^ relative path and canonical name of resource
56 -> MediaBag
72 -> MediaBag
57 -> MediaBag
73 -> MediaBag
58insertMedia fp mbMime contents (MediaBag mediamap) =
74insertMedia fp mbMime contents (MediaBag mediamap) =
59− MediaBag (M.insert (splitDirectories fp) (mime, contents) mediamap)
75+ MediaBag (M.insert fp' mediaItem mediamap)
60− where mime = fromMaybe fallback mbMime
76+ where mediaItem = MediaItem{ mediaPath = newpath
77+ , mediaContents = contents
78+ , mediaMimeType = mt }
79+ fp' = canonicalize fp
80+ uri = parseURI fp
81+ newpath = if isRelative fp
82+ && isNothing uri
83+ && ".." `notElem` splitPath fp
84+ then T.unpack fp'
85+ else showDigest (sha1 contents) <> "." <> ext
61 fallback = case takeExtension fp of
86 fallback = case takeExtension fp of
62 ".gz" -> getMimeTypeDef $ dropExtension fp
87 ".gz" -> getMimeTypeDef $ dropExtension fp
63 _ -> getMimeTypeDef fp
88 _ -> getMimeTypeDef fp
89+ mt = fromMaybe fallback mbMime
90+ path = maybe fp uriPath uri
91+ ext = case takeExtension path of
92+ '.':e -> e
93+ _ -> maybe "" T.unpack $ extensionFromMimeType mt
94+
64
95
65-- | Lookup a media item in a 'MediaBag', returning mime type and contents.
96-- | Lookup a media item in a 'MediaBag', returning mime type and contents.
66lookupMedia :: FilePath
97lookupMedia :: FilePath
67 -> MediaBag
98 -> MediaBag
68− -> Maybe (MimeType, BL.ByteString)
99+ -> Maybe MediaItem
69−lookupMedia fp (MediaBag mediamap) = M.lookup (splitDirectories fp) mediamap
100+lookupMedia fp (MediaBag mediamap) = M.lookup (canonicalize fp) mediamap
70
101
71-- | Get a list of the file paths stored in a 'MediaBag', with
102-- | Get a list of the file paths stored in a 'MediaBag', with
72-- their corresponding mime types and the lengths in bytes of the contents.
103-- their corresponding mime types and the lengths in bytes of the contents.
73mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)]
104mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)]
74−mediaDirectory (MediaBag mediamap) =
105+mediaDirectory mediabag =
75− M.foldrWithKey (\fp (mime,contents) ->
76− ((Posix.joinPath fp, mime, fromIntegral $ BL.length contents):)) [] mediamap
106+ map (\(fp, mt, bs) -> (fp, mt, fromIntegral (BL.length bs)))
107+ (mediaItems mediabag)
77
108
78mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)]
109mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)]
79mediaItems (MediaBag mediamap) =
110mediaItems (MediaBag mediamap) =
80− M.foldrWithKey (\fp (mime,contents) ->
111+ map (\item -> (mediaPath item, mediaMimeType item, mediaContents item))
81− ((Posix.joinPath fp, mime, contents):)) [] mediamap
112+ (M.elems mediamap)
src/Text/Pandoc/Options.hs+17−17
@@ -315,6 +315,23 @@ defaultMathJaxURL = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.j
315defaultKaTeXURL :: Text
315defaultKaTeXURL :: Text
316defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.11.1/"
316defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.11.1/"
317
317
318+-- Update documentation in doc/filters.md if this is changed.
319+$(deriveJSON defaultOptions{ constructorTagModifier =
320+ camelCaseStrToHyphenated
321+ } ''TrackChanges)
322+
323+$(deriveJSON defaultOptions{ constructorTagModifier =
324+ camelCaseStrToHyphenated
325+ } ''WrapOption)
326+
327+$(deriveJSON defaultOptions{ constructorTagModifier =
328+ camelCaseStrToHyphenated . drop 8
329+ } ''TopLevelDivision)
330+
331+$(deriveJSON defaultOptions{ constructorTagModifier =
332+ camelCaseStrToHyphenated
333+ } ''ReferenceLocation)
334+
318-- Update documentation in doc/filters.md if this is changed.
335-- Update documentation in doc/filters.md if this is changed.
319$(deriveJSON defaultOptions ''ReaderOptions)
336$(deriveJSON defaultOptions ''ReaderOptions)
320
337
@@ -338,20 +355,3 @@ $(deriveJSON defaultOptions{ constructorTagModifier =
338 } ''ObfuscationMethod)
355 } ''ObfuscationMethod)
339
356
340$(deriveJSON defaultOptions ''HTMLSlideVariant)
357$(deriveJSON defaultOptions ''HTMLSlideVariant)
341−
342−-- Update documentation in doc/filters.md if this is changed.
343−$(deriveJSON defaultOptions{ constructorTagModifier =
344− camelCaseStrToHyphenated
345− } ''TrackChanges)
346−
347−$(deriveJSON defaultOptions{ constructorTagModifier =
348− camelCaseStrToHyphenated
349− } ''WrapOption)
350−
351−$(deriveJSON defaultOptions{ constructorTagModifier =
352− camelCaseStrToHyphenated . drop 8
353− } ''TopLevelDivision)
354−
355−$(deriveJSON defaultOptions{ constructorTagModifier =
356− camelCaseStrToHyphenated
357− } ''ReferenceLocation)
src/Text/Pandoc/PDF.hs+16−3
@@ -202,7 +202,7 @@ convertImage opts tmpdir fname = do
202 Just "image/svg+xml" -> E.catch (do
202 Just "image/svg+xml" -> E.catch (do
203 (exit, _) <- pipeProcess Nothing "rsvg-convert"
203 (exit, _) <- pipeProcess Nothing "rsvg-convert"
204 ["-f","pdf","-a","--dpi-x",dpi,"--dpi-y",dpi,
204 ["-f","pdf","-a","--dpi-x",dpi,"--dpi-y",dpi,
205− "-o",pdfOut,fname] BL.empty
205+ "-o",pdfOut,svgIn] BL.empty
206 if exit == ExitSuccess
206 if exit == ExitSuccess
207 then return $ Right pdfOut
207 then return $ Right pdfOut
208 else return $ Left "conversion from SVG failed")
208 else return $ Left "conversion from SVG failed")
@@ -217,6 +217,7 @@ convertImage opts tmpdir fname = do
217 where
217 where
218 pngOut = replaceDirectory (replaceExtension fname ".png") tmpdir
218 pngOut = replaceDirectory (replaceExtension fname ".png") tmpdir
219 pdfOut = replaceDirectory (replaceExtension fname ".pdf") tmpdir
219 pdfOut = replaceDirectory (replaceExtension fname ".pdf") tmpdir
220+ svgIn = tmpdir </> fname
220 mime = getMimeType fname
221 mime = getMimeType fname
221 doNothing = return (Right fname)
222 doNothing = return (Right fname)
222
223
@@ -506,8 +507,20 @@ showVerboseInfo mbTmpDir program programArgs env source = do
506 UTF8.hPutStrLn stderr $
507 UTF8.hPutStrLn stderr $
507 T.pack program <> " " <> T.pack (unwords (map show programArgs))
508 T.pack program <> " " <> T.pack (unwords (map show programArgs))
508 UTF8.hPutStr stderr "\n"
509 UTF8.hPutStr stderr "\n"
509− UTF8.hPutStrLn stderr "[makePDF] Environment:"
510+ UTF8.hPutStrLn stderr "[makePDF] Relevant environment variables:"
511+ -- we filter out irrelevant stuff to avoid leaking passwords and keys!
512+ let isRelevant ("PATH",_) = True
513+ isRelevant ("TMPDIR",_) = True
514+ isRelevant ("PWD",_) = True
515+ isRelevant ("LANG",_) = True
516+ isRelevant ("HOME",_) = True
517+ isRelevant ("LUA_PATH",_) = True
518+ isRelevant ("LUA_CPATH",_) = True
519+ isRelevant ("SHELL",_) = True
520+ isRelevant ("TEXINPUTS",_) = True
521+ isRelevant ("TEXMFOUTPUT",_) = True
522+ isRelevant _ = False
510− mapM_ (UTF8.hPutStrLn stderr . tshow) env
523+ mapM_ (UTF8.hPutStrLn stderr . tshow) (filter isRelevant env)
511 UTF8.hPutStr stderr "\n"
524 UTF8.hPutStr stderr "\n"
512 UTF8.hPutStrLn stderr "[makePDF] Source:"
525 UTF8.hPutStrLn stderr "[makePDF] Source:"
513 UTF8.hPutStrLn stderr source
526 UTF8.hPutStrLn stderr source
src/Text/Pandoc/Parsing.hs+360−242
@@ -5,7 +5,6 @@
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE IncoherentInstances #-}
6{-# LANGUAGE IncoherentInstances #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE ViewPatterns #-}
9{-# LANGUAGE OverloadedStrings #-}
8{-# LANGUAGE OverloadedStrings #-}
10{- |
9{- |
11 Module : Text.Pandoc.Parsing
10 Module : Text.Pandoc.Parsing
@@ -19,8 +18,7 @@
19A utility library with parsers used in pandoc readers.
18A utility library with parsers used in pandoc readers.
20-}
19-}
21
20
22−module Text.Pandoc.Parsing ( take1WhileP,
21+module Text.Pandoc.Parsing ( module Text.Pandoc.Sources,
23− takeP,
24 countChar,
22 countChar,
25 textStr,
23 textStr,
26 anyLine,
24 anyLine,
@@ -105,14 +103,14 @@ module Text.Pandoc.Parsing ( take1WhileP,
105 singleQuoteEnd,
103 singleQuoteEnd,
106 doubleQuoteStart,
104 doubleQuoteStart,
107 doubleQuoteEnd,
105 doubleQuoteEnd,
108− ellipses,
109 apostrophe,
106 apostrophe,
107+ doubleCloseQuote,
108+ ellipses,
110 dash,
109 dash,
111 nested,
110 nested,
112 citeKey,
111 citeKey,
113 Parser,
112 Parser,
114 ParserT,
113 ParserT,
115− F,
116 Future(..),
114 Future(..),
117 runF,
115 runF,
118 askF,
116 askF,
@@ -123,7 +121,6 @@ module Text.Pandoc.Parsing ( take1WhileP,
123 (<+?>),
121 (<+?>),
124 extractIdClass,
122 extractIdClass,
125 insertIncludedFile,
123 insertIncludedFile,
126− insertIncludedFileF,
127 -- * Re-exports from Text.Parsec
124 -- * Re-exports from Text.Parsec
128 Stream,
125 Stream,
129 runParser,
126 runParser,
@@ -134,22 +131,10 @@ module Text.Pandoc.Parsing ( take1WhileP,
134 getInput,
131 getInput,
135 setInput,
132 setInput,
136 unexpected,
133 unexpected,
137− char,
138− letter,
139− digit,
140− alphaNum,
141 skipMany,
134 skipMany,
142 skipMany1,
135 skipMany1,
143− spaces,
144− space,
145− anyChar,
146− satisfy,
147− newline,
148− string,
149 count,
136 count,
150 eof,
137 eof,
151− noneOf,
152− oneOf,
153 lookAhead,
138 lookAhead,
154 notFollowedBy,
139 notFollowedBy,
155 many,
140 many,
@@ -174,6 +159,8 @@ module Text.Pandoc.Parsing ( take1WhileP,
174 SourcePos,
159 SourcePos,
175 getPosition,
160 getPosition,
176 setPosition,
161 setPosition,
162+ sourceName,
163+ setSourceName,
177 sourceColumn,
164 sourceColumn,
178 sourceLine,
165 sourceLine,
179 setSourceColumn,
166 setSourceColumn,
@@ -189,48 +176,141 @@ module Text.Pandoc.Parsing ( take1WhileP,
189where
176where
190
177
191import Control.Monad.Identity
178import Control.Monad.Identity
179+ ( guard,
180+ join,
181+ unless,
182+ when,
183+ void,
184+ liftM2,
185+ liftM,
186+ Identity(..),
187+ MonadPlus(mzero) )
192import Control.Monad.Reader
188import Control.Monad.Reader
189+ ( asks, runReader, MonadReader(ask), Reader, ReaderT(ReaderT) )
193import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isAsciiLower,
190import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isAsciiLower,
194− isPunctuation, isSpace, ord, toLower, toUpper)
191+ isSpace, ord, toLower, toUpper)
195−import Data.Default
192+import Data.Default ( Default(..) )
196import Data.Functor (($>))
193import Data.Functor (($>))
197import Data.List (intercalate, transpose)
194import Data.List (intercalate, transpose)
198import qualified Data.Map as M
195import qualified Data.Map as M
199−import Data.Maybe (mapMaybe, fromMaybe)
196+import Data.Maybe (fromMaybe)
200import qualified Data.Set as Set
197import qualified Data.Set as Set
201−import Data.String
202import Data.Text (Text)
198import Data.Text (Text)
203import qualified Data.Text as T
199import qualified Data.Text as T
204import Text.HTML.TagSoup.Entity (lookupEntity)
200import Text.HTML.TagSoup.Entity (lookupEntity)
205−import Text.Pandoc.Asciify (toAsciiChar)
201+import Text.Pandoc.Asciify (toAsciiText)
206import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines)
202import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines)
207import qualified Text.Pandoc.Builder as B
203import qualified Text.Pandoc.Builder as B
208import Text.Pandoc.Class.PandocMonad (PandocMonad, readFileFromDirs, report)
204import Text.Pandoc.Class.PandocMonad (PandocMonad, readFileFromDirs, report)
209import Text.Pandoc.Definition
205import Text.Pandoc.Definition
206+ ( Target,
207+ nullMeta,
208+ nullAttr,
209+ Meta,
210+ ColWidth(ColWidthDefault, ColWidth),
211+ TableFoot(TableFoot),
212+ TableBody(TableBody),
213+ Attr,
214+ TableHead(TableHead),
215+ Row(..),
216+ Alignment(..),
217+ Inline(Str),
218+ ListNumberDelim(..),
219+ ListAttributes,
220+ ListNumberStyle(..) )
210import Text.Pandoc.Logging
221import Text.Pandoc.Logging
222+ ( LogMessage(CouldNotLoadIncludeFile, DuplicateIdentifier) )
211import Text.Pandoc.Options
223import Text.Pandoc.Options
224+ ( extensionEnabled,
225+ Extension(Ext_old_dashes, Ext_tex_math_dollars,
226+ Ext_tex_math_single_backslash, Ext_tex_math_double_backslash,
227+ Ext_auto_identifiers, Ext_ascii_identifiers, Ext_smart),
228+ ReaderOptions(readerTabStop, readerColumns, readerExtensions) )
212import Text.Pandoc.Readers.LaTeX.Types (Macro)
229import Text.Pandoc.Readers.LaTeX.Types (Macro)
213import Text.Pandoc.Shared
230import Text.Pandoc.Shared
231+ ( uniqueIdent,
232+ tshow,
233+ mapLeft,
234+ compactify,
235+ trim,
236+ trimr,
237+ splitTextByIndices,
238+ safeRead,
239+ trimMath,
240+ schemes,
241+ escapeURI )
242+import Text.Pandoc.Sources
214import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
243import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
215import Text.Pandoc.XML (fromEntities)
244import Text.Pandoc.XML (fromEntities)
216−import Text.Parsec hiding (token)
245+import Text.Parsec
246+ ( between,
247+ setSourceName,
248+ Parsec,
249+ Column,
250+ Line,
251+ incSourceLine,
252+ incSourceColumn,
253+ setSourceLine,
254+ setSourceColumn,
255+ sourceLine,
256+ sourceColumn,
257+ sourceName,
258+ setSourceName,
259+ setPosition,
260+ getPosition,
261+ updateState,
262+ setState,
263+ getState,
264+ optionMaybe,
265+ optional,
266+ option,
267+ endBy1,
268+ endBy,
269+ sepEndBy1,
270+ sepEndBy,
271+ sepBy1,
272+ sepBy,
273+ try,
274+ choice,
275+ (<?>),
276+ (<|>),
277+ manyTill,
278+ many1,
279+ many,
280+ notFollowedBy,
281+ lookAhead,
282+ eof,
283+ count,
284+ skipMany1,
285+ skipMany,
286+ unexpected,
287+ setInput,
288+ getInput,
289+ anyToken,
290+ tokenPrim,
291+ parse,
292+ runParserT,
293+ runParser,
294+ ParseError,
295+ ParsecT,
296+ SourcePos,
297+ Stream(..) )
217−import Text.Parsec.Pos (initialPos, newPos, updatePosString)
298+import Text.Parsec.Pos (initialPos, newPos)
218−
219−import Control.Monad.Except
299+import Control.Monad.Except ( MonadError(throwError) )
220import Text.Pandoc.Error
300import Text.Pandoc.Error
301+ ( PandocError(PandocParseError, PandocParsecError) )
221
302
222type Parser t s = Parsec t s
303type Parser t s = Parsec t s
223
304
224type ParserT = ParsecT
305type ParserT = ParsecT
225
306
307+
226-- | Reader monad wrapping the parser state. This is used to possibly delay
308-- | Reader monad wrapping the parser state. This is used to possibly delay
227-- evaluation until all relevant information has been parsed and made available
309-- evaluation until all relevant information has been parsed and made available
228-- in the parser state.
310-- in the parser state.
229newtype Future s a = Future { runDelayed :: Reader s a }
311newtype Future s a = Future { runDelayed :: Reader s a }
230 deriving (Monad, Applicative, Functor)
312 deriving (Monad, Applicative, Functor)
231
313
232−type F = Future ParserState
233−
234runF :: Future s a -> s -> a
314runF :: Future s a -> s -> a
235runF = runReader . runDelayed
315runF = runReader . runDelayed
236
316
@@ -253,70 +333,48 @@ instance (Semigroup a, Monoid a) => Monoid (Future s a) where
253 mappend = (<>)
333 mappend = (<>)
254
334
255-- | Like @count@, but packs its result
335-- | Like @count@, but packs its result
256−countChar :: (Stream s m Char, Monad m)
336+countChar :: (Stream s m Char, UpdateSourcePos s Char, Monad m)
257 => Int
337 => Int
258 -> ParsecT s st m Char
338 -> ParsecT s st m Char
259 -> ParsecT s st m Text
339 -> ParsecT s st m Text
260countChar n = fmap T.pack . count n
340countChar n = fmap T.pack . count n
261
341
262-- | Like @string@, but uses @Text@.
342-- | Like @string@, but uses @Text@.
343+textStr :: (Stream s m Char, UpdateSourcePos s Char)
263−textStr :: Stream s m Char => Text -> ParsecT s u m Text
344+ => Text -> ParsecT s u m Text
264textStr t = string (T.unpack t) $> t
345textStr t = string (T.unpack t) $> t
265
346
266−-- | Parse characters while a predicate is true.
347+
267−take1WhileP :: Monad m
268− => (Char -> Bool)
269− -> ParserT Text st m Text
270−take1WhileP f = do
271− -- needed to persuade parsec that this won't match an empty string:
272− c <- satisfy f
273− inp <- getInput
274− pos <- getPosition
275− let (t, rest) = T.span f inp
276− setInput rest
277− setPosition $
278− if f '\t' || f '\n'
279− then updatePosString pos $ T.unpack t
280− else incSourceColumn pos (T.length t)
281− return $ T.singleton c <> t
282−
283−-- Parse n characters of input (or the rest of the input if
284−-- there aren't n characters).
285−takeP :: Monad m => Int -> ParserT Text st m Text
286−takeP n = do
287− guard (n > 0)
288− -- faster than 'count n anyChar'
289− inp <- getInput
290− pos <- getPosition
291− let (xs, rest) = T.splitAt n inp
292− -- needed to persuade parsec that this won't match an empty string:
293− anyChar
294− setInput rest
295− setPosition $ updatePosString pos $ T.unpack xs
296− return xs
297−
298−-- | Parse any line of text
348+-- | Parse any line of text, returning the contents without the
349+-- final newline.
299−anyLine :: Monad m => ParserT Text st m Text
350+anyLine :: Monad m => ParserT Sources st m Text
300anyLine = do
351anyLine = do
301 -- This is much faster than:
352 -- This is much faster than:
302 -- manyTill anyChar newline
353 -- manyTill anyChar newline
303 inp <- getInput
354 inp <- getInput
304− pos <- getPosition
355+ case inp of
356+ Sources [] -> mzero
357+ Sources ((fp,t):inps) ->
358+ -- we assume that lines don't span different input files
305− case T.break (=='\n') inp of
359+ case T.break (=='\n') t of
306− (this, T.uncons -> Just ('\n', rest)) -> do
360+ (this, rest)
361+ | T.null rest
362+ , not (null inps) ->
363+ -- line may span different input files, so do it
364+ -- character by character
365+ T.pack <$> manyTill anyChar newline
366+ | otherwise -> do -- either end of inputs or newline in rest
367+ setInput $ Sources ((fp, rest):inps)
307− -- needed to persuade parsec that this won't match an empty string:
368+ char '\n' -- needed so parsec knows we won't match empty string
308− anyChar
369+ -- and so source pos is updated
309− setInput rest
310− setPosition $ incSourceLine (setSourceColumn pos 1) 1
311− return this
370+ return this
312− _ -> mzero
313
371
314-- | Parse any line, include the final newline in the output
372-- | Parse any line, include the final newline in the output
315−anyLineNewline :: Monad m => ParserT Text st m Text
373+anyLineNewline :: Monad m => ParserT Sources st m Text
316anyLineNewline = (<> "\n") <$> anyLine
374anyLineNewline = (<> "\n") <$> anyLine
317
375
318-- | Parse indent by specified number of spaces (or equiv. tabs)
376-- | Parse indent by specified number of spaces (or equiv. tabs)
319−indentWith :: Stream s m Char
377+indentWith :: (Stream s m Char, UpdateSourcePos s Char)
320 => HasReaderOptions st
378 => HasReaderOptions st
321 => Int -> ParserT s st m Text
379 => Int -> ParserT s st m Text
322indentWith num = do
380indentWith num = do
@@ -401,11 +459,13 @@ notFollowedBy' p = try $ join $ do a <- try p
401 return (return ())
459 return (return ())
402-- (This version due to Andrew Pimlott on the Haskell mailing list.)
460-- (This version due to Andrew Pimlott on the Haskell mailing list.)
403
461
462+oneOfStrings' :: (Stream s m Char, UpdateSourcePos s Char)
404−oneOfStrings' :: Stream s m Char => (Char -> Char -> Bool) -> [Text] -> ParserT s st m Text
463+ => (Char -> Char -> Bool) -> [Text] -> ParserT s st m Text
405oneOfStrings' f = fmap T.pack . oneOfStrings'' f . fmap T.unpack
464oneOfStrings' f = fmap T.pack . oneOfStrings'' f . fmap T.unpack
406
465
407-- TODO: This should be re-implemented in a Text-aware way
466-- TODO: This should be re-implemented in a Text-aware way
467+oneOfStrings'' :: (Stream s m Char, UpdateSourcePos s Char)
408−oneOfStrings'' :: Stream s m Char => (Char -> Char -> Bool) -> [String] -> ParserT s st m String
468+ => (Char -> Char -> Bool) -> [String] -> ParserT s st m String
409oneOfStrings'' _ [] = Prelude.fail "no strings"
469oneOfStrings'' _ [] = Prelude.fail "no strings"
410oneOfStrings'' matches strs = try $ do
470oneOfStrings'' matches strs = try $ do
411 c <- anyChar
471 c <- anyChar
@@ -420,14 +480,16 @@ oneOfStrings'' matches strs = try $ do
420-- | Parses one of a list of strings. If the list contains
480-- | Parses one of a list of strings. If the list contains
421-- two strings one of which is a prefix of the other, the longer
481-- two strings one of which is a prefix of the other, the longer
422-- string will be matched if possible.
482-- string will be matched if possible.
483+oneOfStrings :: (Stream s m Char, UpdateSourcePos s Char)
423−oneOfStrings :: Stream s m Char => [Text] -> ParserT s st m Text
484+ => [Text] -> ParserT s st m Text
424oneOfStrings = oneOfStrings' (==)
485oneOfStrings = oneOfStrings' (==)
425
486
426-- | Parses one of a list of strings (tried in order), case insensitive.
487-- | Parses one of a list of strings (tried in order), case insensitive.
427
488
428-- TODO: This will not be accurate with general Unicode (neither
489-- TODO: This will not be accurate with general Unicode (neither
429-- Text.toLower nor Text.toCaseFold can be implemented with a map)
490-- Text.toLower nor Text.toCaseFold can be implemented with a map)
491+oneOfStringsCI :: (Stream s m Char, UpdateSourcePos s Char)
430−oneOfStringsCI :: Stream s m Char => [Text] -> ParserT s st m Text
492+ => [Text] -> ParserT s st m Text
431oneOfStringsCI = oneOfStrings' ciMatch
493oneOfStringsCI = oneOfStrings' ciMatch
432 where ciMatch x y = toLower' x == toLower' y
494 where ciMatch x y = toLower' x == toLower' y
433 -- this optimizes toLower by checking common ASCII case
495 -- this optimizes toLower by checking common ASCII case
@@ -438,11 +500,13 @@ oneOfStringsCI = oneOfStrings' ciMatch
438 | otherwise = toLower c
500 | otherwise = toLower c
439
501
440-- | Parses a space or tab.
502-- | Parses a space or tab.
503+spaceChar :: (Stream s m Char, UpdateSourcePos s Char)
441−spaceChar :: Stream s m Char => ParserT s st m Char
504+ => ParserT s st m Char
442spaceChar = satisfy $ \c -> c == ' ' || c == '\t'
505spaceChar = satisfy $ \c -> c == ' ' || c == '\t'
443
506
444-- | Parses a nonspace, nonnewline character.
507-- | Parses a nonspace, nonnewline character.
445−nonspaceChar :: Stream s m Char => ParserT s st m Char
508+nonspaceChar :: (Stream s m Char, UpdateSourcePos s Char)
509+ => ParserT s st m Char
446nonspaceChar = satisfy (not . isSpaceChar)
510nonspaceChar = satisfy (not . isSpaceChar)
447
511
448isSpaceChar :: Char -> Bool
512isSpaceChar :: Char -> Bool
@@ -453,21 +517,24 @@ isSpaceChar '\r' = True
453isSpaceChar _ = False
517isSpaceChar _ = False
454
518
455-- | Skips zero or more spaces or tabs.
519-- | Skips zero or more spaces or tabs.
456−skipSpaces :: Stream s m Char => ParserT s st m ()
520+skipSpaces :: (Stream s m Char, UpdateSourcePos s Char)
521+ => ParserT s st m ()
457skipSpaces = skipMany spaceChar
522skipSpaces = skipMany spaceChar
458
523
459-- | Skips zero or more spaces or tabs, then reads a newline.
524-- | Skips zero or more spaces or tabs, then reads a newline.
525+blankline :: (Stream s m Char, UpdateSourcePos s Char)
460−blankline :: Stream s m Char => ParserT s st m Char
526+ => ParserT s st m Char
461blankline = try $ skipSpaces >> newline
527blankline = try $ skipSpaces >> newline
462
528
463-- | Parses one or more blank lines and returns a string of newlines.
529-- | Parses one or more blank lines and returns a string of newlines.
530+blanklines :: (Stream s m Char, UpdateSourcePos s Char)
464−blanklines :: Stream s m Char => ParserT s st m Text
531+ => ParserT s st m Text
465blanklines = T.pack <$> many1 blankline
532blanklines = T.pack <$> many1 blankline
466
533
467-- | Gobble n spaces; if tabs are encountered, expand them
534-- | Gobble n spaces; if tabs are encountered, expand them
468-- and gobble some or all of their spaces, leaving the rest.
535-- and gobble some or all of their spaces, leaving the rest.
469gobbleSpaces :: (HasReaderOptions st, Monad m)
536gobbleSpaces :: (HasReaderOptions st, Monad m)
470− => Int -> ParserT Text st m ()
537+ => Int -> ParserT Sources st m ()
471gobbleSpaces 0 = return ()
538gobbleSpaces 0 = return ()
472gobbleSpaces n
539gobbleSpaces n
473 | n < 0 = error "gobbleSpaces called with negative number"
540 | n < 0 = error "gobbleSpaces called with negative number"
@@ -475,18 +542,26 @@ gobbleSpaces n
475 char ' ' <|> eatOneSpaceOfTab
542 char ' ' <|> eatOneSpaceOfTab
476 gobbleSpaces (n - 1)
543 gobbleSpaces (n - 1)
477
544
478−eatOneSpaceOfTab :: (HasReaderOptions st, Monad m) => ParserT Text st m Char
545+eatOneSpaceOfTab :: (HasReaderOptions st, Monad m) => ParserT Sources st m Char
479eatOneSpaceOfTab = do
546eatOneSpaceOfTab = do
480− char '\t'
547+ lookAhead (char '\t')
548+ pos <- getPosition
481 tabstop <- getOption readerTabStop
549 tabstop <- getOption readerTabStop
550+ -- replace the tab on the input stream with spaces
551+ let numSpaces = tabstop - ((sourceColumn pos - 1) `mod` tabstop)
482 inp <- getInput
552 inp <- getInput
483− setInput $ T.replicate (tabstop - 1) " " <> inp
553+ setInput $
484− return ' '
554+ case inp of
555+ Sources [] -> error "eatOneSpaceOfTab - empty Sources list"
556+ Sources ((fp,t):rest) ->
557+ -- drop the tab and add spaces
558+ Sources ((fp, T.replicate numSpaces " " <> T.drop 1 t):rest)
559+ char ' '
485
560
486-- | Gobble up to n spaces; if tabs are encountered, expand them
561-- | Gobble up to n spaces; if tabs are encountered, expand them
487-- and gobble some or all of their spaces, leaving the rest.
562-- and gobble some or all of their spaces, leaving the rest.
488gobbleAtMostSpaces :: (HasReaderOptions st, Monad m)
563gobbleAtMostSpaces :: (HasReaderOptions st, Monad m)
489− => Int -> ParserT Text st m Int
564+ => Int -> ParserT Sources st m Int
490gobbleAtMostSpaces 0 = return 0
565gobbleAtMostSpaces 0 = return 0
491gobbleAtMostSpaces n
566gobbleAtMostSpaces n
492 | n < 0 = error "gobbleAtMostSpaces called with negative number"
567 | n < 0 = error "gobbleAtMostSpaces called with negative number"
@@ -495,7 +570,8 @@ gobbleAtMostSpaces n
495 (+ 1) <$> gobbleAtMostSpaces (n - 1)
570 (+ 1) <$> gobbleAtMostSpaces (n - 1)
496
571
497-- | Parses material enclosed between start and end parsers.
572-- | Parses material enclosed between start and end parsers.
573+enclosed :: (Show end, Stream s m Char, UpdateSourcePos s Char)
498−enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser
574+ => ParserT s st m t -- ^ start parser
499 -> ParserT s st m end -- ^ end parser
575 -> ParserT s st m end -- ^ end parser
500 -> ParserT s st m a -- ^ content parser (to be used repeatedly)
576 -> ParserT s st m a -- ^ content parser (to be used repeatedly)
501 -> ParserT s st m [a]
577 -> ParserT s st m [a]
@@ -503,39 +579,41 @@ enclosed start end parser = try $
503 start >> notFollowedBy space >> many1Till parser end
579 start >> notFollowedBy space >> many1Till parser end
504
580
505-- | Parse string, case insensitive.
581-- | Parse string, case insensitive.
582+stringAnyCase :: (Stream s m Char, UpdateSourcePos s Char)
506−stringAnyCase :: Stream s m Char => Text -> ParserT s st m Text
583+ => Text -> ParserT s st m Text
507stringAnyCase = fmap T.pack . stringAnyCase' . T.unpack
584stringAnyCase = fmap T.pack . stringAnyCase' . T.unpack
508
585
586+stringAnyCase' :: (Stream s m Char, UpdateSourcePos s Char)
509−stringAnyCase' :: Stream s m Char => String -> ParserT s st m String
587+ => String -> ParserT s st m String
510stringAnyCase' [] = string ""
588stringAnyCase' [] = string ""
511stringAnyCase' (x:xs) = do
589stringAnyCase' (x:xs) = do
512 firstChar <- char (toUpper x) <|> char (toLower x)
590 firstChar <- char (toUpper x) <|> char (toLower x)
513 rest <- stringAnyCase' xs
591 rest <- stringAnyCase' xs
514 return (firstChar:rest)
592 return (firstChar:rest)
515
593
594+-- TODO rewrite by just adding to Sources stream?
516-- | Parse contents of 'str' using 'parser' and return result.
595-- | Parse contents of 'str' using 'parser' and return result.
517−parseFromString :: (Stream s m Char, IsString s)
596+parseFromString :: Monad m
518− => ParserT s st m r
597+ => ParserT Sources st m r
519 -> Text
598 -> Text
520− -> ParserT s st m r
599+ -> ParserT Sources st m r
521parseFromString parser str = do
600parseFromString parser str = do
522 oldPos <- getPosition
601 oldPos <- getPosition
523 setPosition $ initialPos "chunk"
602 setPosition $ initialPos "chunk"
524 oldInput <- getInput
603 oldInput <- getInput
525− setInput $ fromString $ T.unpack str
604+ setInput $ toSources str
526 result <- parser
605 result <- parser
527 spaces
606 spaces
528− eof
529 setInput oldInput
607 setInput oldInput
530 setPosition oldPos
608 setPosition oldPos
531 return result
609 return result
532
610
533-- | Like 'parseFromString' but specialized for 'ParserState'.
611-- | Like 'parseFromString' but specialized for 'ParserState'.
534-- This resets 'stateLastStrPos', which is almost always what we want.
612-- This resets 'stateLastStrPos', which is almost always what we want.
535−parseFromString' :: (Stream s m Char, IsString s, HasLastStrPosition u)
613+parseFromString' :: (Monad m, HasLastStrPosition u)
536− => ParserT s u m a
614+ => ParserT Sources u m a
537 -> Text
615 -> Text
538− -> ParserT s u m a
616+ -> ParserT Sources u m a
539parseFromString' parser str = do
617parseFromString' parser str = do
540 oldLastStrPos <- getLastStrPos <$> getState
618 oldLastStrPos <- getLastStrPos <$> getState
541 updateState $ setLastStrPos Nothing
619 updateState $ setLastStrPos Nothing
@@ -544,7 +622,7 @@ parseFromString' parser str = do
544 return res
622 return res
545
623
546-- | Parse raw line block up to and including blank lines.
624-- | Parse raw line block up to and including blank lines.
547−lineClump :: Monad m => ParserT Text st m Text
625+lineClump :: Monad m => ParserT Sources st m Text
548lineClump = blanklines
626lineClump = blanklines
549 <|> (T.unlines <$> many1 (notFollowedBy blankline >> anyLine))
627 <|> (T.unlines <$> many1 (notFollowedBy blankline >> anyLine))
550
628
@@ -553,7 +631,7 @@ lineClump = blanklines
553-- pairs of open and close, which must be different. For example,
631-- pairs of open and close, which must be different. For example,
554-- @charsInBalanced '(' ')' anyChar@ will parse "(hello (there))"
632-- @charsInBalanced '(' ')' anyChar@ will parse "(hello (there))"
555-- and return "hello (there)".
633-- and return "hello (there)".
556−charsInBalanced :: Stream s m Char => Char -> Char -> ParserT s st m Char
634+charsInBalanced :: (Stream s m Char, UpdateSourcePos s Char) => Char -> Char -> ParserT s st m Char
557 -> ParserT s st m Text
635 -> ParserT s st m Text
558charsInBalanced open close parser = try $ do
636charsInBalanced open close parser = try $ do
559 char open
637 char open
@@ -572,7 +650,7 @@ charsInBalanced open close parser = try $ do
572-- Auxiliary functions for romanNumeral:
650-- Auxiliary functions for romanNumeral:
573
651
574-- | Parses a roman numeral (uppercase or lowercase), returns number.
652-- | Parses a roman numeral (uppercase or lowercase), returns number.
575−romanNumeral :: Stream s m Char => Bool -- ^ Uppercase if true
653+romanNumeral :: (Stream s m Char, UpdateSourcePos s Char) => Bool -- ^ Uppercase if true
576 -> ParserT s st m Int
654 -> ParserT s st m Int
577romanNumeral upperCase = do
655romanNumeral upperCase = do
578 let rchar uc = char $ if upperCase then uc else toLower uc
656 let rchar uc = char $ if upperCase then uc else toLower uc
@@ -608,20 +686,19 @@ romanNumeral upperCase = do
608
686
609-- | Parses an email address; returns original and corresponding
687-- | Parses an email address; returns original and corresponding
610-- escaped mailto: URI.
688-- escaped mailto: URI.
611−emailAddress :: Stream s m Char => ParserT s st m (Text, Text)
689+emailAddress :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (Text, Text)
612emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain)
690emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain)
613 where toResult mbox dom = let full = fromEntities $ T.pack $ mbox ++ '@':dom
691 where toResult mbox dom = let full = fromEntities $ T.pack $ mbox ++ '@':dom
614 in (full, escapeURI $ "mailto:" <> full)
692 in (full, escapeURI $ "mailto:" <> full)
615 mailbox = intercalate "." <$> (emailWord `sepBy1'` dot)
693 mailbox = intercalate "." <$> (emailWord `sepBy1'` dot)
616 domain = intercalate "." <$> (subdomain `sepBy1'` dot)
694 domain = intercalate "." <$> (subdomain `sepBy1'` dot)
617 dot = char '.'
695 dot = char '.'
618− subdomain = many1 $ alphaNum <|> innerPunct
696+ subdomain = many1 $ alphaNum <|> innerPunct (=='-')
619 -- this excludes some valid email addresses, since an
697 -- this excludes some valid email addresses, since an
620 -- email could contain e.g. '__', but gives better results
698 -- email could contain e.g. '__', but gives better results
621 -- for our purposes, when combined with markdown parsing:
699 -- for our purposes, when combined with markdown parsing:
622− innerPunct = try (satisfy (\c -> isEmailPunct c || c == '@')
700+ innerPunct f = try (satisfy f
623− <* notFollowedBy space
701+ <* notFollowedBy (satisfy (not . isAlphaNum)))
624− <* notFollowedBy (satisfy isPunctuation))
625 -- technically an email address could begin with a symbol,
702 -- technically an email address could begin with a symbol,
626 -- but allowing this creates too many problems.
703 -- but allowing this creates too many problems.
627 -- See e.g. https://github.com/jgm/pandoc/issues/2940
704 -- See e.g. https://github.com/jgm/pandoc/issues/2940
@@ -632,11 +709,11 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain)
632 isEmailPunct c = T.any (== c) "!\"#$%&'*+-/=?^_{|}~;"
709 isEmailPunct c = T.any (== c) "!\"#$%&'*+-/=?^_{|}~;"
633
710
634
711
635−uriScheme :: Stream s m Char => ParserT s st m Text
712+uriScheme :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Text
636uriScheme = oneOfStringsCI (Set.toList schemes)
713uriScheme = oneOfStringsCI (Set.toList schemes)
637
714
638-- | Parses a URI. Returns pair of original and URI-escaped version.
715-- | Parses a URI. Returns pair of original and URI-escaped version.
639−uri :: Stream s m Char => ParserT s st m (Text, Text)
716+uri :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (Text, Text)
640uri = try $ do
717uri = try $ do
641 scheme <- uriScheme
718 scheme <- uriScheme
642 char ':'
719 char ':'
@@ -679,7 +756,7 @@ uri = try $ do
679 uriChunkBetween l r = try $ do chunk <- between (char l) (char r) uriChunk
756 uriChunkBetween l r = try $ do chunk <- between (char l) (char r) uriChunk
680 return (T.pack $ [l] ++ chunk ++ [r])
757 return (T.pack $ [l] ++ chunk ++ [r])
681
758
682−mathInlineWith :: Stream s m Char => Text -> Text -> ParserT s st m Text
759+mathInlineWith :: (Stream s m Char, UpdateSourcePos s Char) => Text -> Text -> ParserT s st m Text
683mathInlineWith op cl = try $ do
760mathInlineWith op cl = try $ do
684 textStr op
761 textStr op
685 when (op == "$") $ notFollowedBy space
762 when (op == "$") $ notFollowedBy space
@@ -700,10 +777,10 @@ mathInlineWith op cl = try $ do
700 notFollowedBy digit -- to prevent capture of $5
777 notFollowedBy digit -- to prevent capture of $5
701 return $ trimMath $ T.concat words'
778 return $ trimMath $ T.concat words'
702 where
779 where
703− inBalancedBraces :: Stream s m Char => Int -> Text -> ParserT s st m Text
780+ inBalancedBraces :: (Stream s m Char, UpdateSourcePos s Char) => Int -> Text -> ParserT s st m Text
704 inBalancedBraces n = fmap T.pack . inBalancedBraces' n . T.unpack
781 inBalancedBraces n = fmap T.pack . inBalancedBraces' n . T.unpack
705
782
706− inBalancedBraces' :: Stream s m Char => Int -> String -> ParserT s st m String
783+ inBalancedBraces' :: (Stream s m Char, UpdateSourcePos s Char) => Int -> String -> ParserT s st m String
707 inBalancedBraces' 0 "" = do
784 inBalancedBraces' 0 "" = do
708 c <- anyChar
785 c <- anyChar
709 if c == '{'
786 if c == '{'
@@ -720,13 +797,13 @@ mathInlineWith op cl = try $ do
720 '{' -> inBalancedBraces' (numOpen + 1) (c:xs)
797 '{' -> inBalancedBraces' (numOpen + 1) (c:xs)
721 _ -> inBalancedBraces' numOpen (c:xs)
798 _ -> inBalancedBraces' numOpen (c:xs)
722
799
723−mathDisplayWith :: Stream s m Char => Text -> Text -> ParserT s st m Text
800+mathDisplayWith :: (Stream s m Char, UpdateSourcePos s Char) => Text -> Text -> ParserT s st m Text
724mathDisplayWith op cl = try $ fmap T.pack $ do
801mathDisplayWith op cl = try $ fmap T.pack $ do
725 textStr op
802 textStr op
726 many1Till (satisfy (/= '\n') <|> (newline <* notFollowedBy' blankline))
803 many1Till (satisfy (/= '\n') <|> (newline <* notFollowedBy' blankline))
727 (try $ textStr cl)
804 (try $ textStr cl)
728
805
729−mathDisplay :: (HasReaderOptions st, Stream s m Char)
806+mathDisplay :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char)
730 => ParserT s st m Text
807 => ParserT s st m Text
731mathDisplay =
808mathDisplay =
732 (guardEnabled Ext_tex_math_dollars >> mathDisplayWith "$$" "$$")
809 (guardEnabled Ext_tex_math_dollars >> mathDisplayWith "$$" "$$")
@@ -735,7 +812,7 @@ mathDisplay =
735 <|> (guardEnabled Ext_tex_math_double_backslash >>
812 <|> (guardEnabled Ext_tex_math_double_backslash >>
736 mathDisplayWith "\\\\[" "\\\\]")
813 mathDisplayWith "\\\\[" "\\\\]")
737
814
738−mathInline :: (HasReaderOptions st , Stream s m Char)
815+mathInline :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char)
739 => ParserT s st m Text
816 => ParserT s st m Text
740mathInline =
817mathInline =
741 (guardEnabled Ext_tex_math_dollars >> mathInlineWith "$" "$")
818 (guardEnabled Ext_tex_math_dollars >> mathInlineWith "$" "$")
@@ -748,7 +825,7 @@ mathInline =
748-- displacement (the difference between the source column at the end
825-- displacement (the difference between the source column at the end
749-- and the source column at the beginning). Vertical displacement
826-- and the source column at the beginning). Vertical displacement
750-- (source row) is ignored.
827-- (source row) is ignored.
751−withHorizDisplacement :: Stream s m Char
828+withHorizDisplacement :: (Stream s m Char, UpdateSourcePos s Char)
752 => ParserT s st m a -- ^ Parser to apply
829 => ParserT s st m a -- ^ Parser to apply
753 -> ParserT s st m (a, Int) -- ^ (result, displacement)
830 -> ParserT s st m (a, Int) -- ^ (result, displacement)
754withHorizDisplacement parser = do
831withHorizDisplacement parser = do
@@ -760,30 +837,37 @@ withHorizDisplacement parser = do
760-- | Applies a parser and returns the raw string that was parsed,
837-- | Applies a parser and returns the raw string that was parsed,
761-- along with the value produced by the parser.
838-- along with the value produced by the parser.
762withRaw :: Monad m
839withRaw :: Monad m
763− => ParsecT Text st m a
840+ => ParsecT Sources st m a
764− -> ParsecT Text st m (a, Text)
841+ -> ParsecT Sources st m (a, Text)
765withRaw parser = do
842withRaw parser = do
766− pos1 <- getPosition
767− inp <- getInput
843+ inps1 <- getInput
768 result <- parser
844 result <- parser
769− pos2 <- getPosition
845+ inps2 <- getInput
770− let (l1,c1) = (sourceLine pos1, sourceColumn pos1)
846+ -- 'raw' is the difference between inps1 and inps2
771− let (l2,c2) = (sourceLine pos2, sourceColumn pos2)
847+ return (result, sourcesDifference inps1 inps2)
772− let inplines = take ((l2 - l1) + 1) $ T.lines inp
848+
773− let raw = case inplines of
849+sourcesDifference :: Sources -> Sources -> Text
850+sourcesDifference (Sources is1) (Sources is2) = go is1 is2
851+ where
852+ go inps1 inps2 =
853+ case (inps1, inps2) of
774− [] -> ""
854+ ([], _) -> mempty
775− [l] -> T.take (c2 - c1) l
855+ (_, []) -> mconcat $ map snd inps1
776− ls -> T.unlines (init ls) <> T.take (c2 - 1) (last ls)
856+ ((p1,t1):rest1, (p2, t2):rest2)
777− return (result, raw)
857+ | p1 == p2
858+ , t1 == t2 -> go rest1 rest2
859+ | p1 == p2
860+ , t1 /= t2 -> fromMaybe mempty $ T.stripSuffix t2 t1
861+ | otherwise -> t1 <> go rest1 inps2
778
862
779-- | Parses backslash, then applies character parser.
863-- | Parses backslash, then applies character parser.
780−escaped :: Stream s m Char
864+escaped :: (Stream s m Char, UpdateSourcePos s Char)
781 => ParserT s st m Char -- ^ Parser for character to escape
865 => ParserT s st m Char -- ^ Parser for character to escape
782 -> ParserT s st m Char
866 -> ParserT s st m Char
783escaped parser = try $ char '\\' >> parser
867escaped parser = try $ char '\\' >> parser
784
868
785-- | Parse character entity.
869-- | Parse character entity.
786−characterReference :: Stream s m Char => ParserT s st m Char
870+characterReference :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Char
787characterReference = try $ do
871characterReference = try $ do
788 char '&'
872 char '&'
789 ent <- many1Till nonspaceChar (char ';')
873 ent <- many1Till nonspaceChar (char ';')
@@ -796,19 +880,19 @@ characterReference = try $ do
796 _ -> Prelude.fail "entity not found"
880 _ -> Prelude.fail "entity not found"
797
881
798-- | Parses an uppercase roman numeral and returns (UpperRoman, number).
882-- | Parses an uppercase roman numeral and returns (UpperRoman, number).
799−upperRoman :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
883+upperRoman :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
800upperRoman = do
884upperRoman = do
801 num <- romanNumeral True
885 num <- romanNumeral True
802 return (UpperRoman, num)
886 return (UpperRoman, num)
803
887
804-- | Parses a lowercase roman numeral and returns (LowerRoman, number).
888-- | Parses a lowercase roman numeral and returns (LowerRoman, number).
805−lowerRoman :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
889+lowerRoman :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
806lowerRoman = do
890lowerRoman = do
807 num <- romanNumeral False
891 num <- romanNumeral False
808 return (LowerRoman, num)
892 return (LowerRoman, num)
809
893
810-- | Parses a decimal numeral and returns (Decimal, number).
894-- | Parses a decimal numeral and returns (Decimal, number).
811−decimal :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
895+decimal :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
812decimal = do
896decimal = do
813 num <- many1 digit
897 num <- many1 digit
814 return (Decimal, fromMaybe 1 $ safeRead $ T.pack num)
898 return (Decimal, fromMaybe 1 $ safeRead $ T.pack num)
@@ -817,7 +901,7 @@ decimal = do
817-- returns (DefaultStyle, [next example number]). The next
901-- returns (DefaultStyle, [next example number]). The next
818-- example number is incremented in parser state, and the label
902-- example number is incremented in parser state, and the label
819-- (if present) is added to the label table.
903-- (if present) is added to the label table.
820−exampleNum :: Stream s m Char
904+exampleNum :: (Stream s m Char, UpdateSourcePos s Char)
821 => ParserT s ParserState m (ListNumberStyle, Int)
905 => ParserT s ParserState m (ListNumberStyle, Int)
822exampleNum = do
906exampleNum = do
823 char '@'
907 char '@'
@@ -836,37 +920,37 @@ exampleNum = do
836 return (Example, num)
920 return (Example, num)
837
921
838-- | Parses a '#' returns (DefaultStyle, 1).
922-- | Parses a '#' returns (DefaultStyle, 1).
839−defaultNum :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
923+defaultNum :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
840defaultNum = do
924defaultNum = do
841 char '#'
925 char '#'
842 return (DefaultStyle, 1)
926 return (DefaultStyle, 1)
843
927
844-- | Parses a lowercase letter and returns (LowerAlpha, number).
928-- | Parses a lowercase letter and returns (LowerAlpha, number).
845−lowerAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
929+lowerAlpha :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
846lowerAlpha = do
930lowerAlpha = do
847 ch <- satisfy isAsciiLower
931 ch <- satisfy isAsciiLower
848 return (LowerAlpha, ord ch - ord 'a' + 1)
932 return (LowerAlpha, ord ch - ord 'a' + 1)
849
933
850-- | Parses an uppercase letter and returns (UpperAlpha, number).
934-- | Parses an uppercase letter and returns (UpperAlpha, number).
851−upperAlpha :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
935+upperAlpha :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
852upperAlpha = do
936upperAlpha = do
853 ch <- satisfy isAsciiUpper
937 ch <- satisfy isAsciiUpper
854 return (UpperAlpha, ord ch - ord 'A' + 1)
938 return (UpperAlpha, ord ch - ord 'A' + 1)
855
939
856-- | Parses a roman numeral i or I
940-- | Parses a roman numeral i or I
857−romanOne :: Stream s m Char => ParserT s st m (ListNumberStyle, Int)
941+romanOne :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m (ListNumberStyle, Int)
858romanOne = (char 'i' >> return (LowerRoman, 1)) <|>
942romanOne = (char 'i' >> return (LowerRoman, 1)) <|>
859 (char 'I' >> return (UpperRoman, 1))
943 (char 'I' >> return (UpperRoman, 1))
860
944
861-- | Parses an ordered list marker and returns list attributes.
945-- | Parses an ordered list marker and returns list attributes.
862−anyOrderedListMarker :: Stream s m Char => ParserT s ParserState m ListAttributes
946+anyOrderedListMarker :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s ParserState m ListAttributes
863anyOrderedListMarker = choice
947anyOrderedListMarker = choice
864 [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens],
948 [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens],
865 numParser <- [decimal, exampleNum, defaultNum, romanOne,
949 numParser <- [decimal, exampleNum, defaultNum, romanOne,
866 lowerAlpha, lowerRoman, upperAlpha, upperRoman]]
950 lowerAlpha, lowerRoman, upperAlpha, upperRoman]]
867
951
868-- | Parses a list number (num) followed by a period, returns list attributes.
952-- | Parses a list number (num) followed by a period, returns list attributes.
869−inPeriod :: Stream s m Char
953+inPeriod :: (Stream s m Char, UpdateSourcePos s Char)
870 => ParserT s st m (ListNumberStyle, Int)
954 => ParserT s st m (ListNumberStyle, Int)
871 -> ParserT s st m ListAttributes
955 -> ParserT s st m ListAttributes
872inPeriod num = try $ do
956inPeriod num = try $ do
@@ -878,7 +962,7 @@ inPeriod num = try $ do
878 return (start, style, delim)
962 return (start, style, delim)
879
963
880-- | Parses a list number (num) followed by a paren, returns list attributes.
964-- | Parses a list number (num) followed by a paren, returns list attributes.
881−inOneParen :: Stream s m Char
965+inOneParen :: (Stream s m Char, UpdateSourcePos s Char)
882 => ParserT s st m (ListNumberStyle, Int)
966 => ParserT s st m (ListNumberStyle, Int)
883 -> ParserT s st m ListAttributes
967 -> ParserT s st m ListAttributes
884inOneParen num = try $ do
968inOneParen num = try $ do
@@ -887,7 +971,7 @@ inOneParen num = try $ do
887 return (start, style, OneParen)
971 return (start, style, OneParen)
888
972
889-- | Parses a list number (num) enclosed in parens, returns list attributes.
973-- | Parses a list number (num) enclosed in parens, returns list attributes.
890−inTwoParens :: Stream s m Char
974+inTwoParens :: (Stream s m Char, UpdateSourcePos s Char)
891 => ParserT s st m (ListNumberStyle, Int)
975 => ParserT s st m (ListNumberStyle, Int)
892 -> ParserT s st m ListAttributes
976 -> ParserT s st m ListAttributes
893inTwoParens num = try $ do
977inTwoParens num = try $ do
@@ -898,7 +982,7 @@ inTwoParens num = try $ do
898
982
899-- | Parses an ordered list marker with a given style and delimiter,
983-- | Parses an ordered list marker with a given style and delimiter,
900-- returns number.
984-- returns number.
901−orderedListMarker :: Stream s m Char
985+orderedListMarker :: (Stream s m Char, UpdateSourcePos s Char)
902 => ListNumberStyle
986 => ListNumberStyle
903 -> ListNumberDelim
987 -> ListNumberDelim
904 -> ParserT s ParserState m Int
988 -> ParserT s ParserState m Int
@@ -921,10 +1005,10 @@ orderedListMarker style delim = do
921 return start
1005 return start
922
1006
923-- | Parses a character reference and returns a Str element.
1007-- | Parses a character reference and returns a Str element.
924−charRef :: Stream s m Char => ParserT s st m Inline
1008+charRef :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inline
925charRef = Str . T.singleton <$> characterReference
1009charRef = Str . T.singleton <$> characterReference
926
1010
927−lineBlockLine :: Monad m => ParserT Text st m Text
1011+lineBlockLine :: Monad m => ParserT Sources st m Text
928lineBlockLine = try $ do
1012lineBlockLine = try $ do
929 char '|'
1013 char '|'
930 char ' '
1014 char ' '
@@ -934,11 +1018,11 @@ lineBlockLine = try $ do
934 continuations <- many (try $ char ' ' >> anyLine)
1018 continuations <- many (try $ char ' ' >> anyLine)
935 return $ white <> T.unwords (line : continuations)
1019 return $ white <> T.unwords (line : continuations)
936
1020
937−blankLineBlockLine :: Stream s m Char => ParserT s st m Char
1021+blankLineBlockLine :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Char
938blankLineBlockLine = try (char '|' >> blankline)
1022blankLineBlockLine = try (char '|' >> blankline)
939
1023
940-- | Parses an RST-style line block and returns a list of strings.
1024-- | Parses an RST-style line block and returns a list of strings.
941−lineBlockLines :: Monad m => ParserT Text st m [Text]
1025+lineBlockLines :: Monad m => ParserT Sources st m [Text]
942lineBlockLines = try $ do
1026lineBlockLines = try $ do
943 lines' <- many1 (lineBlockLine <|> (T.singleton <$> blankLineBlockLine))
1027 lines' <- many1 (lineBlockLine <|> (T.singleton <$> blankLineBlockLine))
944 skipMany blankline
1028 skipMany blankline
@@ -946,7 +1030,8 @@ lineBlockLines = try $ do
946
1030
947-- | Parse a table using 'headerParser', 'rowParser',
1031-- | Parse a table using 'headerParser', 'rowParser',
948-- 'lineParser', and 'footerParser'.
1032-- 'lineParser', and 'footerParser'.
1033+tableWith :: (Stream s m Char, UpdateSourcePos s Char,
949−tableWith :: (Stream s m Char, HasReaderOptions st, Monad mf)
1034+ HasReaderOptions st, Monad mf)
950 => ParserT s st m (mf [Blocks], [Alignment], [Int])
1035 => ParserT s st m (mf [Blocks], [Alignment], [Int])
951 -> ([Int] -> ParserT s st m (mf [Blocks]))
1036 -> ([Int] -> ParserT s st m (mf [Blocks]))
952 -> ParserT s st m sep
1037 -> ParserT s st m sep
@@ -966,7 +1051,8 @@ tableWith headerParser rowParser lineParser footerParser = try $ do
966
1051
967type TableComponents mf = ([Alignment], [Double], mf [Row], mf [Row])
1052type TableComponents mf = ([Alignment], [Double], mf [Row], mf [Row])
968
1053
1054+tableWith' :: (Stream s m Char, UpdateSourcePos s Char,
969−tableWith' :: (Stream s m Char, HasReaderOptions st, Monad mf)
1055+ HasReaderOptions st, Monad mf)
970 => ParserT s st m (mf [Blocks], [Alignment], [Int])
1056 => ParserT s st m (mf [Blocks], [Alignment], [Int])
971 -> ([Int] -> ParserT s st m (mf [Blocks]))
1057 -> ([Int] -> ParserT s st m (mf [Blocks]))
972 -> ParserT s st m sep
1058 -> ParserT s st m sep
@@ -1015,20 +1101,19 @@ widthsFromIndices numColumns' indices =
1015-- (which may be grid), then the rows,
1101-- (which may be grid), then the rows,
1016-- which may be grid, separated by blank lines, and
1102-- which may be grid, separated by blank lines, and
1017-- ending with a footer (dashed line followed by blank line).
1103-- ending with a footer (dashed line followed by blank line).
1018−gridTableWith :: (Stream s m Char, HasReaderOptions st, HasLastStrPosition st,
1104+gridTableWith :: (Monad m, HasReaderOptions st, HasLastStrPosition st, Monad mf)
1019− Monad mf, IsString s)
1020− => ParserT s st m (mf Blocks) -- ^ Block list parser
1105+ => ParserT Sources st m (mf Blocks) -- ^ Block list parser
1021 -> Bool -- ^ Headerless table
1106 -> Bool -- ^ Headerless table
1022− -> ParserT s st m (mf Blocks)
1107+ -> ParserT Sources st m (mf Blocks)
1023gridTableWith blocks headless =
1108gridTableWith blocks headless =
1024 tableWith (gridTableHeader headless blocks) (gridTableRow blocks)
1109 tableWith (gridTableHeader headless blocks) (gridTableRow blocks)
1025 (gridTableSep '-') gridTableFooter
1110 (gridTableSep '-') gridTableFooter
1026
1111
1027−gridTableWith' :: (Stream s m Char, HasReaderOptions st, HasLastStrPosition st,
1112+gridTableWith' :: (Monad m, HasReaderOptions st, HasLastStrPosition st,
1028− Monad mf, IsString s)
1113+ Monad mf)
1029− => ParserT s st m (mf Blocks) -- ^ Block list parser
1114+ => ParserT Sources st m (mf Blocks) -- ^ Block list parser
1030 -> Bool -- ^ Headerless table
1115 -> Bool -- ^ Headerless table
1031− -> ParserT s st m (TableComponents mf)
1116+ -> ParserT Sources st m (TableComponents mf)
1032gridTableWith' blocks headless =
1117gridTableWith' blocks headless =
1033 tableWith' (gridTableHeader headless blocks) (gridTableRow blocks)
1118 tableWith' (gridTableHeader headless blocks) (gridTableRow blocks)
1034 (gridTableSep '-') gridTableFooter
1119 (gridTableSep '-') gridTableFooter
@@ -1037,7 +1122,7 @@ gridTableSplitLine :: [Int] -> Text -> [Text]
1037gridTableSplitLine indices line = map removeFinalBar $ tail $
1122gridTableSplitLine indices line = map removeFinalBar $ tail $
1038 splitTextByIndices (init indices) $ trimr line
1123 splitTextByIndices (init indices) $ trimr line
1039
1124
1040−gridPart :: Stream s m Char => Char -> ParserT s st m ((Int, Int), Alignment)
1125+gridPart :: Monad m => Char -> ParserT Sources st m ((Int, Int), Alignment)
1041gridPart ch = do
1126gridPart ch = do
1042 leftColon <- option False (True <$ char ':')
1127 leftColon <- option False (True <$ char ':')
1043 dashes <- many1 (char ch)
1128 dashes <- many1 (char ch)
@@ -1052,7 +1137,7 @@ gridPart ch = do
1052 (False, False) -> AlignDefault
1137 (False, False) -> AlignDefault
1053 return ((lengthDashes, lengthDashes + 1), alignment)
1138 return ((lengthDashes, lengthDashes + 1), alignment)
1054
1139
1055−gridDashedLines :: Stream s m Char => Char -> ParserT s st m [((Int, Int), Alignment)]
1140+gridDashedLines :: Monad m => Char -> ParserT Sources st m [((Int, Int), Alignment)]
1056gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline
1141gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline
1057
1142
1058removeFinalBar :: Text -> Text
1143removeFinalBar :: Text -> Text
@@ -1061,14 +1146,14 @@ removeFinalBar = T.dropWhileEnd go . T.dropWhileEnd (=='|')
1061 go c = T.any (== c) " \t"
1146 go c = T.any (== c) " \t"
1062
1147
1063-- | Separator between rows of grid table.
1148-- | Separator between rows of grid table.
1064−gridTableSep :: Stream s m Char => Char -> ParserT s st m Char
1149+gridTableSep :: Monad m => Char -> ParserT Sources st m Char
1065gridTableSep ch = try $ gridDashedLines ch >> return '\n'
1150gridTableSep ch = try $ gridDashedLines ch >> return '\n'
1066
1151
1067-- | Parse header for a grid table.
1152-- | Parse header for a grid table.
1068−gridTableHeader :: (Stream s m Char, Monad mf, IsString s, HasLastStrPosition st)
1153+gridTableHeader :: (Monad m, Monad mf, HasLastStrPosition st)
1069 => Bool -- ^ Headerless table
1154 => Bool -- ^ Headerless table
1070− -> ParserT s st m (mf Blocks)
1155+ -> ParserT Sources st m (mf Blocks)
1071− -> ParserT s st m (mf [Blocks], [Alignment], [Int])
1156+ -> ParserT Sources st m (mf [Blocks], [Alignment], [Int])
1072gridTableHeader True _ = do
1157gridTableHeader True _ = do
1073 optional blanklines
1158 optional blanklines
1074 dashes <- gridDashedLines '-'
1159 dashes <- gridDashedLines '-'
@@ -1091,17 +1176,17 @@ gridTableHeader False blocks = try $ do
1091 heads <- sequence <$> mapM (parseFromString' blocks . trim) rawHeads
1176 heads <- sequence <$> mapM (parseFromString' blocks . trim) rawHeads
1092 return (heads, aligns, indices)
1177 return (heads, aligns, indices)
1093
1178
1094−gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [Text]
1179+gridTableRawLine :: (Stream s m Char, UpdateSourcePos s Char) => [Int] -> ParserT s st m [Text]
1095gridTableRawLine indices = do
1180gridTableRawLine indices = do
1096 char '|'
1181 char '|'
1097 line <- many1Till anyChar newline
1182 line <- many1Till anyChar newline
1098 return (gridTableSplitLine indices $ T.pack line)
1183 return (gridTableSplitLine indices $ T.pack line)
1099
1184
1100-- | Parse row of grid table.
1185-- | Parse row of grid table.
1101−gridTableRow :: (Stream s m Char, Monad mf, IsString s, HasLastStrPosition st)
1186+gridTableRow :: (Monad m, Monad mf, HasLastStrPosition st)
1102− => ParserT s st m (mf Blocks)
1187+ => ParserT Sources st m (mf Blocks)
1103 -> [Int]
1188 -> [Int]
1104− -> ParserT s st m (mf [Blocks])
1189+ -> ParserT Sources st m (mf [Blocks])
1105gridTableRow blocks indices = do
1190gridTableRow blocks indices = do
1106 colLines <- many1 (gridTableRawLine indices)
1191 colLines <- many1 (gridTableRawLine indices)
1107 let cols = map ((<> "\n") . T.unlines . removeOneLeadingSpace) $
1192 let cols = map ((<> "\n") . T.unlines . removeOneLeadingSpace) $
@@ -1122,34 +1207,38 @@ removeOneLeadingSpace xs =
1122 Just (c, _) -> c == ' '
1207 Just (c, _) -> c == ' '
1123
1208
1124-- | Parse footer for a grid table.
1209-- | Parse footer for a grid table.
1125−gridTableFooter :: Stream s m Char => ParserT s st m ()
1210+gridTableFooter :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m ()
1126gridTableFooter = optional blanklines
1211gridTableFooter = optional blanklines
1127
1212
1128---
1213---
1129
1214
1130-- | Removes the ParsecT layer from the monad transformer stack
1215-- | Removes the ParsecT layer from the monad transformer stack
1131−readWithM :: (Stream s m Char, ToText s)
1216+readWithM :: (Monad m, ToSources t)
1132− => ParserT s st m a -- ^ parser
1217+ => ParserT Sources st m a -- ^ parser
1133− -> st -- ^ initial state
1218+ -> st -- ^ initial state
1134− -> s -- ^ input
1219+ -> t -- ^ input
1135 -> m (Either PandocError a)
1220 -> m (Either PandocError a)
1136readWithM parser state input =
1221readWithM parser state input =
1137− mapLeft (PandocParsecError $ toText input) `liftM` runParserT parser state "source" input
1222+ mapLeft (PandocParsecError sources)
1223+ <$> runParserT parser state (initialSourceName sources) sources
1224+ where
1225+ sources = toSources input
1138
1226
1139-- | Parse a string with a given parser and state
1227-- | Parse a string with a given parser and state
1228+readWith :: ToSources t
1140−readWith :: Parser Text st a
1229+ => Parser Sources st a
1141 -> st
1230 -> st
1142− -> Text
1231+ -> t
1143 -> Either PandocError a
1232 -> Either PandocError a
1144readWith p t inp = runIdentity $ readWithM p t inp
1233readWith p t inp = runIdentity $ readWithM p t inp
1145
1234
1146-- | Parse a string with @parser@ (for testing).
1235-- | Parse a string with @parser@ (for testing).
1147testStringWith :: Show a
1236testStringWith :: Show a
1148− => ParserT Text ParserState Identity a
1237+ => ParserT Sources ParserState Identity a
1149 -> Text
1238 -> Text
1150 -> IO ()
1239 -> IO ()
1151testStringWith parser str = UTF8.putStrLn $ tshow $
1240testStringWith parser str = UTF8.putStrLn $ tshow $
1152− readWith parser defaultParserState str
1241+ readWith parser defaultParserState (toSources str)
1153
1242
1154-- | Parsing options.
1243-- | Parsing options.
1155data ParserState = ParserState
1244data ParserState = ParserState
@@ -1169,7 +1258,7 @@ data ParserState = ParserState
1169 stateInNote :: Bool, -- ^ True if parsing note contents
1258 stateInNote :: Bool, -- ^ True if parsing note contents
1170 stateNoteNumber :: Int, -- ^ Last note number for citations
1259 stateNoteNumber :: Int, -- ^ Last note number for citations
1171 stateMeta :: Meta, -- ^ Document metadata
1260 stateMeta :: Meta, -- ^ Document metadata
1172− stateMeta' :: F Meta, -- ^ Document metadata
1261+ stateMeta' :: Future ParserState Meta, -- ^ Document metadata
1173 stateCitations :: M.Map Text Text, -- ^ RST-style citations
1262 stateCitations :: M.Map Text Text, -- ^ RST-style citations
1174 stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used
1263 stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used
1175 stateIdentifiers :: Set.Set Text, -- ^ Header identifiers used
1264 stateIdentifiers :: Set.Set Text, -- ^ Header identifiers used
@@ -1348,7 +1437,7 @@ data QuoteContext
1348
1437
1349type NoteTable = [(Text, Text)]
1438type NoteTable = [(Text, Text)]
1350
1439
1351−type NoteTable' = M.Map Text (SourcePos, F Blocks)
1440+type NoteTable' = M.Map Text (SourcePos, Future ParserState Blocks)
1352-- used in markdown reader
1441-- used in markdown reader
1353
1442
1354newtype Key = Key Text deriving (Show, Read, Eq, Ord)
1443newtype Key = Key Text deriving (Show, Read, Eq, Ord)
@@ -1383,7 +1472,7 @@ registerHeader (ident,classes,kvs) header' = do
1383 then do
1472 then do
1384 let id' = uniqueIdent exts (B.toList header') ids
1473 let id' = uniqueIdent exts (B.toList header') ids
1385 let id'' = if Ext_ascii_identifiers `extensionEnabled` exts
1474 let id'' = if Ext_ascii_identifiers `extensionEnabled` exts
1386− then T.pack $ mapMaybe toAsciiChar $ T.unpack id'
1475+ then toAsciiText id'
1387 else id'
1476 else id'
1388 updateState $ updateIdentifierList $ Set.insert id'
1477 updateState $ updateIdentifierList $ Set.insert id'
1389 updateState $ updateIdentifierList $ Set.insert id''
1478 updateState $ updateIdentifierList $ Set.insert id''
@@ -1396,34 +1485,42 @@ registerHeader (ident,classes,kvs) header' = do
1396 updateState $ updateIdentifierList $ Set.insert ident
1485 updateState $ updateIdentifierList $ Set.insert ident
1397 return (ident,classes,kvs)
1486 return (ident,classes,kvs)
1398
1487
1399−smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1488+smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st,
1489+ HasQuoteContext st m,
1490+ Stream s m Char, UpdateSourcePos s Char)
1400 => ParserT s st m Inlines
1491 => ParserT s st m Inlines
1401 -> ParserT s st m Inlines
1492 -> ParserT s st m Inlines
1402smartPunctuation inlineParser = do
1493smartPunctuation inlineParser = do
1403 guardEnabled Ext_smart
1494 guardEnabled Ext_smart
1404− choice [ quoted inlineParser, apostrophe, dash, ellipses ]
1495+ choice [ quoted inlineParser, apostrophe, doubleCloseQuote, dash, ellipses ]
1405
1496
1406−apostrophe :: Stream s m Char => ParserT s st m Inlines
1407−apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\x2019")
1408−
1409−quoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1497+quoted :: (HasLastStrPosition st, HasQuoteContext st m,
1498+ Stream s m Char, UpdateSourcePos s Char)
1410 => ParserT s st m Inlines
1499 => ParserT s st m Inlines
1411 -> ParserT s st m Inlines
1500 -> ParserT s st m Inlines
1412quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser
1501quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser
1413
1502
1414−singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1503+singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m,
1504+ Stream s m Char, UpdateSourcePos s Char)
1415 => ParserT s st m Inlines
1505 => ParserT s st m Inlines
1416 -> ParserT s st m Inlines
1506 -> ParserT s st m Inlines
1417−singleQuoted inlineParser = try $ B.singleQuoted . mconcat
1507+singleQuoted inlineParser = do
1418− <$ singleQuoteStart
1508+ singleQuoteStart
1509+ (B.singleQuoted . mconcat <$>
1510+ try
1419− <*> withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd)
1511+ (withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd)))
1420−
1512+ <|> pure "\8217"
1513+
1421−doubleQuoted :: (HasQuoteContext st m, Stream s m Char)
1514+doubleQuoted :: (HasQuoteContext st m, HasLastStrPosition st,
1515+ Stream s m Char, UpdateSourcePos s Char)
1422 => ParserT s st m Inlines
1516 => ParserT s st m Inlines
1423 -> ParserT s st m Inlines
1517 -> ParserT s st m Inlines
1424−doubleQuoted inlineParser = try $ B.doubleQuoted . mconcat
1518+doubleQuoted inlineParser = do
1425− <$ doubleQuoteStart
1519+ doubleQuoteStart
1520+ (B.doubleQuoted . mconcat <$>
1521+ try
1426− <*> withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd)
1522+ (withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd)))
1523+ <|> pure (B.str "\8220")
1427
1524
1428failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t)
1525failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t)
1429 => QuoteContext
1526 => QuoteContext
@@ -1432,13 +1529,14 @@ failIfInQuoteContext context = do
1432 context' <- getQuoteContext
1529 context' <- getQuoteContext
1433 when (context' == context) $ Prelude.fail "already inside quotes"
1530 when (context' == context) $ Prelude.fail "already inside quotes"
1434
1531
1435−charOrRef :: Stream s m Char => [Char] -> ParserT s st m Char
1532+charOrRef :: (Stream s m Char, UpdateSourcePos s Char) => [Char] -> ParserT s st m Char
1436charOrRef cs =
1533charOrRef cs =
1437 oneOf cs <|> try (do c <- characterReference
1534 oneOf cs <|> try (do c <- characterReference
1438 guard (c `elem` cs)
1535 guard (c `elem` cs)
1439 return c)
1536 return c)
1440
1537
1441−singleQuoteStart :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1538+singleQuoteStart :: (HasLastStrPosition st, HasQuoteContext st m,
1539+ Stream s m Char, UpdateSourcePos s Char)
1442 => ParserT s st m ()
1540 => ParserT s st m ()
1443singleQuoteStart = do
1541singleQuoteStart = do
1444 failIfInQuoteContext InSingleQuote
1542 failIfInQuoteContext InSingleQuote
@@ -1446,30 +1544,39 @@ singleQuoteStart = do
1446 guard =<< notAfterString
1544 guard =<< notAfterString
1447 try $ do
1545 try $ do
1448 charOrRef "'\8216\145"
1546 charOrRef "'\8216\145"
1449− notFollowedBy (oneOf [' ', '\t', '\n'])
1547+ void $ lookAhead (satisfy (not . isSpaceChar))
1450
1548
1451−singleQuoteEnd :: Stream s m Char
1549+singleQuoteEnd :: (Stream s m Char, UpdateSourcePos s Char)
1452 => ParserT s st m ()
1550 => ParserT s st m ()
1453singleQuoteEnd = try $ do
1551singleQuoteEnd = try $ do
1454 charOrRef "'\8217\146"
1552 charOrRef "'\8217\146"
1455 notFollowedBy alphaNum
1553 notFollowedBy alphaNum
1456
1554
1555+doubleQuoteStart :: (HasLastStrPosition st,
1457−doubleQuoteStart :: (HasQuoteContext st m, Stream s m Char)
1556+ HasQuoteContext st m,
1557+ Stream s m Char, UpdateSourcePos s Char)
1458 => ParserT s st m ()
1558 => ParserT s st m ()
1459doubleQuoteStart = do
1559doubleQuoteStart = do
1460 failIfInQuoteContext InDoubleQuote
1560 failIfInQuoteContext InDoubleQuote
1561+ guard =<< notAfterString
1461 try $ do charOrRef "\"\8220\147"
1562 try $ do charOrRef "\"\8220\147"
1462− notFollowedBy (oneOf [' ', '\t', '\n'])
1563+ void $ lookAhead (satisfy (not . isSpaceChar))
1463
1564
1464−doubleQuoteEnd :: Stream s m Char
1565+doubleQuoteEnd :: (Stream s m Char, UpdateSourcePos s Char)
1465 => ParserT s st m ()
1566 => ParserT s st m ()
1466doubleQuoteEnd = void (charOrRef "\"\8221\148")
1567doubleQuoteEnd = void (charOrRef "\"\8221\148")
1467
1568
1569+apostrophe :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines
1570+apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\8217")
1571+
1572+doubleCloseQuote :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m Inlines
1573+doubleCloseQuote = B.str "\8221" <$ char '"'
1574+
1468−ellipses :: Stream s m Char
1575+ellipses :: (Stream s m Char, UpdateSourcePos s Char)
1469 => ParserT s st m Inlines
1576 => ParserT s st m Inlines
1470ellipses = try (string "..." >> return (B.str "\8230"))
1577ellipses = try (string "..." >> return (B.str "\8230"))
1471
1578
1472−dash :: (HasReaderOptions st, Stream s m Char)
1579+dash :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char)
1473 => ParserT s st m Inlines
1580 => ParserT s st m Inlines
1474dash = try $ do
1581dash = try $ do
1475 oldDashes <- extensionEnabled Ext_old_dashes <$> getOption readerExtensions
1582 oldDashes <- extensionEnabled Ext_old_dashes <$> getOption readerExtensions
@@ -1496,20 +1603,28 @@ nested p = do
1496 updateState $ \st -> st{ stateMaxNestingLevel = nestlevel }
1603 updateState $ \st -> st{ stateMaxNestingLevel = nestlevel }
1497 return res
1604 return res
1498
1605
1499−citeKey :: (Stream s m Char, HasLastStrPosition st)
1606+citeKey :: (Stream s m Char, UpdateSourcePos s Char, HasLastStrPosition st)
1607+ => Bool -- ^ If True, allow expanded @{..} syntax.
1500− => ParserT s st m (Bool, Text)
1608+ -> ParserT s st m (Bool, Text)
1501−citeKey = try $ do
1609+citeKey allowBraced = try $ do
1502 guard =<< notAfterString
1610 guard =<< notAfterString
1503 suppress_author <- option False (True <$ char '-')
1611 suppress_author <- option False (True <$ char '-')
1504 char '@'
1612 char '@'
1613+ key <- simpleCiteIdentifier
1614+ <|> if allowBraced
1615+ then charsInBalanced '{' '}' (satisfy (not . isSpace))
1616+ else mzero
1617+ return (suppress_author, key)
1618+
1619+simpleCiteIdentifier :: (Stream s m Char, UpdateSourcePos s Char)
1620+ => ParserT s st m Text
1621+simpleCiteIdentifier = do
1505 firstChar <- alphaNum <|> char '_' <|> char '*' -- @* for wildcard in nocite
1622 firstChar <- alphaNum <|> char '_' <|> char '*' -- @* for wildcard in nocite
1506 let regchar = satisfy (\c -> isAlphaNum c || c == '_')
1623 let regchar = satisfy (\c -> isAlphaNum c || c == '_')
1507 let internal p = try $ p <* lookAhead regchar
1624 let internal p = try $ p <* lookAhead regchar
1508 rest <- many $ regchar <|> internal (oneOf ":.#$%&-+?<>~/") <|>
1625 rest <- many $ regchar <|> internal (oneOf ":.#$%&-+?<>~/") <|>
1509 try (oneOf ":/" <* lookAhead (char '/'))
1626 try (oneOf ":/" <* lookAhead (char '/'))
1510− let key = firstChar:rest
1511− return (suppress_author, T.pack key)
1627+ return $ T.pack $ firstChar:rest
1512−
1513
1628
1514token :: (Stream s m t)
1629token :: (Stream s m t)
1515 => (t -> Text)
1630 => (t -> Text)
@@ -1529,12 +1644,15 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs')
1529 cls' = maybe cls T.words $ lookup "class" kvs
1644 cls' = maybe cls T.words $ lookup "class" kvs
1530 kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs
1645 kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs
1531
1646
1532−insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st)
1647+insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1533− => ParserT a st m (mf Blocks)
1648+ => ParserT a st m b -- ^ parser to apply
1534− -> (Text -> a)
1649+ -> (Text -> a) -- ^ convert Text to stream type
1535− -> [FilePath] -> FilePath
1650+ -> [FilePath] -- ^ search path (directories)
1651+ -> FilePath -- ^ path of file to include
1652+ -> Maybe Int -- ^ start line (negative counts from end)
1653+ -> Maybe Int -- ^ end line (negative counts from end)
1536− -> ParserT a st m (mf Blocks)
1654+ -> ParserT a st m b
1537−insertIncludedFile' blocks totoks dirs f = do
1655+insertIncludedFile parser toStream dirs f mbstartline mbendline = do
1538 oldPos <- getPosition
1656 oldPos <- getPosition
1539 oldInput <- getInput
1657 oldInput <- getInput
1540 containers <- getIncludeFiles <$> getState
1658 containers <- getIncludeFiles <$> getState
@@ -1543,32 +1661,32 @@ insertIncludedFile' blocks totoks dirs f = do
1543 updateState $ addIncludeFile $ T.pack f
1661 updateState $ addIncludeFile $ T.pack f
1544 mbcontents <- readFileFromDirs dirs f
1662 mbcontents <- readFileFromDirs dirs f
1545 contents <- case mbcontents of
1663 contents <- case mbcontents of
1546− Just s -> return s
1664+ Just s -> return $ exciseLines mbstartline mbendline s
1547 Nothing -> do
1665 Nothing -> do
1548 report $ CouldNotLoadIncludeFile (T.pack f) oldPos
1666 report $ CouldNotLoadIncludeFile (T.pack f) oldPos
1549 return ""
1667 return ""
1668+ setInput $ toStream contents
1550− setPosition $ newPos f 1 1
1669+ setPosition $ newPos f (fromMaybe 1 mbstartline) 1
1551− setInput $ totoks contents
1670+ result <- parser
1552− bs <- blocks
1553 setInput oldInput
1671 setInput oldInput
1554 setPosition oldPos
1672 setPosition oldPos
1555 updateState dropLatestIncludeFile
1673 updateState dropLatestIncludeFile
1556− return bs
1674+ return result
1675+
1676+exciseLines :: Maybe Int -> Maybe Int -> Text -> Text
1677+exciseLines Nothing Nothing t = t
1678+exciseLines mbstartline mbendline t =
1679+ T.unlines $ take (endline' - (startline' - 1))
1680+ $ drop (startline' - 1) contentLines
1681+ where
1682+ contentLines = T.lines t
1683+ numLines = length contentLines
1684+ startline' = case mbstartline of
1685+ Nothing -> 1
1686+ Just x | x >= 0 -> x
1687+ | otherwise -> numLines + x -- negative from end
1688+ endline' = case mbendline of
1689+ Nothing -> numLines
1690+ Just x | x >= 0 -> x
1691+ | otherwise -> numLines + x -- negative from end
1557
1692
1558−-- | Parse content of include file as blocks. Circular includes result in an
1559−-- @PandocParseError@.
1560−insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1561− => ParserT [a] st m Blocks
1562− -> (Text -> [a])
1563− -> [FilePath] -> FilePath
1564− -> ParserT [a] st m Blocks
1565−insertIncludedFile blocks totoks dirs f =
1566− runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f
1567−
1568−-- | Parse content of include file as future blocks. Circular includes result in
1569−-- an @PandocParseError@.
1570−insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st)
1571− => ParserT Text st m (Future st Blocks)
1572− -> [FilePath] -> FilePath
1573− -> ParserT Text st m (Future st Blocks)
1574−insertIncludedFileF p = insertIncludedFile' p id
src/Text/Pandoc/Readers.hs+55−48
@@ -1,4 +1,5 @@
1{-# LANGUAGE MonoLocalBinds #-}
1{-# LANGUAGE MonoLocalBinds #-}
2+{-# LANGUAGE RankNTypes #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
4{- |
5{- |
@@ -65,12 +66,14 @@ import Data.Aeson
65import qualified Data.ByteString.Lazy as BL
66import qualified Data.ByteString.Lazy as BL
66import Data.Text (Text)
67import Data.Text (Text)
67import qualified Data.Text as T
68import qualified Data.Text as T
69+import Text.Pandoc.Shared (tshow)
68import Text.Pandoc.Class
70import Text.Pandoc.Class
69import Text.Pandoc.Definition
71import Text.Pandoc.Definition
70import Text.Pandoc.Error
72import Text.Pandoc.Error
71import Text.Pandoc.Extensions
73import Text.Pandoc.Extensions
72import Text.Pandoc.Options
74import Text.Pandoc.Options
73import Text.Pandoc.Readers.CommonMark
75import Text.Pandoc.Readers.CommonMark
76+import Text.Pandoc.Readers.Markdown
74import Text.Pandoc.Readers.Creole
77import Text.Pandoc.Readers.Creole
75import Text.Pandoc.Readers.DocBook
78import Text.Pandoc.Readers.DocBook
76import Text.Pandoc.Readers.Docx
79import Text.Pandoc.Readers.Docx
@@ -83,7 +86,6 @@ import Text.Pandoc.Readers.HTML (readHtml)
83import Text.Pandoc.Readers.JATS (readJATS)
86import Text.Pandoc.Readers.JATS (readJATS)
84import Text.Pandoc.Readers.Jira (readJira)
87import Text.Pandoc.Readers.Jira (readJira)
85import Text.Pandoc.Readers.LaTeX
88import Text.Pandoc.Readers.LaTeX
86−import Text.Pandoc.Readers.Markdown
87import Text.Pandoc.Readers.MediaWiki
89import Text.Pandoc.Readers.MediaWiki
88import Text.Pandoc.Readers.Muse
90import Text.Pandoc.Readers.Muse
89import Text.Pandoc.Readers.Native
91import Text.Pandoc.Readers.Native
@@ -101,59 +103,60 @@ import Text.Pandoc.Readers.CSV
101import Text.Pandoc.Readers.CslJson
103import Text.Pandoc.Readers.CslJson
102import Text.Pandoc.Readers.BibTeX
104import Text.Pandoc.Readers.BibTeX
103import qualified Text.Pandoc.UTF8 as UTF8
105import qualified Text.Pandoc.UTF8 as UTF8
104−import Text.Parsec.Error
106+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
105
107
108+data Reader m = TextReader (forall a . ToSources a =>
106−data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc)
109+ ReaderOptions -> a -> m Pandoc)
107 | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc)
110 | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc)
108
111
109-- | Association list of formats and readers.
112-- | Association list of formats and readers.
110readers :: PandocMonad m => [(Text, Reader m)]
113readers :: PandocMonad m => [(Text, Reader m)]
111−readers = [ ("native" , TextReader readNative)
114+readers = [("native" , TextReader readNative)
112− ,("json" , TextReader readJSON)
115+ ,("json" , TextReader readJSON)
113− ,("markdown" , TextReader readMarkdown)
116+ ,("markdown" , TextReader readMarkdown)
114− ,("markdown_strict" , TextReader readMarkdown)
117+ ,("markdown_strict" , TextReader readMarkdown)
115− ,("markdown_phpextra" , TextReader readMarkdown)
118+ ,("markdown_phpextra" , TextReader readMarkdown)
116− ,("markdown_github" , TextReader readMarkdown)
119+ ,("markdown_github" , TextReader readMarkdown)
117− ,("markdown_mmd", TextReader readMarkdown)
120+ ,("markdown_mmd", TextReader readMarkdown)
118− ,("commonmark" , TextReader readCommonMark)
121+ ,("commonmark" , TextReader readCommonMark)
119− ,("commonmark_x" , TextReader readCommonMark)
122+ ,("commonmark_x" , TextReader readCommonMark)
120− ,("creole" , TextReader readCreole)
123+ ,("creole" , TextReader readCreole)
121− ,("dokuwiki" , TextReader readDokuWiki)
124+ ,("dokuwiki" , TextReader readDokuWiki)
122− ,("gfm" , TextReader readCommonMark)
125+ ,("gfm" , TextReader readCommonMark)
123− ,("rst" , TextReader readRST)
126+ ,("rst" , TextReader readRST)
124− ,("mediawiki" , TextReader readMediaWiki)
127+ ,("mediawiki" , TextReader readMediaWiki)
125− ,("vimwiki" , TextReader readVimwiki)
128+ ,("vimwiki" , TextReader readVimwiki)
126− ,("docbook" , TextReader readDocBook)
129+ ,("docbook" , TextReader readDocBook)
127− ,("opml" , TextReader readOPML)
130+ ,("opml" , TextReader readOPML)
128− ,("org" , TextReader readOrg)
131+ ,("org" , TextReader readOrg)
129− ,("textile" , TextReader readTextile) -- TODO : textile+lhs
132+ ,("textile" , TextReader readTextile) -- TODO : textile+lhs
130− ,("html" , TextReader readHtml)
133+ ,("html" , TextReader readHtml)
131− ,("jats" , TextReader readJATS)
134+ ,("jats" , TextReader readJATS)
132− ,("jira" , TextReader readJira)
135+ ,("jira" , TextReader readJira)
133− ,("latex" , TextReader readLaTeX)
136+ ,("latex" , TextReader readLaTeX)
134− ,("haddock" , TextReader readHaddock)
137+ ,("haddock" , TextReader readHaddock)
135− ,("twiki" , TextReader readTWiki)
138+ ,("twiki" , TextReader readTWiki)
136− ,("tikiwiki" , TextReader readTikiWiki)
139+ ,("tikiwiki" , TextReader readTikiWiki)
137− ,("docx" , ByteStringReader readDocx)
140+ ,("docx" , ByteStringReader readDocx)
138− ,("odt" , ByteStringReader readOdt)
141+ ,("odt" , ByteStringReader readOdt)
139− ,("t2t" , TextReader readTxt2Tags)
142+ ,("t2t" , TextReader readTxt2Tags)
140− ,("epub" , ByteStringReader readEPUB)
143+ ,("epub" , ByteStringReader readEPUB)
141− ,("muse" , TextReader readMuse)
144+ ,("muse" , TextReader readMuse)
142− ,("man" , TextReader readMan)
145+ ,("man" , TextReader readMan)
143− ,("fb2" , TextReader readFB2)
146+ ,("fb2" , TextReader readFB2)
144− ,("ipynb" , TextReader readIpynb)
147+ ,("ipynb" , TextReader readIpynb)
145− ,("csv" , TextReader readCSV)
148+ ,("csv" , TextReader readCSV)
146− ,("csljson" , TextReader readCslJson)
149+ ,("csljson" , TextReader readCslJson)
147− ,("bibtex" , TextReader readBibTeX)
150+ ,("bibtex" , TextReader readBibTeX)
148− ,("biblatex" , TextReader readBibLaTeX)
151+ ,("biblatex" , TextReader readBibLaTeX)
149 ]
152 ]
150
153
151-- | Retrieve reader, extensions based on formatSpec (format+extensions).
154-- | Retrieve reader, extensions based on formatSpec (format+extensions).
152getReader :: PandocMonad m => Text -> m (Reader m, Extensions)
155getReader :: PandocMonad m => Text -> m (Reader m, Extensions)
153getReader s =
156getReader s =
154 case parseFormatSpec s of
157 case parseFormatSpec s of
155− Left e -> throwError $ PandocAppError
158+ Left e -> throwError $ PandocAppError $
156− $ T.intercalate "\n" [T.pack m | Message m <- errorMessages e]
159+ "Error parsing reader format " <> tshow s <> ": " <> tshow e
157 Right (readerName, extsToEnable, extsToDisable) ->
160 Right (readerName, extsToEnable, extsToDisable) ->
158 case lookup readerName readers of
161 case lookup readerName readers of
159 Nothing -> throwError $ PandocUnknownReaderError
162 Nothing -> throwError $ PandocUnknownReaderError
@@ -173,9 +176,13 @@ getReader s =
173 return (r, exts)
176 return (r, exts)
174
177
175-- | Read pandoc document from JSON format.
178-- | Read pandoc document from JSON format.
176−readJSON :: PandocMonad m
179+readJSON :: (PandocMonad m, ToSources a)
177− => ReaderOptions -> Text -> m Pandoc
180+ => ReaderOptions
181+ -> a
182+ -> m Pandoc
178−readJSON _ t =
183+readJSON _ s =
179− case eitherDecode' . BL.fromStrict . UTF8.fromText $ t of
184+ case eitherDecode' . BL.fromStrict . UTF8.fromText
185+ . sourcesToText . toSources $ s of
180 Right doc -> return doc
186 Right doc -> return doc
181− Left e -> throwError $ PandocParseError ("JSON parse error: " <> T.pack e)
187+ Left e -> throwError $ PandocParseError ("JSON parse error: "
188+ <> T.pack e)
src/Text/Pandoc/Readers/BibTeX.hs+14−8
@@ -23,41 +23,47 @@ where
23import Text.Pandoc.Options
23import Text.Pandoc.Options
24import Text.Pandoc.Definition
24import Text.Pandoc.Definition
25import Text.Pandoc.Builder (setMeta, cite, str)
25import Text.Pandoc.Builder (setMeta, cite, str)
26−import Data.Text (Text)
27import Citeproc (Lang(..), parseLang)
26import Citeproc (Lang(..), parseLang)
28import Citeproc.Locale (getLocale)
27import Citeproc.Locale (getLocale)
29import Text.Pandoc.Error (PandocError(..))
28import Text.Pandoc.Error (PandocError(..))
30import Text.Pandoc.Class (PandocMonad, lookupEnv)
29import Text.Pandoc.Class (PandocMonad, lookupEnv)
31import Text.Pandoc.Citeproc.BibTeX as BibTeX
30import Text.Pandoc.Citeproc.BibTeX as BibTeX
32import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue)
31import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue)
32+import Text.Pandoc.Sources (ToSources(..))
33import Control.Monad.Except (throwError)
33import Control.Monad.Except (throwError)
34
34
35-- | Read BibTeX from an input string and return a Pandoc document.
35-- | Read BibTeX from an input string and return a Pandoc document.
36-- The document will have only metadata, with an empty body.
36-- The document will have only metadata, with an empty body.
37-- The metadata will contain a `references` field with the
37-- The metadata will contain a `references` field with the
38-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
38-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
39+readBibTeX :: (PandocMonad m, ToSources a)
39−readBibTeX :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
40+ => ReaderOptions -> a -> m Pandoc
40readBibTeX = readBibTeX' BibTeX.Bibtex
41readBibTeX = readBibTeX' BibTeX.Bibtex
41
42
42-- | Read BibLaTeX from an input string and return a Pandoc document.
43-- | Read BibLaTeX from an input string and return a Pandoc document.
43-- The document will have only metadata, with an empty body.
44-- The document will have only metadata, with an empty body.
44-- The metadata will contain a `references` field with the
45-- The metadata will contain a `references` field with the
45-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
46-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
46−readBibLaTeX :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
47+readBibLaTeX :: (PandocMonad m, ToSources a)
48+ => ReaderOptions -> a -> m Pandoc
47readBibLaTeX = readBibTeX' BibTeX.Biblatex
49readBibLaTeX = readBibTeX' BibTeX.Biblatex
48
50
51+readBibTeX' :: (PandocMonad m, ToSources a)
49−readBibTeX' :: PandocMonad m => Variant -> ReaderOptions -> Text -> m Pandoc
52+ => Variant -> ReaderOptions -> a -> m Pandoc
50readBibTeX' variant _opts t = do
53readBibTeX' variant _opts t = do
51− lang <- maybe (Lang "en" (Just "US")) parseLang
54+ mblangEnv <- lookupEnv "LANG"
52− <$> lookupEnv "LANG"
55+ let defaultLang = Lang "en" Nothing (Just "US") [] [] []
56+ let lang = case mblangEnv of
57+ Nothing -> defaultLang
58+ Just l -> either (const defaultLang) id $ parseLang l
53 locale <- case getLocale lang of
59 locale <- case getLocale lang of
54 Left e ->
60 Left e ->
55− case getLocale (Lang "en" (Just "US")) of
61+ case getLocale (Lang "en" Nothing (Just "US") [] [] []) of
56 Right l -> return l
62 Right l -> return l
57 Left _ -> throwError $ PandocCiteprocError e
63 Left _ -> throwError $ PandocCiteprocError e
58 Right l -> return l
64 Right l -> return l
59 case BibTeX.readBibtexString variant locale (const True) t of
65 case BibTeX.readBibtexString variant locale (const True) t of
60− Left e -> throwError $ PandocParsecError t e
66+ Left e -> throwError $ PandocParsecError (toSources t) e
61 Right refs -> return $ setMeta "references"
67 Right refs -> return $ setMeta "references"
62 (map referenceToMetaValue refs)
68 (map referenceToMetaValue refs)
63 . setMeta "nocite"
69 . setMeta "nocite"
src/Text/Pandoc/Readers/CSV.hs+7−7
@@ -13,23 +13,23 @@
13Conversion from CSV to a 'Pandoc' table.
13Conversion from CSV to a 'Pandoc' table.
14-}
14-}
15module Text.Pandoc.Readers.CSV ( readCSV ) where
15module Text.Pandoc.Readers.CSV ( readCSV ) where
16−import Data.Text (Text)
17import qualified Data.Text as T
16import qualified Data.Text as T
18import Text.Pandoc.CSV (parseCSV, defaultCSVOptions)
17import Text.Pandoc.CSV (parseCSV, defaultCSVOptions)
19import Text.Pandoc.Definition
18import Text.Pandoc.Definition
20import qualified Text.Pandoc.Builder as B
19import qualified Text.Pandoc.Builder as B
21import Text.Pandoc.Class (PandocMonad)
20import Text.Pandoc.Class (PandocMonad)
22−import Text.Pandoc.Shared (crFilter)
23import Text.Pandoc.Error
21import Text.Pandoc.Error
22+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
24import Text.Pandoc.Options (ReaderOptions)
23import Text.Pandoc.Options (ReaderOptions)
25import Control.Monad.Except (throwError)
24import Control.Monad.Except (throwError)
26
25
27−readCSV :: PandocMonad m
26+readCSV :: (PandocMonad m, ToSources a)
28 => ReaderOptions -- ^ Reader options
27 => ReaderOptions -- ^ Reader options
29− -> Text -- ^ Text to parse (assuming @'\n'@ line endings)
28+ -> a
30 -> m Pandoc
29 -> m Pandoc
31−readCSV _opts s =
30+readCSV _opts s = do
31+ let txt = sourcesToText $ toSources s
32− case parseCSV defaultCSVOptions (crFilter s) of
32+ case parseCSV defaultCSVOptions txt of
33 Right (r:rs) -> return $ B.doc $ B.table capt
33 Right (r:rs) -> return $ B.doc $ B.table capt
34 (zip aligns widths)
34 (zip aligns widths)
35 (TableHead nullAttr hdrs)
35 (TableHead nullAttr hdrs)
@@ -45,4 +45,4 @@ readCSV _opts s =
45 aligns = replicate numcols AlignDefault
45 aligns = replicate numcols AlignDefault
46 widths = replicate numcols ColWidthDefault
46 widths = replicate numcols ColWidthDefault
47 Right [] -> return $ B.doc mempty
47 Right [] -> return $ B.doc mempty
48− Left e -> throwError $ PandocParsecError s e
48+ Left e -> throwError $ PandocParsecError (toSources [("",txt)]) e
src/Text/Pandoc/Readers/CommonMark.hs+46−4
@@ -26,19 +26,59 @@ import Text.Pandoc.Definition
26import Text.Pandoc.Builder as B
26import Text.Pandoc.Builder as B
27import Text.Pandoc.Options
27import Text.Pandoc.Options
28import Text.Pandoc.Error
28import Text.Pandoc.Error
29+import Text.Pandoc.Readers.Metadata (yamlMetaBlock)
29import Control.Monad.Except
30import Control.Monad.Except
30import Data.Functor.Identity (runIdentity)
31import Data.Functor.Identity (runIdentity)
31import Data.Typeable
32import Data.Typeable
33+import Text.Pandoc.Parsing (runParserT, getInput,
34+ runF, defaultParserState, option, many1, anyChar,
35+ Sources(..), ToSources(..), ParserT, Future,
36+ sourceName)
37+import qualified Data.Text as T
32
38
33-- | Parse a CommonMark formatted string into a 'Pandoc' structure.
39-- | Parse a CommonMark formatted string into a 'Pandoc' structure.
34−readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
40+readCommonMark :: (PandocMonad m, ToSources a)
41+ => ReaderOptions -> a -> m Pandoc
35readCommonMark opts s
42readCommonMark opts s
43+ | isEnabled Ext_yaml_metadata_block opts = do
44+ let sources = toSources s
45+ let toks = concatMap sourceToToks (unSources sources)
46+ res <- runParserT (do meta <- yamlMetaBlock (metaValueParser opts)
47+ rest <- getInput
48+ return (meta, rest))
49+ defaultParserState "YAML metadata" (toSources s)
50+ case res of
51+ Left _ -> readCommonMarkBody opts sources toks
52+ Right (meta, rest) -> do
53+ -- strip off metadata section and parse body
54+ let body = concatMap sourceToToks (unSources rest)
55+ Pandoc _ bs <- readCommonMarkBody opts sources body
56+ return $ Pandoc (runF meta defaultParserState) bs
57+ | otherwise = do
58+ let sources = toSources s
59+ let toks = concatMap sourceToToks (unSources sources)
60+ readCommonMarkBody opts sources toks
61+
62+sourceToToks :: (SourcePos, Text) -> [Tok]
63+sourceToToks (pos, s) = tokenize (sourceName pos) s
64+
65+metaValueParser :: Monad m
66+ => ReaderOptions -> ParserT Sources st m (Future st MetaValue)
67+metaValueParser opts = do
68+ inp <- option "" $ T.pack <$> many1 anyChar
69+ let toks = concatMap sourceToToks (unSources (toSources inp))
70+ case runIdentity (parseCommonmarkWith (specFor opts) toks) of
71+ Left _ -> mzero
72+ Right (Cm bls :: Cm () Blocks) -> return $ return $ B.toMetaValue bls
73+
74+readCommonMarkBody :: PandocMonad m => ReaderOptions -> Sources -> [Tok] -> m Pandoc
75+readCommonMarkBody opts s toks
36 | isEnabled Ext_sourcepos opts =
76 | isEnabled Ext_sourcepos opts =
37− case runIdentity (commonmarkWith (specFor opts) "" s) of
77+ case runIdentity (parseCommonmarkWith (specFor opts) toks) of
38 Left err -> throwError $ PandocParsecError s err
78 Left err -> throwError $ PandocParsecError s err
39 Right (Cm bls :: Cm SourceRange Blocks) -> return $ B.doc bls
79 Right (Cm bls :: Cm SourceRange Blocks) -> return $ B.doc bls
40 | otherwise =
80 | otherwise =
41− case runIdentity (commonmarkWith (specFor opts) "" s) of
81+ case runIdentity (parseCommonmarkWith (specFor opts) toks) of
42 Left err -> throwError $ PandocParsecError s err
82 Left err -> throwError $ PandocParsecError s err
43 Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls
83 Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls
44
84
@@ -72,5 +112,7 @@ specFor opts = foldr ($) defaultSyntaxSpec exts
72 | isEnabled Ext_implicit_header_references opts ] ++
112 | isEnabled Ext_implicit_header_references opts ] ++
73 [ (footnoteSpec <>) | isEnabled Ext_footnotes opts ] ++
113 [ (footnoteSpec <>) | isEnabled Ext_footnotes opts ] ++
74 [ (definitionListSpec <>) | isEnabled Ext_definition_lists opts ] ++
114 [ (definitionListSpec <>) | isEnabled Ext_definition_lists opts ] ++
75− [ (taskListSpec <>) | isEnabled Ext_task_lists opts ]
115+ [ (taskListSpec <>) | isEnabled Ext_task_lists opts ] ++
116+ [ (rebaseRelativePathsSpec <>)
117+ | isEnabled Ext_rebase_relative_paths opts ]
76
118
src/Text/Pandoc/Readers/Creole.hs+5−6
@@ -23,21 +23,20 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad (..))
23import Text.Pandoc.Definition
23import Text.Pandoc.Definition
24import Text.Pandoc.Options
24import Text.Pandoc.Options
25import Text.Pandoc.Parsing hiding (enclosed)
25import Text.Pandoc.Parsing hiding (enclosed)
26−import Text.Pandoc.Shared (crFilter)
27−
28
26
29-- | Read creole from an input string and return a Pandoc document.
27-- | Read creole from an input string and return a Pandoc document.
30−readCreole :: PandocMonad m
28+readCreole :: (PandocMonad m, ToSources a)
31 => ReaderOptions
29 => ReaderOptions
32− -> Text
30+ -> a
33 -> m Pandoc
31 -> m Pandoc
34readCreole opts s = do
32readCreole opts s = do
33+ let sources = ensureFinalNewlines 2 (toSources s)
35− res <- readWithM parseCreole def{ stateOptions = opts } $ crFilter s <> "\n\n"
34+ res <- readWithM parseCreole def{ stateOptions = opts } sources
36 case res of
35 case res of
37 Left e -> throwError e
36 Left e -> throwError e
38 Right d -> return d
37 Right d -> return d
39
38
40−type CRLParser = ParserT Text ParserState
39+type CRLParser = ParserT Sources ParserState
41
40
42--
41--
43-- Utility functions
42-- Utility functions
src/Text/Pandoc/Readers/CslJson.hs+5−4
@@ -24,21 +24,22 @@ import Text.Pandoc.Options
24import Text.Pandoc.Definition
24import Text.Pandoc.Definition
25import Text.Pandoc.Builder (setMeta, cite, str)
25import Text.Pandoc.Builder (setMeta, cite, str)
26import qualified Text.Pandoc.UTF8 as UTF8
26import qualified Text.Pandoc.UTF8 as UTF8
27−import Data.Text (Text)
28import qualified Data.Text as T
27import qualified Data.Text as T
29import Text.Pandoc.Error (PandocError(..))
28import Text.Pandoc.Error (PandocError(..))
30import Text.Pandoc.Class (PandocMonad)
29import Text.Pandoc.Class (PandocMonad)
31import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences)
30import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences)
32import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue)
31import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue)
33import Control.Monad.Except (throwError)
32import Control.Monad.Except (throwError)
33+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
34
34
35-- | Read CSL JSON from an input string and return a Pandoc document.
35-- | Read CSL JSON from an input string and return a Pandoc document.
36-- The document will have only metadata, with an empty body.
36-- The document will have only metadata, with an empty body.
37-- The metadata will contain a `references` field with the
37-- The metadata will contain a `references` field with the
38-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
38-- bibliography entries, and a `nocite` field with the wildcard `[@*]`.
39+readCslJson :: (PandocMonad m, ToSources a)
39−readCslJson :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
40+ => ReaderOptions -> a -> m Pandoc
40−readCslJson _opts t =
41+readCslJson _opts x =
41− case cslJsonToReferences (UTF8.fromText t) of
42+ case cslJsonToReferences (UTF8.fromText $ sourcesToText $ toSources x) of
42 Left e -> throwError $ PandocParseError $ T.pack e
43 Left e -> throwError $ PandocParseError $ T.pack e
43 Right refs -> return $ setMeta "references"
44 Right refs -> return $ setMeta "references"
44 (map referenceToMetaValue refs)
45 (map referenceToMetaValue refs)
src/Text/Pandoc/Readers/DocBook.hs+30−15
@@ -18,6 +18,7 @@ import Data.Either (rights)
18import Data.Foldable (asum)
18import Data.Foldable (asum)
19import Data.Generics
19import Data.Generics
20import Data.List (intersperse,elemIndex)
20import Data.List (intersperse,elemIndex)
21+import Data.List.NonEmpty (nonEmpty)
21import Data.Maybe (fromMaybe,mapMaybe)
22import Data.Maybe (fromMaybe,mapMaybe)
22import Data.Text (Text)
23import Data.Text (Text)
23import qualified Data.Text as T
24import qualified Data.Text as T
@@ -29,7 +30,8 @@ import Text.Pandoc.Builder
29import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
30import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
30import Text.Pandoc.Options
31import Text.Pandoc.Options
31import Text.Pandoc.Logging (LogMessage(..))
32import Text.Pandoc.Logging (LogMessage(..))
32−import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces)
33+import Text.Pandoc.Shared (safeRead, extractSpaces)
34+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
33import Text.TeXMath (readMathML, writeTeX)
35import Text.TeXMath (readMathML, writeTeX)
34import Text.Pandoc.XML.Light
36import Text.Pandoc.XML.Light
35
37
@@ -132,6 +134,7 @@ List of all DocBook tags, with [x] indicating implemented,
132[ ] corpcredit - A corporation or organization credited in a document
134[ ] corpcredit - A corporation or organization credited in a document
133[ ] corpname - The name of a corporation
135[ ] corpname - The name of a corporation
134[ ] country - The name of a country
136[ ] country - The name of a country
137+[x] danger - An admonition set off from the text indicating hazardous situation
135[ ] database - The name of a database, or part of a database
138[ ] database - The name of a database, or part of a database
136[x] date - The date of publication or revision of a document
139[x] date - The date of publication or revision of a document
137[ ] dedication - A wrapper for the dedication section of a book
140[ ] dedication - A wrapper for the dedication section of a book
@@ -538,11 +541,15 @@ instance Default DBState where
538 , dbContent = [] }
541 , dbContent = [] }
539
542
540
543
541−readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
544+readDocBook :: (PandocMonad m, ToSources a)
545+ => ReaderOptions
546+ -> a
547+ -> m Pandoc
542readDocBook _ inp = do
548readDocBook _ inp = do
549+ let sources = toSources inp
543 tree <- either (throwError . PandocXMLError "") return $
550 tree <- either (throwError . PandocXMLError "") return $
544 parseXMLContents
551 parseXMLContents
545− (TL.fromStrict . handleInstructions $ crFilter inp)
552+ (TL.fromStrict . handleInstructions . sourcesToText $ sources)
546 (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree
553 (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree
547 return $ Pandoc (dbMeta st') (toList . mconcat $ bs)
554 return $ Pandoc (dbMeta st') (toList . mconcat $ bs)
548
555
@@ -594,16 +601,24 @@ addMetadataFromElement e = do
594 Nothing -> return ()
601 Nothing -> return ()
595 Just z -> addMetaField "author" z
602 Just z -> addMetaField "author" z
596 addMetaField "subtitle" e
603 addMetaField "subtitle" e
597− addMetaField "author" e
604+ addAuthor e
598 addMetaField "date" e
605 addMetaField "date" e
599 addMetaField "release" e
606 addMetaField "release" e
600 addMetaField "releaseinfo" e
607 addMetaField "releaseinfo" e
601 return mempty
608 return mempty
609+ where
610+ addAuthor elt =
611+ case filterChildren (named "author") elt of
612+ [] -> return ()
613+ [z] -> fromAuthor z >>= addMeta "author"
614+ zs -> mapM fromAuthor zs >>= addMeta "author"
615+ fromAuthor elt =
616+ mconcat . intersperse space <$> mapM getInlines (elChildren elt)
602− where addMetaField fieldname elt =
617+ addMetaField fieldname elt =
603− case filterChildren (named fieldname) elt of
618+ case filterChildren (named fieldname) elt of
604− [] -> return ()
619+ [] -> return ()
605− [z] -> getInlines z >>= addMeta fieldname
620+ [z] -> getInlines z >>= addMeta fieldname
606− zs -> mapM getInlines zs >>= addMeta fieldname
621+ zs -> mapM getInlines zs >>= addMeta fieldname
607
622
608addMeta :: PandocMonad m => ToMetaValue a => Text -> a -> DB m ()
623addMeta :: PandocMonad m => ToMetaValue a => Text -> a -> DB m ()
609addMeta field val = modify (setMeta field val)
624addMeta field val = modify (setMeta field val)
@@ -704,7 +719,7 @@ blockTags =
704 ] ++ admonitionTags
719 ] ++ admonitionTags
705
720
706admonitionTags :: [Text]
721admonitionTags :: [Text]
707−admonitionTags = ["important","caution","note","tip","warning"]
722+admonitionTags = ["caution","danger","important","note","tip","warning"]
708
723
709-- Trim leading and trailing newline characters
724-- Trim leading and trailing newline characters
710trimNl :: Text -> Text
725trimNl :: Text -> Text
@@ -949,9 +964,8 @@ parseBlock (Elem e) =
949 (x >= '0' && x <= '9')
964 (x >= '0' && x <= '9')
950 || x == '.') w
965 || x == '.') w
951 if n > 0 then Just n else Nothing
966 if n > 0 then Just n else Nothing
952− let numrows = case bodyrows of
967+ let numrows = maybe 0 maximum $ nonEmpty
953− [] -> 0
954− xs -> maximum $ map length xs
968+ $ map length bodyrows
955 let aligns = case colspecs of
969 let aligns = case colspecs of
956 [] -> replicate numrows AlignDefault
970 [] -> replicate numrows AlignDefault
957 cs -> map toAlignment cs
971 cs -> map toAlignment cs
@@ -1075,7 +1089,7 @@ parseInline (Elem e) =
1075 return $ spanWith (attrValue "id" e, [], []) mempty
1089 return $ spanWith (attrValue "id" e, [], []) mempty
1076 "phrase" -> do
1090 "phrase" -> do
1077 let ident = attrValue "id" e
1091 let ident = attrValue "id" e
1078− let classes = T.words $ attrValue "class" e
1092+ let classes = T.words $ attrValue "role" e
1079 if ident /= "" || classes /= []
1093 if ident /= "" || classes /= []
1080 then innerInlines (spanWith (ident,classes,[]))
1094 then innerInlines (spanWith (ident,classes,[]))
1081 else innerInlines id
1095 else innerInlines id
@@ -1239,7 +1253,8 @@ equation e constructor =
1239 where
1253 where
1240 mathMLEquations :: [Text]
1254 mathMLEquations :: [Text]
1241 mathMLEquations = map writeTeX $ rights $ readMath
1255 mathMLEquations = map writeTeX $ rights $ readMath
1242− (\x -> qName (elName x) == "math" && qPrefix (elName x) == Just "mml")
1256+ (\x -> qName (elName x) == "math" &&
1257+ qURI (elName x) == Just "http://www.w3.org/1998/Math/MathML")
1243 (readMathML . showElement)
1258 (readMathML . showElement)
1244
1259
1245 latexEquations :: [Text]
1260 latexEquations :: [Text]
src/Text/Pandoc/Readers/Docx.hs+59−42
@@ -61,13 +61,14 @@ module Text.Pandoc.Readers.Docx
61import Codec.Archive.Zip
61import Codec.Archive.Zip
62import Control.Monad.Reader
62import Control.Monad.Reader
63import Control.Monad.State.Strict
63import Control.Monad.State.Strict
64+import Data.Bifunctor (bimap, first)
64import qualified Data.ByteString.Lazy as B
65import qualified Data.ByteString.Lazy as B
65import Data.Default (Default)
66import Data.Default (Default)
66−import Data.List (delete, intersect)
67+import Data.List (delete, intersect, foldl')
67import Data.Char (isSpace)
68import Data.Char (isSpace)
68import qualified Data.Map as M
69import qualified Data.Map as M
69import qualified Data.Text as T
70import qualified Data.Text as T
70−import Data.Maybe (isJust, fromMaybe)
71+import Data.Maybe (catMaybes, isJust, fromMaybe)
71import Data.Sequence (ViewL (..), viewl)
72import Data.Sequence (ViewL (..), viewl)
72import qualified Data.Sequence as Seq
73import qualified Data.Sequence as Seq
73import qualified Data.Set as Set
74import qualified Data.Set as Set
@@ -85,6 +86,7 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad)
85import qualified Text.Pandoc.Class.PandocMonad as P
86import qualified Text.Pandoc.Class.PandocMonad as P
86import Text.Pandoc.Error
87import Text.Pandoc.Error
87import Text.Pandoc.Logging
88import Text.Pandoc.Logging
89+import Data.List.NonEmpty (nonEmpty)
88
90
89readDocx :: PandocMonad m
91readDocx :: PandocMonad m
90 => ReaderOptions
92 => ReaderOptions
@@ -112,6 +114,7 @@ data DState = DState { docxAnchorMap :: M.Map T.Text T.Text
112 -- restarting
114 -- restarting
113 , docxListState :: M.Map (T.Text, T.Text) Integer
115 , docxListState :: M.Map (T.Text, T.Text) Integer
114 , docxPrevPara :: Inlines
116 , docxPrevPara :: Inlines
117+ , docxTableCaptions :: [Blocks]
115 }
118 }
116
119
117instance Default DState where
120instance Default DState where
@@ -122,6 +125,7 @@ instance Default DState where
122 , docxDropCap = mempty
125 , docxDropCap = mempty
123 , docxListState = M.empty
126 , docxListState = M.empty
124 , docxPrevPara = mempty
127 , docxPrevPara = mempty
128+ , docxTableCaptions = []
125 }
129 }
126
130
127data DEnv = DEnv { docxOptions :: ReaderOptions
131data DEnv = DEnv { docxOptions :: ReaderOptions
@@ -490,15 +494,32 @@ singleParaToPlain blks
490 singleton $ Plain ils
494 singleton $ Plain ils
491singleParaToPlain blks = blks
495singleParaToPlain blks = blks
492
496
493−cellToBlocks :: PandocMonad m => Docx.Cell -> DocxContext m Blocks
497+cellToCell :: PandocMonad m => RowSpan -> Docx.Cell -> DocxContext m Pandoc.Cell
494−cellToBlocks (Docx.Cell bps) = do
498+cellToCell rowSpan (Docx.Cell gridSpan _ bps) = do
495 blks <- smushBlocks <$> mapM bodyPartToBlocks bps
499 blks <- smushBlocks <$> mapM bodyPartToBlocks bps
496− return $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks
500+ let blks' = singleParaToPlain $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks
501+ return (cell AlignDefault rowSpan (ColSpan (fromIntegral gridSpan)) blks')
502+
503+rowsToRows :: PandocMonad m => [Docx.Row] -> DocxContext m [Pandoc.Row]
504+rowsToRows rows = do
505+ let rowspans = (fmap . fmap) (first RowSpan) (Docx.rowsToRowspans rows)
506+ cells <- traverse (traverse (uncurry cellToCell)) rowspans
507+ return (fmap (Pandoc.Row nullAttr) cells)
508+
509+splitHeaderRows :: Bool -> [Docx.Row] -> ([Docx.Row], [Docx.Row])
510+splitHeaderRows hasFirstRowFormatting rs = bimap reverse reverse $ fst
511+ $ if hasFirstRowFormatting
512+ then foldl' f ((take 1 rs, []), True) (drop 1 rs)
513+ else foldl' f (([], []), False) rs
514+ where
515+ f ((headerRows, bodyRows), previousRowWasHeader) r@(Docx.Row h cs)
516+ | h == HasTblHeader || (previousRowWasHeader && any isContinuationCell cs)
517+ = ((r : headerRows, bodyRows), True)
518+ | otherwise
519+ = ((headerRows, r : bodyRows), False)
520+
521+ isContinuationCell (Docx.Cell _ vm _) = vm == Docx.Continue
497
522
498−rowToBlocksList :: PandocMonad m => Docx.Row -> DocxContext m [Blocks]
499−rowToBlocksList (Docx.Row cells) = do
500− blksList <- mapM cellToBlocks cells
501− return $ map singleParaToPlain blksList
502
523
503-- like trimInlines, but also take out linebreaks
524-- like trimInlines, but also take out linebreaks
504trimSps :: Inlines -> Inlines
525trimSps :: Inlines -> Inlines
@@ -545,6 +566,11 @@ normalizeToClassName = T.map go . fromStyleName
545 where go c | isSpace c = '-'
566 where go c | isSpace c = '-'
546 | otherwise = c
567 | otherwise = c
547
568
569+bodyPartToTableCaption :: PandocMonad m => BodyPart -> DocxContext m (Maybe Blocks)
570+bodyPartToTableCaption (TblCaption pPr parparts) =
571+ Just <$> bodyPartToBlocks (Paragraph pPr parparts)
572+bodyPartToTableCaption _ = pure Nothing
573+
548bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks
574bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks
549bodyPartToBlocks (Paragraph pPr parparts)
575bodyPartToBlocks (Paragraph pPr parparts)
550 | Just True <- pBidi pPr = do
576 | Just True <- pBidi pPr = do
@@ -636,54 +662,43 @@ bodyPartToBlocks (ListItem pPr _ _ _ parparts) =
636 let pPr' = pPr {pStyle = constructBogusParStyleData "list-paragraph": pStyle pPr}
662 let pPr' = pPr {pStyle = constructBogusParStyleData "list-paragraph": pStyle pPr}
637 in
663 in
638 bodyPartToBlocks $ Paragraph pPr' parparts
664 bodyPartToBlocks $ Paragraph pPr' parparts
665+bodyPartToBlocks (TblCaption _ _) =
666+ return $ para mempty -- collected separately
639bodyPartToBlocks (Tbl _ _ _ []) =
667bodyPartToBlocks (Tbl _ _ _ []) =
640 return $ para mempty
668 return $ para mempty
641−bodyPartToBlocks (Tbl cap _ look parts@(r:rs)) = do
669+bodyPartToBlocks (Tbl cap grid look parts) = do
642− let cap' = simpleCaption $ plain $ text cap
670+ captions <- gets docxTableCaptions
643− (hdr, rows) = case firstRowFormatting look of
671+ fullCaption <- case captions of
644− True | null rs -> (Nothing, [r])
672+ c : cs -> do
645− | otherwise -> (Just r, rs)
673+ modify (\s -> s { docxTableCaptions = cs })
646− False -> (Nothing, r:rs)
674+ return c
647−
675+ [] -> return $ if T.null cap then mempty else plain (text cap)
648− cells <- mapM rowToBlocksList rows
676+ let shortCaption = if T.null cap then Nothing else Just (toList (text cap))
677+ cap' = caption shortCaption fullCaption
678+ (hdr, rows) = splitHeaderRows (firstRowFormatting look) parts
649
679
650 let width = maybe 0 maximum $ nonEmpty $ map rowLength parts
680 let width = maybe 0 maximum $ nonEmpty $ map rowLength parts
651− -- Data.List.NonEmpty is not available with ghc 7.10 so we roll out
652− -- our own, see
653− -- https://github.com/jgm/pandoc/pull/4361#issuecomment-365416155
654− nonEmpty [] = Nothing
655− nonEmpty l = Just l
656 rowLength :: Docx.Row -> Int
681 rowLength :: Docx.Row -> Int
657− rowLength (Docx.Row c) = length c
682+ rowLength (Docx.Row _ c) = sum (fmap (\(Docx.Cell gridSpan _ _) -> fromIntegral gridSpan) c)
658
683
659− let toRow = Pandoc.Row nullAttr . map simpleCell
684+ headerCells <- rowsToRows hdr
660− toHeaderRow l = [toRow l | not (null l)]
685+ bodyCells <- rowsToRows rows
661
686
662− -- pad cells. New Text.Pandoc.Builder will do that for us,
687+ -- Horizontal column alignment goes to the default at the moment. Getting
663− -- so this is for compatibility while we switch over.
664− let cells' = map (\row -> toRow $ take width (row ++ repeat mempty)) cells
665−
666− hdrCells <- case hdr of
667− Just r' -> toHeaderRow <$> rowToBlocksList r'
668− Nothing -> return []
669−
670− -- The two following variables (horizontal column alignment and
671− -- relative column widths) go to the default at the
672− -- moment. Width information is in the TblGrid field of the Tbl,
673− -- so should be possible. Alignment might be more difficult,
674− -- since there doesn't seem to be a column entity in docx.
688+ -- it might be difficult, since there doesn't seem to be a column entity
689+ -- in docx.
675 let alignments = replicate width AlignDefault
690 let alignments = replicate width AlignDefault
676− widths = replicate width ColWidthDefault
691+ totalWidth = sum grid
692+ widths = (\w -> ColWidth (fromInteger w / fromInteger totalWidth)) <$> grid
677
693
678 return $ table cap'
694 return $ table cap'
679 (zip alignments widths)
695 (zip alignments widths)
680− (TableHead nullAttr hdrCells)
696+ (TableHead nullAttr headerCells)
681− [TableBody nullAttr 0 [] cells']
697+ [TableBody nullAttr 0 [] bodyCells]
682 (TableFoot nullAttr [])
698 (TableFoot nullAttr [])
683bodyPartToBlocks (OMathPara e) =
699bodyPartToBlocks (OMathPara e) =
684 return $ para $ displayMath (writeTeX e)
700 return $ para $ displayMath (writeTeX e)
685
701
686−
687-- replace targets with generated anchors.
702-- replace targets with generated anchors.
688rewriteLink' :: PandocMonad m => Inline -> DocxContext m Inline
703rewriteLink' :: PandocMonad m => Inline -> DocxContext m Inline
689rewriteLink' l@(Link attr ils (T.uncons -> Just ('#',target), title)) = do
704rewriteLink' l@(Link attr ils (T.uncons -> Just ('#',target), title)) = do
@@ -719,6 +734,8 @@ bodyToOutput :: PandocMonad m => Body -> DocxContext m (Meta, [Block])
719bodyToOutput (Body bps) = do
734bodyToOutput (Body bps) = do
720 let (metabps, blkbps) = sepBodyParts bps
735 let (metabps, blkbps) = sepBodyParts bps
721 meta <- bodyPartsToMeta metabps
736 meta <- bodyPartsToMeta metabps
737+ captions <- catMaybes <$> mapM bodyPartToTableCaption blkbps
738+ modify (\s -> s { docxTableCaptions = captions })
722 blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps
739 blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps
723 blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks
740 blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks
724 blks'' <- removeOrphanAnchors blks'
741 blks'' <- removeOrphanAnchors blks'
src/Text/Pandoc/Readers/Docx/Combine.hs+5−5
@@ -61,7 +61,7 @@ import Data.List
61import Data.Bifunctor
61import Data.Bifunctor
62import Data.Sequence ( ViewL (..), ViewR (..), viewl, viewr, spanr, spanl
62import Data.Sequence ( ViewL (..), ViewR (..), viewl, viewr, spanr, spanl
63 , (><), (|>) )
63 , (><), (|>) )
64−import Text.Pandoc.Builder
64+import Text.Pandoc.Builder as B
65
65
66data Modifier a = Modifier (a -> a)
66data Modifier a = Modifier (a -> a)
67 | AttrModifier (Attr -> a -> a) Attr
67 | AttrModifier (Attr -> a -> a) Attr
@@ -116,12 +116,12 @@ ilModifierAndInnards ils = case viewl $ unMany ils of
116
116
117inlinesL :: Inlines -> (Inlines, Inlines)
117inlinesL :: Inlines -> (Inlines, Inlines)
118inlinesL ils = case viewl $ unMany ils of
118inlinesL ils = case viewl $ unMany ils of
119− (s :< sq) -> (singleton s, Many sq)
119+ (s :< sq) -> (B.singleton s, Many sq)
120 _ -> (mempty, ils)
120 _ -> (mempty, ils)
121
121
122inlinesR :: Inlines -> (Inlines, Inlines)
122inlinesR :: Inlines -> (Inlines, Inlines)
123inlinesR ils = case viewr $ unMany ils of
123inlinesR ils = case viewr $ unMany ils of
124− (sq :> s) -> (Many sq, singleton s)
124+ (sq :> s) -> (Many sq, B.singleton s)
125 _ -> (ils, mempty)
125 _ -> (ils, mempty)
126
126
127combineInlines :: Inlines -> Inlines -> Inlines
127combineInlines :: Inlines -> Inlines -> Inlines
@@ -182,7 +182,7 @@ isAttrModifier _ = False
182
182
183smushInlines :: [Inlines] -> Inlines
183smushInlines :: [Inlines] -> Inlines
184smushInlines xs = combineInlines xs' mempty
184smushInlines xs = combineInlines xs' mempty
185− where xs' = foldl combineInlines mempty xs
185+ where xs' = foldl' combineInlines mempty xs
186
186
187smushBlocks :: [Blocks] -> Blocks
187smushBlocks :: [Blocks] -> Blocks
188−smushBlocks xs = foldl combineBlocks mempty xs
188+smushBlocks xs = foldl' combineBlocks mempty xs
src/Text/Pandoc/Readers/Docx/Parse.hs+121−21
@@ -33,7 +33,9 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..)
33 , ParStyle
33 , ParStyle
34 , CharStyle(cStyleData)
34 , CharStyle(cStyleData)
35 , Row(..)
35 , Row(..)
36+ , TblHeader(..)
36 , Cell(..)
37 , Cell(..)
38+ , VMerge(..)
37 , TrackedChange(..)
39 , TrackedChange(..)
38 , ChangeType(..)
40 , ChangeType(..)
39 , ChangeInfo(..)
41 , ChangeInfo(..)
@@ -50,6 +52,7 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..)
50 , pHeading
52 , pHeading
51 , constructBogusParStyleData
53 , constructBogusParStyleData
52 , leftBiasedMergeRunStyle
54 , leftBiasedMergeRunStyle
55+ , rowsToRowspans
53 ) where
56 ) where
54import Text.Pandoc.Readers.Docx.Parse.Styles
57import Text.Pandoc.Readers.Docx.Parse.Styles
55import Codec.Archive.Zip
58import Codec.Archive.Zip
@@ -225,6 +228,7 @@ defaultParagraphStyle = ParagraphStyle { pStyle = []
225data BodyPart = Paragraph ParagraphStyle [ParPart]
228data BodyPart = Paragraph ParagraphStyle [ParPart]
226 | ListItem ParagraphStyle T.Text T.Text (Maybe Level) [ParPart]
229 | ListItem ParagraphStyle T.Text T.Text (Maybe Level) [ParPart]
227 | Tbl T.Text TblGrid TblLook [Row]
230 | Tbl T.Text TblGrid TblLook [Row]
231+ | TblCaption ParagraphStyle [ParPart]
228 | OMathPara [Exp]
232 | OMathPara [Exp]
229 deriving Show
233 deriving Show
230
234
@@ -236,12 +240,61 @@ newtype TblLook = TblLook {firstRowFormatting::Bool}
236defaultTblLook :: TblLook
240defaultTblLook :: TblLook
237defaultTblLook = TblLook{firstRowFormatting = False}
241defaultTblLook = TblLook{firstRowFormatting = False}
238
242
239−newtype Row = Row [Cell]
243+data Row = Row TblHeader [Cell] deriving Show
240− deriving Show
244+
245+data TblHeader = HasTblHeader | NoTblHeader deriving (Show, Eq)
241
246
242−newtype Cell = Cell [BodyPart]
247+data Cell = Cell GridSpan VMerge [BodyPart]
243 deriving Show
248 deriving Show
244
249
250+type GridSpan = Integer
251+
252+data VMerge = Continue
253+ -- ^ This cell should be merged with the one above it
254+ | Restart
255+ -- ^ This cell should not be merged with the one above it
256+ deriving (Show, Eq)
257+
258+rowsToRowspans :: [Row] -> [[(Int, Cell)]]
259+rowsToRowspans rows = let
260+ removeMergedCells = fmap (filter (\(_, Cell _ vmerge _) -> vmerge == Restart))
261+ in removeMergedCells (foldr f [] rows)
262+ where
263+ f :: Row -> [[(Int, Cell)]] -> [[(Int, Cell)]]
264+ f (Row _ cells) acc = let
265+ spans = g cells Nothing (listToMaybe acc)
266+ in spans : acc
267+
268+ g ::
269+ -- | The current row
270+ [Cell] ->
271+ -- | Number of columns left below
272+ Maybe Integer ->
273+ -- | (rowspan so far, cell) for the row below this one
274+ Maybe [(Int, Cell)] ->
275+ -- | (rowspan so far, cell) for this row
276+ [(Int, Cell)]
277+ g cells _ Nothing = zip (repeat 1) cells
278+ g cells columnsLeftBelow (Just rowBelow) =
279+ case cells of
280+ [] -> []
281+ thisCell@(Cell thisGridSpan _ _) : restOfRow -> case rowBelow of
282+ [] -> zip (repeat 1) cells
283+ (spanSoFarBelow, Cell gridSpanBelow vmerge _) : _ ->
284+ let spanSoFar = case vmerge of
285+ Restart -> 1
286+ Continue -> 1 + spanSoFarBelow
287+ columnsToDrop = thisGridSpan + (gridSpanBelow - fromMaybe gridSpanBelow columnsLeftBelow)
288+ (newColumnsLeftBelow, restOfRowBelow) = dropColumns columnsToDrop rowBelow
289+ in (spanSoFar, thisCell) : g restOfRow (Just newColumnsLeftBelow) (Just restOfRowBelow)
290+
291+ dropColumns :: Integer -> [(a, Cell)] -> (Integer, [(a, Cell)])
292+ dropColumns n [] = (n, [])
293+ dropColumns n cells@((_, Cell gridSpan _ _) : otherCells) =
294+ if n < gridSpan
295+ then (gridSpan - n, cells)
296+ else dropColumns (n - gridSpan) otherCells
297+
245leftBiasedMergeRunStyle :: RunStyle -> RunStyle -> RunStyle
298leftBiasedMergeRunStyle :: RunStyle -> RunStyle -> RunStyle
246leftBiasedMergeRunStyle a b = RunStyle
299leftBiasedMergeRunStyle a b = RunStyle
247 { isBold = isBold a <|> isBold b
300 { isBold = isBold a <|> isBold b
@@ -426,20 +479,26 @@ filePathToRelType path docXmlPath =
426 then Just InDocument
479 then Just InDocument
427 else Nothing
480 else Nothing
428
481
429−relElemToRelationship :: DocumentLocation -> Element -> Maybe Relationship
482+relElemToRelationship :: FilePath -> DocumentLocation -> Element
483+ -> Maybe Relationship
430−relElemToRelationship relType element | qName (elName element) == "Relationship" =
484+relElemToRelationship fp relType element | qName (elName element) == "Relationship" =
431 do
485 do
432 relId <- findAttr (QName "Id" Nothing Nothing) element
486 relId <- findAttr (QName "Id" Nothing Nothing) element
433 target <- findAttr (QName "Target" Nothing Nothing) element
487 target <- findAttr (QName "Target" Nothing Nothing) element
488+ -- target may be relative (media/image1.jpeg) or absolute
489+ -- (/word/media/image1.jpeg); we need to relativize it (see #7374)
490+ let frontOfFp = T.pack $ takeWhile (/= '_') fp
491+ let target' = fromMaybe target $
492+ T.stripPrefix frontOfFp $ T.dropWhile (== '/') target
434− return $ Relationship relType relId target
493+ return $ Relationship relType relId target'
435−relElemToRelationship _ _ = Nothing
494+relElemToRelationship _ _ _ = Nothing
436
495
437filePathToRelationships :: Archive -> FilePath -> FilePath -> [Relationship]
496filePathToRelationships :: Archive -> FilePath -> FilePath -> [Relationship]
438filePathToRelationships ar docXmlPath fp
497filePathToRelationships ar docXmlPath fp
439 | Just relType <- filePathToRelType fp docXmlPath
498 | Just relType <- filePathToRelType fp docXmlPath
440 , Just entry <- findEntryByPath fp ar
499 , Just entry <- findEntryByPath fp ar
441 , Just relElems <- parseXMLFromEntry entry =
500 , Just relElems <- parseXMLFromEntry entry =
442− mapMaybe (relElemToRelationship relType) $ elChildren relElems
501+ mapMaybe (relElemToRelationship fp relType) $ elChildren relElems
443filePathToRelationships _ _ _ = []
502filePathToRelationships _ _ _ = []
444
503
445archiveToRelationships :: Archive -> FilePath -> [Relationship]
504archiveToRelationships :: Archive -> FilePath -> [Relationship]
@@ -563,7 +622,7 @@ elemToTblGrid :: NameSpaces -> Element -> D TblGrid
563elemToTblGrid ns element | isElem ns "w" "tblGrid" element =
622elemToTblGrid ns element | isElem ns "w" "tblGrid" element =
564 let cols = findChildrenByName ns "w" "gridCol" element
623 let cols = findChildrenByName ns "w" "gridCol" element
565 in
624 in
566− mapD (\e -> maybeToD (findAttrByName ns "w" "val" e >>= stringToInteger))
625+ mapD (\e -> maybeToD (findAttrByName ns "w" "w" e >>= stringToInteger))
567 cols
626 cols
568elemToTblGrid _ _ = throwError WrongElem
627elemToTblGrid _ _ = throwError WrongElem
569
628
@@ -587,14 +646,31 @@ elemToRow ns element | isElem ns "w" "tr" element =
587 do
646 do
588 let cellElems = findChildrenByName ns "w" "tc" element
647 let cellElems = findChildrenByName ns "w" "tc" element
589 cells <- mapD (elemToCell ns) cellElems
648 cells <- mapD (elemToCell ns) cellElems
649+ let hasTblHeader = maybe NoTblHeader (const HasTblHeader)
650+ (findChildByName ns "w" "trPr" element
651+ >>= findChildByName ns "w" "tblHeader")
590− return $ Row cells
652+ return $ Row hasTblHeader cells
591elemToRow _ _ = throwError WrongElem
653elemToRow _ _ = throwError WrongElem
592
654
593elemToCell :: NameSpaces -> Element -> D Cell
655elemToCell :: NameSpaces -> Element -> D Cell
594elemToCell ns element | isElem ns "w" "tc" element =
656elemToCell ns element | isElem ns "w" "tc" element =
595 do
657 do
658+ let properties = findChildByName ns "w" "tcPr" element
659+ let gridSpan = properties
660+ >>= findChildByName ns "w" "gridSpan"
661+ >>= findAttrByName ns "w" "val"
662+ >>= stringToInteger
663+ let vMerge = case properties >>= findChildByName ns "w" "vMerge" of
664+ Nothing -> Restart
665+ Just e ->
666+ fromMaybe Continue $ do
667+ s <- findAttrByName ns "w" "val" e
668+ case s of
669+ "continue" -> Just Continue
670+ "restart" -> Just Restart
671+ _ -> Nothing
596 cellContents <- mapD (elemToBodyPart ns) (elChildren element)
672 cellContents <- mapD (elemToBodyPart ns) (elChildren element)
597− return $ Cell cellContents
673+ return $ Cell (fromMaybe 1 gridSpan) vMerge cellContents
598elemToCell _ _ = throwError WrongElem
674elemToCell _ _ = throwError WrongElem
599
675
600elemToParIndentation :: NameSpaces -> Element -> Maybe ParIndentation
676elemToParIndentation :: NameSpaces -> Element -> Maybe ParIndentation
@@ -626,10 +702,9 @@ pNumInfo = getParStyleField numInfo . pStyle
626elemToBodyPart :: NameSpaces -> Element -> D BodyPart
702elemToBodyPart :: NameSpaces -> Element -> D BodyPart
627elemToBodyPart ns element
703elemToBodyPart ns element
628 | isElem ns "w" "p" element
704 | isElem ns "w" "p" element
629− , (c:_) <- findChildrenByName ns "m" "oMathPara" element =
705+ , (c:_) <- findChildrenByName ns "m" "oMathPara" element = do
630− do
631− expsLst <- eitherToD $ readOMML $ showElement c
706+ expsLst <- eitherToD $ readOMML $ showElement c
632− return $ OMathPara expsLst
707+ return $ OMathPara expsLst
633elemToBodyPart ns element
708elemToBodyPart ns element
634 | isElem ns "w" "p" element
709 | isElem ns "w" "p" element
635 , Just (numId, lvl) <- getNumInfo ns element = do
710 , Just (numId, lvl) <- getNumInfo ns element = do
@@ -647,13 +722,31 @@ elemToBodyPart ns element
647 Nothing | Just (numId, lvl) <- pNumInfo parstyle -> do
722 Nothing | Just (numId, lvl) <- pNumInfo parstyle -> do
648 levelInfo <- lookupLevel numId lvl <$> asks envNumbering
723 levelInfo <- lookupLevel numId lvl <$> asks envNumbering
649 return $ ListItem parstyle numId lvl levelInfo parparts
724 return $ ListItem parstyle numId lvl levelInfo parparts
725+ _ -> let
726+ hasCaptionStyle = elem "Caption" (pStyleId <$> pStyle parstyle)
727+
728+ hasSimpleTableField = fromMaybe False $ do
729+ fldSimple <- findChildByName ns "w" "fldSimple" element
730+ instr <- findAttrByName ns "w" "instr" fldSimple
731+ pure ("Table" `elem` T.words instr)
732+
733+ hasComplexTableField = fromMaybe False $ do
734+ instrText <- findElementByName ns "w" "instrText" element
735+ pure ("Table" `elem` T.words (strContent instrText))
736+
737+ in if hasCaptionStyle && (hasSimpleTableField || hasComplexTableField)
738+ then return $ TblCaption parstyle parparts
650− _ -> return $ Paragraph parstyle parparts
739+ else return $ Paragraph parstyle parparts
740+
651elemToBodyPart ns element
741elemToBodyPart ns element
652 | isElem ns "w" "tbl" element = do
742 | isElem ns "w" "tbl" element = do
653− let caption' = findChildByName ns "w" "tblPr" element
743+ let tblProperties = findChildByName ns "w" "tblPr" element
744+ caption = fromMaybe "" $ tblProperties
654 >>= findChildByName ns "w" "tblCaption"
745 >>= findChildByName ns "w" "tblCaption"
655 >>= findAttrByName ns "w" "val"
746 >>= findAttrByName ns "w" "val"
656− caption = fromMaybe "" caption'
747+ description = fromMaybe "" $ tblProperties
748+ >>= findChildByName ns "w" "tblDescription"
749+ >>= findAttrByName ns "w" "val"
657 grid' = case findChildByName ns "w" "tblGrid" element of
750 grid' = case findChildByName ns "w" "tblGrid" element of
658 Just g -> elemToTblGrid ns g
751 Just g -> elemToTblGrid ns g
659 Nothing -> return []
752 Nothing -> return []
@@ -666,7 +759,7 @@ elemToBodyPart ns element
666 grid <- grid'
759 grid <- grid'
667 tblLook <- tblLook'
760 tblLook <- tblLook'
668 rows <- mapD (elemToRow ns) (elChildren element)
761 rows <- mapD (elemToRow ns) (elChildren element)
669− return $ Tbl caption grid tblLook rows
762+ return $ Tbl (caption <> description) grid tblLook rows
670elemToBodyPart _ _ = throwError WrongElem
763elemToBodyPart _ _ = throwError WrongElem
671
764
672lookupRelationship :: DocumentLocation -> RelId -> [Relationship] -> Maybe Target
765lookupRelationship :: DocumentLocation -> RelId -> [Relationship] -> Maybe Target
@@ -709,7 +802,8 @@ elemToParPart ns element
709 case drawing of
802 case drawing of
710 Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem)
803 Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem)
711 Nothing -> throwError WrongElem
804 Nothing -> throwError WrongElem
712−-- The below is an attempt to deal with images in deprecated vml format.
805+-- The two cases below are an attempt to deal with images in deprecated vml format.
806+-- Todo: check out title and attr for deprecated format.
713elemToParPart ns element
807elemToParPart ns element
714 | isElem ns "w" "r" element
808 | isElem ns "w" "r" element
715 , Just _ <- findChildByName ns "w" "pict" element =
809 , Just _ <- findChildByName ns "w" "pict" element =
@@ -717,9 +811,15 @@ elemToParPart ns element
717 >>= findAttrByName ns "r" "id"
811 >>= findAttrByName ns "r" "id"
718 in
812 in
719 case drawing of
813 case drawing of
720− -- Todo: check out title and attr for deprecated format.
721 Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp "" "" bs Nothing)
814 Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp "" "" bs Nothing)
722 Nothing -> throwError WrongElem
815 Nothing -> throwError WrongElem
816+elemToParPart ns element
817+ | isElem ns "w" "r" element
818+ , Just objectElem <- findChildByName ns "w" "object" element
819+ , Just shapeElem <- findChildByName ns "v" "shape" objectElem
820+ , Just imagedataElem <- findChildByName ns "v" "imagedata" shapeElem
821+ , Just drawingId <- findAttrByName ns "r" "id" imagedataElem
822+ = expandDrawingId drawingId >>= (\(fp, bs) -> return $ Drawing fp "" "" bs Nothing)
723-- Chart
823-- Chart
724elemToParPart ns element
824elemToParPart ns element
725 | isElem ns "w" "r" element
825 | isElem ns "w" "r" element
src/Text/Pandoc/Readers/Docx/Util.hs+7
@@ -19,6 +19,7 @@ module Text.Pandoc.Readers.Docx.Util (
19 , elemToNameSpaces
19 , elemToNameSpaces
20 , findChildByName
20 , findChildByName
21 , findChildrenByName
21 , findChildrenByName
22+ , findElementByName
22 , findAttrByName
23 , findAttrByName
23 ) where
24 ) where
24
25
@@ -56,6 +57,12 @@ findChildrenByName ns pref name el =
56 let ns' = ns <> elemToNameSpaces el
57 let ns' = ns <> elemToNameSpaces el
57 in findChildren (elemName ns' pref name) el
58 in findChildren (elemName ns' pref name) el
58
59
60+-- | Like 'findChildrenByName', but searches descendants.
61+findElementByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element
62+findElementByName ns pref name el =
63+ let ns' = ns <> elemToNameSpaces el
64+ in findElement (elemName ns' pref name) el
65+
59findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text
66findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text
60findAttrByName ns pref name el =
67findAttrByName ns pref name el =
61 let ns' = ns <> elemToNameSpaces el
68 let ns' = ns <> elemToNameSpaces el
src/Text/Pandoc/Readers/DokuWiki.hs+9−8
@@ -29,26 +29,27 @@ import Text.Pandoc.Definition
29import Text.Pandoc.Error (PandocError (PandocParsecError))
29import Text.Pandoc.Error (PandocError (PandocParsecError))
30import Text.Pandoc.Options
30import Text.Pandoc.Options
31import Text.Pandoc.Parsing hiding (enclosed, nested)
31import Text.Pandoc.Parsing hiding (enclosed, nested)
32−import Text.Pandoc.Shared (crFilter, trim, stringify, tshow)
32+import Text.Pandoc.Shared (trim, stringify, tshow)
33
33
34-- | Read DokuWiki from an input string and return a Pandoc document.
34-- | Read DokuWiki from an input string and return a Pandoc document.
35−readDokuWiki :: PandocMonad m
35+readDokuWiki :: (PandocMonad m, ToSources a)
36 => ReaderOptions
36 => ReaderOptions
37− -> Text
37+ -> a
38 -> m Pandoc
38 -> m Pandoc
39readDokuWiki opts s = do
39readDokuWiki opts s = do
40− let input = crFilter s
40+ let sources = toSources s
41− res <- runParserT parseDokuWiki def {stateOptions = opts } "source" input
41+ res <- runParserT parseDokuWiki def {stateOptions = opts }
42+ (initialSourceName sources) sources
42 case res of
43 case res of
43− Left e -> throwError $ PandocParsecError input e
44+ Left e -> throwError $ PandocParsecError sources e
44 Right d -> return d
45 Right d -> return d
45
46
46−type DWParser = ParserT Text ParserState
47+type DWParser = ParserT Sources ParserState
47
48
48-- * Utility functions
49-- * Utility functions
49
50
50-- | Parse end-of-line, which can be either a newline or end-of-file.
51-- | Parse end-of-line, which can be either a newline or end-of-file.
51−eol :: Stream s m Char => ParserT s st m ()
52+eol :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m ()
52eol = void newline <|> eof
53eol = void newline <|> eof
53
54
54nested :: PandocMonad m => DWParser m a -> DWParser m a
55nested :: PandocMonad m => DWParser m a -> DWParser m a
src/Text/Pandoc/Readers/FB2.hs+6−3
@@ -40,9 +40,9 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad, insertMedia, report)
40import Text.Pandoc.Error
40import Text.Pandoc.Error
41import Text.Pandoc.Logging
41import Text.Pandoc.Logging
42import Text.Pandoc.Options
42import Text.Pandoc.Options
43−import Text.Pandoc.Shared (crFilter)
44import Text.Pandoc.XML.Light
43import Text.Pandoc.XML.Light
45import qualified Text.Pandoc.UTF8 as UTF8
44import qualified Text.Pandoc.UTF8 as UTF8
45+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
46
46
47type FB2 m = StateT FB2State m
47type FB2 m = StateT FB2State m
48
48
@@ -63,9 +63,12 @@ instance HasMeta FB2State where
63 setMeta field v s = s {fb2Meta = setMeta field v (fb2Meta s)}
63 setMeta field v s = s {fb2Meta = setMeta field v (fb2Meta s)}
64 deleteMeta field s = s {fb2Meta = deleteMeta field (fb2Meta s)}
64 deleteMeta field s = s {fb2Meta = deleteMeta field (fb2Meta s)}
65
65
66−readFB2 :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
66+readFB2 :: (PandocMonad m, ToSources a)
67+ => ReaderOptions
68+ -> a
69+ -> m Pandoc
67readFB2 _ inp =
70readFB2 _ inp =
68− case parseXMLElement $ TL.fromStrict $ crFilter inp of
71+ case parseXMLElement $ TL.fromStrict $ sourcesToText $ toSources inp of
69 Left msg -> throwError $ PandocXMLError "" msg
72 Left msg -> throwError $ PandocXMLError "" msg
70 Right el -> do
73 Right el -> do
71 (bs, st) <- runStateT (parseRootElement el) def
74 (bs, st) <- runStateT (parseRootElement el) def
src/Text/Pandoc/Readers/HTML.hs+48−28
@@ -32,6 +32,7 @@ import Data.Char (isAlphaNum, isLetter)
32import Data.Default (Default (..), def)
32import Data.Default (Default (..), def)
33import Data.Foldable (for_)
33import Data.Foldable (for_)
34import Data.List.Split (splitWhen)
34import Data.List.Split (splitWhen)
35+import Data.List (foldl')
35import qualified Data.Map as M
36import qualified Data.Map as M
36import Data.Maybe (fromMaybe, isJust, isNothing)
37import Data.Maybe (fromMaybe, isJust, isNothing)
37import Data.Monoid (First (..))
38import Data.Monoid (First (..))
@@ -61,21 +62,21 @@ import Text.Pandoc.Options (
61 extensionEnabled)
62 extensionEnabled)
62import Text.Pandoc.Parsing hiding ((<|>))
63import Text.Pandoc.Parsing hiding ((<|>))
63import Text.Pandoc.Shared (
64import Text.Pandoc.Shared (
64− addMetaField, blocksToInlines', crFilter, escapeURI, extractSpaces,
65+ addMetaField, blocksToInlines', escapeURI, extractSpaces,
65 htmlSpanLikeElements, renderTags', safeRead, tshow)
66 htmlSpanLikeElements, renderTags', safeRead, tshow)
66import Text.Pandoc.Walk
67import Text.Pandoc.Walk
67import Text.Parsec.Error
68import Text.Parsec.Error
68import Text.TeXMath (readMathML, writeTeX)
69import Text.TeXMath (readMathML, writeTeX)
69
70
70-- | Convert HTML-formatted string to 'Pandoc' document.
71-- | Convert HTML-formatted string to 'Pandoc' document.
71−readHtml :: PandocMonad m
72+readHtml :: (PandocMonad m, ToSources a)
72 => ReaderOptions -- ^ Reader options
73 => ReaderOptions -- ^ Reader options
73− -> Text -- ^ String to parse (assumes @'\n'@ line endings)
74+ -> a -- ^ Input to parse
74 -> m Pandoc
75 -> m Pandoc
75readHtml opts inp = do
76readHtml opts inp = do
76 let tags = stripPrefixes $ canonicalizeTags $
77 let tags = stripPrefixes $ canonicalizeTags $
77 parseTagsOptions parseOptions{ optTagPosition = True }
78 parseTagsOptions parseOptions{ optTagPosition = True }
78− (crFilter inp)
79+ (sourcesToText $ toSources inp)
79 parseDoc = do
80 parseDoc = do
80 blocks <- fixPlains False . mconcat <$> manyTill block eof
81 blocks <- fixPlains False . mconcat <$> manyTill block eof
81 meta <- stateMeta . parserState <$> getState
82 meta <- stateMeta . parserState <$> getState
@@ -204,6 +205,7 @@ block = ((do
204 | otherwise
205 | otherwise
205 -> pDiv
206 -> pDiv
206 "section" -> pDiv
207 "section" -> pDiv
208+ "header" -> pDiv
207 "main" -> pDiv
209 "main" -> pDiv
208 "figure" -> pFigure
210 "figure" -> pFigure
209 "iframe" -> pIframe
211 "iframe" -> pIframe
@@ -360,7 +362,10 @@ pDefListItem = try $ do
360 terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline)
362 terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline)
361 defs <- many1 (try $ skipMany nonItem >> pInTags "dd" block)
363 defs <- many1 (try $ skipMany nonItem >> pInTags "dd" block)
362 skipMany nonItem
364 skipMany nonItem
363− let term = foldl1 (\x y -> x <> B.linebreak <> y) $ map trimInlines terms
365+ let term = foldl' (\x y -> if null x
366+ then trimInlines y
367+ else x <> B.linebreak <> trimInlines y)
368+ mempty terms
364 return (term, map (fixPlains True) defs)
369 return (term, map (fixPlains True) defs)
365
370
366fixPlains :: Bool -> Blocks -> Blocks
371fixPlains :: Bool -> Blocks -> Blocks
@@ -400,6 +405,7 @@ pLineBlock = try $ do
400isDivLike :: Text -> Bool
405isDivLike :: Text -> Bool
401isDivLike "div" = True
406isDivLike "div" = True
402isDivLike "section" = True
407isDivLike "section" = True
408+isDivLike "header" = True
403isDivLike "main" = True
409isDivLike "main" = True
404isDivLike _ = False
410isDivLike _ = False
405
411
@@ -495,17 +501,13 @@ pHeader = try $ do
495 tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"])
501 tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"])
496 (const True)
502 (const True)
497 let attr = toStringAttr attr'
503 let attr = toStringAttr attr'
498− let bodyTitle = TagOpen tagtype attr' ~== TagOpen ("h1" :: Text)
499− [("class","title")]
500 level <- headerLevel tagtype
504 level <- headerLevel tagtype
501 contents <- trimInlines . mconcat <$> manyTill inline (pCloses tagtype <|> eof)
505 contents <- trimInlines . mconcat <$> manyTill inline (pCloses tagtype <|> eof)
502 let ident = fromMaybe "" $ lookup "id" attr
506 let ident = fromMaybe "" $ lookup "id" attr
503 let classes = maybe [] T.words $ lookup "class" attr
507 let classes = maybe [] T.words $ lookup "class" attr
504 let keyvals = [(k,v) | (k,v) <- attr, k /= "class", k /= "id"]
508 let keyvals = [(k,v) | (k,v) <- attr, k /= "class", k /= "id"]
505 attr'' <- registerHeader (ident, classes, keyvals) contents
509 attr'' <- registerHeader (ident, classes, keyvals) contents
506− return $ if bodyTitle
507− then mempty -- skip a representation of the title in the body
508− else B.headerWith attr'' level contents
510+ return $ B.headerWith attr'' level contents
509
511
510pHrule :: PandocMonad m => TagParser m Blocks
512pHrule :: PandocMonad m => TagParser m Blocks
511pHrule = do
513pHrule = do
@@ -555,7 +557,18 @@ pFigure = try $ do
555pCodeBlock :: PandocMonad m => TagParser m Blocks
557pCodeBlock :: PandocMonad m => TagParser m Blocks
556pCodeBlock = try $ do
558pCodeBlock = try $ do
557 TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" [])
559 TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" [])
558− let attr = toAttr attr'
560+ -- if the `pre` has no attributes, try if it is followed by a `code`
561+ -- element and use those attributes if possible.
562+ attr <- case attr' of
563+ _:_ -> pure (toAttr attr')
564+ [] -> option nullAttr $ do
565+ TagOpen _ codeAttr <- pSatisfy (matchTagOpen "code" [])
566+ pure $ toAttr
567+ [ (k, v') | (k, v) <- codeAttr
568+ -- strip language from class
569+ , let v' = if k == "class"
570+ then fromMaybe v (T.stripPrefix "language-" v)
571+ else v ]
559 contents <- manyTill pAny (pCloses "pre" <|> eof)
572 contents <- manyTill pAny (pCloses "pre" <|> eof)
560 let rawText = T.concat $ map tagToText contents
573 let rawText = T.concat $ map tagToText contents
561 -- drop leading newline if any
574 -- drop leading newline if any
@@ -826,17 +839,19 @@ pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline
826
839
827pTagText :: PandocMonad m => TagParser m Inlines
840pTagText :: PandocMonad m => TagParser m Inlines
828pTagText = try $ do
841pTagText = try $ do
842+ pos <- getPosition
829 (TagText str) <- pSatisfy isTagText
843 (TagText str) <- pSatisfy isTagText
830 st <- getState
844 st <- getState
831 qu <- ask
845 qu <- ask
832 parsed <- lift $ lift $
846 parsed <- lift $ lift $
833− flip runReaderT qu $ runParserT (many pTagContents) st "text" str
847+ flip runReaderT qu $ runParserT (many pTagContents) st "text"
848+ (Sources [(pos, str)])
834 case parsed of
849 case parsed of
835 Left _ -> throwError $ PandocParseError $
850 Left _ -> throwError $ PandocParseError $
836 "Could not parse `" <> str <> "'"
851 "Could not parse `" <> str <> "'"
837 Right result -> return $ mconcat result
852 Right result -> return $ mconcat result
838
853
839−type InlinesParser m = HTMLParser m Text
854+type InlinesParser m = HTMLParser m Sources
840
855
841pTagContents :: PandocMonad m => InlinesParser m Inlines
856pTagContents :: PandocMonad m => InlinesParser m Inlines
842pTagContents =
857pTagContents =
@@ -936,13 +951,15 @@ getTagName (TagClose t) = Just t
936getTagName _ = Nothing
951getTagName _ = Nothing
937
952
938isInlineTag :: Tag Text -> Bool
953isInlineTag :: Tag Text -> Bool
939−isInlineTag t =
940− isCommentTag t ||
954+isInlineTag t = isCommentTag t || case t of
941− case getTagName t of
942− Nothing -> False
943− Just "script" -> "math/tex" `T.isPrefixOf` fromAttrib "type" t
955+ TagOpen "script" _ -> "math/tex" `T.isPrefixOf` fromAttrib "type" t
956+ TagClose "script" -> True
957+ TagOpen name _ -> isInlineTagName name
958+ TagClose name -> isInlineTagName name
959+ _ -> False
960+ where isInlineTagName x =
944− Just x -> x `Set.notMember` blockTags ||
961+ x `Set.notMember` blockTags ||
945− T.take 1 x == "?" -- processing instr.
962+ T.take 1 x == "?" -- processing instr.
946
963
947isBlockTag :: Tag Text -> Bool
964isBlockTag :: Tag Text -> Bool
948isBlockTag t = isBlockTagName || isTagComment t
965isBlockTag t = isBlockTagName || isTagComment t
@@ -966,13 +983,14 @@ isCommentTag = tagComment (const True)
966-- | Matches a stretch of HTML in balanced tags.
983-- | Matches a stretch of HTML in balanced tags.
967htmlInBalanced :: Monad m
984htmlInBalanced :: Monad m
968 => (Tag Text -> Bool)
985 => (Tag Text -> Bool)
969− -> ParserT Text st m Text
986+ -> ParserT Sources st m Text
970htmlInBalanced f = try $ do
987htmlInBalanced f = try $ do
971 lookAhead (char '<')
988 lookAhead (char '<')
972− inp <- getInput
989+ sources <- getInput
973− let ts = canonicalizeTags $
990+ let ts = canonicalizeTags
974− parseTagsOptions parseOptions{ optTagWarning = True,
991+ $ parseTagsOptions parseOptions{ optTagWarning = True,
975− optTagPosition = True } inp
992+ optTagPosition = True }
993+ $ sourcesToText sources
976 case ts of
994 case ts of
977 (TagPosition sr sc : t@(TagOpen tn _) : rest) -> do
995 (TagPosition sr sc : t@(TagOpen tn _) : rest) -> do
978 guard $ f t
996 guard $ f t
@@ -1014,15 +1032,17 @@ hasTagWarning _ = False
1014-- | Matches a tag meeting a certain condition.
1032-- | Matches a tag meeting a certain condition.
1015htmlTag :: (HasReaderOptions st, Monad m)
1033htmlTag :: (HasReaderOptions st, Monad m)
1016 => (Tag Text -> Bool)
1034 => (Tag Text -> Bool)
1017− -> ParserT Text st m (Tag Text, Text)
1035+ -> ParserT Sources st m (Tag Text, Text)
1018htmlTag f = try $ do
1036htmlTag f = try $ do
1019 lookAhead (char '<')
1037 lookAhead (char '<')
1020 startpos <- getPosition
1038 startpos <- getPosition
1021− inp <- getInput
1039+ sources <- getInput
1040+ let inp = sourcesToText sources
1022 let ts = canonicalizeTags $ parseTagsOptions
1041 let ts = canonicalizeTags $ parseTagsOptions
1023 parseOptions{ optTagWarning = False
1042 parseOptions{ optTagWarning = False
1024 , optTagPosition = True }
1043 , optTagPosition = True }
1044+ (inp <> " ")
1025− (inp <> " ") -- add space to ensure that
1045+ -- add space to ensure that
1026 -- we get a TagPosition after the tag
1046 -- we get a TagPosition after the tag
1027 (next, ln, col) <- case ts of
1047 (next, ln, col) <- case ts of
1028 (TagPosition{} : next : TagPosition ln col : _)
1048 (TagPosition{} : next : TagPosition ln col : _)
src/Text/Pandoc/Readers/HTML/Table.hs+35−15
@@ -17,6 +17,8 @@ module Text.Pandoc.Readers.HTML.Table (pTable) where
17
17
18import Control.Applicative ((<|>))
18import Control.Applicative ((<|>))
19import Data.Maybe (fromMaybe)
19import Data.Maybe (fromMaybe)
20+import Data.Either (lefts, rights)
21+import Data.List.NonEmpty (nonEmpty)
20import Data.Text (Text)
22import Data.Text (Text)
21import Text.HTML.TagSoup
23import Text.HTML.TagSoup
22import Text.Pandoc.Builder (Blocks)
24import Text.Pandoc.Builder (Blocks)
@@ -32,34 +34,51 @@ import Text.Pandoc.Shared (onlySimpleTableCells, safeRead)
32import qualified Data.Text as T
34import qualified Data.Text as T
33import qualified Text.Pandoc.Builder as B
35import qualified Text.Pandoc.Builder as B
34
36
35−-- | Parses a @<col>@ element, returning the column's width. Defaults to
37+-- | Parses a @<col>@ element, returning the column's width.
38+-- An Either value is used: Left i means a "relative length" with
39+-- integral value i (see https://www.w3.org/TR/html4/types.html#h-6.6);
40+-- Right w means a regular width. Defaults to @'Right ColWidthDefault'@
36−-- @'ColWidthDefault'@ if the width is not set or cannot be determined.
41+-- if the width is not set or cannot be determined.
37−pCol :: PandocMonad m => TagParser m ColWidth
42+pCol :: PandocMonad m => TagParser m (Either Int ColWidth)
38pCol = try $ do
43pCol = try $ do
39 TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" [])
44 TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" [])
40 let attribs = toStringAttr attribs'
45 let attribs = toStringAttr attribs'
41 skipMany pBlank
46 skipMany pBlank
42 optional $ pSatisfy (matchTagClose "col")
47 optional $ pSatisfy (matchTagClose "col")
43 skipMany pBlank
48 skipMany pBlank
44− let width = case lookup "width" attribs of
49+ return $ case lookup "width" attribs of
45 Nothing -> case lookup "style" attribs of
50 Nothing -> case lookup "style" attribs of
46 Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs ->
51 Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs ->
52+ maybe (Right ColWidthDefault) (Right . ColWidth . (/ 100.0))
47− fromMaybe 0.0 $ safeRead (T.filter
53+ $ safeRead (T.filter
48− (`notElem` (" \t\r\n%'\";" :: [Char])) xs)
54+ (`notElem` (" \t\r\n%'\";" :: [Char])) xs)
49− _ -> 0.0
55+ _ -> Right ColWidthDefault
56+ Just (T.unsnoc -> Just (xs, '*')) ->
57+ maybe (Left 1) Left $ safeRead xs
50 Just (T.unsnoc -> Just (xs, '%')) ->
58 Just (T.unsnoc -> Just (xs, '%')) ->
59+ maybe (Right ColWidthDefault)
51− fromMaybe 0.0 $ safeRead xs
60+ (Right . ColWidth . (/ 100.0)) $ safeRead xs
52− _ -> 0.0
53− if width > 0.0
54− then return $ ColWidth $ width / 100.0
55− else return ColWidthDefault
61+ _ -> Right ColWidthDefault
56
62
57−pColgroup :: PandocMonad m => TagParser m [ColWidth]
63+pColgroup :: PandocMonad m => TagParser m [Either Int ColWidth]
58pColgroup = try $ do
64pColgroup = try $ do
59 pSatisfy (matchTagOpen "colgroup" [])
65 pSatisfy (matchTagOpen "colgroup" [])
60 skipMany pBlank
66 skipMany pBlank
61 manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank
67 manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank
62
68
69+resolveRelativeLengths :: [Either Int ColWidth] -> [ColWidth]
70+resolveRelativeLengths ws =
71+ let remaining = 1 - sum (map getColWidth $ rights ws)
72+ relatives = sum $ lefts ws
73+ relUnit = remaining / fromIntegral relatives
74+ toColWidth (Right x) = x
75+ toColWidth (Left i) = ColWidth (fromIntegral i * relUnit)
76+ in map toColWidth ws
77+
78+getColWidth :: ColWidth -> Double
79+getColWidth ColWidthDefault = 0
80+getColWidth (ColWidth w) = w
81+
63data CellType
82data CellType
64 = HeaderCell
83 = HeaderCell
65 | BodyCell
84 | BodyCell
@@ -181,7 +200,8 @@ pTable :: PandocMonad m
181pTable block = try $ do
200pTable block = try $ do
182 TagOpen _ attribs <- pSatisfy (matchTagOpen "table" []) <* skipMany pBlank
201 TagOpen _ attribs <- pSatisfy (matchTagOpen "table" []) <* skipMany pBlank
183 caption <- option mempty $ pInTags "caption" block <* skipMany pBlank
202 caption <- option mempty $ pInTags "caption" block <* skipMany pBlank
203+ widths <- resolveRelativeLengths <$>
184− widths <- ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank
204+ ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank
185 thead <- pTableHead block <* skipMany pBlank
205 thead <- pTableHead block <* skipMany pBlank
186 topfoot <- optionMaybe (pTableFoot block) <* skipMany pBlank
206 topfoot <- optionMaybe (pTableFoot block) <* skipMany pBlank
187 tbodies <- many (pTableBody block) <* skipMany pBlank
207 tbodies <- many (pTableBody block) <* skipMany pBlank
@@ -216,7 +236,7 @@ normalize widths head' bodies foot = do
216 let rows = headRows head' <> concatMap bodyRows bodies <> footRows foot
236 let rows = headRows head' <> concatMap bodyRows bodies <> footRows foot
217 let cellWidth (Cell _ _ _ (ColSpan cs) _) = cs
237 let cellWidth (Cell _ _ _ (ColSpan cs) _) = cs
218 let rowLength = foldr (\cell acc -> cellWidth cell + acc) 0 . rowCells
238 let rowLength = foldr (\cell acc -> cellWidth cell + acc) 0 . rowCells
219− let ncols = maximum (map rowLength rows)
239+ let ncols = maybe 0 maximum $ nonEmpty $ map rowLength rows
220 let tblType = tableType (map rowCells rows)
240 let tblType = tableType (map rowCells rows)
221 -- fail on empty table
241 -- fail on empty table
222 if null rows
242 if null rows
src/Text/Pandoc/Readers/Haddock.hs+11−7
@@ -17,8 +17,9 @@ module Text.Pandoc.Readers.Haddock
17
17
18import Control.Monad.Except (throwError)
18import Control.Monad.Except (throwError)
19import Data.List (intersperse)
19import Data.List (intersperse)
20+import Data.List.NonEmpty (nonEmpty)
20import Data.Maybe (fromMaybe)
21import Data.Maybe (fromMaybe)
21−import Data.Text (Text, unpack)
22+import Data.Text (unpack)
22import qualified Data.Text as T
23import qualified Data.Text as T
23import Documentation.Haddock.Parser
24import Documentation.Haddock.Parser
24import Documentation.Haddock.Types as H
25import Documentation.Haddock.Types as H
@@ -28,15 +29,17 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad)
28import Text.Pandoc.Definition
29import Text.Pandoc.Definition
29import Text.Pandoc.Error
30import Text.Pandoc.Error
30import Text.Pandoc.Options
31import Text.Pandoc.Options
32+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
31−import Text.Pandoc.Shared (crFilter, splitTextBy, trim)
33+import Text.Pandoc.Shared (splitTextBy, trim)
32
34
33
35
34-- | Parse Haddock markup and return a 'Pandoc' document.
36-- | Parse Haddock markup and return a 'Pandoc' document.
35−readHaddock :: PandocMonad m
37+readHaddock :: (PandocMonad m, ToSources a)
36 => ReaderOptions
38 => ReaderOptions
37− -> Text
39+ -> a
38 -> m Pandoc
40 -> m Pandoc
39−readHaddock opts s = case readHaddockEither opts (unpack (crFilter s)) of
41+readHaddock opts s = case readHaddockEither opts
42+ (unpack . sourcesToText . toSources $ s) of
40 Right result -> return result
43 Right result -> return result
41 Left e -> throwError e
44 Left e -> throwError e
42
45
@@ -92,7 +95,7 @@ docHToBlocks d' =
92 then ([], map toCells bodyRows)
95 then ([], map toCells bodyRows)
93 else (toCells (head headerRows),
96 else (toCells (head headerRows),
94 map toCells (tail headerRows ++ bodyRows))
97 map toCells (tail headerRows ++ bodyRows))
95− colspecs = replicate (maximum (map length body))
98+ colspecs = replicate (maybe 0 maximum (nonEmpty (map length body)))
96 (AlignDefault, ColWidthDefault)
99 (AlignDefault, ColWidthDefault)
97 in B.table B.emptyCaption
100 in B.table B.emptyCaption
98 colspecs
101 colspecs
@@ -128,7 +131,8 @@ docHToInlines isCode d' =
128 DocIdentifier s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s
131 DocIdentifier s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s
129 _ -> mempty
132 _ -> mempty
130 DocIdentifierUnchecked s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s
133 DocIdentifierUnchecked s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s
131− DocModule s -> B.codeWith ("",["haskell","module"],[]) $ T.pack s
134+ DocModule s -> B.codeWith ("",["haskell","module"],[]) $
135+ T.pack (modLinkName s)
132 DocWarning _ -> mempty -- TODO
136 DocWarning _ -> mempty -- TODO
133 DocEmphasis d -> B.emph (docHToInlines isCode d)
137 DocEmphasis d -> B.emph (docHToInlines isCode d)
134 DocMonospaced (DocString s) -> B.code $ T.pack s
138 DocMonospaced (DocString s) -> B.code $ T.pack s
src/Text/Pandoc/Readers/Ipynb.hs+5−3
@@ -39,10 +39,12 @@ import Data.Aeson as Aeson
39import Control.Monad.Except (throwError)
39import Control.Monad.Except (throwError)
40import Text.Pandoc.Readers.Markdown (readMarkdown)
40import Text.Pandoc.Readers.Markdown (readMarkdown)
41import qualified Text.Pandoc.UTF8 as UTF8
41import qualified Text.Pandoc.UTF8 as UTF8
42+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
42
43
44+readIpynb :: (PandocMonad m, ToSources a)
43−readIpynb :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
45+ => ReaderOptions -> a -> m Pandoc
44−readIpynb opts t = do
46+readIpynb opts x = do
45− let src = BL.fromStrict (TE.encodeUtf8 t)
47+ let src = BL.fromStrict . TE.encodeUtf8 . sourcesToText $ toSources x
46 case eitherDecode src of
48 case eitherDecode src of
47 Right (notebook4 :: Notebook NbV4) -> notebookToPandoc opts notebook4
49 Right (notebook4 :: Notebook NbV4) -> notebookToPandoc opts notebook4
48 Left _ ->
50 Left _ ->
src/Text/Pandoc/Readers/JATS.hs+13−6
@@ -29,11 +29,12 @@ import Text.HTML.TagSoup.Entity (lookupEntity)
29import Text.Pandoc.Builder
29import Text.Pandoc.Builder
30import Text.Pandoc.Class.PandocMonad (PandocMonad)
30import Text.Pandoc.Class.PandocMonad (PandocMonad)
31import Text.Pandoc.Options
31import Text.Pandoc.Options
32−import Text.Pandoc.Shared (crFilter, safeRead, extractSpaces)
32+import Text.Pandoc.Shared (safeRead, extractSpaces)
33import Text.TeXMath (readMathML, writeTeX)
33import Text.TeXMath (readMathML, writeTeX)
34import Text.Pandoc.XML.Light
34import Text.Pandoc.XML.Light
35import qualified Data.Set as S (fromList, member)
35import qualified Data.Set as S (fromList, member)
36import Data.Set ((\\))
36import Data.Set ((\\))
37+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
37
38
38type JATS m = StateT JATSState m
39type JATS m = StateT JATSState m
39
40
@@ -52,10 +53,14 @@ instance Default JATSState where
52 , jatsContent = [] }
53 , jatsContent = [] }
53
54
54
55
55−readJATS :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
56+readJATS :: (PandocMonad m, ToSources a)
57+ => ReaderOptions
58+ -> a
59+ -> m Pandoc
56readJATS _ inp = do
60readJATS _ inp = do
61+ let sources = toSources inp
57 tree <- either (throwError . PandocXMLError "") return $
62 tree <- either (throwError . PandocXMLError "") return $
58− parseXMLContents (TL.fromStrict $ crFilter inp)
63+ parseXMLContents (TL.fromStrict . sourcesToText $ sources)
59 (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree
64 (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree
60 return $ Pandoc (jatsMeta st') (toList . mconcat $ bs)
65 return $ Pandoc (jatsMeta st') (toList . mconcat $ bs)
61
66
@@ -491,7 +496,8 @@ parseInline (Elem e) =
491
496
492 "disp-formula" -> formula displayMath
497 "disp-formula" -> formula displayMath
493 "inline-formula" -> formula math
498 "inline-formula" -> formula math
499+ "math" | qURI (elName e) == Just "http://www.w3.org/1998/Math/MathML"
494− "math" | qPrefix (elName e) == Just "mml" -> return . math $ mathML e
500+ -> return . math $ mathML e
495 "tex-math" -> return . math $ textContent e
501 "tex-math" -> return . math $ textContent e
496
502
497 "email" -> return $ link ("mailto:" <> textContent e) ""
503 "email" -> return $ link ("mailto:" <> textContent e) ""
@@ -514,8 +520,9 @@ parseInline (Elem e) =
514 filterChildren isMathML whereToLook
520 filterChildren isMathML whereToLook
515 return . mconcat . take 1 . map constructor $ texMaths ++ mathMLs
521 return . mconcat . take 1 . map constructor $ texMaths ++ mathMLs
516
522
517− isMathML x = qName (elName x) == "math" &&
523+ isMathML x = qName (elName x) == "math" &&
518− qPrefix (elName x) == Just "mml"
524+ qURI (elName x) ==
525+ Just "http://www.w3.org/1998/Math/MathML"
519 removePrefix elname = elname { qPrefix = Nothing }
526 removePrefix elname = elname { qPrefix = Nothing }
520 codeWithLang = do
527 codeWithLang = do
521 let classes' = case attrValue "language" e of
528 let classes' = case attrValue "language" e of
src/Text/Pandoc/Readers/Jira.hs+13−9
@@ -20,18 +20,20 @@ import Text.Pandoc.Builder hiding (cell)
20import Text.Pandoc.Error (PandocError (PandocParseError))
20import Text.Pandoc.Error (PandocError (PandocParseError))
21import Text.Pandoc.Options (ReaderOptions)
21import Text.Pandoc.Options (ReaderOptions)
22import Text.Pandoc.Shared (stringify)
22import Text.Pandoc.Shared (stringify)
23−
23+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
24import qualified Text.Jira.Markup as Jira
24import qualified Text.Jira.Markup as Jira
25
25
26-- | Read Jira wiki markup.
26-- | Read Jira wiki markup.
27−readJira :: PandocMonad m
27+readJira :: (PandocMonad m, ToSources a)
28 => ReaderOptions
28 => ReaderOptions
29− -> Text
29+ -> a
30 -> m Pandoc
30 -> m Pandoc
31−readJira _opts s = case parse s of
31+readJira _opts inp = do
32+ let sources = toSources inp
33+ case parse (sourcesToText sources) of
32− Right d -> return $ jiraToPandoc d
34+ Right d -> return $ jiraToPandoc d
33− Left e -> throwError . PandocParseError $
35+ Left e -> throwError . PandocParseError $
34− "Jira parse error" `append` pack (show e)
36+ "Jira parse error" `append` pack (show e)
35
37
36jiraToPandoc :: Jira.Doc -> Pandoc
38jiraToPandoc :: Jira.Doc -> Pandoc
37jiraToPandoc (Jira.Doc blks) = doc $ foldMap jiraToPandocBlocks blks
39jiraToPandoc (Jira.Doc blks) = doc $ foldMap jiraToPandocBlocks blks
@@ -71,10 +73,10 @@ toPandocCodeBlocks langMay params txt =
71 Nothing -> []
73 Nothing -> []
72 in codeBlockWith ("", classes, map paramToPair params) txt
74 in codeBlockWith ("", classes, map paramToPair params) txt
73
75
74−-- | Create a pandoc @'Div'@
76+-- | Create a pandoc @'Div'@ from a panel.
75toPandocDiv :: [Jira.Parameter] -> [Jira.Block] -> Blocks
77toPandocDiv :: [Jira.Parameter] -> [Jira.Block] -> Blocks
76toPandocDiv params =
78toPandocDiv params =
77− divWith ("", [], map paramToPair params) . foldMap jiraToPandocBlocks
79+ divWith ("", ["panel"], map paramToPair params) . foldMap jiraToPandocBlocks
78
80
79paramToPair :: Jira.Parameter -> (Text, Text)
81paramToPair :: Jira.Parameter -> (Text, Text)
80paramToPair (Jira.Parameter key value) = (key, value)
82paramToPair (Jira.Parameter key value) = (key, value)
@@ -170,6 +172,8 @@ jiraLinkToPandoc linkType alias url =
170 Jira.Email -> link ("mailto:" <> url') "" alias'
172 Jira.Email -> link ("mailto:" <> url') "" alias'
171 Jira.Attachment -> linkWith ("", ["attachment"], []) url' "" alias'
173 Jira.Attachment -> linkWith ("", ["attachment"], []) url' "" alias'
172 Jira.User -> linkWith ("", ["user-account"], []) url' "" alias'
174 Jira.User -> linkWith ("", ["user-account"], []) url' "" alias'
175+ Jira.SmartCard -> linkWith ("", ["smart-card"], []) url' "" alias'
176+ Jira.SmartLink -> linkWith ("", ["smart-link"], []) url' "" alias'
173
177
174-- | Get unicode representation of a Jira icon.
178-- | Get unicode representation of a Jira icon.
175iconUnicode :: Jira.Icon -> Text
179iconUnicode :: Jira.Icon -> Text
src/Text/Pandoc/Readers/LaTeX.hs+136−427
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE PatternGuards #-}
2{-# LANGUAGE PatternGuards #-}
4{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
@@ -25,7 +24,7 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX,
25import Control.Applicative (many, optional, (<|>))
24import Control.Applicative (many, optional, (<|>))
26import Control.Monad
25import Control.Monad
27import Control.Monad.Except (throwError)
26import Control.Monad.Except (throwError)
28−import Data.Char (isDigit, isLetter, toUpper, chr)
27+import Data.Char (isDigit, isLetter, isAlphaNum, toUpper, chr)
29import Data.Default
28import Data.Default
30import Data.List (intercalate)
29import Data.List (intercalate)
31import qualified Data.Map as M
30import qualified Data.Map as M
@@ -33,15 +32,16 @@ import Data.Maybe (fromMaybe, maybeToList)
33import qualified Data.Set as Set
32import qualified Data.Set as Set
34import Data.Text (Text)
33import Data.Text (Text)
35import qualified Data.Text as T
34import qualified Data.Text as T
35+import Skylighting (defaultSyntaxMap)
36import System.FilePath (addExtension, replaceExtension, takeExtension)
36import System.FilePath (addExtension, replaceExtension, takeExtension)
37−import Text.Pandoc.BCP47 (Lang (..), renderLang)
37+import Text.Collate.Lang (renderLang)
38−import Text.Pandoc.Builder
38+import Text.Pandoc.Builder as B
39import Text.Pandoc.Class.PandocPure (PandocPure)
39import Text.Pandoc.Class.PandocPure (PandocPure)
40import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getResourcePath,
40import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getResourcePath,
41− readFileFromDirs, report, setResourcePath,
41+ readFileFromDirs, report,
42− translateTerm)
42+ setResourcePath)
43import Text.Pandoc.Error (PandocError (PandocParseError, PandocParsecError))
43import Text.Pandoc.Error (PandocError (PandocParseError, PandocParsecError))
44−import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
44+import Text.Pandoc.Highlighting (languagesByExtension)
45import Text.Pandoc.ImageSize (numUnit, showFl)
45import Text.Pandoc.ImageSize (numUnit, showFl)
46import Text.Pandoc.Logging
46import Text.Pandoc.Logging
47import Text.Pandoc.Options
47import Text.Pandoc.Options
@@ -49,7 +49,6 @@ import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,
49 optional, space, spaces, withRaw, (<|>))
49 optional, space, spaces, withRaw, (<|>))
50import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
50import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
51import Text.Pandoc.Readers.LaTeX.Parsing
51import Text.Pandoc.Readers.LaTeX.Parsing
52−import Text.Pandoc.Readers.LaTeX.Accent (accentCommands)
53import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites)
52import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites)
54import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
53import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
55 inlineEnvironment,
54 inlineEnvironment,
@@ -58,14 +57,19 @@ import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
58 theoremEnvironment)
57 theoremEnvironment)
59import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments)
58import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments)
60import Text.Pandoc.Readers.LaTeX.Macro (macroDef)
59import Text.Pandoc.Readers.LaTeX.Macro (macroDef)
61−import Text.Pandoc.Readers.LaTeX.Lang (polyglossiaLangToBCP47,
60+import Text.Pandoc.Readers.LaTeX.Lang (inlineLanguageCommands,
61+ enquoteCommands,
62 babelLangToBCP47, setDefaultLanguage)
62 babelLangToBCP47, setDefaultLanguage)
63import Text.Pandoc.Readers.LaTeX.SIunitx (siunitxCommands)
63import 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)
64import Text.Pandoc.Shared
70import Text.Pandoc.Shared
65−import qualified Text.Pandoc.Translations as Translations
66import Text.Pandoc.Walk
71import Text.Pandoc.Walk
67−import qualified Text.Pandoc.Builder as B
72+import Data.List.NonEmpty (nonEmpty)
68−import Safe
69
73
70-- for debugging:
74-- for debugging:
71-- import Text.Pandoc.Extensions (getDefaultExtensions)
75-- import Text.Pandoc.Extensions (getDefaultExtensions)
@@ -73,16 +77,17 @@ import Safe
73-- import Debug.Trace (traceShowId)
77-- import Debug.Trace (traceShowId)
74
78
75-- | Parse LaTeX from string and return 'Pandoc' document.
79-- | Parse LaTeX from string and return 'Pandoc' document.
76−readLaTeX :: PandocMonad m
80+readLaTeX :: (PandocMonad m, ToSources a)
77 => ReaderOptions -- ^ Reader options
81 => ReaderOptions -- ^ Reader options
78− -> Text -- ^ String to parse (assumes @'\n'@ line endings)
82+ -> a -- ^ Input to parse
79 -> m Pandoc
83 -> m Pandoc
80readLaTeX opts ltx = do
84readLaTeX opts ltx = do
85+ let sources = toSources ltx
81 parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"
86 parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"
82− (tokenize "source" (crFilter ltx))
87+ (tokenizeSources sources)
83 case parsed of
88 case parsed of
84 Right result -> return result
89 Right result -> return result
85− Left e -> throwError $ PandocParsecError ltx e
90+ Left e -> throwError $ PandocParsecError sources e
86
91
87parseLaTeX :: PandocMonad m => LP m Pandoc
92parseLaTeX :: PandocMonad m => LP m Pandoc
88parseLaTeX = do
93parseLaTeX = do
@@ -93,11 +98,7 @@ parseLaTeX = do
93 let doc' = doc bs
98 let doc' = doc bs
94 let headerLevel (Header n _ _) = [n]
99 let headerLevel (Header n _ _) = [n]
95 headerLevel _ = []
100 headerLevel _ = []
96−#if MIN_VERSION_safe(0,3,18)
101+ let bottomLevel = maybe 1 minimum $ nonEmpty $ query headerLevel doc'
97− let bottomLevel = minimumBound 1 $ query headerLevel doc'
98−#else
99− let bottomLevel = minimumDef 1 $ query headerLevel doc'
100−#endif
101 let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils
102 let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils
102 adjustHeaders _ x = x
103 adjustHeaders _ x = x
103 let (Pandoc _ bs') =
104 let (Pandoc _ bs') =
@@ -132,11 +133,11 @@ resolveRefs _ x = x
132
133
133
134
134rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s)
135rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s)
135− => ParserT Text s m Text
136+ => ParserT Sources s m Text
136rawLaTeXBlock = do
137rawLaTeXBlock = do
137 lookAhead (try (char '\\' >> letter))
138 lookAhead (try (char '\\' >> letter))
138 inp <- getInput
139 inp <- getInput
139− let toks = tokenize "source" inp
140+ let toks = tokenizeSources inp
140 snd <$> (rawLaTeXParser toks False (macroDef (const mempty)) blocks
141 snd <$> (rawLaTeXParser toks False (macroDef (const mempty)) blocks
141 <|> rawLaTeXParser toks True
142 <|> rawLaTeXParser toks True
142 (do choice (map controlSeq
143 (do choice (map controlSeq
@@ -163,11 +164,11 @@ beginOrEndCommand = try $ do
163 (txt <> untokenize rawargs)
164 (txt <> untokenize rawargs)
164
165
165rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s)
166rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s)
166− => ParserT Text s m Text
167+ => ParserT Sources s m Text
167rawLaTeXInline = do
168rawLaTeXInline = do
168 lookAhead (try (char '\\' >> letter))
169 lookAhead (try (char '\\' >> letter))
169 inp <- getInput
170 inp <- getInput
170− let toks = tokenize "source" inp
171+ let toks = tokenizeSources inp
171 raw <- snd <$>
172 raw <- snd <$>
172 ( rawLaTeXParser toks True
173 ( rawLaTeXParser toks True
173 (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced))
174 (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced))
@@ -178,11 +179,11 @@ rawLaTeXInline = do
178 finalbraces <- mconcat <$> many (try (string "{}")) -- see #5439
179 finalbraces <- mconcat <$> many (try (string "{}")) -- see #5439
179 return $ raw <> T.pack finalbraces
180 return $ raw <> T.pack finalbraces
180
181
181−inlineCommand :: PandocMonad m => ParserT Text ParserState m Inlines
182+inlineCommand :: PandocMonad m => ParserT Sources ParserState m Inlines
182inlineCommand = do
183inlineCommand = do
183 lookAhead (try (char '\\' >> letter))
184 lookAhead (try (char '\\' >> letter))
184 inp <- getInput
185 inp <- getInput
185− let toks = tokenize "source" inp
186+ let toks = tokenizeSources inp
186 fst <$> rawLaTeXParser toks True (inlineEnvironment <|> inlineCommand')
187 fst <$> rawLaTeXParser toks True (inlineEnvironment <|> inlineCommand')
187 inlines
188 inlines
188
189
@@ -231,16 +232,6 @@ mkImage options (T.unpack -> src) = do
231 _ -> return src
232 _ -> return src
232 return $ imageWith attr (T.pack src') "" alt
233 return $ imageWith attr (T.pack src') "" alt
233
234
234−doxspace :: PandocMonad m => LP m Inlines
235−doxspace =
236− (space <$ lookAhead (satisfyTok startsWithLetter)) <|> return mempty
237− where startsWithLetter (Tok _ Word t) =
238− case T.uncons t of
239− Just (c, _) | isLetter c -> True
240− _ -> False
241− startsWithLetter _ = False
242−
243−
244removeDoubleQuotes :: Text -> Text
235removeDoubleQuotes :: Text -> Text
245removeDoubleQuotes t =
236removeDoubleQuotes t =
246 Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
237 Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
@@ -290,18 +281,6 @@ quoted' f starter ender = do
290lit :: Text -> LP m Inlines
281lit :: Text -> LP m Inlines
291lit = pure . str
282lit = pure . str
292
283
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−
305blockquote :: PandocMonad m => Bool -> Maybe Text -> LP m Blocks
284blockquote :: PandocMonad m => Bool -> Maybe Text -> LP m Blocks
306blockquote cvariant mblang = do
285blockquote cvariant mblang = do
307 citepar <- if cvariant
286 citepar <- if cvariant
@@ -317,81 +296,13 @@ blockquote cvariant mblang = do
317 optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored
296 optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored
318 return $ blockQuote . langdiv $ (bs <> citepar)
297 return $ blockQuote . langdiv $ (bs <> citepar)
319
298
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−
390inlineCommand' :: PandocMonad m => LP m Inlines
299inlineCommand' :: PandocMonad m => LP m Inlines
391inlineCommand' = try $ do
300inlineCommand' = try $ do
392 Tok _ (CtrlSeq name) cmd <- anyControlSeq
301 Tok _ (CtrlSeq name) cmd <- anyControlSeq
393 guard $ name /= "begin" && name /= "end" && name /= "and"
302 guard $ name /= "begin" && name /= "end" && name /= "and"
303+ star <- if T.all isAlphaNum name
394− star <- option "" ("*" <$ symbol '*' <* sp)
304+ then option "" ("*" <$ symbol '*' <* sp)
305+ else pure ""
395 overlay <- option "" overlaySpecification
306 overlay <- option "" overlaySpecification
396 let name' = name <> star <> overlay
307 let name' = name <> star <> overlay
397 let names = ordNub [name', name] -- check non-starred as fallback
308 let names = ordNub [name', name] -- check non-starred as fallback
@@ -405,9 +316,6 @@ inlineCommand' = try $ do
405tok :: PandocMonad m => LP m Inlines
316tok :: PandocMonad m => LP m Inlines
406tok = tokWith inline
317tok = tokWith inline
407
318
408−inBrackets :: Inlines -> Inlines
409−inBrackets x = str "[" <> x <> str "]"
410−
411unescapeURL :: Text -> Text
319unescapeURL :: Text -> Text
412unescapeURL = T.concat . go . T.splitOn "\\"
320unescapeURL = T.concat . go . T.splitOn "\\"
413 where
321 where
@@ -420,234 +328,102 @@ unescapeURL = T.concat . go . T.splitOn "\\"
420 | otherwise = "\\" <> t
328 | otherwise = "\\" <> t
421
329
422inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
330inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
423−inlineCommands =
331+inlineCommands = M.unions
424− M.union inlineLanguageCommands $
425− M.union (accentCommands tok) $
332+ [ accentCommands tok
426− M.union (citationCommands inline) $
333+ , citationCommands inline
427− M.union (siunitxCommands tok) $
334+ , siunitxCommands tok
335+ , acronymCommands
336+ , refCommands
337+ , nameCommands
338+ , verbCommands
339+ , charCommands
340+ , enquoteCommands tok
341+ , inlineLanguageCommands tok
342+ , biblatexInlineCommands tok
343+ , rest ]
344+ where
428− M.fromList
345+ rest = M.fromList
429− [ ("emph", extractSpaces emph <$> tok)
346+ [ ("emph", extractSpaces emph <$> tok)
430− , ("textit", extractSpaces emph <$> tok)
347+ , ("textit", extractSpaces emph <$> tok)
431− , ("textsl", extractSpaces emph <$> tok)
348+ , ("textsl", extractSpaces emph <$> tok)
432− , ("textsc", extractSpaces smallcaps <$> tok)
349+ , ("textsc", extractSpaces smallcaps <$> tok)
433− , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
350+ , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
434− , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
351+ , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
435− , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
352+ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
436− , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
353+ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
437− , ("texttt", ttfamily)
354+ , ("texttt", ttfamily)
438− , ("sout", extractSpaces strikeout <$> tok)
355+ , ("sout", extractSpaces strikeout <$> tok)
439− , ("alert", skipopts >> spanWith ("",["alert"],[]) <$> tok) -- beamer
356+ , ("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)
357+ , ("textsuperscript", extractSpaces superscript <$> tok)
447− , ("textsubscript", extractSpaces subscript <$> tok)
358+ , ("textsubscript", extractSpaces subscript <$> tok)
448− , ("textbackslash", lit "\\")
449− , ("backslash", lit "\\")
450− , ("slash", lit "/")
451− , ("textbf", extractSpaces strong <$> tok)
359+ , ("textbf", extractSpaces strong <$> tok)
452− , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
360+ , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
453− , ("underline", underline <$> tok)
361+ , ("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)
362+ , ("mbox", rawInlineOr "mbox" $ processHBox <$> tok)
466− , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
363+ , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
467− , ("lettrine", rawInlineOr "lettrine" lettrine)
364+ , ("lettrine", rawInlineOr "lettrine" lettrine)
468− , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
365+ , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
469− , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
366+ , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
470− , ("ensuremath", mathInline . untokenize <$> braced)
367+ , ("ensuremath", mathInline . untokenize <$> braced)
471− , ("texorpdfstring", const <$> tok <*> tok)
368+ , ("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
369+ -- old TeX commands
483− , ("em", extractSpaces emph <$> inlines)
370+ , ("em", extractSpaces emph <$> inlines)
484− , ("it", extractSpaces emph <$> inlines)
371+ , ("it", extractSpaces emph <$> inlines)
485− , ("sl", extractSpaces emph <$> inlines)
372+ , ("sl", extractSpaces emph <$> inlines)
486− , ("bf", extractSpaces strong <$> inlines)
373+ , ("bf", extractSpaces strong <$> inlines)
487− , ("tt", code . stringify . toList <$> inlines)
374+ , ("tt", code . stringify . toList <$> inlines)
488− , ("rm", inlines)
375+ , ("rm", inlines)
489− , ("itshape", extractSpaces emph <$> inlines)
376+ , ("itshape", extractSpaces emph <$> inlines)
490− , ("slshape", extractSpaces emph <$> inlines)
377+ , ("slshape", extractSpaces emph <$> inlines)
491− , ("scshape", extractSpaces smallcaps <$> inlines)
378+ , ("scshape", extractSpaces smallcaps <$> inlines)
492− , ("bfseries", extractSpaces strong <$> inlines)
379+ , ("bfseries", extractSpaces strong <$> inlines)
493− , ("MakeUppercase", makeUppercase <$> tok)
380+ , ("MakeUppercase", makeUppercase <$> tok)
494− , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
381+ , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
495− , ("uppercase", makeUppercase <$> tok)
382+ , ("uppercase", makeUppercase <$> tok)
496− , ("MakeLowercase", makeLowercase <$> tok)
383+ , ("MakeLowercase", makeLowercase <$> tok)
497− , ("MakeTextLowercase", makeLowercase <$> tok)
384+ , ("MakeTextLowercase", makeLowercase <$> tok)
498− , ("lowercase", makeLowercase <$> tok)
385+ , ("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)
386+ , ("thanks", skipopts >> note <$> grouped block)
514− , ("footnote", skipopts >> note <$> grouped block)
387+ , ("footnote", skipopts >> note <$> grouped block)
515− , ("passthrough", tok) -- \passthrough macro used by latex writer
388+ , ("passthrough", tok) -- \passthrough macro used by latex writer
516− -- for listings
389+ -- 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
390+ , ("includegraphics", do options <- option [] keyvals
528− src <- braced
391+ src <- braced
529− mkImage options . unescapeURL . removeDoubleQuotes $
392+ mkImage options .
530− untokenize src)
393+ unescapeURL .
531− , ("enquote*", enquote True Nothing)
394+ removeDoubleQuotes $ untokenize src)
532− , ("enquote", enquote False Nothing)
395+ -- hyperref
533− -- foreignquote is supposed to use native quote marks
396+ , ("url", (\url -> link url "" (str url)) . unescapeURL . untokenize <$>
534− , ("foreignquote*", braced >>= enquote True . Just . untokenize)
397+ bracedUrl)
535− , ("foreignquote", braced >>= enquote False . Just . untokenize)
398+ , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl)
536− -- hypehnquote uses regular quotes
399+ , ("href", do url <- bracedUrl
537− , ("hyphenquote*", braced >>= enquote True . Just . untokenize)
400+ sp
538− , ("hyphenquote", braced >>= enquote False . Just . untokenize)
401+ link (unescapeURL $ untokenize url) "" <$> tok)
539− , ("figurename", doTerm Translations.Figure)
402+ , ("hyperlink", hyperlink)
540− , ("prefacename", doTerm Translations.Preface)
403+ , ("hyperref", hyperref)
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)
404+ , ("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
405+ -- hyphenat
597− , ("bshyp", lit "\\\173")
598− , ("fshyp", lit "/\173")
599− , ("dothyp", lit ".\173")
600− , ("colonhyp", lit ":\173")
601− , ("hyp", lit "-")
602− , ("nohyphens", tok)
406+ , ("nohyphens", tok)
603− , ("textnhtt", ttfamily)
407+ , ("textnhtt", ttfamily)
604− , ("nhttfamily", ttfamily)
408+ , ("nhttfamily", ttfamily)
605− -- LaTeX colors
409+ -- LaTeX colors
606− , ("textcolor", coloredInline "color")
410+ , ("textcolor", coloredInline "color")
607− , ("colorbox", coloredInline "background-color")
411+ , ("colorbox", coloredInline "background-color")
608− -- fontawesome
609− , ("faCheck", lit "\10003")
610− , ("faClose", lit "\10007")
611− -- xspace
612− , ("xspace", doxspace)
613− -- etoolbox
412+ -- etoolbox
614− , ("ifstrequal", ifstrequal)
413+ , ("ifstrequal", ifstrequal)
615− , ("newtoggle", braced >>= newToggle)
414+ , ("newtoggle", braced >>= newToggle)
616− , ("toggletrue", braced >>= setToggle True)
415+ , ("toggletrue", braced >>= setToggle True)
617− , ("togglefalse", braced >>= setToggle False)
416+ , ("togglefalse", braced >>= setToggle False)
618− , ("iftoggle", try $ ifToggle >> inline)
417+ , ("iftoggle", try $ ifToggle >> inline)
619− -- biblatex misc
620− , ("RN", romanNumeralUpper)
621− , ("Rn", romanNumeralLower)
622− -- babel
623− , ("foreignlanguage", foreignlanguage)
624− -- include
418+ -- include
625− , ("input", rawInlineOr "input" $ include "input")
419+ , ("input", rawInlineOr "input" $ include "input")
626− -- soul package
420+ -- soul package
627− , ("ul", underline <$> tok)
421+ , ("ul", underline <$> tok)
628− -- ulem package
422+ -- ulem package
629− , ("uline", underline <$> tok)
423+ , ("uline", underline <$> tok)
630− -- plain tex stuff that should just be passed through as raw tex
424+ -- plain tex stuff that should just be passed through as raw tex
631− , ("ifdim", ifdim)
425+ , ("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− ]
426+ ]
651
427
652lettrine :: PandocMonad m => LP m Inlines
428lettrine :: PandocMonad m => LP m Inlines
653lettrine = do
429lettrine = do
@@ -671,32 +447,18 @@ alterStr :: (Text -> Text) -> Inline -> Inline
671alterStr f (Str xs) = Str (f xs)
447alterStr f (Str xs) = Str (f xs)
672alterStr _ x = x
448alterStr _ x = x
673
449
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−
694hyperlink :: PandocMonad m => LP m Inlines
450hyperlink :: PandocMonad m => LP m Inlines
695hyperlink = try $ do
451hyperlink = try $ do
696 src <- untokenize <$> braced
452 src <- untokenize <$> braced
697 lab <- tok
453 lab <- tok
698 return $ link ("#" <> src) "" lab
454 return $ link ("#" <> src) "" lab
699
455
456+hyperref :: PandocMonad m => LP m Inlines
457+hyperref = try $ do
458+ url <- (("#" <>) . untokenize <$> try (sp *> bracketedToks <* sp))
459+ <|> untokenize <$> (bracedUrl <* bracedUrl <* bracedUrl)
460+ link url "" <$> tok
461+
700hypertargetBlock :: PandocMonad m => LP m Blocks
462hypertargetBlock :: PandocMonad m => LP m Blocks
701hypertargetBlock = try $ do
463hypertargetBlock = try $ do
702 ref <- untokenize <$> braced
464 ref <- untokenize <$> braced
@@ -711,31 +473,6 @@ hypertargetInline = try $ do
711 ils <- grouped inline
473 ils <- grouped inline
712 return $ spanWith (ref, [], []) ils
474 return $ spanWith (ref, [], []) ils
713
475
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−
739newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a
476newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a
740newToggle name = do
477newToggle name = do
741 updateState $ \st ->
478 updateState $ \st ->
@@ -766,9 +503,6 @@ ifToggle = do
766 report $ UndefinedToggle name' pos
503 report $ UndefinedToggle name' pos
767 return ()
504 return ()
768
505
769−doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
770−doTerm term = str <$> translateTerm term
771−
772ifstrequal :: (PandocMonad m, Monoid a) => LP m a
506ifstrequal :: (PandocMonad m, Monoid a) => LP m a
773ifstrequal = do
507ifstrequal = do
774 str1 <- tok
508 str1 <- tok
@@ -789,13 +523,6 @@ coloredInline stylename = do
789ttfamily :: PandocMonad m => LP m Inlines
523ttfamily :: PandocMonad m => LP m Inlines
790ttfamily = code . stringify . toList <$> tok
524ttfamily = code . stringify . toList <$> tok
791
525
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−
799processHBox :: Inlines -> Inlines
526processHBox :: Inlines -> Inlines
800processHBox = walk convert
527processHBox = walk convert
801 where
528 where
@@ -846,25 +573,6 @@ treatAsInline = Set.fromList
846 , "pagebreak"
573 , "pagebreak"
847 ]
574 ]
848
575
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−
868lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
576lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
869lookupListDefault d = (fromMaybe d .) . lookupList
577lookupListDefault d = (fromMaybe d .) . lookupList
870 where lookupList l m = msum $ map (`M.lookup` m) l
578 where lookupList l m = msum $ map (`M.lookup` m) l
@@ -924,7 +632,7 @@ opt = do
924 parsed <- runParserT (mconcat <$> many inline) st "bracketed option" toks
632 parsed <- runParserT (mconcat <$> many inline) st "bracketed option" toks
925 case parsed of
633 case parsed of
926 Right result -> return result
634 Right result -> return result
927− Left e -> throwError $ PandocParsecError (untokenize toks) e
635+ Left e -> throwError $ PandocParsecError (toSources toks) e
928
636
929-- block elements:
637-- block elements:
930
638
@@ -1454,7 +1162,8 @@ inputListing = do
1454 let (ident,classes,kvs) = parseListingsOptions options
1162 let (ident,classes,kvs) = parseListingsOptions options
1455 let classes' =
1163 let classes' =
1456 (case listingsLanguage options of
1164 (case listingsLanguage options of
1165+ Nothing -> (take 1 (languagesByExtension defaultSyntaxMap
1457− Nothing -> (take 1 (languagesByExtension (T.pack $ takeExtension $ T.unpack f)) <>)
1166+ (T.pack $ takeExtension $ T.unpack f)) <>)
1458 Just _ -> id) classes
1167 Just _ -> id) classes
1459 let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1168 let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1460 let lastline = fromMaybe (length codeLines) $
1169 let lastline = fromMaybe (length codeLines) $
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/Citation.hs+1−1
@@ -120,7 +120,7 @@ simpleCiteArgs inline = try $ do
120 runParserT (mconcat <$> many inline) st "bracketed option" toks
120 runParserT (mconcat <$> many inline) st "bracketed option" toks
121 case parsed of
121 case parsed of
122 Right result -> return result
122 Right result -> return result
123− Left e -> throwError $ PandocParsecError (untokenize toks) e
123+ Left e -> throwError $ PandocParsecError (toSources toks) e
124
124
125
125
126
126
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+182−121
@@ -15,15 +15,73 @@ module Text.Pandoc.Readers.LaTeX.Lang
15 ( setDefaultLanguage
15 ( setDefaultLanguage
16 , polyglossiaLangToBCP47
16 , polyglossiaLangToBCP47
17 , babelLangToBCP47
17 , babelLangToBCP47
18+ , enquoteCommands
19+ , inlineLanguageCommands
18 )
20 )
19where
21where
20import qualified Data.Map as M
22import qualified Data.Map as M
23+import Data.Text (Text)
21import qualified Data.Text as T
24import qualified Data.Text as T
22−import Text.Pandoc.BCP47 (Lang(..), renderLang)
25+import Text.Pandoc.Shared (extractSpaces)
26+import Text.Collate.Lang (Lang(..), renderLang)
23import Text.Pandoc.Class (PandocMonad(..), setTranslations)
27import Text.Pandoc.Class (PandocMonad(..), setTranslations)
24import Text.Pandoc.Readers.LaTeX.Parsing
28import Text.Pandoc.Readers.LaTeX.Parsing
25−import Text.Pandoc.Parsing (updateState, option)
29+import Text.Pandoc.Parsing (updateState, option, getState, QuoteContext(..),
30+ withQuoteContext)
26−import Text.Pandoc.Builder (Blocks, setMeta, str)
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
27
85
28setDefaultLanguage :: PandocMonad m => LP m Blocks
86setDefaultLanguage :: PandocMonad m => LP m Blocks
29setDefaultLanguage = do
87setDefaultLanguage = do
@@ -41,133 +99,136 @@ setDefaultLanguage = do
41polyglossiaLangToBCP47 :: M.Map T.Text (T.Text -> Lang)
99polyglossiaLangToBCP47 :: M.Map T.Text (T.Text -> Lang)
42polyglossiaLangToBCP47 = M.fromList
100polyglossiaLangToBCP47 = M.fromList
43 [ ("arabic", \o -> case T.filter (/=' ') o of
101 [ ("arabic", \o -> case T.filter (/=' ') o of
44− "locale=algeria" -> Lang "ar" "" "DZ" []
102+ "locale=algeria" -> Lang "ar" Nothing (Just "DZ") [] [] []
45− "locale=mashriq" -> Lang "ar" "" "SY" []
103+ "locale=mashriq" -> Lang "ar" Nothing (Just "SY") [] [] []
46− "locale=libya" -> Lang "ar" "" "LY" []
104+ "locale=libya" -> Lang "ar" Nothing (Just "LY") [] [] []
47− "locale=morocco" -> Lang "ar" "" "MA" []
105+ "locale=morocco" -> Lang "ar" Nothing (Just "MA") [] [] []
48− "locale=mauritania" -> Lang "ar" "" "MR" []
106+ "locale=mauritania" -> Lang "ar" Nothing (Just "MR") [] [] []
49− "locale=tunisia" -> Lang "ar" "" "TN" []
107+ "locale=tunisia" -> Lang "ar" Nothing (Just "TN") [] [] []
50− _ -> Lang "ar" "" "" [])
108+ _ -> Lang "ar" Nothing (Just "") [] [] [])
51 , ("german", \o -> case T.filter (/=' ') o of
109 , ("german", \o -> case T.filter (/=' ') o of
52− "spelling=old" -> Lang "de" "" "DE" ["1901"]
110+ "spelling=old" -> Lang "de" Nothing (Just "DE") ["1901"] [] []
53 "variant=austrian,spelling=old"
111 "variant=austrian,spelling=old"
54− -> Lang "de" "" "AT" ["1901"]
112+ -> Lang "de" Nothing (Just "AT") ["1901"] [] []
55− "variant=austrian" -> Lang "de" "" "AT" []
113+ "variant=austrian" -> Lang "de" Nothing (Just "AT") [] [] []
56 "variant=swiss,spelling=old"
114 "variant=swiss,spelling=old"
57− -> Lang "de" "" "CH" ["1901"]
115+ -> Lang "de" Nothing (Just "CH") ["1901"] [] []
58− "variant=swiss" -> Lang "de" "" "CH" []
116+ "variant=swiss" -> Lang "de" Nothing (Just "CH") [] [] []
59− _ -> Lang "de" "" "" [])
117+ _ -> Lang "de" Nothing Nothing [] [] [])
60− , ("lsorbian", \_ -> Lang "dsb" "" "" [])
118+ , ("lsorbian", \_ -> Lang "dsb" Nothing Nothing [] [] [])
61 , ("greek", \o -> case T.filter (/=' ') o of
119 , ("greek", \o -> case T.filter (/=' ') o of
62− "variant=poly" -> Lang "el" "" "polyton" []
120+ "variant=poly" -> Lang "el" Nothing (Just "polyton") [] [] []
63− "variant=ancient" -> Lang "grc" "" "" []
121+ "variant=ancient" -> Lang "grc" Nothing Nothing [] [] []
64− _ -> Lang "el" "" "" [])
122+ _ -> Lang "el" Nothing Nothing [] [] [])
65 , ("english", \o -> case T.filter (/=' ') o of
123 , ("english", \o -> case T.filter (/=' ') o of
66− "variant=australian" -> Lang "en" "" "AU" []
124+ "variant=australian" -> Lang "en" Nothing (Just "AU") [] [] []
67− "variant=canadian" -> Lang "en" "" "CA" []
125+ "variant=canadian" -> Lang "en" Nothing (Just "CA") [] [] []
68− "variant=british" -> Lang "en" "" "GB" []
126+ "variant=british" -> Lang "en" Nothing (Just "GB") [] [] []
69− "variant=newzealand" -> Lang "en" "" "NZ" []
127+ "variant=newzealand" -> Lang "en" Nothing (Just "NZ") [] [] []
70− "variant=american" -> Lang "en" "" "US" []
128+ "variant=american" -> Lang "en" Nothing (Just "US") [] [] []
71− _ -> Lang "en" "" "" [])
129+ _ -> Lang "en" Nothing (Just "") [] [] [])
72− , ("usorbian", \_ -> Lang "hsb" "" "" [])
130+ , ("usorbian", \_ -> Lang "hsb" Nothing Nothing [] [] [])
73 , ("latin", \o -> case T.filter (/=' ') o of
131 , ("latin", \o -> case T.filter (/=' ') o of
74− "variant=classic" -> Lang "la" "" "" ["x-classic"]
132+ "variant=classic" -> Lang "la" Nothing Nothing ["x-classic"] [] []
75− _ -> Lang "la" "" "" [])
133+ _ -> Lang "la" Nothing Nothing [] [] [])
76− , ("slovenian", \_ -> Lang "sl" "" "" [])
134+ , ("slovenian", \_ -> Lang "sl" Nothing Nothing [] [] [])
77− , ("serbianc", \_ -> Lang "sr" "cyrl" "" [])
135+ , ("serbianc", \_ -> Lang "sr" (Just "Cyrl") Nothing [] [] [])
78− , ("pinyin", \_ -> Lang "zh" "Latn" "" ["pinyin"])
136+ , ("pinyin", \_ -> Lang "zh" (Just "Latn") Nothing ["pinyin"] [] [])
79− , ("afrikaans", \_ -> Lang "af" "" "" [])
137+ , ("afrikaans", \_ -> simpleLang "af")
80− , ("amharic", \_ -> Lang "am" "" "" [])
138+ , ("amharic", \_ -> simpleLang "am")
81− , ("assamese", \_ -> Lang "as" "" "" [])
139+ , ("assamese", \_ -> simpleLang "as")
82− , ("asturian", \_ -> Lang "ast" "" "" [])
140+ , ("asturian", \_ -> simpleLang "ast")
83− , ("bulgarian", \_ -> Lang "bg" "" "" [])
141+ , ("bulgarian", \_ -> simpleLang "bg")
84− , ("bengali", \_ -> Lang "bn" "" "" [])
142+ , ("bengali", \_ -> simpleLang "bn")
85− , ("tibetan", \_ -> Lang "bo" "" "" [])
143+ , ("tibetan", \_ -> simpleLang "bo")
86− , ("breton", \_ -> Lang "br" "" "" [])
144+ , ("breton", \_ -> simpleLang "br")
87− , ("catalan", \_ -> Lang "ca" "" "" [])
145+ , ("catalan", \_ -> simpleLang "ca")
88− , ("welsh", \_ -> Lang "cy" "" "" [])
146+ , ("welsh", \_ -> simpleLang "cy")
89− , ("czech", \_ -> Lang "cs" "" "" [])
147+ , ("czech", \_ -> simpleLang "cs")
90− , ("coptic", \_ -> Lang "cop" "" "" [])
148+ , ("coptic", \_ -> simpleLang "cop")
91− , ("danish", \_ -> Lang "da" "" "" [])
149+ , ("danish", \_ -> simpleLang "da")
92− , ("divehi", \_ -> Lang "dv" "" "" [])
150+ , ("divehi", \_ -> simpleLang "dv")
93− , ("esperanto", \_ -> Lang "eo" "" "" [])
151+ , ("esperanto", \_ -> simpleLang "eo")
94− , ("spanish", \_ -> Lang "es" "" "" [])
152+ , ("spanish", \_ -> simpleLang "es")
95− , ("estonian", \_ -> Lang "et" "" "" [])
153+ , ("estonian", \_ -> simpleLang "et")
96− , ("basque", \_ -> Lang "eu" "" "" [])
154+ , ("basque", \_ -> simpleLang "eu")
97− , ("farsi", \_ -> Lang "fa" "" "" [])
155+ , ("farsi", \_ -> simpleLang "fa")
98− , ("finnish", \_ -> Lang "fi" "" "" [])
156+ , ("finnish", \_ -> simpleLang "fi")
99− , ("french", \_ -> Lang "fr" "" "" [])
157+ , ("french", \_ -> simpleLang "fr")
100− , ("friulan", \_ -> Lang "fur" "" "" [])
158+ , ("friulan", \_ -> simpleLang "fur")
101− , ("irish", \_ -> Lang "ga" "" "" [])
159+ , ("irish", \_ -> simpleLang "ga")
102− , ("scottish", \_ -> Lang "gd" "" "" [])
160+ , ("scottish", \_ -> simpleLang "gd")
103− , ("ethiopic", \_ -> Lang "gez" "" "" [])
161+ , ("ethiopic", \_ -> simpleLang "gez")
104− , ("galician", \_ -> Lang "gl" "" "" [])
162+ , ("galician", \_ -> simpleLang "gl")
105− , ("hebrew", \_ -> Lang "he" "" "" [])
163+ , ("hebrew", \_ -> simpleLang "he")
106− , ("hindi", \_ -> Lang "hi" "" "" [])
164+ , ("hindi", \_ -> simpleLang "hi")
107− , ("croatian", \_ -> Lang "hr" "" "" [])
165+ , ("croatian", \_ -> simpleLang "hr")
108− , ("magyar", \_ -> Lang "hu" "" "" [])
166+ , ("magyar", \_ -> simpleLang "hu")
109− , ("armenian", \_ -> Lang "hy" "" "" [])
167+ , ("armenian", \_ -> simpleLang "hy")
110− , ("interlingua", \_ -> Lang "ia" "" "" [])
168+ , ("interlingua", \_ -> simpleLang "ia")
111− , ("indonesian", \_ -> Lang "id" "" "" [])
169+ , ("indonesian", \_ -> simpleLang "id")
112− , ("icelandic", \_ -> Lang "is" "" "" [])
170+ , ("icelandic", \_ -> simpleLang "is")
113− , ("italian", \_ -> Lang "it" "" "" [])
171+ , ("italian", \_ -> simpleLang "it")
114− , ("japanese", \_ -> Lang "jp" "" "" [])
172+ , ("japanese", \_ -> simpleLang "jp")
115− , ("khmer", \_ -> Lang "km" "" "" [])
173+ , ("khmer", \_ -> simpleLang "km")
116− , ("kurmanji", \_ -> Lang "kmr" "" "" [])
174+ , ("kurmanji", \_ -> simpleLang "kmr")
117− , ("kannada", \_ -> Lang "kn" "" "" [])
175+ , ("kannada", \_ -> simpleLang "kn")
118− , ("korean", \_ -> Lang "ko" "" "" [])
176+ , ("korean", \_ -> simpleLang "ko")
119− , ("lao", \_ -> Lang "lo" "" "" [])
177+ , ("lao", \_ -> simpleLang "lo")
120− , ("lithuanian", \_ -> Lang "lt" "" "" [])
178+ , ("lithuanian", \_ -> simpleLang "lt")
121− , ("latvian", \_ -> Lang "lv" "" "" [])
179+ , ("latvian", \_ -> simpleLang "lv")
122− , ("malayalam", \_ -> Lang "ml" "" "" [])
180+ , ("malayalam", \_ -> simpleLang "ml")
123− , ("mongolian", \_ -> Lang "mn" "" "" [])
181+ , ("mongolian", \_ -> simpleLang "mn")
124− , ("marathi", \_ -> Lang "mr" "" "" [])
182+ , ("marathi", \_ -> simpleLang "mr")
125− , ("dutch", \_ -> Lang "nl" "" "" [])
183+ , ("dutch", \_ -> simpleLang "nl")
126− , ("nynorsk", \_ -> Lang "nn" "" "" [])
184+ , ("nynorsk", \_ -> simpleLang "nn")
127− , ("norsk", \_ -> Lang "no" "" "" [])
185+ , ("norsk", \_ -> simpleLang "no")
128− , ("nko", \_ -> Lang "nqo" "" "" [])
186+ , ("nko", \_ -> simpleLang "nqo")
129− , ("occitan", \_ -> Lang "oc" "" "" [])
187+ , ("occitan", \_ -> simpleLang "oc")
130− , ("panjabi", \_ -> Lang "pa" "" "" [])
188+ , ("panjabi", \_ -> simpleLang "pa")
131− , ("polish", \_ -> Lang "pl" "" "" [])
189+ , ("polish", \_ -> simpleLang "pl")
132− , ("piedmontese", \_ -> Lang "pms" "" "" [])
190+ , ("piedmontese", \_ -> simpleLang "pms")
133− , ("portuguese", \_ -> Lang "pt" "" "" [])
191+ , ("portuguese", \_ -> simpleLang "pt")
134− , ("romansh", \_ -> Lang "rm" "" "" [])
192+ , ("romansh", \_ -> simpleLang "rm")
135− , ("romanian", \_ -> Lang "ro" "" "" [])
193+ , ("romanian", \_ -> simpleLang "ro")
136− , ("russian", \_ -> Lang "ru" "" "" [])
194+ , ("russian", \_ -> simpleLang "ru")
137− , ("sanskrit", \_ -> Lang "sa" "" "" [])
195+ , ("sanskrit", \_ -> simpleLang "sa")
138− , ("samin", \_ -> Lang "se" "" "" [])
196+ , ("samin", \_ -> simpleLang "se")
139− , ("slovak", \_ -> Lang "sk" "" "" [])
197+ , ("slovak", \_ -> simpleLang "sk")
140− , ("albanian", \_ -> Lang "sq" "" "" [])
198+ , ("albanian", \_ -> simpleLang "sq")
141− , ("serbian", \_ -> Lang "sr" "" "" [])
199+ , ("serbian", \_ -> simpleLang "sr")
142− , ("swedish", \_ -> Lang "sv" "" "" [])
200+ , ("swedish", \_ -> simpleLang "sv")
143− , ("syriac", \_ -> Lang "syr" "" "" [])
201+ , ("syriac", \_ -> simpleLang "syr")
144− , ("tamil", \_ -> Lang "ta" "" "" [])
202+ , ("tamil", \_ -> simpleLang "ta")
145− , ("telugu", \_ -> Lang "te" "" "" [])
203+ , ("telugu", \_ -> simpleLang "te")
146− , ("thai", \_ -> Lang "th" "" "" [])
204+ , ("thai", \_ -> simpleLang "th")
147− , ("turkmen", \_ -> Lang "tk" "" "" [])
205+ , ("turkmen", \_ -> simpleLang "tk")
148− , ("turkish", \_ -> Lang "tr" "" "" [])
206+ , ("turkish", \_ -> simpleLang "tr")
149− , ("ukrainian", \_ -> Lang "uk" "" "" [])
207+ , ("ukrainian", \_ -> simpleLang "uk")
150− , ("urdu", \_ -> Lang "ur" "" "" [])
208+ , ("urdu", \_ -> simpleLang "ur")
151− , ("vietnamese", \_ -> Lang "vi" "" "" [])
209+ , ("vietnamese", \_ -> simpleLang "vi")
152 ]
210 ]
153
211
212+simpleLang :: Text -> Lang
213+simpleLang l = Lang l Nothing Nothing [] [] []
214+
154babelLangToBCP47 :: T.Text -> Maybe Lang
215babelLangToBCP47 :: T.Text -> Maybe Lang
155babelLangToBCP47 s =
216babelLangToBCP47 s =
156 case s of
217 case s of
157− "austrian" -> Just $ Lang "de" "" "AT" ["1901"]
218+ "austrian" -> Just $ Lang "de" Nothing (Just "AT") ["1901"] [] []
158− "naustrian" -> Just $ Lang "de" "" "AT" []
219+ "naustrian" -> Just $ Lang "de" Nothing (Just "AT") [] [] []
159− "swissgerman" -> Just $ Lang "de" "" "CH" ["1901"]
220+ "swissgerman" -> Just $ Lang "de" Nothing (Just "CH") ["1901"] [] []
160− "nswissgerman" -> Just $ Lang "de" "" "CH" []
221+ "nswissgerman" -> Just $ Lang "de" Nothing (Just "CH") [] [] []
161− "german" -> Just $ Lang "de" "" "DE" ["1901"]
222+ "german" -> Just $ Lang "de" Nothing (Just "DE") ["1901"] [] []
162− "ngerman" -> Just $ Lang "de" "" "DE" []
223+ "ngerman" -> Just $ Lang "de" Nothing (Just "DE") [] [] []
163− "lowersorbian" -> Just $ Lang "dsb" "" "" []
224+ "lowersorbian" -> Just $ Lang "dsb" Nothing Nothing [] [] []
164− "uppersorbian" -> Just $ Lang "hsb" "" "" []
225+ "uppersorbian" -> Just $ Lang "hsb" Nothing Nothing [] [] []
165− "polutonikogreek" -> Just $ Lang "el" "" "" ["polyton"]
226+ "polutonikogreek" -> Just $ Lang "el" Nothing Nothing ["polyton"] [] []
166− "slovene" -> Just $ Lang "sl" "" "" []
227+ "slovene" -> Just $ simpleLang "sl"
167− "australian" -> Just $ Lang "en" "" "AU" []
228+ "australian" -> Just $ Lang "en" Nothing (Just "AU") [] [] []
168− "canadian" -> Just $ Lang "en" "" "CA" []
229+ "canadian" -> Just $ Lang "en" Nothing (Just "CA") [] [] []
169− "british" -> Just $ Lang "en" "" "GB" []
230+ "british" -> Just $ Lang "en" Nothing (Just "GB") [] [] []
170− "newzealand" -> Just $ Lang "en" "" "NZ" []
231+ "newzealand" -> Just $ Lang "en" Nothing (Just "NZ") [] [] []
171− "american" -> Just $ Lang "en" "" "US" []
232+ "american" -> Just $ Lang "en" Nothing (Just "US") [] [] []
172− "classiclatin" -> Just $ Lang "la" "" "" ["x-classic"]
233+ "classiclatin" -> Just $ Lang "la" Nothing Nothing ["x-classic"] [] []
173 _ -> ($ "") <$> M.lookup s polyglossiaLangToBCP47
234 _ -> ($ "") <$> M.lookup s polyglossiaLangToBCP47
src/Text/Pandoc/Readers/LaTeX/Macro.hs+45−14
@@ -14,6 +14,7 @@ import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline,
14import Control.Applicative ((<|>), optional)
14import Control.Applicative ((<|>), optional)
15import qualified Data.Map as M
15import qualified Data.Map as M
16import Data.Text (Text)
16import Data.Text (Text)
17+import qualified Data.Text as T
17
18
18macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a
19macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a
19macroDef constructor = do
20macroDef constructor = do
@@ -22,9 +23,11 @@ macroDef constructor = do
22 guardDisabled Ext_latex_macros)
23 guardDisabled Ext_latex_macros)
23 <|> return mempty
24 <|> return mempty
24 where commandDef = do
25 where commandDef = do
25− (name, macro') <- newcommand <|> letmacro <|> defmacro
26+ nameMacroPairs <- newcommand <|> letmacro <|> defmacro <|> newif
26 guardDisabled Ext_latex_macros <|>
27 guardDisabled Ext_latex_macros <|>
28+ mapM_ (\(name, macro') ->
27− updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) })
29+ updateState (\s -> s{ sMacros = M.insert name macro'
30+ (sMacros s) })) nameMacroPairs
28 environmentDef = do
31 environmentDef = do
29 mbenv <- newenvironment
32 mbenv <- newenvironment
30 case mbenv of
33 case mbenv of
@@ -40,7 +43,7 @@ macroDef constructor = do
40 -- @\newcommand{\envname}[n-args][default]{begin}@
43 -- @\newcommand{\envname}[n-args][default]{begin}@
41 -- @\newcommand{\endenvname}@
44 -- @\newcommand{\endenvname}@
42
45
43−letmacro :: PandocMonad m => LP m (Text, Macro)
46+letmacro :: PandocMonad m => LP m [(Text, Macro)]
44letmacro = do
47letmacro = do
45 controlSeq "let"
48 controlSeq "let"
46 (name, contents) <- withVerbatimMode $ do
49 (name, contents) <- withVerbatimMode $ do
@@ -53,18 +56,47 @@ letmacro = do
53 contents <- bracedOrToken
56 contents <- bracedOrToken
54 return (name, contents)
57 return (name, contents)
55 contents' <- doMacros' 0 contents
58 contents' <- doMacros' 0 contents
56− return (name, Macro ExpandWhenDefined [] Nothing contents')
59+ return [(name, Macro ExpandWhenDefined [] Nothing contents')]
57
60
58−defmacro :: PandocMonad m => LP m (Text, Macro)
61+defmacro :: PandocMonad m => LP m [(Text, Macro)]
59−defmacro = try $
62+defmacro = do
60 -- we use withVerbatimMode, because macros are to be expanded
63 -- we use withVerbatimMode, because macros are to be expanded
61 -- at point of use, not point of definition
64 -- at point of use, not point of definition
65+ controlSeq "def"
62 withVerbatimMode $ do
66 withVerbatimMode $ do
63− controlSeq "def"
64 Tok _ (CtrlSeq name) _ <- anyControlSeq
67 Tok _ (CtrlSeq name) _ <- anyControlSeq
65 argspecs <- many (argspecArg <|> argspecPattern)
68 argspecs <- many (argspecArg <|> argspecPattern)
66 contents <- bracedOrToken
69 contents <- bracedOrToken
67− return (name, Macro ExpandWhenUsed argspecs Nothing contents)
70+ return [(name, Macro ExpandWhenUsed argspecs Nothing contents)]
71+
72+-- \newif\iffoo' defines:
73+-- \iffoo to be \iffalse
74+-- \footrue to be a command that defines \iffoo to be \iftrue
75+-- \foofalse to be a command that defines \iffoo to be \iffalse
76+newif :: PandocMonad m => LP m [(Text, Macro)]
77+newif = do
78+ controlSeq "newif"
79+ withVerbatimMode $ do
80+ Tok pos (CtrlSeq name) _ <- anyControlSeq
81+ -- \def\iffoo\iffalse
82+ -- \def\footrue{\def\iffoo\iftrue}
83+ -- \def\foofalse{\def\iffoo\iffalse}
84+ let base = T.drop 2 name
85+ return [ (name, Macro ExpandWhenUsed [] Nothing
86+ [Tok pos (CtrlSeq "iffalse") "\\iffalse"])
87+ , (base <> "true",
88+ Macro ExpandWhenUsed [] Nothing
89+ [ Tok pos (CtrlSeq "def") "\\def"
90+ , Tok pos (CtrlSeq name) ("\\" <> name)
91+ , Tok pos (CtrlSeq "iftrue") "\\iftrue"
92+ ])
93+ , (base <> "false",
94+ Macro ExpandWhenUsed [] Nothing
95+ [ Tok pos (CtrlSeq "def") "\\def"
96+ , Tok pos (CtrlSeq name) ("\\" <> name)
97+ , Tok pos (CtrlSeq "iffalse") "\\iffalse"
98+ ])
99+ ]
68
100
69argspecArg :: PandocMonad m => LP m ArgSpec
101argspecArg :: PandocMonad m => LP m ArgSpec
70argspecArg = do
102argspecArg = do
@@ -77,10 +109,9 @@ argspecPattern =
77 (toktype' == Symbol || toktype' == Word) &&
109 (toktype' == Symbol || toktype' == Word) &&
78 (txt /= "{" && txt /= "\\" && txt /= "}")))
110 (txt /= "{" && txt /= "\\" && txt /= "}")))
79
111
80−newcommand :: PandocMonad m => LP m (Text, Macro)
112+newcommand :: PandocMonad m => LP m [(Text, Macro)]
81newcommand = do
113newcommand = do
82− pos <- getPosition
83− Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
114+ Tok pos (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
84 controlSeq "renewcommand" <|>
115 controlSeq "renewcommand" <|>
85 controlSeq "providecommand" <|>
116 controlSeq "providecommand" <|>
86 controlSeq "DeclareMathOperator" <|>
117 controlSeq "DeclareMathOperator" <|>
@@ -112,9 +143,9 @@ newcommand = do
112 Just macro
143 Just macro
113 | mtype == "newcommand" -> do
144 | mtype == "newcommand" -> do
114 report $ MacroAlreadyDefined txt pos
145 report $ MacroAlreadyDefined txt pos
115− return (name, macro)
146+ return [(name, macro)]
116− | mtype == "providecommand" -> return (name, macro)
147+ | mtype == "providecommand" -> return [(name, macro)]
117− _ -> return (name, Macro ExpandWhenUsed argspecs optarg contents)
148+ _ -> return [(name, Macro ExpandWhenUsed argspecs optarg contents)]
118
149
119newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro))
150newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro))
120newenvironment = do
151newenvironment = do
src/Text/Pandoc/Readers/LaTeX/Parsing.hs+48−7
@@ -27,6 +27,7 @@ module Text.Pandoc.Readers.LaTeX.Parsing
27 , rawLaTeXParser
27 , rawLaTeXParser
28 , applyMacros
28 , applyMacros
29 , tokenize
29 , tokenize
30+ , tokenizeSources
30 , untokenize
31 , untokenize
31 , untoken
32 , untoken
32 , totoks
33 , totoks
@@ -112,7 +113,6 @@ import Text.Pandoc.Readers.LaTeX.Types (ExpansionPoint (..), Macro (..),
112 ArgSpec (..), Tok (..), TokType (..))
113 ArgSpec (..), Tok (..), TokType (..))
113import Text.Pandoc.Shared
114import Text.Pandoc.Shared
114import Text.Parsec.Pos
115import Text.Parsec.Pos
115−-- import Debug.Trace
116
116
117newtype DottedNum = DottedNum [Int]
117newtype DottedNum = DottedNum [Int]
118 deriving (Show, Eq)
118 deriving (Show, Eq)
@@ -248,7 +248,7 @@ withVerbatimMode parser = do
248
248
249rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s)
249rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s)
250 => [Tok] -> Bool -> LP m a -> LP m a
250 => [Tok] -> Bool -> LP m a -> LP m a
251− -> ParserT Text s m (a, Text)
251+ -> ParserT Sources s m (a, Text)
252rawLaTeXParser toks retokenize parser valParser = do
252rawLaTeXParser toks retokenize parser valParser = do
253 pstate <- getState
253 pstate <- getState
254 let lstate = def{ sOptions = extractReaderOptions pstate }
254 let lstate = def{ sOptions = extractReaderOptions pstate }
@@ -268,7 +268,7 @@ rawLaTeXParser toks retokenize parser valParser = do
268 Left _ -> mzero
268 Left _ -> mzero
269 Right ((val, raw), st) -> do
269 Right ((val, raw), st) -> do
270 updateState (updateMacros (sMacros st <>))
270 updateState (updateMacros (sMacros st <>))
271− _ <- takeP (T.length (untokenize toks'))
271+ void $ count (T.length (untokenize toks')) anyChar
272 let result = untokenize raw
272 let result = untokenize raw
273 -- ensure we end with space if input did, see #4442
273 -- ensure we end with space if input did, see #4442
274 let result' =
274 let result' =
@@ -281,7 +281,7 @@ rawLaTeXParser toks retokenize parser valParser = do
281 return (val, result')
281 return (val, result')
282
282
283applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s)
283applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s)
284− => Text -> ParserT Text s m Text
284+ => Text -> ParserT Sources s m Text
285applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|>
285applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|>
286 do let retokenize = untokenize <$> many (satisfyTok (const True))
286 do let retokenize = untokenize <$> many (satisfyTok (const True))
287 pstate <- getState
287 pstate <- getState
@@ -301,6 +301,11 @@ QuickCheck property:
301> let t = T.pack s in untokenize (tokenize "random" t) == t
301> let t = T.pack s in untokenize (tokenize "random" t) == t
302-}
302-}
303
303
304+tokenizeSources :: Sources -> [Tok]
305+tokenizeSources = concatMap tokenizeSource . unSources
306+ where
307+ tokenizeSource (pos, t) = totoks pos t
308+
304tokenize :: SourceName -> Text -> [Tok]
309tokenize :: SourceName -> Text -> [Tok]
305tokenize sourcename = totoks (initialPos sourcename)
310tokenize sourcename = totoks (initialPos sourcename)
306
311
@@ -458,7 +463,7 @@ satisfyTok f = do
458doMacros :: PandocMonad m => LP m ()
463doMacros :: PandocMonad m => LP m ()
459doMacros = do
464doMacros = do
460 st <- getState
465 st <- getState
461− unless (sVerbatimMode st || M.null (sMacros st)) $ do
466+ unless (sVerbatimMode st) $
462 getInput >>= doMacros' 1 >>= setInput
467 getInput >>= doMacros' 1 >>= setInput
463
468
464doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok]
469doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok]
@@ -520,7 +525,7 @@ doMacros' n inp =
520 $ throwError $ PandocMacroLoop name
525 $ throwError $ PandocMacroLoop name
521 macros <- sMacros <$> getState
526 macros <- sMacros <$> getState
522 case M.lookup name macros of
527 case M.lookup name macros of
523− Nothing -> mzero
528+ Nothing -> trySpecialMacro name ts
524 Just (Macro expansionPoint argspecs optarg newtoks) -> do
529 Just (Macro expansionPoint argspecs optarg newtoks) -> do
525 let getargs' = do
530 let getargs' = do
526 args <-
531 args <-
@@ -548,6 +553,41 @@ doMacros' n inp =
548 ExpandWhenUsed -> doMacros' (n' + 1) result
553 ExpandWhenUsed -> doMacros' (n' + 1) result
549 ExpandWhenDefined -> return result
554 ExpandWhenDefined -> return result
550
555
556+-- | Certain macros do low-level tex manipulations that can't
557+-- be represented in our Macro type, so we handle them here.
558+trySpecialMacro :: PandocMonad m => Text -> [Tok] -> LP m [Tok]
559+trySpecialMacro "xspace" ts = do
560+ ts' <- doMacros' 1 ts
561+ case ts' of
562+ Tok pos Word t : _
563+ | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts'
564+ _ -> return ts'
565+trySpecialMacro "iftrue" ts = handleIf True ts
566+trySpecialMacro "iffalse" ts = handleIf False ts
567+trySpecialMacro _ _ = mzero
568+
569+handleIf :: PandocMonad m => Bool -> [Tok] -> LP m [Tok]
570+handleIf b ts = do
571+ res' <- lift $ runParserT (ifParser b) defaultLaTeXState "tokens" ts
572+ case res' of
573+ Left _ -> Prelude.fail "Could not parse conditional"
574+ Right ts' -> return ts'
575+
576+ifParser :: PandocMonad m => Bool -> LP m [Tok]
577+ifParser b = do
578+ ifToks <- many (notFollowedBy (controlSeq "else" <|> controlSeq "fi")
579+ *> anyTok)
580+ elseToks <- (controlSeq "else" >> manyTill anyTok (controlSeq "fi"))
581+ <|> ([] <$ controlSeq "fi")
582+ rest <- getInput
583+ return $ (if b then ifToks else elseToks) ++ rest
584+
585+startsWithAlphaNum :: Text -> Bool
586+startsWithAlphaNum t =
587+ case T.uncons t of
588+ Just (c, _) | isAlphaNum c -> True
589+ _ -> False
590+
551setpos :: SourcePos -> Tok -> Tok
591setpos :: SourcePos -> Tok -> Tok
552setpos spos (Tok _ tt txt) = Tok spos tt txt
592setpos spos (Tok _ tt txt) = Tok spos tt txt
553
593
@@ -783,7 +823,8 @@ withRaw parser = do
783
823
784keyval :: PandocMonad m => LP m (Text, Text)
824keyval :: PandocMonad m => LP m (Text, Text)
785keyval = try $ do
825keyval = try $ do
826+ key <- untokenize <$> many1 (notFollowedBy (symbol '=') >>
786− Tok _ Word key <- satisfyTok isWordTok
827+ (symbol '-' <|> symbol '_' <|> satisfyTok isWordTok))
787 sp
828 sp
788 val <- option mempty $ do
829 val <- option mempty $ do
789 symbol '='
830 symbol '='
src/Text/Pandoc/Readers/LaTeX/SIunitx.hs+140−66
@@ -1,3 +1,4 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2module Text.Pandoc.Readers.LaTeX.SIunitx
3module Text.Pandoc.Readers.LaTeX.SIunitx
3 ( siunitxCommands )
4 ( siunitxCommands )
@@ -9,27 +10,32 @@ import Text.Pandoc.Class
9import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline,
10import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline,
10 optional, space, spaces, withRaw, (<|>))
11 optional, space, spaces, withRaw, (<|>))
11import Control.Applicative ((<|>))
12import Control.Applicative ((<|>))
13+import Control.Monad (void)
12import qualified Data.Map as M
14import qualified Data.Map as M
13import Data.Char (isDigit)
15import Data.Char (isDigit)
14import Data.Text (Text)
16import Data.Text (Text)
15import qualified Data.Text as T
17import qualified Data.Text as T
16import Data.List (intersperse)
18import Data.List (intersperse)
17−
19+import qualified Data.Sequence as Seq
20+import Text.Pandoc.Walk (walk)
18
21
19siunitxCommands :: PandocMonad m
22siunitxCommands :: PandocMonad m
20 => LP m Inlines -> M.Map Text (LP m Inlines)
23 => LP m Inlines -> M.Map Text (LP m Inlines)
21siunitxCommands tok = M.fromList
24siunitxCommands tok = M.fromList
22− [ ("si", skipopts *> dosi tok)
25+ [ ("si", dosi tok)
23 , ("SI", doSI tok)
26 , ("SI", doSI tok)
24 , ("SIrange", doSIrange True tok)
27 , ("SIrange", doSIrange True tok)
25 , ("numrange", doSIrange False tok)
28 , ("numrange", doSIrange False tok)
26 , ("numlist", doSInumlist)
29 , ("numlist", doSInumlist)
30+ , ("SIlist", doSIlist tok)
27 , ("num", doSInum)
31 , ("num", doSInum)
28 , ("ang", doSIang)
32 , ("ang", doSIang)
29 ]
33 ]
30
34
31dosi :: PandocMonad m => LP m Inlines -> LP m Inlines
35dosi :: PandocMonad m => LP m Inlines -> LP m Inlines
36+dosi tok = do
37+ options <- option [] keyvals
32−dosi tok = grouped (siUnit tok) <|> siUnit tok
38+ grouped (siUnit options tok) <|> siUnit options tok
33
39
34-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
40-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
35doSI :: PandocMonad m => LP m Inlines -> LP m Inlines
41doSI :: PandocMonad m => LP m Inlines -> LP m Inlines
@@ -64,23 +70,50 @@ doSInumlist = do
64 mconcat (intersperse (str "," <> space) (init xs)) <>
70 mconcat (intersperse (str "," <> space) (init xs)) <>
65 text ", & " <> last xs
71 text ", & " <> last xs
66
72
73+doSIlist :: PandocMonad m => LP m Inlines -> LP m Inlines
74+doSIlist tok = do
75+ options <- option [] keyvals
76+ nums <- map tonum . T.splitOn ";" . untokenize <$> braced
77+ unit <- grouped (siUnit options tok) <|> siUnit options tok
78+ let xs = map (<> (str "\xa0" <> unit)) nums
79+ case xs of
80+ [] -> return mempty
81+ [x] -> return x
82+ _ -> return $
83+ mconcat (intersperse (str "," <> space) (init xs)) <>
84+ text ", & " <> last xs
85+
67parseNum :: Parser Text () Inlines
86parseNum :: Parser Text () Inlines
68parseNum = (mconcat <$> many parseNumPart) <* eof
87parseNum = (mconcat <$> many parseNumPart) <* eof
69
88
89+minus :: Text
90+minus = "\x2212"
91+
92+hyphenToMinus :: Inline -> Inline
93+hyphenToMinus (Str t) = Str (T.replace "-" minus t)
94+hyphenToMinus x = x
95+
70parseNumPart :: Parser Text () Inlines
96parseNumPart :: Parser Text () Inlines
71parseNumPart =
97parseNumPart =
72 parseDecimalNum <|>
98 parseDecimalNum <|>
73 parseComma <|>
99 parseComma <|>
74 parsePlusMinus <|>
100 parsePlusMinus <|>
101+ parsePM <|>
75 parseI <|>
102 parseI <|>
76 parseExp <|>
103 parseExp <|>
77 parseX <|>
104 parseX <|>
78 parseSpace
105 parseSpace
79 where
106 where
80− parseDecimalNum = do
107+ parseDecimalNum, parsePlusMinus, parsePM,
108+ parseComma, parseI, parseX,
109+ parseExp, parseSpace :: Parser Text () Inlines
110+ parseDecimalNum = try $ do
81− pref <- option mempty $ (mempty <$ char '+') <|> ("\x2212" <$ char '-')
111+ pref <- option mempty $ (mempty <$ char '+') <|> (minus <$ char '-')
82− basenum <- (pref <>) . T.pack
83− <$> many1 (satisfy (\c -> isDigit c || c == '.'))
112+ basenum' <- many1 (satisfy (\c -> isDigit c || c == '.'))
113+ let basenum = pref <> T.pack
114+ (case basenum' of
115+ '.':_ -> '0':basenum'
116+ _ -> basenum')
84 uncertainty <- option mempty $ T.pack <$> parseParens
117 uncertainty <- option mempty $ T.pack <$> parseParens
85 if T.null uncertainty
118 if T.null uncertainty
86 then return $ str basenum
119 then return $ str basenum
@@ -98,6 +131,7 @@ parseNumPart =
98 | otherwise -> "." <> t
131 | otherwise -> "." <> t
99 parseComma = str "." <$ char ','
132 parseComma = str "." <$ char ','
100 parsePlusMinus = str "\xa0\xb1\xa0" <$ try (string "+-")
133 parsePlusMinus = str "\xa0\xb1\xa0" <$ try (string "+-")
134+ parsePM = str "\xa0\xb1\xa0" <$ try (string "\\pm")
101 parseParens =
135 parseParens =
102 char '(' *> many1 (satisfy (\c -> isDigit c || c == '.')) <* char ')'
136 char '(' *> many1 (satisfy (\c -> isDigit c || c == '.')) <* char ')'
103 parseI = str "i" <$ char 'i'
137 parseI = str "i" <$ char 'i'
@@ -110,11 +144,14 @@ doSIang :: PandocMonad m => LP m Inlines
110doSIang = do
144doSIang = do
111 skipopts
145 skipopts
112 ps <- T.splitOn ";" . untokenize <$> braced
146 ps <- T.splitOn ";" . untokenize <$> braced
147+ let dropPlus t = case T.uncons t of
148+ Just ('+',t') -> t'
149+ _ -> t
113 case ps ++ repeat "" of
150 case ps ++ repeat "" of
114 (d:m:s:_) -> return $
151 (d:m:s:_) -> return $
115− (if T.null d then mempty else str d <> str "\xb0") <>
152+ (if T.null d then mempty else str (dropPlus d) <> str "\xb0") <>
116− (if T.null m then mempty else str m <> str "\x2032") <>
153+ (if T.null m then mempty else str (dropPlus m) <> str "\x2032") <>
117− (if T.null s then mempty else str s <> str "\x2033")
154+ (if T.null s then mempty else str (dropPlus s) <> str "\x2033")
118 _ -> return mempty
155 _ -> return mempty
119
156
120-- converts e.g. \SIrange{100}{200}{\ms} to "100 ms--200 ms"
157-- converts e.g. \SIrange{100}{200}{\ms} to "100 ms--200 ms"
@@ -143,40 +180,99 @@ doSIrange includeUnits tok = do
143emptyOr160 :: Inlines -> Inlines
180emptyOr160 :: Inlines -> Inlines
144emptyOr160 x = if x == mempty then x else str "\160"
181emptyOr160 x = if x == mempty then x else str "\160"
145
182
146−siUnit :: PandocMonad m => LP m Inlines -> LP m Inlines
183+siUnit :: forall m. PandocMonad m => [(Text,Text)] -> LP m Inlines -> LP m Inlines
147−siUnit tok = try (do
184+siUnit options tok = mconcat . intersperse (str "\xa0") <$> many1 siUnitPart
148− Tok _ (CtrlSeq name) _ <- anyControlSeq
185+ where
149− case name of
186+ siUnitPart :: LP m Inlines
187+ siUnitPart = try $ do
188+ skipMany (void (symbol '.') <|> void (symbol '~') <|> spaces1)
189+ x <- ((siPrefix <*> siBase)
190+ <|> (do u <- siBase <|> tok
191+ option u $ siSuffix <*> pure u))
192+ option x (siInfix x)
193+ siInfix :: Inlines -> LP m Inlines
194+ siInfix u1 = try $
195+ (do _ <- controlSeq "per"
196+ u2 <- siUnitPart
197+ let useSlash = lookup "per-mode" options == Just "symbol"
198+ if useSlash
199+ then return (u1 <> str "/" <> u2)
200+ else return (u1 <> str "\xa0" <> negateExponent u2))
201+ <|> (do _ <- symbol '/'
202+ u2 <- siUnitPart
203+ return (u1 <> str "/" <> u2))
204+ siPrefix :: LP m (Inlines -> Inlines)
205+ siPrefix =
150− "square" -> do
206+ (do _ <- controlSeq "square"
151− unit <- siUnit tok
207+ skipopts
152− return $ unit <> superscript "2"
208+ return (<> superscript "2"))
153− "cubic" -> do
209+ <|> (do _ <- controlSeq "cubic"
154− unit <- siUnit tok
210+ skipopts
155− return $ unit <> superscript "3"
211+ return (<> superscript "3"))
156− "raisetothe" -> do
212+ <|> (do _ <- controlSeq "raisetothe"
157− n <- tok
213+ skipopts
158− unit <- siUnit tok
214+ n <- walk hyphenToMinus <$> tok
159− return $ unit <> superscript n
215+ return (<> superscript n))
160− _ ->
216+ siSuffix :: LP m (Inlines -> Inlines)
161− case M.lookup name siUnitMap of
217+ siSuffix =
162− Just il ->
163− option il $
164− choice
165− [ (il <> superscript "2") <$ controlSeq "squared"
218+ (do _ <- controlSeq "squared"
219+ skipopts
220+ return (<> superscript "2"))
166− , (il <> superscript "3") <$ controlSeq "cubed"
221+ <|> (do _ <- controlSeq "cubed"
222+ skipopts
223+ return (<> superscript "3"))
167− , (\n -> il <> superscript n) <$> (controlSeq "tothe" *> tok)
224+ <|> (do _ <- controlSeq "tothe"
168− ]
225+ skipopts
226+ n <- walk hyphenToMinus <$> tok
227+ return (<> superscript n))
228+ <|> (symbol '^' *> (do n <- walk hyphenToMinus <$> tok
229+ return (<> superscript n)))
230+ <|> (symbol '_' *> (do n <- walk hyphenToMinus <$> tok
231+ return (<> subscript n)))
232+ negateExponent :: Inlines -> Inlines
233+ negateExponent ils =
234+ case Seq.viewr (unMany ils) of
235+ xs Seq.:> Superscript ss -> (Many xs) <>
236+ superscript (str minus <> fromList ss)
237+ _ -> ils <> superscript (str (minus <> "1"))
238+ siBase :: LP m Inlines
239+ siBase =
240+ ((try
241+ (do Tok _ (CtrlSeq name) _ <- anyControlSeq
242+ case M.lookup name siUnitModifierMap of
243+ Just il -> (il <>) <$> siBase
244+ Nothing ->
245+ case M.lookup name siUnitMap of
246+ Just il -> pure il
169− Nothing -> fail "not an siunit unit command")
247+ Nothing -> fail "not a unit command"))
170− <|> (lookAhead anyControlSeq >> tok)
171− <|> (do Tok _ Word t <- satisfyTok isWordTok
248+ <|> (do Tok _ Word t <- satisfyTok isWordTok
172− return $ str t)
249+ return $ str t)
173− <|> (symbol '^' *> (superscript <$> tok))
250+ )
174− <|> (symbol '_' *> (subscript <$> tok))
251+
175− <|> ("\xa0" <$ symbol '.')
252+siUnitModifierMap :: M.Map Text Inlines
176− <|> ("\xa0" <$ symbol '~')
253+siUnitModifierMap = M.fromList
177− <|> tok
254+ [ ("atto", str "a")
178− <|> (do Tok _ _ t <- anyTok
255+ , ("centi", str "c")
179− return (str t))
256+ , ("deca", str "d")
257+ , ("deci", str "d")
258+ , ("deka", str "d")
259+ , ("exa", str "E")
260+ , ("femto", str "f")
261+ , ("giga", str "G")
262+ , ("hecto", str "h")
263+ , ("kilo", str "k")
264+ , ("mega", str "M")
265+ , ("micro", str "μ")
266+ , ("milli", str "m")
267+ , ("nano", str "n")
268+ , ("peta", str "P")
269+ , ("pico", str "p")
270+ , ("tera", str "T")
271+ , ("yocto", str "y")
272+ , ("yotta", str "Y")
273+ , ("zepto", str "z")
274+ , ("zetta", str "Z")
275+ ]
180
276
181siUnitMap :: M.Map Text Inlines
277siUnitMap :: M.Map Text Inlines
182siUnitMap = M.fromList
278siUnitMap = M.fromList
@@ -276,7 +372,6 @@ siUnitMap = M.fromList
276 , ("arcsecond", str "″")
372 , ("arcsecond", str "″")
277 , ("astronomicalunit", str "ua")
373 , ("astronomicalunit", str "ua")
278 , ("atomicmassunit", str "u")
374 , ("atomicmassunit", str "u")
279− , ("atto", str "a")
280 , ("bar", str "bar")
375 , ("bar", str "bar")
281 , ("barn", str "b")
376 , ("barn", str "b")
282 , ("becquerel", str "Bq")
377 , ("becquerel", str "Bq")
@@ -284,51 +379,38 @@ siUnitMap = M.fromList
284 , ("bohr", emph (str "a") <> subscript (str "0"))
379 , ("bohr", emph (str "a") <> subscript (str "0"))
285 , ("candela", str "cd")
380 , ("candela", str "cd")
286 , ("celsius", str "°C")
381 , ("celsius", str "°C")
287− , ("centi", str "c")
288 , ("clight", emph (str "c") <> subscript (str "0"))
382 , ("clight", emph (str "c") <> subscript (str "0"))
289 , ("coulomb", str "C")
383 , ("coulomb", str "C")
290 , ("dalton", str "Da")
384 , ("dalton", str "Da")
291 , ("day", str "d")
385 , ("day", str "d")
292− , ("deca", str "d")
293− , ("deci", str "d")
294 , ("decibel", str "db")
386 , ("decibel", str "db")
295 , ("degreeCelsius",str "°C")
387 , ("degreeCelsius",str "°C")
296 , ("degree", str "°")
388 , ("degree", str "°")
297− , ("deka", str "d")
298 , ("electronmass", emph (str "m") <> subscript (str "e"))
389 , ("electronmass", emph (str "m") <> subscript (str "e"))
299 , ("electronvolt", str "eV")
390 , ("electronvolt", str "eV")
300 , ("elementarycharge", emph (str "e"))
391 , ("elementarycharge", emph (str "e"))
301− , ("exa", str "E")
302 , ("farad", str "F")
392 , ("farad", str "F")
303− , ("femto", str "f")
304− , ("giga", str "G")
305 , ("gram", str "g")
393 , ("gram", str "g")
306 , ("gray", str "Gy")
394 , ("gray", str "Gy")
307 , ("hartree", emph (str "E") <> subscript (str "h"))
395 , ("hartree", emph (str "E") <> subscript (str "h"))
308 , ("hectare", str "ha")
396 , ("hectare", str "ha")
309− , ("hecto", str "h")
310 , ("henry", str "H")
397 , ("henry", str "H")
311 , ("hertz", str "Hz")
398 , ("hertz", str "Hz")
312 , ("hour", str "h")
399 , ("hour", str "h")
313 , ("joule", str "J")
400 , ("joule", str "J")
314 , ("katal", str "kat")
401 , ("katal", str "kat")
315 , ("kelvin", str "K")
402 , ("kelvin", str "K")
316− , ("kilo", str "k")
317 , ("kilogram", str "kg")
403 , ("kilogram", str "kg")
318 , ("knot", str "kn")
404 , ("knot", str "kn")
319 , ("liter", str "L")
405 , ("liter", str "L")
320 , ("litre", str "l")
406 , ("litre", str "l")
321 , ("lumen", str "lm")
407 , ("lumen", str "lm")
322 , ("lux", str "lx")
408 , ("lux", str "lx")
323− , ("mega", str "M")
324 , ("meter", str "m")
409 , ("meter", str "m")
325 , ("metre", str "m")
410 , ("metre", str "m")
326− , ("micro", str "μ")
327− , ("milli", str "m")
328 , ("minute", str "min")
411 , ("minute", str "min")
329 , ("mmHg", str "mmHg")
412 , ("mmHg", str "mmHg")
330 , ("mole", str "mol")
413 , ("mole", str "mol")
331− , ("nano", str "n")
332 , ("nauticalmile", str "M")
414 , ("nauticalmile", str "M")
333 , ("neper", str "Np")
415 , ("neper", str "Np")
334 , ("newton", str "N")
416 , ("newton", str "N")
@@ -336,25 +418,17 @@ siUnitMap = M.fromList
336 , ("Pa", str "Pa")
418 , ("Pa", str "Pa")
337 , ("pascal", str "Pa")
419 , ("pascal", str "Pa")
338 , ("percent", str "%")
420 , ("percent", str "%")
339− , ("per", str "/")
340− , ("peta", str "P")
341− , ("pico", str "p")
342 , ("planckbar", emph (str "\x210f"))
421 , ("planckbar", emph (str "\x210f"))
343 , ("radian", str "rad")
422 , ("radian", str "rad")
344 , ("second", str "s")
423 , ("second", str "s")
345 , ("siemens", str "S")
424 , ("siemens", str "S")
346 , ("sievert", str "Sv")
425 , ("sievert", str "Sv")
347 , ("steradian", str "sr")
426 , ("steradian", str "sr")
348− , ("tera", str "T")
349 , ("tesla", str "T")
427 , ("tesla", str "T")
350 , ("tonne", str "t")
428 , ("tonne", str "t")
351 , ("volt", str "V")
429 , ("volt", str "V")
352 , ("watt", str "W")
430 , ("watt", str "W")
353 , ("weber", str "Wb")
431 , ("weber", str "Wb")
354− , ("yocto", str "y")
355− , ("yotta", str "Y")
356− , ("zepto", str "z")
357− , ("zetta", str "Z")
358 ]
432 ]
359
433
360
434
src/Text/Pandoc/Readers/LaTeX/Table.hs+6−1
@@ -134,6 +134,11 @@ parseTableRow :: PandocMonad m
134 -> LP m Row
134 -> LP m Row
135parseTableRow blocks inline envname prefsufs = do
135parseTableRow blocks inline envname prefsufs = do
136 notFollowedBy (spaces *> end_ envname)
136 notFollowedBy (spaces *> end_ envname)
137+ -- contexts that can contain & that is not colsep:
138+ let canContainAmp (Tok _ (CtrlSeq "begin") _) = True
139+ canContainAmp (Tok _ (CtrlSeq "verb") _) = True
140+ canContainAmp (Tok _ (CtrlSeq "Verb") _) = True
141+ canContainAmp _ = False
137 -- add prefixes and suffixes in token stream:
142 -- add prefixes and suffixes in token stream:
138 let celltoks (pref, suff) = do
143 let celltoks (pref, suff) = do
139 prefpos <- getPosition
144 prefpos <- getPosition
@@ -142,7 +147,7 @@ parseTableRow blocks inline envname prefsufs = do
142 ((lookAhead (controlSeq "parbox") >>
147 ((lookAhead (controlSeq "parbox") >>
143 void blocks) -- #5711
148 void blocks) -- #5711
144 <|>
149 <|>
145− (lookAhead (controlSeq "begin") >> void inline)
150+ (lookAhead (satisfyTok canContainAmp) >> void inline)
146 <|>
151 <|>
147 (lookAhead (symbol '$') >> void inline))
152 (lookAhead (symbol '$') >> void inline))
148 <|>
153 <|>
src/Text/Pandoc/Readers/LaTeX/Types.hs+14−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE FlexibleInstances #-}
1{- |
2{- |
2 Module : Text.Pandoc.Readers.LaTeX.Types
3 Module : Text.Pandoc.Readers.LaTeX.Types
3 Copyright : Copyright (C) 2017-2021 John MacFarlane
4 Copyright : Copyright (C) 2017-2021 John MacFarlane
@@ -18,7 +19,9 @@ module Text.Pandoc.Readers.LaTeX.Types ( Tok(..)
18 )
19 )
19where
20where
20import Data.Text (Text)
21import Data.Text (Text)
21−import Text.Parsec.Pos (SourcePos)
22+import Text.Parsec.Pos (SourcePos, sourceName)
23+import Text.Pandoc.Sources
24+import Data.List (groupBy)
22
25
23data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
26data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
24 Esc1 | Esc2 | Arg Int
27 Esc1 | Esc2 | Arg Int
@@ -27,6 +30,16 @@ data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
27data Tok = Tok SourcePos TokType Text
30data Tok = Tok SourcePos TokType Text
28 deriving (Eq, Ord, Show)
31 deriving (Eq, Ord, Show)
29
32
33+instance ToSources [Tok] where
34+ toSources = Sources
35+ . map (\ts -> case ts of
36+ Tok p _ _ : _ -> (p, mconcat $ map tokToText ts)
37+ _ -> error "toSources [Tok] encountered empty group")
38+ . groupBy (\(Tok p1 _ _) (Tok p2 _ _) -> sourceName p1 == sourceName p2)
39+
40+tokToText :: Tok -> Text
41+tokToText (Tok _ _ t) = t
42+
30data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed
43data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed
31 deriving (Eq, Ord, Show)
44 deriving (Eq, Ord, Show)
32
45
src/Text/Pandoc/Readers/Man.hs+18−11
@@ -20,7 +20,7 @@ import Control.Monad (liftM, mzero, guard, void)
20import Control.Monad.Trans (lift)
20import Control.Monad.Trans (lift)
21import Control.Monad.Except (throwError)
21import Control.Monad.Except (throwError)
22import Data.Maybe (catMaybes, isJust)
22import Data.Maybe (catMaybes, isJust)
23−import Data.List (intersperse, intercalate)
23+import Data.List (intersperse)
24import qualified Data.Text as T
24import qualified Data.Text as T
25import Text.Pandoc.Builder as B
25import Text.Pandoc.Builder as B
26import Text.Pandoc.Class.PandocMonad (PandocMonad(..), report)
26import Text.Pandoc.Class.PandocMonad (PandocMonad(..), report)
@@ -29,9 +29,8 @@ import Text.Pandoc.Logging (LogMessage(..))
29import Text.Pandoc.Options
29import Text.Pandoc.Options
30import Text.Pandoc.Parsing
30import Text.Pandoc.Parsing
31import Text.Pandoc.Walk (query)
31import Text.Pandoc.Walk (query)
32−import Text.Pandoc.Shared (crFilter, mapLeft)
32+import Text.Pandoc.Shared (mapLeft)
33import Text.Pandoc.Readers.Roff -- TODO explicit imports
33import Text.Pandoc.Readers.Roff -- TODO explicit imports
34−import Text.Parsec hiding (tokenPrim)
35import qualified Text.Parsec as Parsec
34import qualified Text.Parsec as Parsec
36import Text.Parsec.Pos (updatePosString)
35import Text.Parsec.Pos (updatePosString)
37import qualified Data.Foldable as Foldable
36import qualified Data.Foldable as Foldable
@@ -50,13 +49,20 @@ type ManParser m = ParserT [RoffToken] ManState m
50
49
51
50
52-- | Read man (troff) from an input string and return a Pandoc document.
51-- | Read man (troff) from an input string and return a Pandoc document.
53−readMan :: PandocMonad m => ReaderOptions -> T.Text -> m Pandoc
52+readMan :: (PandocMonad m, ToSources a)
53+ => ReaderOptions
54+ -> a
55+ -> m Pandoc
54−readMan opts txt = do
56+readMan opts s = do
55− tokenz <- lexRoff (initialPos "input") (crFilter txt)
57+ let Sources inps = toSources s
58+ tokenz <- mconcat <$> mapM (uncurry lexRoff) inps
56 let state = def {readerOptions = opts} :: ManState
59 let state = def {readerOptions = opts} :: ManState
60+ let fixError (PandocParsecError _ e) = PandocParsecError (Sources inps) e
61+ fixError e = e
57 eitherdoc <- readWithMTokens parseMan state
62 eitherdoc <- readWithMTokens parseMan state
58 (Foldable.toList . unRoffTokens $ tokenz)
63 (Foldable.toList . unRoffTokens $ tokenz)
59− either throwError return eitherdoc
64+ either (throwError . fixError) return eitherdoc
65+
60
66
61readWithMTokens :: PandocMonad m
67readWithMTokens :: PandocMonad m
62 => ParserT [RoffToken] ManState m a -- ^ parser
68 => ParserT [RoffToken] ManState m a -- ^ parser
@@ -64,9 +70,10 @@ readWithMTokens :: PandocMonad m
64 -> [RoffToken] -- ^ input
70 -> [RoffToken] -- ^ input
65 -> m (Either PandocError a)
71 -> m (Either PandocError a)
66readWithMTokens parser state input =
72readWithMTokens parser state input =
67− let leftF = PandocParsecError . T.pack . intercalate "\n" $ show <$> input
73+ let leftF = PandocParsecError mempty
68 in mapLeft leftF `liftM` runParserT parser state "source" input
74 in mapLeft leftF `liftM` runParserT parser state "source" input
69
75
76+
70parseMan :: PandocMonad m => ManParser m Pandoc
77parseMan :: PandocMonad m => ManParser m Pandoc
71parseMan = do
78parseMan = do
72 bs <- many parseBlock <* eof
79 bs <- many parseBlock <* eof
@@ -89,7 +96,7 @@ parseBlock = choice [ parseList
89
96
90parseTable :: PandocMonad m => ManParser m Blocks
97parseTable :: PandocMonad m => ManParser m Blocks
91parseTable = do
98parseTable = do
92− modifyState $ \st -> st { tableCellsPlain = True }
99+ updateState $ \st -> st { tableCellsPlain = True }
93 let isTbl Tbl{} = True
100 let isTbl Tbl{} = True
94 isTbl _ = False
101 isTbl _ = False
95 Tbl _opts rows pos <- msatisfy isTbl
102 Tbl _opts rows pos <- msatisfy isTbl
@@ -135,7 +142,7 @@ parseTable = do
135 case res' of
142 case res' of
136 Left _ -> Prelude.fail "Could not parse table cell"
143 Left _ -> Prelude.fail "Could not parse table cell"
137 Right x -> do
144 Right x -> do
138− modifyState $ \s -> s{ tableCellsPlain = False }
145+ updateState $ \s -> s{ tableCellsPlain = False }
139 return x
146 return x
140 Right x -> return x
147 Right x -> return x
141
148
@@ -222,7 +229,7 @@ parseTitle = do
222 setMeta "section" (linePartsToInlines y)
229 setMeta "section" (linePartsToInlines y)
223 [x] -> setMeta "title" (linePartsToInlines x)
230 [x] -> setMeta "title" (linePartsToInlines x)
224 [] -> id
231 [] -> id
225− modifyState $ \st -> st{ metadata = adjustMeta $ metadata st }
232+ updateState $ \st -> st{ metadata = adjustMeta $ metadata st }
226 return mempty
233 return mempty
227
234
228linePartsToInlines :: [LinePart] -> Inlines
235linePartsToInlines :: [LinePart] -> Inlines
src/Text/Pandoc/Readers/Markdown.hs+93−73
@@ -21,15 +21,17 @@ module Text.Pandoc.Readers.Markdown (
21import Control.Monad
21import Control.Monad
22import Control.Monad.Except (throwError)
22import Control.Monad.Except (throwError)
23import Data.Char (isAlphaNum, isPunctuation, isSpace)
23import Data.Char (isAlphaNum, isPunctuation, isSpace)
24−import Data.List (transpose, elemIndex, sortOn)
24+import Data.List (transpose, elemIndex, sortOn, foldl')
25+import Data.List.NonEmpty (NonEmpty(..))
25import qualified Data.Map as M
26import qualified Data.Map as M
26import Data.Maybe
27import Data.Maybe
27import qualified Data.Set as Set
28import qualified Data.Set as Set
28import Data.Text (Text)
29import Data.Text (Text)
29import qualified Data.Text as T
30import qualified Data.Text as T
30−import qualified Data.Text.Lazy as TL
31import qualified Data.ByteString.Lazy as BL
31import qualified Data.ByteString.Lazy as BL
32−import System.FilePath (addExtension, takeExtension)
32+import System.FilePath (addExtension, takeExtension, takeDirectory)
33+import qualified System.FilePath.Windows as Windows
34+import qualified System.FilePath.Posix as Posix
33import Text.HTML.TagSoup hiding (Row)
35import Text.HTML.TagSoup hiding (Row)
34import Text.Pandoc.Builder (Blocks, Inlines)
36import Text.Pandoc.Builder (Blocks, Inlines)
35import qualified Text.Pandoc.Builder as B
37import qualified Text.Pandoc.Builder as B
@@ -45,20 +47,22 @@ import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag,
45 isCommentTag, isInlineTag, isTextTag)
47 isCommentTag, isInlineTag, isTextTag)
46import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline)
48import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline)
47import Text.Pandoc.Shared
49import Text.Pandoc.Shared
48−import qualified Text.Pandoc.UTF8 as UTF8
49import Text.Pandoc.XML (fromEntities)
50import Text.Pandoc.XML (fromEntities)
50−import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs)
51+import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock)
52+-- import Debug.Trace (traceShowId)
51
53
52−type MarkdownParser m = ParserT Text ParserState m
54+type MarkdownParser m = ParserT Sources ParserState m
55+
56+type F = Future ParserState
53
57
54-- | Read markdown from an input string and return a Pandoc document.
58-- | Read markdown from an input string and return a Pandoc document.
55−readMarkdown :: PandocMonad m
59+readMarkdown :: (PandocMonad m, ToSources a)
56 => ReaderOptions -- ^ Reader options
60 => ReaderOptions -- ^ Reader options
57− -> Text -- ^ String to parse (assuming @'\n'@ line endings)
61+ -> a -- ^ Input
58 -> m Pandoc
62 -> m Pandoc
59readMarkdown opts s = do
63readMarkdown opts s = do
60 parsed <- readWithM parseMarkdown def{ stateOptions = opts }
64 parsed <- readWithM parseMarkdown def{ stateOptions = opts }
61− (crFilter s <> "\n\n")
65+ (ensureFinalNewlines 3 (toSources s))
62 case parsed of
66 case parsed of
63 Right result -> return result
67 Right result -> return result
64 Left e -> throwError e
68 Left e -> throwError e
@@ -79,7 +83,7 @@ yamlToMeta opts mbfp bstr = do
79 meta <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks) bstr
83 meta <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks) bstr
80 setPosition oldPos
84 setPosition oldPos
81 return $ runF meta defaultParserState
85 return $ runF meta defaultParserState
82− parsed <- readWithM parser def{ stateOptions = opts } ""
86+ parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text)
83 case parsed of
87 case parsed of
84 Right result -> return result
88 Right result -> return result
85 Left e -> throwError e
89 Left e -> throwError e
@@ -95,14 +99,12 @@ yamlToRefs :: PandocMonad m
95 -> m [MetaValue]
99 -> m [MetaValue]
96yamlToRefs idpred opts mbfp bstr = do
100yamlToRefs idpred opts mbfp bstr = do
97 let parser = do
101 let parser = do
98− oldPos <- getPosition
99 case mbfp of
102 case mbfp of
100 Nothing -> return ()
103 Nothing -> return ()
101 Just fp -> setPosition $ initialPos fp
104 Just fp -> setPosition $ initialPos fp
102 refs <- yamlBsToRefs (fmap B.toMetaValue <$> parseBlocks) idpred bstr
105 refs <- yamlBsToRefs (fmap B.toMetaValue <$> parseBlocks) idpred bstr
103− setPosition oldPos
104 return $ runF refs defaultParserState
106 return $ runF refs defaultParserState
105− parsed <- readWithM parser def{ stateOptions = opts } ""
107+ parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text)
106 case parsed of
108 case parsed of
107 Right result -> return result
109 Right result -> return result
108 Left e -> throwError e
110 Left e -> throwError e
@@ -145,14 +147,14 @@ inList = do
145 ctx <- stateParserContext <$> getState
147 ctx <- stateParserContext <$> getState
146 guard (ctx == ListItemState)
148 guard (ctx == ListItemState)
147
149
148−spnl :: PandocMonad m => ParserT Text st m ()
150+spnl :: PandocMonad m => ParserT Sources st m ()
149spnl = try $ do
151spnl = try $ do
150 skipSpaces
152 skipSpaces
151 optional newline
153 optional newline
152 skipSpaces
154 skipSpaces
153 notFollowedBy (char '\n')
155 notFollowedBy (char '\n')
154
156
155−spnl' :: PandocMonad m => ParserT Text st m Text
157+spnl' :: PandocMonad m => ParserT Sources st m Text
156spnl' = try $ do
158spnl' = try $ do
157 xs <- many spaceChar
159 xs <- many spaceChar
158 ys <- option "" $ try $ (:) <$> newline
160 ys <- option "" $ try $ (:) <$> newline
@@ -271,25 +273,13 @@ pandocTitleBlock = do
271 $ nullMeta
273 $ nullMeta
272 updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
274 updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
273
275
274−yamlMetaBlock :: PandocMonad m => MarkdownParser m (F Blocks)
276+yamlMetaBlock' :: PandocMonad m => MarkdownParser m (F Blocks)
275−yamlMetaBlock = do
277+yamlMetaBlock' = do
276 guardEnabled Ext_yaml_metadata_block
278 guardEnabled Ext_yaml_metadata_block
277− try $ do
278− string "---"
279− blankline
280− notFollowedBy blankline -- if --- is followed by a blank it's an HRULE
281− rawYamlLines <- manyTill anyLine stopLine
282− -- by including --- and ..., we allow yaml blocks with just comments:
283− let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."]))
284− optional blanklines
285− newMetaF <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks)
279+ newMetaF <- yamlMetaBlock (fmap B.toMetaValue <$> parseBlocks)
286− $ UTF8.fromTextLazy $ TL.fromStrict rawYaml
287− -- Since `<>` is left-biased, existing values are not touched:
280+ -- Since `<>` is left-biased, existing values are not touched:
288− updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF }
281+ updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF }
289− return mempty
282+ return mempty
290−
291−stopLine :: PandocMonad m => MarkdownParser m ()
292−stopLine = try $ (string "---" <|> string "...") >> blankline >> return ()
293
283
294mmdTitleBlock :: PandocMonad m => MarkdownParser m ()
284mmdTitleBlock :: PandocMonad m => MarkdownParser m ()
295mmdTitleBlock = do
285mmdTitleBlock = do
@@ -309,7 +299,7 @@ kvPair allowEmpty = try $ do
309 (try $ newline >> lookAhead (blankline <|> nonspaceChar))
299 (try $ newline >> lookAhead (blankline <|> nonspaceChar))
310 guard $ allowEmpty || not (T.null val)
300 guard $ allowEmpty || not (T.null val)
311 let key' = T.concat $ T.words $ T.toLower key
301 let key' = T.concat $ T.words $ T.toLower key
312− let val' = MetaBlocks $ B.toList $ B.plain $ B.text val
302+ let val' = MetaInlines $ B.toList $ B.text val
313 return (key',val')
303 return (key',val')
314
304
315parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc
305parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc
@@ -348,7 +338,9 @@ referenceKey = try $ do
348 notFollowedBy' (() <$ reference)
338 notFollowedBy' (() <$ reference)
349 many1Char $ notFollowedBy space >> litChar
339 many1Char $ notFollowedBy space >> litChar
350 let betweenAngles = try $ char '<' >> manyTillChar litChar (char '>')
340 let betweenAngles = try $ char '<' >> manyTillChar litChar (char '>')
341+ rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths)
342+ src <- (if rebase then rebasePath pos else id) <$>
351− src <- try betweenAngles <|> sourceURL
343+ (try betweenAngles <|> sourceURL)
352 tit <- option "" referenceTitle
344 tit <- option "" referenceTitle
353 attr <- option nullAttr $ try $
345 attr <- option nullAttr $ try $
354 do guardEnabled Ext_link_attributes
346 do guardEnabled Ext_link_attributes
@@ -357,7 +349,7 @@ referenceKey = try $ do
357 addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes
349 addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes
358 >> many (try $ spnl >> keyValAttr)
350 >> many (try $ spnl >> keyValAttr)
359 blanklines
351 blanklines
360− let attr' = extractIdClass $ foldl (\x f -> f x) attr addKvs
352+ let attr' = extractIdClass $ foldl' (\x f -> f x) attr addKvs
361 target = (escapeURI $ trimr src, tit)
353 target = (escapeURI $ trimr src, tit)
362 st <- getState
354 st <- getState
363 let oldkeys = stateKeys st
355 let oldkeys = stateKeys st
@@ -453,7 +445,7 @@ block :: PandocMonad m => MarkdownParser m (F Blocks)
453block = do
445block = do
454 res <- choice [ mempty <$ blanklines
446 res <- choice [ mempty <$ blanklines
455 , codeBlockFenced
447 , codeBlockFenced
456− , yamlMetaBlock
448+ , yamlMetaBlock'
457 -- note: bulletList needs to be before header because of
449 -- note: bulletList needs to be before header because of
458 -- the possibility of empty list items: -
450 -- the possibility of empty list items: -
459 , bulletList
451 , bulletList
@@ -579,7 +571,7 @@ registerImplicitHeader raw attr@(ident, _, _)
579-- hrule block
571-- hrule block
580--
572--
581
573
582−hrule :: PandocMonad m => ParserT Text st m (F Blocks)
574+hrule :: PandocMonad m => ParserT Sources st m (F Blocks)
583hrule = try $ do
575hrule = try $ do
584 skipSpaces
576 skipSpaces
585 start <- satisfy isHruleChar
577 start <- satisfy isHruleChar
@@ -599,7 +591,7 @@ indentedLine = indentSpaces >> anyLineNewline
599blockDelimiter :: PandocMonad m
591blockDelimiter :: PandocMonad m
600 => (Char -> Bool)
592 => (Char -> Bool)
601 -> Maybe Int
593 -> Maybe Int
602− -> ParserT Text ParserState m Int
594+ -> ParserT Sources ParserState m Int
603blockDelimiter f len = try $ do
595blockDelimiter f len = try $ do
604 skipNonindentSpaces
596 skipNonindentSpaces
605 c <- lookAhead (satisfy f)
597 c <- lookAhead (satisfy f)
@@ -613,7 +605,7 @@ attributes = try $ do
613 spnl
605 spnl
614 attrs <- many (attribute <* spnl)
606 attrs <- many (attribute <* spnl)
615 char '}'
607 char '}'
616− return $ foldl (\x f -> f x) nullAttr attrs
608+ return $ foldl' (\x f -> f x) nullAttr attrs
617
609
618attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr)
610attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr)
619attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr
611attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr
@@ -743,7 +735,7 @@ lhsCodeBlockBirdWith c = try $ do
743 blanklines
735 blanklines
744 return $ T.intercalate "\n" lns'
736 return $ T.intercalate "\n" lns'
745
737
746−birdTrackLine :: PandocMonad m => Char -> ParserT Text st m Text
738+birdTrackLine :: PandocMonad m => Char -> ParserT Sources st m Text
747birdTrackLine c = try $ do
739birdTrackLine c = try $ do
748 char c
740 char c
749 -- allow html tags on left margin:
741 -- allow html tags on left margin:
@@ -1036,7 +1028,7 @@ para = try $ do
1036 option (B.plain <$> result)
1028 option (B.plain <$> result)
1037 $ try $ do
1029 $ try $ do
1038 newline
1030 newline
1039− (blanklines >> return mempty)
1031+ (mempty <$ blanklines)
1040 <|> (guardDisabled Ext_blank_before_blockquote >> () <$ lookAhead blockQuote)
1032 <|> (guardDisabled Ext_blank_before_blockquote >> () <$ lookAhead blockQuote)
1041 <|> (guardEnabled Ext_backtick_code_blocks >> () <$ lookAhead codeBlockFenced)
1033 <|> (guardEnabled Ext_backtick_code_blocks >> () <$ lookAhead codeBlockFenced)
1042 <|> (guardDisabled Ext_blank_before_header >> () <$ lookAhead header)
1034 <|> (guardDisabled Ext_blank_before_header >> () <$ lookAhead header)
@@ -1181,7 +1173,7 @@ lineBlock = do
1181-- and the length including trailing space.
1173-- and the length including trailing space.
1182dashedLine :: PandocMonad m
1174dashedLine :: PandocMonad m
1183 => Char
1175 => Char
1184− -> ParserT Text st m (Int, Int)
1176+ -> ParserT Sources st m (Int, Int)
1185dashedLine ch = do
1177dashedLine ch = do
1186 dashes <- many1 (char ch)
1178 dashes <- many1 (char ch)
1187 sp <- many spaceChar
1179 sp <- many spaceChar
@@ -1250,7 +1242,7 @@ rawTableLine :: PandocMonad m
1250 -> MarkdownParser m [Text]
1242 -> MarkdownParser m [Text]
1251rawTableLine indices = do
1243rawTableLine indices = do
1252 notFollowedBy' (blanklines' <|> tableFooter)
1244 notFollowedBy' (blanklines' <|> tableFooter)
1253− line <- take1WhileP (/='\n') <* newline
1245+ line <- anyLine
1254 return $ map trim $ tail $
1246 return $ map trim $ tail $
1255 splitTextByIndices (init indices) line
1247 splitTextByIndices (init indices) line
1256
1248
@@ -1364,7 +1356,7 @@ pipeTable = try $ do
1364 lines' <- many pipeTableRow
1356 lines' <- many pipeTableRow
1365 let lines'' = map (take (length aligns) <$>) lines'
1357 let lines'' = map (take (length aligns) <$>) lines'
1366 let maxlength = maximum $
1358 let maxlength = maximum $
1367− map (\x -> T.length . stringify $ runF x def) (heads' : lines'')
1359+ fmap (\x -> T.length . stringify $ runF x def) (heads' :| lines'')
1368 numColumns <- getOption readerColumns
1360 numColumns <- getOption readerColumns
1369 let widths = if maxlength > numColumns
1361 let widths = if maxlength > numColumns
1370 then map (\len ->
1362 then map (\len ->
@@ -1401,7 +1393,7 @@ pipeTableCell =
1401 return $ B.plain <$> result)
1393 return $ B.plain <$> result)
1402 <|> return mempty
1394 <|> return mempty
1403
1395
1404−pipeTableHeaderPart :: PandocMonad m => ParserT Text st m (Alignment, Int)
1396+pipeTableHeaderPart :: PandocMonad m => ParserT Sources st m (Alignment, Int)
1405pipeTableHeaderPart = try $ do
1397pipeTableHeaderPart = try $ do
1406 skipMany spaceChar
1398 skipMany spaceChar
1407 left <- optionMaybe (char ':')
1399 left <- optionMaybe (char ':')
@@ -1417,10 +1409,14 @@ pipeTableHeaderPart = try $ do
1417 (Just _,Just _) -> AlignCenter, len)
1409 (Just _,Just _) -> AlignCenter, len)
1418
1410
1419-- Succeed only if current line contains a pipe.
1411-- Succeed only if current line contains a pipe.
1420−scanForPipe :: PandocMonad m => ParserT Text st m ()
1412+scanForPipe :: PandocMonad m => ParserT Sources st m ()
1421scanForPipe = do
1413scanForPipe = do
1422− inp <- getInput
1414+ Sources inps <- getInput
1415+ let ln = case inps of
1416+ [] -> ""
1417+ ((_,t):(_,t'):_) | T.null t -> t'
1418+ ((_,t):_) -> t
1423− case T.break (\c -> c == '\n' || c == '|') inp of
1419+ case T.break (\c -> c == '\n' || c == '|') ln of
1424 (_, T.uncons -> Just ('|', _)) -> return ()
1420 (_, T.uncons -> Just ('|', _)) -> return ()
1425 _ -> mzero
1421 _ -> mzero
1426
1422
@@ -1503,7 +1499,7 @@ inline = do
1503 '$' -> math
1499 '$' -> math
1504 '~' -> strikeout <|> subscript
1500 '~' -> strikeout <|> subscript
1505 '<' -> autoLink <|> spanHtml <|> rawHtmlInline <|> ltSign
1501 '<' -> autoLink <|> spanHtml <|> rawHtmlInline <|> ltSign
1506− '\\' -> escapedNewline <|> escapedChar <|> rawLaTeXInline'
1502+ '\\' -> math <|> escapedNewline <|> escapedChar <|> rawLaTeXInline'
1507 '@' -> cite <|> exampleRef
1503 '@' -> cite <|> exampleRef
1508 '"' -> smart
1504 '"' -> smart
1509 '\'' -> smart
1505 '\'' -> smart
@@ -1714,18 +1710,18 @@ whitespace = spaceChar >> return <$> (lb <|> regsp) <?> "whitespace"
1714 where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak)
1710 where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak)
1715 regsp = skipMany spaceChar >> return B.space
1711 regsp = skipMany spaceChar >> return B.space
1716
1712
1717−nonEndline :: PandocMonad m => ParserT Text st m Char
1713+nonEndline :: PandocMonad m => ParserT Sources st m Char
1718nonEndline = satisfy (/='\n')
1714nonEndline = satisfy (/='\n')
1719
1715
1720str :: PandocMonad m => MarkdownParser m (F Inlines)
1716str :: PandocMonad m => MarkdownParser m (F Inlines)
1721str = do
1717str = do
1722 result <- mconcat <$> many1
1718 result <- mconcat <$> many1
1723− ( take1WhileP isAlphaNum
1719+ ( T.pack <$> (many1 alphaNum)
1724 <|> "." <$ try (char '.' <* notFollowedBy (char '.')) )
1720 <|> "." <$ try (char '.' <* notFollowedBy (char '.')) )
1725 updateLastStrPos
1721 updateLastStrPos
1726 (do guardEnabled Ext_smart
1722 (do guardEnabled Ext_smart
1727 abbrevs <- getOption readerAbbreviations
1723 abbrevs <- getOption readerAbbreviations
1728− if not (T.null result) && T.last result == '.' && result `Set.member` abbrevs
1724+ if result `Set.member` abbrevs
1729 then try (do ils <- whitespace
1725 then try (do ils <- whitespace
1730 notFollowedBy (() <$ cite <|> () <$ note)
1726 notFollowedBy (() <$ cite <|> () <$ note)
1731 -- ?? lookAhead alphaNum
1727 -- ?? lookAhead alphaNum
@@ -1844,9 +1840,12 @@ regLink :: PandocMonad m
1844 -> MarkdownParser m (F Inlines)
1840 -> MarkdownParser m (F Inlines)
1845regLink constructor lab = try $ do
1841regLink constructor lab = try $ do
1846 (src, tit) <- source
1842 (src, tit) <- source
1843+ rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths)
1844+ pos <- getPosition
1845+ let src' = if rebase then rebasePath pos src else src
1847 attr <- option nullAttr $
1846 attr <- option nullAttr $
1848 guardEnabled Ext_link_attributes >> attributes
1847 guardEnabled Ext_link_attributes >> attributes
1849− return $ constructor attr src tit <$> lab
1848+ return $ constructor attr src' tit <$> lab
1850
1849
1851-- a link like [this][ref] or [this][] or [this]
1850-- a link like [this][ref] or [this][] or [this]
1852referenceLink :: PandocMonad m
1851referenceLink :: PandocMonad m
@@ -1886,7 +1885,8 @@ referenceLink constructor (lab, raw) = do
1886 Just ((src, tit), _) -> constructor nullAttr src tit <$> lab
1885 Just ((src, tit), _) -> constructor nullAttr src tit <$> lab
1887 Nothing -> makeFallback
1886 Nothing -> makeFallback
1888 else makeFallback
1887 else makeFallback
1888+ Just ((src,tit), attr) ->
1889− Just ((src,tit), attr) -> constructor attr src tit <$> lab
1889+ constructor attr src tit <$> lab
1890
1890
1891dropBrackets :: Text -> Text
1891dropBrackets :: Text -> Text
1892dropBrackets = dropRB . dropLB
1892dropBrackets = dropRB . dropLB
@@ -1919,15 +1919,33 @@ autoLink = try $ do
1919 return $ return $ B.linkWith attr (src <> escapeURI extra) ""
1919 return $ return $ B.linkWith attr (src <> escapeURI extra) ""
1920 (B.str $ orig <> extra)
1920 (B.str $ orig <> extra)
1921
1921
1922+-- | Rebase a relative path, by adding the (relative) directory
1923+-- of the containing source position. Absolute links and URLs
1924+-- are untouched.
1925+rebasePath :: SourcePos -> Text -> Text
1926+rebasePath pos path = do
1927+ let fp = sourceName pos
1928+ isFragment = T.take 1 path == "#"
1929+ path' = T.unpack path
1930+ isAbsolutePath = Posix.isAbsolute path' || Windows.isAbsolute path'
1931+ in if T.null path || isFragment || isAbsolutePath || isURI path
1932+ then path
1933+ else
1934+ case takeDirectory fp of
1935+ "" -> path
1936+ "." -> path
1937+ d -> T.pack d <> "/" <> path
1938+
1922image :: PandocMonad m => MarkdownParser m (F Inlines)
1939image :: PandocMonad m => MarkdownParser m (F Inlines)
1923image = try $ do
1940image = try $ do
1924 char '!'
1941 char '!'
1925 (lab,raw) <- reference
1942 (lab,raw) <- reference
1926 defaultExt <- getOption readerDefaultImageExtension
1943 defaultExt <- getOption readerDefaultImageExtension
1944+ let constructor attr' src =
1927− let constructor attr' src = case takeExtension (T.unpack src) of
1945+ case takeExtension (T.unpack src) of
1928− "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src)
1946+ "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src)
1929− $ T.unpack defaultExt)
1947+ $ T.unpack defaultExt)
1930− _ -> B.imageWith attr' src
1948+ _ -> B.imageWith attr' src
1931 regLink constructor lab <|> referenceLink constructor (lab,raw)
1949 regLink constructor lab <|> referenceLink constructor (lab,raw)
1932
1950
1933note :: PandocMonad m => MarkdownParser m (F Inlines)
1951note :: PandocMonad m => MarkdownParser m (F Inlines)
@@ -1973,7 +1991,7 @@ rawLaTeXInline' = do
1973 s <- rawLaTeXInline
1991 s <- rawLaTeXInline
1974 return $ return $ B.rawInline "tex" s -- "tex" because it might be context
1992 return $ return $ B.rawInline "tex" s -- "tex" because it might be context
1975
1993
1976−rawConTeXtEnvironment :: PandocMonad m => ParserT Text st m Text
1994+rawConTeXtEnvironment :: PandocMonad m => ParserT Sources st m Text
1977rawConTeXtEnvironment = try $ do
1995rawConTeXtEnvironment = try $ do
1978 string "\\start"
1996 string "\\start"
1979 completion <- inBrackets (letter <|> digit <|> spaceChar)
1997 completion <- inBrackets (letter <|> digit <|> spaceChar)
@@ -1982,7 +2000,7 @@ rawConTeXtEnvironment = try $ do
1982 (try $ string "\\stop" >> textStr completion)
2000 (try $ string "\\stop" >> textStr completion)
1983 return $ "\\start" <> completion <> T.concat contents <> "\\stop" <> completion
2001 return $ "\\start" <> completion <> T.concat contents <> "\\stop" <> completion
1984
2002
1985−inBrackets :: PandocMonad m => ParserT Text st m Char -> ParserT Text st m Text
2003+inBrackets :: PandocMonad m => ParserT Sources st m Char -> ParserT Sources st m Text
1986inBrackets parser = do
2004inBrackets parser = do
1987 char '['
2005 char '['
1988 contents <- manyChar parser
2006 contents <- manyChar parser
@@ -2102,7 +2120,7 @@ cite = do
2102
2120
2103textualCite :: PandocMonad m => MarkdownParser m (F Inlines)
2121textualCite :: PandocMonad m => MarkdownParser m (F Inlines)
2104textualCite = try $ do
2122textualCite = try $ do
2105− (suppressAuthor, key) <- citeKey
2123+ (suppressAuthor, key) <- citeKey True
2106 -- If this is a reference to an earlier example list item,
2124 -- If this is a reference to an earlier example list item,
2107 -- then don't parse it as a citation. If the example list
2125 -- then don't parse it as a citation. If the example list
2108 -- item comes later, we'll parse it here and figure out in
2126 -- item comes later, we'll parse it here and figure out in
@@ -2182,7 +2200,7 @@ prefix = trimInlinesF . mconcat <$>
2182 manyTill inline (char ']'
2200 manyTill inline (char ']'
2183 <|> lookAhead
2201 <|> lookAhead
2184 (try $ do optional (try (char ';' >> spnl))
2202 (try $ do optional (try (char ';' >> spnl))
2185− citeKey
2203+ citeKey True
2186 return ']'))
2204 return ']'))
2187
2205
2188citeList :: PandocMonad m => MarkdownParser m (F [Citation])
2206citeList :: PandocMonad m => MarkdownParser m (F [Citation])
@@ -2191,7 +2209,7 @@ citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl)
2191citation :: PandocMonad m => MarkdownParser m (F Citation)
2209citation :: PandocMonad m => MarkdownParser m (F Citation)
2192citation = try $ do
2210citation = try $ do
2193 pref <- prefix
2211 pref <- prefix
2194− (suppress_author, key) <- citeKey
2212+ (suppress_author, key) <- citeKey True
2195 suff <- suffix
2213 suff <- suffix
2196 noteNum <- stateNoteNumber <$> getState
2214 noteNum <- stateNoteNumber <$> getState
2197 return $ do
2215 return $ do
@@ -2210,28 +2228,30 @@ citation = try $ do
2210smart :: PandocMonad m => MarkdownParser m (F Inlines)
2228smart :: PandocMonad m => MarkdownParser m (F Inlines)
2211smart = do
2229smart = do
2212 guardEnabled Ext_smart
2230 guardEnabled Ext_smart
2213− doubleQuoted <|> singleQuoted <|>
2231+ doubleQuoted <|> singleQuoted <|> (return <$> doubleCloseQuote) <|>
2214− choice (map (return <$>) [apostrophe, dash, ellipses])
2232+ (return <$> apostrophe) <|> (return <$> dash) <|> (return <$> ellipses)
2215
2233
2216singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2234singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2217−singleQuoted = try $ do
2235+singleQuoted = do
2218 singleQuoteStart
2236 singleQuoteStart
2219− withQuoteContext InSingleQuote $
2237+ (try (withQuoteContext InSingleQuote $
2220 fmap B.singleQuoted . trimInlinesF . mconcat <$>
2238 fmap B.singleQuoted . trimInlinesF . mconcat <$>
2221− many1Till inline singleQuoteEnd
2239+ many1Till inline singleQuoteEnd))
2240+ <|> (return (return (B.str "\8217")))
2222
2241
2223-- doubleQuoted will handle regular double-quoted sections, as well
2242-- doubleQuoted will handle regular double-quoted sections, as well
2224-- as dialogues with an open double-quote without a close double-quote
2243-- as dialogues with an open double-quote without a close double-quote
2225-- in the same paragraph.
2244-- in the same paragraph.
2226doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2245doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2227−doubleQuoted = try $ do
2246+doubleQuoted = do
2228 doubleQuoteStart
2247 doubleQuoteStart
2229− withQuoteContext InDoubleQuote $
2248+ (try (withQuoteContext InDoubleQuote $
2230 fmap B.doubleQuoted . trimInlinesF . mconcat <$>
2249 fmap B.doubleQuoted . trimInlinesF . mconcat <$>
2231− many1Till inline doubleQuoteEnd
2250+ many1Till inline doubleQuoteEnd))
2251+ <|> (return (return (B.str "\8220")))
2232
2252
2233toRow :: [Blocks] -> Row
2253toRow :: [Blocks] -> Row
2234toRow = Row nullAttr . map B.simpleCell
2254toRow = Row nullAttr . map B.simpleCell
2235
2255
2236toHeaderRow :: [Blocks] -> [Row]
2256toHeaderRow :: [Blocks] -> [Row]
2237−toHeaderRow l = [toRow l | not (null l)]
2257+toHeaderRow l = [toRow l | not (null l) && not (all null l)]
src/Text/Pandoc/Readers/MediaWiki.hs+16−7
@@ -36,17 +36,18 @@ import Text.Pandoc.Logging
36import Text.Pandoc.Options
36import Text.Pandoc.Options
37import Text.Pandoc.Parsing hiding (nested)
37import Text.Pandoc.Parsing hiding (nested)
38import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag)
38import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag)
39−import Text.Pandoc.Shared (crFilter, safeRead, stringify, stripTrailingNewlines,
39+import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines,
40 trim, splitTextBy, tshow)
40 trim, splitTextBy, tshow)
41import Text.Pandoc.Walk (walk)
41import Text.Pandoc.Walk (walk)
42import Text.Pandoc.XML (fromEntities)
42import Text.Pandoc.XML (fromEntities)
43
43
44-- | Read mediawiki from an input string and return a Pandoc document.
44-- | Read mediawiki from an input string and return a Pandoc document.
45−readMediaWiki :: PandocMonad m
45+readMediaWiki :: (PandocMonad m, ToSources a)
46− => ReaderOptions -- ^ Reader options
46+ => ReaderOptions
47− -> Text -- ^ String to parse (assuming @'\n'@ line endings)
47+ -> a
48 -> m Pandoc
48 -> m Pandoc
49readMediaWiki opts s = do
49readMediaWiki opts s = do
50+ let sources = toSources s
50 parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts
51 parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts
51 , mwMaxNestingLevel = 4
52 , mwMaxNestingLevel = 4
52 , mwNextLinkNumber = 1
53 , mwNextLinkNumber = 1
@@ -55,7 +56,7 @@ readMediaWiki opts s = do
55 , mwLogMessages = []
56 , mwLogMessages = []
56 , mwInTT = False
57 , mwInTT = False
57 }
58 }
58− (crFilter s <> "\n")
59+ sources
59 case parsed of
60 case parsed of
60 Right result -> return result
61 Right result -> return result
61 Left e -> throwError e
62 Left e -> throwError e
@@ -69,7 +70,7 @@ data MWState = MWState { mwOptions :: ReaderOptions
69 , mwInTT :: Bool
70 , mwInTT :: Bool
70 }
71 }
71
72
72−type MWParser m = ParserT Text MWState m
73+type MWParser m = ParserT Sources MWState m
73
74
74instance HasReaderOptions MWState where
75instance HasReaderOptions MWState where
75 extractReaderOptions = mwOptions
76 extractReaderOptions = mwOptions
@@ -112,12 +113,14 @@ newBlockTags = ["haskell","syntaxhighlight","source","gallery","references"]
112isBlockTag' :: Tag Text -> Bool
113isBlockTag' :: Tag Text -> Bool
113isBlockTag' tag@(TagOpen t _) = (isBlockTag tag || t `elem` newBlockTags) &&
114isBlockTag' tag@(TagOpen t _) = (isBlockTag tag || t `elem` newBlockTags) &&
114 t `notElem` eitherBlockOrInline
115 t `notElem` eitherBlockOrInline
116+isBlockTag' (TagClose "ref") = True -- needed so 'special' doesn't parse it
115isBlockTag' tag@(TagClose t) = (isBlockTag tag || t `elem` newBlockTags) &&
117isBlockTag' tag@(TagClose t) = (isBlockTag tag || t `elem` newBlockTags) &&
116 t `notElem` eitherBlockOrInline
118 t `notElem` eitherBlockOrInline
117isBlockTag' tag = isBlockTag tag
119isBlockTag' tag = isBlockTag tag
118
120
119isInlineTag' :: Tag Text -> Bool
121isInlineTag' :: Tag Text -> Bool
120isInlineTag' (TagComment _) = True
122isInlineTag' (TagComment _) = True
123+isInlineTag' (TagClose "ref") = False -- see below inlineTag
121isInlineTag' t = not (isBlockTag' t)
124isInlineTag' t = not (isBlockTag' t)
122
125
123eitherBlockOrInline :: [Text]
126eitherBlockOrInline :: [Text]
@@ -554,11 +557,17 @@ variable = try $ do
554 contents <- manyTillChar anyChar (try $ string "}}}")
557 contents <- manyTillChar anyChar (try $ string "}}}")
555 return $ "{{{" <> contents <> "}}}"
558 return $ "{{{" <> contents <> "}}}"
556
559
560+singleParaToPlain :: Blocks -> Blocks
561+singleParaToPlain bs =
562+ case B.toList bs of
563+ [Para ils] -> B.fromList [Plain ils]
564+ _ -> bs
565+
557inlineTag :: PandocMonad m => MWParser m Inlines
566inlineTag :: PandocMonad m => MWParser m Inlines
558inlineTag = do
567inlineTag = do
559 (tag, _) <- lookAhead $ htmlTag isInlineTag'
568 (tag, _) <- lookAhead $ htmlTag isInlineTag'
560 case tag of
569 case tag of
561− TagOpen "ref" _ -> B.note . B.plain <$> inlinesInTags "ref"
570+ TagOpen "ref" _ -> B.note . singleParaToPlain <$> blocksInTags "ref"
562 TagOpen "nowiki" _ -> try $ do
571 TagOpen "nowiki" _ -> try $ do
563 (_,raw) <- htmlTag (~== tag)
572 (_,raw) <- htmlTag (~== tag)
564 if T.any (== '/') raw
573 if T.any (== '/') raw
src/Text/Pandoc/Readers/Metadata.hs+44−17
@@ -14,6 +14,7 @@ Parse YAML/JSON metadata to 'Pandoc' 'Meta'.
14module Text.Pandoc.Readers.Metadata (
14module Text.Pandoc.Readers.Metadata (
15 yamlBsToMeta,
15 yamlBsToMeta,
16 yamlBsToRefs,
16 yamlBsToRefs,
17+ yamlMetaBlock,
17 yamlMap ) where
18 yamlMap ) where
18
19
19import Control.Monad
20import Control.Monad
@@ -30,11 +31,13 @@ import Text.Pandoc.Definition
30import Text.Pandoc.Error
31import Text.Pandoc.Error
31import Text.Pandoc.Parsing hiding (tableWith)
32import Text.Pandoc.Parsing hiding (tableWith)
32import Text.Pandoc.Shared
33import Text.Pandoc.Shared
34+import qualified Data.Text.Lazy as TL
35+import qualified Text.Pandoc.UTF8 as UTF8
33
36
34−yamlBsToMeta :: PandocMonad m
37+yamlBsToMeta :: (PandocMonad m, HasLastStrPosition st)
35− => ParserT Text ParserState m (F MetaValue)
38+ => ParserT Sources st m (Future st MetaValue)
36 -> BL.ByteString
39 -> BL.ByteString
37− -> ParserT Text ParserState m (F Meta)
40+ -> ParserT Sources st m (Future st Meta)
38yamlBsToMeta pMetaValue bstr = do
41yamlBsToMeta pMetaValue bstr = do
39 case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of
42 case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of
40 Right (YAML.Doc (YAML.Mapping _ _ o):_)
43 Right (YAML.Doc (YAML.Mapping _ _ o):_)
@@ -42,6 +45,9 @@ yamlBsToMeta pMetaValue bstr = do
42 Right [] -> return . return $ mempty
45 Right [] -> return . return $ mempty
43 Right [YAML.Doc (YAML.Scalar _ YAML.SNull)]
46 Right [YAML.Doc (YAML.Scalar _ YAML.SNull)]
44 -> return . return $ mempty
47 -> return . return $ mempty
48+ -- the following is what we get from a comment:
49+ Right [YAML.Doc (YAML.Scalar _ (YAML.SUnknown _ ""))]
50+ -> return . return $ mempty
45 Right _ -> Prelude.fail "expected YAML object"
51 Right _ -> Prelude.fail "expected YAML object"
46 Left (yamlpos, err')
52 Left (yamlpos, err')
47 -> do pos <- getPosition
53 -> do pos <- getPosition
@@ -63,11 +69,11 @@ lookupYAML t (YAML.Mapping _ _ m) =
63lookupYAML _ _ = Nothing
69lookupYAML _ _ = Nothing
64
70
65-- Returns filtered list of references.
71-- Returns filtered list of references.
66−yamlBsToRefs :: PandocMonad m
72+yamlBsToRefs :: (PandocMonad m, HasLastStrPosition st)
67− => ParserT Text ParserState m (F MetaValue)
73+ => ParserT Sources st m (Future st MetaValue)
68 -> (Text -> Bool) -- ^ Filter for id
74 -> (Text -> Bool) -- ^ Filter for id
69 -> BL.ByteString
75 -> BL.ByteString
70− -> ParserT Text ParserState m (F [MetaValue])
76+ -> ParserT Sources st m (Future st [MetaValue])
71yamlBsToRefs pMetaValue idpred bstr =
77yamlBsToRefs pMetaValue idpred bstr =
72 case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of
78 case YAML.decodeNode' YAML.failsafeSchemaResolver False False bstr of
73 Right (YAML.Doc o@YAML.Mapping{}:_)
79 Right (YAML.Doc o@YAML.Mapping{}:_)
@@ -95,8 +101,12 @@ yamlBsToRefs pMetaValue idpred bstr =
95 Right [YAML.Doc (YAML.Scalar _ YAML.SNull)]
101 Right [YAML.Doc (YAML.Scalar _ YAML.SNull)]
96 -> return . return $ mempty
102 -> return . return $ mempty
97 Right _ -> Prelude.fail "expecting YAML object"
103 Right _ -> Prelude.fail "expecting YAML object"
98− Left (_pos, err')
104+ Left (yamlpos, err')
105+ -> do pos <- getPosition
106+ setPosition $ incSourceLine
107+ (setSourceColumn pos (YE.posColumn yamlpos))
108+ (YE.posLine yamlpos - 1)
99− -> Prelude.fail err'
109+ Prelude.fail err'
100
110
101
111
102nodeToKey :: YAML.Node YE.Pos -> Maybe Text
112nodeToKey :: YAML.Node YE.Pos -> Maybe Text
@@ -104,10 +114,10 @@ nodeToKey (YAML.Scalar _ (YAML.SStr t)) = Just t
104nodeToKey (YAML.Scalar _ (YAML.SUnknown _ t)) = Just t
114nodeToKey (YAML.Scalar _ (YAML.SUnknown _ t)) = Just t
105nodeToKey _ = Nothing
115nodeToKey _ = Nothing
106
116
107−normalizeMetaValue :: PandocMonad m
117+normalizeMetaValue :: (PandocMonad m, HasLastStrPosition st)
108− => ParserT Text ParserState m (F MetaValue)
118+ => ParserT Sources st m (Future st MetaValue)
109 -> Text
119 -> Text
110− -> ParserT Text ParserState m (F MetaValue)
120+ -> ParserT Sources st m (Future st MetaValue)
111normalizeMetaValue pMetaValue x =
121normalizeMetaValue pMetaValue x =
112 -- Note: a standard quoted or unquoted YAML value will
122 -- Note: a standard quoted or unquoted YAML value will
113 -- not end in a newline, but a "block" set off with
123 -- not end in a newline, but a "block" set off with
@@ -129,10 +139,10 @@ checkBoolean t
129 | t == T.pack "false" || t == T.pack "False" || t == T.pack "FALSE" = Just False
139 | t == T.pack "false" || t == T.pack "False" || t == T.pack "FALSE" = Just False
130 | otherwise = Nothing
140 | otherwise = Nothing
131
141
132−yamlToMetaValue :: PandocMonad m
142+yamlToMetaValue :: (PandocMonad m, HasLastStrPosition st)
133− => ParserT Text ParserState m (F MetaValue)
143+ => ParserT Sources st m (Future st MetaValue)
134 -> YAML.Node YE.Pos
144 -> YAML.Node YE.Pos
135− -> ParserT Text ParserState m (F MetaValue)
145+ -> ParserT Sources st m (Future st MetaValue)
136yamlToMetaValue pMetaValue (YAML.Scalar _ x) =
146yamlToMetaValue pMetaValue (YAML.Scalar _ x) =
137 case x of
147 case x of
138 YAML.SStr t -> normalizeMetaValue pMetaValue t
148 YAML.SStr t -> normalizeMetaValue pMetaValue t
@@ -152,10 +162,10 @@ yamlToMetaValue pMetaValue (YAML.Mapping _ _ o) =
152 fmap MetaMap <$> yamlMap pMetaValue o
162 fmap MetaMap <$> yamlMap pMetaValue o
153yamlToMetaValue _ _ = return $ return $ MetaString ""
163yamlToMetaValue _ _ = return $ return $ MetaString ""
154
164
155−yamlMap :: PandocMonad m
165+yamlMap :: (PandocMonad m, HasLastStrPosition st)
156− => ParserT Text ParserState m (F MetaValue)
166+ => ParserT Sources st m (Future st MetaValue)
157 -> M.Map (YAML.Node YE.Pos) (YAML.Node YE.Pos)
167 -> M.Map (YAML.Node YE.Pos) (YAML.Node YE.Pos)
158− -> ParserT Text ParserState m (F (M.Map Text MetaValue))
168+ -> ParserT Sources st m (Future st (M.Map Text MetaValue))
159yamlMap pMetaValue o = do
169yamlMap pMetaValue o = do
160 kvs <- forM (M.toList o) $ \(key, v) -> do
170 kvs <- forM (M.toList o) $ \(key, v) -> do
161 k <- maybe (throwError $ PandocParseError
171 k <- maybe (throwError $ PandocParseError
@@ -171,3 +181,20 @@ yamlMap pMetaValue o = do
171 return $ do
181 return $ do
172 v' <- fv
182 v' <- fv
173 return (k, v')
183 return (k, v')
184+
185+-- | Parse a YAML metadata block using the supplied 'MetaValue' parser.
186+yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m)
187+ => ParserT Sources st m (Future st MetaValue)
188+ -> ParserT Sources st m (Future st Meta)
189+yamlMetaBlock parser = try $ do
190+ string "---"
191+ blankline
192+ notFollowedBy blankline -- if --- is followed by a blank it's an HRULE
193+ rawYamlLines <- manyTill anyLine stopLine
194+ -- by including --- and ..., we allow yaml blocks with just comments:
195+ let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."]))
196+ optional blanklines
197+ yamlBsToMeta parser $ UTF8.fromTextLazy $ TL.fromStrict rawYaml
198+
199+stopLine :: Monad m => ParserT Sources st m ()
200+stopLine = try $ (string "---" <|> string "...") >> blankline >> return ()
src/Text/Pandoc/Readers/Muse.hs+10−9
@@ -36,19 +36,20 @@ import Text.Pandoc.Definition
36import Text.Pandoc.Error (PandocError (PandocParsecError))
36import Text.Pandoc.Error (PandocError (PandocParsecError))
37import Text.Pandoc.Logging
37import Text.Pandoc.Logging
38import Text.Pandoc.Options
38import Text.Pandoc.Options
39−import Text.Pandoc.Parsing hiding (F)
39+import Text.Pandoc.Parsing
40−import Text.Pandoc.Shared (crFilter, trimr, tshow)
40+import Text.Pandoc.Shared (trimr, tshow)
41
41
42-- | Read Muse from an input string and return a Pandoc document.
42-- | Read Muse from an input string and return a Pandoc document.
43−readMuse :: PandocMonad m
43+readMuse :: (PandocMonad m, ToSources a)
44 => ReaderOptions
44 => ReaderOptions
45− -> Text
45+ -> a
46 -> m Pandoc
46 -> m Pandoc
47readMuse opts s = do
47readMuse opts s = do
48− let input = crFilter s
48+ let sources = toSources s
49− res <- flip runReaderT def $ runParserT parseMuse def{ museOptions = opts } "source" input
49+ res <- flip runReaderT def $ runParserT parseMuse def{ museOptions = opts }
50+ (initialSourceName sources) sources
50 case res of
51 case res of
51− Left e -> throwError $ PandocParsecError input e
52+ Left e -> throwError $ PandocParsecError sources e
52 Right d -> return d
53 Right d -> return d
53
54
54type F = Future MuseState
55type F = Future MuseState
@@ -82,7 +83,7 @@ instance Default MuseEnv where
82 , museInPara = False
83 , museInPara = False
83 }
84 }
84
85
85−type MuseParser m = ParserT Text MuseState (ReaderT MuseEnv m)
86+type MuseParser m = ParserT Sources MuseState (ReaderT MuseEnv m)
86
87
87instance HasReaderOptions MuseState where
88instance HasReaderOptions MuseState where
88 extractReaderOptions = museOptions
89 extractReaderOptions = museOptions
@@ -155,7 +156,7 @@ firstColumn = getPosition >>= \pos -> guard (sourceColumn pos == 1)
155-- * Parsers
156-- * Parsers
156
157
157-- | Parse end-of-line, which can be either a newline or end-of-file.
158-- | Parse end-of-line, which can be either a newline or end-of-file.
158−eol :: Stream s m Char => ParserT s st m ()
159+eol :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s st m ()
159eol = void newline <|> eof
160eol = void newline <|> eof
160
161
161getIndent :: PandocMonad m
162getIndent :: PandocMonad m
src/Text/Pandoc/Readers/Native.hs+7−5
@@ -21,6 +21,7 @@ import Control.Monad.Except (throwError)
21import Data.Text (Text)
21import Data.Text (Text)
22import Text.Pandoc.Class.PandocMonad (PandocMonad)
22import Text.Pandoc.Class.PandocMonad (PandocMonad)
23import Text.Pandoc.Error
23import Text.Pandoc.Error
24+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
24
25
25-- | Read native formatted text and return a Pandoc document.
26-- | Read native formatted text and return a Pandoc document.
26-- The input may be a full pandoc document, a block list, a block,
27-- The input may be a full pandoc document, a block list, a block,
@@ -32,14 +33,15 @@ import Text.Pandoc.Error
32--
33--
33-- > Pandoc nullMeta [Plain [Str "hi"]]
34-- > Pandoc nullMeta [Plain [Str "hi"]]
34--
35--
35−readNative :: PandocMonad m
36+readNative :: (PandocMonad m, ToSources a)
36 => ReaderOptions
37 => ReaderOptions
37− -> Text -- ^ String to parse (assuming @'\n'@ line endings)
38+ -> a
38 -> m Pandoc
39 -> m Pandoc
39readNative _ s =
40readNative _ s =
41+ let t = sourcesToText . toSources $ s
40− case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of
42+ in case maybe (Pandoc nullMeta <$> readBlocks t) Right (safeRead t) of
41− Right doc -> return doc
43+ Right doc -> return doc
42− Left _ -> throwError $ PandocParseError "couldn't read native"
44+ Left _ -> throwError $ PandocParseError "couldn't read native"
43
45
44readBlocks :: Text -> Either PandocError [Block]
46readBlocks :: Text -> Either PandocError [Block]
45readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s)
47readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s)
src/Text/Pandoc/Readers/OPML.hs+9−4
@@ -24,7 +24,8 @@ import Text.Pandoc.Options
24import Text.Pandoc.Error (PandocError(..))
24import Text.Pandoc.Error (PandocError(..))
25import Text.Pandoc.Readers.HTML (readHtml)
25import Text.Pandoc.Readers.HTML (readHtml)
26import Text.Pandoc.Readers.Markdown (readMarkdown)
26import Text.Pandoc.Readers.Markdown (readMarkdown)
27−import Text.Pandoc.Shared (crFilter, blocksToInlines')
27+import Text.Pandoc.Shared (blocksToInlines')
28+import Text.Pandoc.Sources (ToSources(..), sourcesToText)
28import Text.Pandoc.XML.Light
29import Text.Pandoc.XML.Light
29import Control.Monad.Except (throwError)
30import Control.Monad.Except (throwError)
30
31
@@ -46,10 +47,14 @@ instance Default OPMLState where
46 , opmlOptions = def
47 , opmlOptions = def
47 }
48 }
48
49
49−readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
50+readOPML :: (PandocMonad m, ToSources a)
51+ => ReaderOptions
52+ -> a
53+ -> m Pandoc
50readOPML opts inp = do
54readOPML opts inp = do
55+ let sources = toSources inp
51− (bs, st') <- runStateT
56+ (bs, st') <-
52− (case parseXMLContents (TL.fromStrict (crFilter inp)) of
57+ runStateT (case parseXMLContents (TL.fromStrict . sourcesToText $ sources) of
53 Left msg -> throwError $ PandocXMLError "" msg
58 Left msg -> throwError $ PandocXMLError "" msg
54 Right ns -> mapM parseBlock ns)
59 Right ns -> mapM parseBlock ns)
55 def{ opmlOptions = opts }
60 def{ opmlOptions = opts }
src/Text/Pandoc/Readers/Odt/Arrows/State.hs+2−2
@@ -22,7 +22,7 @@ module Text.Pandoc.Readers.Odt.Arrows.State where
22import Control.Arrow
22import Control.Arrow
23import qualified Control.Category as Cat
23import qualified Control.Category as Cat
24import Control.Monad
24import Control.Monad
25−
25+import Data.List (foldl')
26import Text.Pandoc.Readers.Odt.Arrows.Utils
26import Text.Pandoc.Readers.Odt.Arrows.Utils
27import Text.Pandoc.Readers.Odt.Generic.Fallible
27import Text.Pandoc.Readers.Odt.Generic.Fallible
28
28
@@ -122,7 +122,7 @@ iterateS a = ArrowState $ \(s,f) -> foldr a' (s,mzero) f
122iterateSL :: (Foldable f, MonadPlus m)
122iterateSL :: (Foldable f, MonadPlus m)
123 => ArrowState s x y
123 => ArrowState s x y
124 -> ArrowState s (f x) (m y)
124 -> ArrowState s (f x) (m y)
125−iterateSL a = ArrowState $ \(s,f) -> foldl a' (s,mzero) f
125+iterateSL a = ArrowState $ \(s,f) -> foldl' a' (s,mzero) f
126 where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x)
126 where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x)
127
127
128
128
src/Text/Pandoc/Readers/Odt/ContentReader.hs+12−7
@@ -1,4 +1,5 @@
1{-# LANGUAGE Arrows #-}
1{-# LANGUAGE Arrows #-}
2+{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE PatternGuards #-}
5{-# LANGUAGE PatternGuards #-}
@@ -33,7 +34,7 @@ import Data.List (find)
33import qualified Data.Map as M
34import qualified Data.Map as M
34import qualified Data.Text as T
35import qualified Data.Text as T
35import Data.Maybe
36import Data.Maybe
36−import Data.Semigroup (First(..), Option(..))
37+import Data.Monoid (Alt (..))
37
38
38import Text.TeXMath (readMathML, writeTeX)
39import Text.TeXMath (readMathML, writeTeX)
39import qualified Text.Pandoc.XML.Light as XML
40import qualified Text.Pandoc.XML.Light as XML
@@ -505,13 +506,11 @@ type InlineMatcher = ElementMatcher Inlines
505
506
506type BlockMatcher = ElementMatcher Blocks
507type BlockMatcher = ElementMatcher Blocks
507
508
508−
509−newtype FirstMatch a = FirstMatch (Option (First a))
509+newtype FirstMatch a = FirstMatch (Alt Maybe a)
510− deriving (Foldable, Monoid, Semigroup)
510+ deriving (Foldable, Monoid, Semigroup)
511
511
512firstMatch :: a -> FirstMatch a
512firstMatch :: a -> FirstMatch a
513−firstMatch = FirstMatch . Option . Just . First
513+firstMatch = FirstMatch . Alt . Just
514−
515
514
516--
515--
517matchingElement :: (Monoid e)
516matchingElement :: (Monoid e)
@@ -577,7 +576,10 @@ read_spaces = matchingElement NsText "s" (
577read_line_break :: InlineMatcher
576read_line_break :: InlineMatcher
578read_line_break = matchingElement NsText "line-break"
577read_line_break = matchingElement NsText "line-break"
579 $ returnV linebreak
578 $ returnV linebreak
580−
579+--
580+read_tab :: InlineMatcher
581+read_tab = matchingElement NsText "tab"
582+ $ returnV space
581--
583--
582read_span :: InlineMatcher
584read_span :: InlineMatcher
583read_span = matchingElement NsText "span"
585read_span = matchingElement NsText "span"
@@ -585,6 +587,7 @@ read_span = matchingElement NsText "span"
585 $ matchChildContent [ read_span
587 $ matchChildContent [ read_span
586 , read_spaces
588 , read_spaces
587 , read_line_break
589 , read_line_break
590+ , read_tab
588 , read_link
591 , read_link
589 , read_note
592 , read_note
590 , read_citation
593 , read_citation
@@ -604,6 +607,7 @@ read_paragraph = matchingElement NsText "p"
604 $ matchChildContent [ read_span
607 $ matchChildContent [ read_span
605 , read_spaces
608 , read_spaces
606 , read_line_break
609 , read_line_break
610+ , read_tab
607 , read_link
611 , read_link
608 , read_note
612 , read_note
609 , read_citation
613 , read_citation
@@ -630,6 +634,7 @@ read_header = matchingElement NsText "h"
630 children <- ( matchChildContent [ read_span
634 children <- ( matchChildContent [ read_span
631 , read_spaces
635 , read_spaces
632 , read_line_break
636 , read_line_break
637+ , read_tab
633 , read_link
638 , read_link
634 , read_note
639 , read_note
635 , read_citation
640 , read_citation
src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs+2−2
@@ -64,12 +64,12 @@ import qualified Data.Map as M
64import Data.Text (Text)
64import Data.Text (Text)
65import Data.Default
65import Data.Default
66import Data.Maybe
66import Data.Maybe
67+import Data.List (foldl')
67
68
68import qualified Text.Pandoc.XML.Light as XML
69import qualified Text.Pandoc.XML.Light as XML
69
70
70import Text.Pandoc.Readers.Odt.Arrows.State
71import Text.Pandoc.Readers.Odt.Arrows.State
71import Text.Pandoc.Readers.Odt.Arrows.Utils
72import Text.Pandoc.Readers.Odt.Arrows.Utils
72−
73import Text.Pandoc.Readers.Odt.Generic.Namespaces
73import Text.Pandoc.Readers.Odt.Generic.Namespaces
74import Text.Pandoc.Readers.Odt.Generic.Utils
74import Text.Pandoc.Readers.Odt.Generic.Utils
75import Text.Pandoc.Readers.Odt.Generic.Fallible
75import Text.Pandoc.Readers.Odt.Generic.Fallible
@@ -293,7 +293,7 @@ readNSattributes = fromState $ \state -> maybe (state, failEmpty )
293 => XMLConverterState nsID extraState
293 => XMLConverterState nsID extraState
294 -> Maybe (XMLConverterState nsID extraState)
294 -> Maybe (XMLConverterState nsID extraState)
295 extractNSAttrs startState
295 extractNSAttrs startState
296− = foldl (\state d -> state >>= addNS d)
296+ = foldl' (\state d -> state >>= addNS d)
297 (Just startState)
297 (Just startState)
298 nsAttribs
298 nsAttribs
299 where nsAttribs = mapMaybe readNSattr (XML.elAttribs element)
299 where nsAttribs = mapMaybe readNSattr (XML.elAttribs element)
src/Text/Pandoc/Readers/Odt/StyleReader.hs+8−7
@@ -44,7 +44,7 @@ import Control.Arrow
44
44
45import Data.Default
45import Data.Default
46import qualified Data.Foldable as F
46import qualified Data.Foldable as F
47−import Data.List (unfoldr)
47+import Data.List (unfoldr, foldl')
48import qualified Data.Map as M
48import qualified Data.Map as M
49import Data.Maybe
49import Data.Maybe
50import Data.Text (Text)
50import Data.Text (Text)
@@ -120,7 +120,7 @@ fontPitchReader = executeInSub NsOffice "font-face-decls" (
120 &&&
120 &&&
121 lookupDefaultingAttr NsStyle "font-pitch"
121 lookupDefaultingAttr NsStyle "font-pitch"
122 ))
122 ))
123− >>?^ ( M.fromList . foldl accumLegalPitches [] )
123+ >>?^ ( M.fromList . foldl' accumLegalPitches [] )
124 ) `ifFailedDo` returnV (Right M.empty)
124 ) `ifFailedDo` returnV (Right M.empty)
125 where accumLegalPitches ls (Nothing,_) = ls
125 where accumLegalPitches ls (Nothing,_) = ls
126 accumLegalPitches ls (Just n,p) = (n,p):ls
126 accumLegalPitches ls (Just n,p) = (n,p):ls
@@ -563,12 +563,13 @@ readListLevelStyle levelType = readAttr NsText "level"
563
563
564--
564--
565chooseMostSpecificListLevelStyle :: S.Set ListLevelStyle -> Maybe ListLevelStyle
565chooseMostSpecificListLevelStyle :: S.Set ListLevelStyle -> Maybe ListLevelStyle
566−chooseMostSpecificListLevelStyle ls | ls == mempty = Nothing
566+chooseMostSpecificListLevelStyle ls = F.foldr select Nothing ls
567− | otherwise = Just ( F.foldr1 select ls )
568 where
567 where
568+ select l Nothing = Just l
569− select ( ListLevelStyle t1 p1 s1 f1 b1 )
569+ select ( ListLevelStyle t1 p1 s1 f1 b1 )
570− ( ListLevelStyle t2 p2 s2 f2 _ )
570+ ( Just ( ListLevelStyle t2 p2 s2 f2 _ ))
571− = ListLevelStyle (select' t1 t2) (p1 <|> p2) (s1 <|> s2) (selectLinf f1 f2) b1
571+ = Just $ ListLevelStyle (select' t1 t2) (p1 <|> p2) (s1 <|> s2)
572+ (selectLinf f1 f2) b1
572 select' LltNumbered _ = LltNumbered
573 select' LltNumbered _ = LltNumbered
573 select' _ LltNumbered = LltNumbered
574 select' _ LltNumbered = LltNumbered
574 select' _ _ = LltBullet
575 select' _ _ = LltBullet
src/Text/Pandoc/Readers/Org.hs+4−7
@@ -18,22 +18,19 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad)
18import Text.Pandoc.Definition
18import Text.Pandoc.Definition
19import Text.Pandoc.Options
19import Text.Pandoc.Options
20import Text.Pandoc.Parsing (reportLogMessages)
20import Text.Pandoc.Parsing (reportLogMessages)
21−import Text.Pandoc.Shared (crFilter)
21+import Text.Pandoc.Sources (ToSources(..), ensureFinalNewlines)
22−
23import Control.Monad.Except (throwError)
22import Control.Monad.Except (throwError)
24import Control.Monad.Reader (runReaderT)
23import Control.Monad.Reader (runReaderT)
25
24
26−import Data.Text (Text)
27−
28-- | Parse org-mode string and return a Pandoc document.
25-- | Parse org-mode string and return a Pandoc document.
29−readOrg :: PandocMonad m
26+readOrg :: (PandocMonad m, ToSources a)
30 => ReaderOptions -- ^ Reader options
27 => ReaderOptions -- ^ Reader options
31− -> Text -- ^ String to parse (assuming @'\n'@ line endings)
28+ -> a
32 -> m Pandoc
29 -> m Pandoc
33readOrg opts s = do
30readOrg opts s = do
34 parsed <- flip runReaderT def $
31 parsed <- flip runReaderT def $
35 readWithM parseOrg (optionsToParserState opts)
32 readWithM parseOrg (optionsToParserState opts)
36− (crFilter s <> "\n\n")
33+ (ensureFinalNewlines 2 (toSources s))
37 case parsed of
34 case parsed of
38 Right result -> return result
35 Right result -> return result
39 Left e -> throwError e
36 Left e -> throwError e
src/Text/Pandoc/Readers/Org/Blocks.hs+7−4
@@ -39,10 +39,12 @@ import Data.Functor (($>))
39import Data.List (foldl', intersperse)
39import Data.List (foldl', intersperse)
40import Data.Maybe (fromMaybe, isJust, isNothing)
40import Data.Maybe (fromMaybe, isJust, isNothing)
41import Data.Text (Text)
41import Data.Text (Text)
42−
42+import Data.List.NonEmpty (nonEmpty)
43+import System.FilePath
43import qualified Data.Text as T
44import qualified Data.Text as T
44import qualified Text.Pandoc.Builder as B
45import qualified Text.Pandoc.Builder as B
45import qualified Text.Pandoc.Walk as Walk
46import qualified Text.Pandoc.Walk as Walk
47+import Text.Pandoc.Sources (ToSources(..))
46
48
47--
49--
48-- parsing blocks
50-- parsing blocks
@@ -526,7 +528,9 @@ include = try $ do
526 _ -> nullAttr
528 _ -> nullAttr
527 return $ pure . B.codeBlockWith attr <$> parseRaw
529 return $ pure . B.codeBlockWith attr <$> parseRaw
528 _ -> return $ return . B.fromList . blockFilter params <$> blockList
530 _ -> return $ return . B.fromList . blockFilter params <$> blockList
529− insertIncludedFileF blocksParser ["."] filename
531+ currentDir <- takeDirectory . sourceName <$> getPosition
532+ insertIncludedFile blocksParser toSources
533+ [currentDir] filename Nothing Nothing
530 where
534 where
531 includeTarget :: PandocMonad m => OrgParser m FilePath
535 includeTarget :: PandocMonad m => OrgParser m FilePath
532 includeTarget = do
536 includeTarget = do
@@ -542,8 +546,7 @@ include = try $ do
542 in case (minlvl >>= safeRead :: Maybe Int) of
546 in case (minlvl >>= safeRead :: Maybe Int) of
543 Nothing -> blks
547 Nothing -> blks
544 Just lvl -> let levels = Walk.query headerLevel blks
548 Just lvl -> let levels = Walk.query headerLevel blks
545− -- CAVE: partial function in else
546− curMin = if null levels then 0 else minimum levels
549+ curMin = maybe 0 minimum $ nonEmpty levels
547 in Walk.walk (shiftHeader (curMin - lvl)) blks
550 in Walk.walk (shiftHeader (curMin - lvl)) blks
548
551
549 headerLevel :: Block -> [Int]
552 headerLevel :: Block -> [Int]
src/Text/Pandoc/Readers/Org/Inlines.hs+6−5
@@ -29,6 +29,7 @@ import Text.Pandoc.Definition
29import Text.Pandoc.Options
29import Text.Pandoc.Options
30import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
30import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
31import Text.TeXMath (DisplayType (..), readTeX, writePandoc)
31import Text.TeXMath (DisplayType (..), readTeX, writePandoc)
32+import Text.Pandoc.Sources (ToSources(..))
32import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
33import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
33
34
34import Control.Monad (guard, mplus, mzero, unless, void, when)
35import Control.Monad (guard, mplus, mzero, unless, void, when)
@@ -262,7 +263,7 @@ berkeleyCitationList = try $ do
262 where
263 where
263 citationListPart :: PandocMonad m => OrgParser m (F Inlines)
264 citationListPart :: PandocMonad m => OrgParser m (F Inlines)
264 citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
265 citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
265− notFollowedBy' citeKey
266+ notFollowedBy' $ citeKey False
266 notFollowedBy (oneOf ";]")
267 notFollowedBy (oneOf ";]")
267 inline
268 inline
268
269
@@ -277,7 +278,7 @@ berkeleyBareTag' = try $ void (string "cite")
277
278
278berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation])
279berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation])
279berkeleyTextualCite = try $ do
280berkeleyTextualCite = try $ do
280− (suppressAuthor, key) <- citeKey
281+ (suppressAuthor, key) <- citeKey False
281 returnF . return $ Citation
282 returnF . return $ Citation
282 { citationId = key
283 { citationId = key
283 , citationPrefix = mempty
284 , citationPrefix = mempty
@@ -350,7 +351,7 @@ citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces)
350citation :: PandocMonad m => OrgParser m (F Citation)
351citation :: PandocMonad m => OrgParser m (F Citation)
351citation = try $ do
352citation = try $ do
352 pref <- prefix
353 pref <- prefix
353− (suppress_author, key) <- citeKey
354+ (suppress_author, key) <- citeKey False
354 suff <- suffix
355 suff <- suffix
355 return $ do
356 return $ do
356 x <- pref
357 x <- pref
@@ -367,7 +368,7 @@ citation = try $ do
367 }
368 }
368 where
369 where
369 prefix = trimInlinesF . mconcat <$>
370 prefix = trimInlinesF . mconcat <$>
370− manyTill inline (char ']' <|> (']' <$ lookAhead citeKey))
371+ manyTill inline (char ']' <|> (']' <$ lookAhead (citeKey False)))
371 suffix = try $ do
372 suffix = try $ do
372 hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
373 hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
373 skipSpaces
374 skipSpaces
@@ -802,7 +803,7 @@ inlineLaTeX = try $ do
802 parseAsInlineLaTeX :: PandocMonad m
803 parseAsInlineLaTeX :: PandocMonad m
803 => Text -> TeXExport -> OrgParser m (Maybe Inlines)
804 => Text -> TeXExport -> OrgParser m (Maybe Inlines)
804 parseAsInlineLaTeX cs = \case
805 parseAsInlineLaTeX cs = \case
805− TeXExport -> maybeRight <$> runParserT inlineCommand state "" cs
806+ TeXExport -> maybeRight <$> runParserT inlineCommand state "" (toSources cs)
806 TeXIgnore -> return (Just mempty)
807 TeXIgnore -> return (Just mempty)
807 TeXVerbatim -> return (Just $ B.str cs)
808 TeXVerbatim -> return (Just $ B.str cs)
808
809
src/Text/Pandoc/Readers/Org/Parsing.hs+5−5
@@ -63,8 +63,7 @@ module Text.Pandoc.Readers.Org.Parsing
63 , ellipses
63 , ellipses
64 , citeKey
64 , citeKey
65 , gridTableWith
65 , gridTableWith
66− , insertIncludedFileF
66+ , insertIncludedFile
67− -- * Re-exports from Text.Pandoc.Parsec
68 , runParser
67 , runParser
69 , runParserT
68 , runParserT
70 , getInput
69 , getInput
@@ -100,21 +99,22 @@ module Text.Pandoc.Readers.Org.Parsing
100 , getState
99 , getState
101 , updateState
100 , updateState
102 , SourcePos
101 , SourcePos
102+ , sourceName
103 , getPosition
103 , getPosition
104 ) where
104 ) where
105
105
106import Data.Text (Text)
106import Data.Text (Text)
107import Text.Pandoc.Readers.Org.ParserState
107import Text.Pandoc.Readers.Org.ParserState
108
108
109−import Text.Pandoc.Parsing hiding (F, anyLine, blanklines, newline,
109+import Text.Pandoc.Parsing hiding (anyLine, blanklines, newline,
110− parseFromString)
110+ parseFromString)
111import qualified Text.Pandoc.Parsing as P
111import qualified Text.Pandoc.Parsing as P
112
112
113import Control.Monad (guard)
113import Control.Monad (guard)
114import Control.Monad.Reader (ReaderT)
114import Control.Monad.Reader (ReaderT)
115
115
116-- | The parser used to read org files.
116-- | The parser used to read org files.
117−type OrgParser m = ParserT Text OrgParserState (ReaderT OrgParserLocal m)
117+type OrgParser m = ParserT Sources OrgParserState (ReaderT OrgParserLocal m)
118
118
119--
119--
120-- Adaptions and specializations of parsing utilities
120-- Adaptions and specializations of parsing utilities
src/Text/Pandoc/Readers/RST.hs+78−110
@@ -27,8 +27,7 @@ import Data.Text (Text)
27import qualified Data.Text as T
27import qualified Data.Text as T
28import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines)
28import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines)
29import qualified Text.Pandoc.Builder as B
29import qualified Text.Pandoc.Builder as B
30−import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem,
30+import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, getTimestamp)
31− readFileFromDirs, getTimestamp)
32import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV)
31import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV)
33import Text.Pandoc.Definition
32import Text.Pandoc.Definition
34import Text.Pandoc.Error
33import Text.Pandoc.Error
@@ -38,25 +37,25 @@ import Text.Pandoc.Options
38import Text.Pandoc.Parsing
37import Text.Pandoc.Parsing
39import Text.Pandoc.Shared
38import Text.Pandoc.Shared
40import qualified Text.Pandoc.UTF8 as UTF8
39import qualified Text.Pandoc.UTF8 as UTF8
41−import Text.Printf (printf)
42import Data.Time.Format
40import Data.Time.Format
41+import System.FilePath (takeDirectory)
43
42
44-- TODO:
43-- TODO:
45-- [ ] .. parsed-literal
44-- [ ] .. parsed-literal
46
45
47-- | Parse reStructuredText string and return Pandoc document.
46-- | Parse reStructuredText string and return Pandoc document.
48−readRST :: PandocMonad m
47+readRST :: (PandocMonad m, ToSources a)
49 => ReaderOptions -- ^ Reader options
48 => ReaderOptions -- ^ Reader options
50− -> Text -- ^ Text to parse (assuming @'\n'@ line endings)
49+ -> a
51 -> m Pandoc
50 -> m Pandoc
52readRST opts s = do
51readRST opts s = do
53 parsed <- readWithM parseRST def{ stateOptions = opts }
52 parsed <- readWithM parseRST def{ stateOptions = opts }
54− (crFilter s <> "\n\n")
53+ (ensureFinalNewlines 2 (toSources s))
55 case parsed of
54 case parsed of
56 Right result -> return result
55 Right result -> return result
57 Left e -> throwError e
56 Left e -> throwError e
58
57
59−type RSTParser m = ParserT Text ParserState m
58+type RSTParser m = ParserT Sources ParserState m
60
59
61--
60--
62-- Constants and data structure definitions
61-- Constants and data structure definitions
@@ -151,11 +150,19 @@ parseRST = do
151 startPos <- getPosition
150 startPos <- getPosition
152 -- go through once just to get list of reference keys and notes
151 -- go through once just to get list of reference keys and notes
153 -- docMinusKeys is the raw document with blanks where the keys were...
152 -- docMinusKeys is the raw document with blanks where the keys were...
154− docMinusKeys <- T.concat <$>
155− manyTill (referenceKey <|> anchorDef <|>
153+ let chunk = referenceKey
154+ <|> anchorDef
155+ <|> noteBlock
156− noteBlock <|> citationBlock <|>
156+ <|> citationBlock
157− (snd <$> withRaw comment) <|>
157+ <|> (snd <$> withRaw comment)
158− headerBlock <|> lineClump) eof
158+ <|> headerBlock
159+ <|> lineClump
160+ docMinusKeys <- Sources <$>
161+ manyTill (do pos <- getPosition
162+ t <- chunk
163+ return (pos, t)) eof
164+ -- UGLY: we collapse source position information.
165+ -- TODO: fix the parser to use the F monad instead of two passes
159 setInput docMinusKeys
166 setInput docMinusKeys
160 setPosition startPos
167 setPosition startPos
161 st' <- getState
168 st' <- getState
@@ -348,7 +355,7 @@ singleHeader' = try $ do
348-- hrule block
355-- hrule block
349--
356--
350
357
351−hrule :: Monad m => ParserT Text st m Blocks
358+hrule :: Monad m => ParserT Sources st m Blocks
352hrule = try $ do
359hrule = try $ do
353 chr <- oneOf underlineChars
360 chr <- oneOf underlineChars
354 count 3 (char chr)
361 count 3 (char chr)
@@ -363,7 +370,7 @@ hrule = try $ do
363
370
364-- read a line indented by a given string
371-- read a line indented by a given string
365indentedLine :: (HasReaderOptions st, Monad m)
372indentedLine :: (HasReaderOptions st, Monad m)
366− => Int -> ParserT Text st m Text
373+ => Int -> ParserT Sources st m Text
367indentedLine indents = try $ do
374indentedLine indents = try $ do
368 lookAhead spaceChar
375 lookAhead spaceChar
369 gobbleAtMostSpaces indents
376 gobbleAtMostSpaces indents
@@ -372,7 +379,7 @@ indentedLine indents = try $ do
372-- one or more indented lines, possibly separated by blank lines.
379-- one or more indented lines, possibly separated by blank lines.
373-- any amount of indentation will work.
380-- any amount of indentation will work.
374indentedBlock :: (HasReaderOptions st, Monad m)
381indentedBlock :: (HasReaderOptions st, Monad m)
375− => ParserT Text st m Text
382+ => ParserT Sources st m Text
376indentedBlock = try $ do
383indentedBlock = try $ do
377 indents <- length <$> lookAhead (many1 spaceChar)
384 indents <- length <$> lookAhead (many1 spaceChar)
378 lns <- many1 $ try $ do b <- option "" blanklines
385 lns <- many1 $ try $ do b <- option "" blanklines
@@ -381,20 +388,20 @@ indentedBlock = try $ do
381 optional blanklines
388 optional blanklines
382 return $ T.unlines lns
389 return $ T.unlines lns
383
390
384−quotedBlock :: Monad m => ParserT Text st m Text
391+quotedBlock :: Monad m => ParserT Sources st m Text
385quotedBlock = try $ do
392quotedBlock = try $ do
386 quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
393 quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
387 lns <- many1 $ lookAhead (char quote) >> anyLine
394 lns <- many1 $ lookAhead (char quote) >> anyLine
388 optional blanklines
395 optional blanklines
389 return $ T.unlines lns
396 return $ T.unlines lns
390
397
391−codeBlockStart :: Monad m => ParserT Text st m Char
398+codeBlockStart :: Monad m => ParserT Sources st m Char
392codeBlockStart = string "::" >> blankline >> blankline
399codeBlockStart = string "::" >> blankline >> blankline
393
400
394−codeBlock :: Monad m => ParserT Text ParserState m Blocks
401+codeBlock :: Monad m => ParserT Sources ParserState m Blocks
395codeBlock = try $ codeBlockStart >> codeBlockBody
402codeBlock = try $ codeBlockStart >> codeBlockBody
396
403
397−codeBlockBody :: Monad m => ParserT Text ParserState m Blocks
404+codeBlockBody :: Monad m => ParserT Sources ParserState m Blocks
398codeBlockBody = do
405codeBlockBody = do
399 lang <- stateRstHighlight <$> getState
406 lang <- stateRstHighlight <$> getState
400 try $ B.codeBlockWith ("", maybeToList lang, []) . stripTrailingNewlines <$>
407 try $ B.codeBlockWith ("", maybeToList lang, []) . stripTrailingNewlines <$>
@@ -410,14 +417,14 @@ lhsCodeBlock = try $ do
410 return $ B.codeBlockWith ("", ["haskell","literate"], [])
417 return $ B.codeBlockWith ("", ["haskell","literate"], [])
411 $ T.intercalate "\n" lns
418 $ T.intercalate "\n" lns
412
419
413−latexCodeBlock :: Monad m => ParserT Text st m [Text]
420+latexCodeBlock :: Monad m => ParserT Sources st m [Text]
414latexCodeBlock = try $ do
421latexCodeBlock = try $ do
415 try (latexBlockLine "\\begin{code}")
422 try (latexBlockLine "\\begin{code}")
416 many1Till anyLine (try $ latexBlockLine "\\end{code}")
423 many1Till anyLine (try $ latexBlockLine "\\end{code}")
417 where
424 where
418 latexBlockLine s = skipMany spaceChar >> string s >> blankline
425 latexBlockLine s = skipMany spaceChar >> string s >> blankline
419
426
420−birdCodeBlock :: Monad m => ParserT Text st m [Text]
427+birdCodeBlock :: Monad m => ParserT Sources st m [Text]
421birdCodeBlock = filterSpace <$> many1 birdTrackLine
428birdCodeBlock = filterSpace <$> many1 birdTrackLine
422 where filterSpace lns =
429 where filterSpace lns =
423 -- if (as is normal) there is always a space after >, drop it
430 -- if (as is normal) there is always a space after >, drop it
@@ -425,7 +432,7 @@ birdCodeBlock = filterSpace <$> many1 birdTrackLine
425 then map (T.drop 1) lns
432 then map (T.drop 1) lns
426 else lns
433 else lns
427
434
428−birdTrackLine :: Monad m => ParserT Text st m Text
435+birdTrackLine :: Monad m => ParserT Sources st m Text
429birdTrackLine = char '>' >> anyLine
436birdTrackLine = char '>' >> anyLine
430
437
431--
438--
@@ -446,64 +453,39 @@ encoding
446-}
453-}
447
454
448includeDirective :: PandocMonad m
455includeDirective :: PandocMonad m
456+ => Text
449− => Text -> [(Text, Text)] -> Text
457+ -> [(Text, Text)]
458+ -> Text
450 -> RSTParser m Blocks
459 -> RSTParser m Blocks
451includeDirective top fields body = do
460includeDirective top fields body = do
452− let f = trim top
461+ let f = T.unpack $ trim top
453− guard $ not (T.null f)
462+ guard $ not $ null f
454 guard $ T.null (trim body)
463 guard $ T.null (trim body)
455− -- options
456− let (startLine :: Maybe Int) = lookup "start-line" fields >>= safeRead
464+ let startLine = lookup "start-line" fields >>= safeRead
457− let (endLine :: Maybe Int) = lookup "end-line" fields >>= safeRead
465+ let endLine = lookup "end-line" fields >>= safeRead
458− oldPos <- getPosition
459− oldInput <- getInput
460− containers <- stateContainers <$> getState
461− when (f `elem` containers) $
462− throwError $ PandocParseError $ "Include file loop at " <> tshow oldPos
463− updateState $ \s -> s{ stateContainers = f : stateContainers s }
464− mbContents <- readFileFromDirs ["."] $ T.unpack f
465− contentLines <- case mbContents of
466− Just s -> return $ T.lines s
467− Nothing -> do
468− logMessage $ CouldNotLoadIncludeFile f oldPos
469− return []
470− let numLines = length contentLines
471− let startLine' = case startLine of
472− Nothing -> 1
473− Just x | x >= 0 -> x
474− | otherwise -> numLines + x -- negative from end
475− let endLine' = case endLine of
476− Nothing -> numLines + 1
477− Just x | x >= 0 -> x
478− | otherwise -> numLines + x -- negative from end
479− let contentLines' = drop (startLine' - 1)
480− $ take (endLine' - 1) contentLines
481− let contentLines'' = (case trim <$> lookup "end-before" fields of
482− Just patt -> takeWhile (not . (patt `T.isInfixOf`))
483− Nothing -> id) .
484− (case trim <$> lookup "start-after" fields of
485− Just patt -> drop 1 .
486− dropWhile (not . (patt `T.isInfixOf`))
487− Nothing -> id) $ contentLines'
488− let contents' = T.unlines contentLines''
489− case lookup "code" fields of
490− Just lang -> do
491− let classes = maybe [] T.words (lookup "class" fields)
466+ let classes = maybe [] T.words (lookup "class" fields)
492− let ident = maybe "" trimr $ lookup "name" fields
467+ let ident = maybe "" trimr $ lookup "name" fields
468+ let parser =
469+ case lookup "code" fields of
470+ Just lang ->
493− codeblock ident classes fields (trimr lang) contents' False
471+ (codeblock ident classes fields (trimr lang) False
472+ . sourcesToText) <$> getInput
473+ Nothing ->
494− Nothing -> case lookup "literal" fields of
474+ case lookup "literal" fields of
495− Just _ -> return $ B.rawBlock "rst" contents'
475+ Just _ -> B.rawBlock "rst" . sourcesToText <$> getInput
496− Nothing -> do
476+ Nothing -> parseBlocks
497− setPosition $ newPos (T.unpack f) 1 1
477+ let selectLines =
498− setInput $ contents' <> "\n"
478+ (case trim <$> lookup "end-before" fields of
499− bs <- optional blanklines >>
479+ Just patt -> takeWhile (not . (patt `T.isInfixOf`))
500− (mconcat <$> many block)
480+ Nothing -> id) .
501− setInput oldInput
481+ (case trim <$> lookup "start-after" fields of
502− setPosition oldPos
482+ Just patt -> drop 1 .
503− updateState $ \s -> s{ stateContainers =
483+ dropWhile (not . (patt `T.isInfixOf`))
504− tail $ stateContainers s }
484+ Nothing -> id)
505− return bs
485+ let toStream t =
506−
486+ toSources [(f, T.unlines . selectLines . T.lines $ t)]
487+ currentDir <- takeDirectory . sourceName <$> getPosition
488+ insertIncludedFile parser toStream [currentDir] f startLine endLine
507
489
508--
490--
509-- list blocks
491-- list blocks
@@ -526,7 +508,7 @@ definitionList :: PandocMonad m => RSTParser m Blocks
526definitionList = B.definitionList <$> many1 definitionListItem
508definitionList = B.definitionList <$> many1 definitionListItem
527
509
528-- parses bullet list start and returns its length (inc. following whitespace)
510-- parses bullet list start and returns its length (inc. following whitespace)
529−bulletListStart :: Monad m => ParserT Text st m Int
511+bulletListStart :: Monad m => ParserT Sources st m Int
530bulletListStart = try $ do
512bulletListStart = try $ do
531 notFollowedBy' hrule -- because hrules start out just like lists
513 notFollowedBy' hrule -- because hrules start out just like lists
532 marker <- oneOf bulletListMarkers
514 marker <- oneOf bulletListMarkers
@@ -613,8 +595,9 @@ comment = try $ do
613 string ".."
595 string ".."
614 skipMany1 spaceChar <|> (() <$ lookAhead newline)
596 skipMany1 spaceChar <|> (() <$ lookAhead newline)
615 -- notFollowedBy' directiveLabel -- comment comes after directive so unnec.
597 -- notFollowedBy' directiveLabel -- comment comes after directive so unnec.
616− manyTill anyChar blanklines
598+ _ <- anyLine
617 optional indentedBlock
599 optional indentedBlock
600+ optional blanklines
618 return mempty
601 return mempty
619
602
620directiveLabel :: Monad m => RSTParser m Text
603directiveLabel :: Monad m => RSTParser m Text
@@ -731,8 +714,8 @@ directive' = do
731 "" -> stateRstHighlight def
714 "" -> stateRstHighlight def
732 lang -> Just lang })
715 lang -> Just lang })
733 x | x == "code" || x == "code-block" || x == "sourcecode" ->
716 x | x == "code" || x == "code-block" || x == "sourcecode" ->
734− codeblock name classes (map (second trimr) fields)
717+ return $ codeblock name classes (map (second trimr) fields)
735− (trim top) body True
718+ (trim top) True body
736 "aafig" -> do
719 "aafig" -> do
737 let attribs = (name, ["aafig"], map (second trimr) fields)
720 let attribs = (name, ["aafig"], map (second trimr) fields)
738 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
721 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
@@ -1018,10 +1001,10 @@ toChunks = dropWhile T.null
1018 then "\\begin{aligned}\n" <> s <> "\n\\end{aligned}"
1001 then "\\begin{aligned}\n" <> s <> "\n\\end{aligned}"
1019 else s
1002 else s
1020
1003
1021−codeblock :: Text -> [Text] -> [(Text, Text)] -> Text -> Text -> Bool
1004+codeblock :: Text -> [Text] -> [(Text, Text)] -> Text -> Bool -> Text
1022− -> RSTParser m Blocks
1005+ -> Blocks
1023−codeblock ident classes fields lang body rmTrailingNewlines =
1006+codeblock ident classes fields lang rmTrailingNewlines body =
1024− return $ B.codeBlockWith attribs $ stripTrailingNewlines' body
1007+ B.codeBlockWith attribs $ stripTrailingNewlines' body
1025 where stripTrailingNewlines' = if rmTrailingNewlines
1008 where stripTrailingNewlines' = if rmTrailingNewlines
1026 then stripTrailingNewlines
1009 then stripTrailingNewlines
1027 else id
1010 else id
@@ -1102,7 +1085,7 @@ quotedReferenceName = try $ do
1102-- plus isolated (no two adjacent) internal hyphens, underscores,
1085-- plus isolated (no two adjacent) internal hyphens, underscores,
1103-- periods, colons and plus signs; no whitespace or other characters
1086-- periods, colons and plus signs; no whitespace or other characters
1104-- are allowed.
1087-- are allowed.
1105−simpleReferenceName :: Monad m => ParserT Text st m Text
1088+simpleReferenceName :: Monad m => ParserT Sources st m Text
1106simpleReferenceName = do
1089simpleReferenceName = do
1107 x <- alphaNum
1090 x <- alphaNum
1108 xs <- many $ alphaNum
1091 xs <- many $ alphaNum
@@ -1121,7 +1104,7 @@ referenceKey = do
1121 -- return enough blanks to replace key
1104 -- return enough blanks to replace key
1122 return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n"
1105 return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n"
1123
1106
1124−targetURI :: Monad m => ParserT Text st m Text
1107+targetURI :: Monad m => ParserT Sources st m Text
1125targetURI = do
1108targetURI = do
1126 skipSpaces
1109 skipSpaces
1127 optional $ try $ newline >> notFollowedBy blankline
1110 optional $ try $ newline >> notFollowedBy blankline
@@ -1159,8 +1142,10 @@ anonymousKey :: Monad m => RSTParser m ()
1159anonymousKey = try $ do
1142anonymousKey = try $ do
1160 oneOfStrings [".. __:", "__"]
1143 oneOfStrings [".. __:", "__"]
1161 src <- targetURI
1144 src <- targetURI
1162− pos <- getPosition
1145+ -- we need to ensure that the keys are ordered by occurrence in
1146+ -- the document.
1147+ numKeys <- M.size . stateKeys <$> getState
1163− let key = toKey $ "_" <> T.pack (printf "%09d" (sourceLine pos))
1148+ let key = toKey $ "_" <> T.pack (show numKeys)
1164 updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
1149 updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
1165 stateKeys s }
1150 stateKeys s }
1166
1151
@@ -1249,13 +1234,13 @@ headerBlock = do
1249-- Grid tables TODO:
1234-- Grid tables TODO:
1250-- - column spans
1235-- - column spans
1251
1236
1252−dashedLine :: Monad m => Char -> ParserT Text st m (Int, Int)
1237+dashedLine :: Monad m => Char -> ParserT Sources st m (Int, Int)
1253dashedLine ch = do
1238dashedLine ch = do
1254 dashes <- many1 (char ch)
1239 dashes <- many1 (char ch)
1255 sp <- many (char ' ')
1240 sp <- many (char ' ')
1256 return (length dashes, length $ dashes ++ sp)
1241 return (length dashes, length $ dashes ++ sp)
1257
1242
1258−simpleDashedLines :: Monad m => Char -> ParserT Text st m [(Int,Int)]
1243+simpleDashedLines :: Monad m => Char -> ParserT Sources st m [(Int,Int)]
1259simpleDashedLines ch = try $ many1 (dashedLine ch)
1244simpleDashedLines ch = try $ many1 (dashedLine ch)
1260
1245
1261-- Parse a table row separator
1246-- Parse a table row separator
@@ -1381,7 +1366,7 @@ hyphens = do
1381 -- don't want to treat endline after hyphen or dash as a space
1366 -- don't want to treat endline after hyphen or dash as a space
1382 return $ B.str result
1367 return $ B.str result
1383
1368
1384−escapedChar :: Monad m => ParserT Text st m Inlines
1369+escapedChar :: Monad m => ParserT Sources st m Inlines
1385escapedChar = do c <- escaped anyChar
1370escapedChar = do c <- escaped anyChar
1386 return $ if c == ' ' || c == '\n' || c == '\r'
1371 return $ if c == ' ' || c == '\n' || c == '\r'
1387 -- '\ ' is null in RST
1372 -- '\ ' is null in RST
@@ -1657,21 +1642,4 @@ note = try $ do
1657 return $ B.note contents
1642 return $ B.note contents
1658
1643
1659smart :: PandocMonad m => RSTParser m Inlines
1644smart :: PandocMonad m => RSTParser m Inlines
1660−smart = do
1645+smart = smartPunctuation inline
1661− guardEnabled Ext_smart
1662− doubleQuoted <|> singleQuoted <|>
1663− choice [apostrophe, dash, ellipses]
1664−
1665−singleQuoted :: PandocMonad m => RSTParser m Inlines
1666−singleQuoted = try $ do
1667− singleQuoteStart
1668− withQuoteContext InSingleQuote $
1669− B.singleQuoted . trimInlines . mconcat <$>
1670− many1Till inline singleQuoteEnd
1671−
1672−doubleQuoted :: PandocMonad m => RSTParser m Inlines
1673−doubleQuoted = try $ do
1674− doubleQuoteStart
1675− withQuoteContext InDoubleQuote $
1676− B.doubleQuoted . trimInlines . mconcat <$>
1677− many1Till inline doubleQuoteEnd
src/Text/Pandoc/Readers/Roff.hs+16−18
@@ -42,7 +42,6 @@ import Text.Pandoc.Logging (LogMessage(..))
42import Text.Pandoc.Options
42import Text.Pandoc.Options
43import Text.Pandoc.Parsing
43import Text.Pandoc.Parsing
44import Text.Pandoc.Shared (safeRead)
44import Text.Pandoc.Shared (safeRead)
45−import Text.Parsec hiding (tokenPrim)
46import Text.Pandoc.RoffChar (characterCodes, combiningAccents)
45import Text.Pandoc.RoffChar (characterCodes, combiningAccents)
47import qualified Data.Sequence as Seq
46import qualified Data.Sequence as Seq
48import qualified Data.Foldable as Foldable
47import qualified Data.Foldable as Foldable
@@ -122,16 +121,16 @@ instance Default RoffState where
122 , afterConditional = False
121 , afterConditional = False
123 }
122 }
124
123
125−type RoffLexer m = ParserT T.Text RoffState m
124+type RoffLexer m = ParserT Sources RoffState m
126
125
127--
126--
128-- Lexer: T.Text -> RoffToken
127-- Lexer: T.Text -> RoffToken
129--
128--
130
129
131−eofline :: Stream s m Char => ParsecT s u m ()
130+eofline :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s u m ()
132eofline = void newline <|> eof <|> () <$ lookAhead (string "\\}")
131eofline = void newline <|> eof <|> () <$ lookAhead (string "\\}")
133
132
134−spacetab :: Stream s m Char => ParsecT s u m Char
133+spacetab :: (Stream s m Char, UpdateSourcePos s Char) => ParserT s u m Char
135spacetab = char ' ' <|> char '\t'
134spacetab = char ' ' <|> char '\t'
136
135
137characterCodeMap :: M.Map T.Text Char
136characterCodeMap :: M.Map T.Text Char
@@ -303,8 +302,7 @@ expandString = try $ do
303 char '*'
302 char '*'
304 cs <- escapeArg <|> countChar 1 anyChar
303 cs <- escapeArg <|> countChar 1 anyChar
305 s <- linePartsToText <$> resolveText cs pos
304 s <- linePartsToText <$> resolveText cs pos
306− getInput >>= setInput . (s <>)
305+ addToInput s
307− return ()
308
306
309-- Parses: '..'
307-- Parses: '..'
310quoteArg :: PandocMonad m => RoffLexer m T.Text
308quoteArg :: PandocMonad m => RoffLexer m T.Text
@@ -316,7 +314,7 @@ escFont = do
316 font' <- if T.null font || font == "P"
314 font' <- if T.null font || font == "P"
317 then prevFont <$> getState
315 then prevFont <$> getState
318 else return $ foldr processFontLetter defaultFontSpec $ T.unpack font
316 else return $ foldr processFontLetter defaultFontSpec $ T.unpack font
319− modifyState $ \st -> st{ prevFont = currentFont st
317+ updateState $ \st -> st{ prevFont = currentFont st
320 , currentFont = font' }
318 , currentFont = font' }
321 return [Font font']
319 return [Font font']
322 where
320 where
@@ -372,8 +370,8 @@ lexTable pos = do
372 spaces
370 spaces
373 opts <- try tableOptions <|> [] <$ optional (char ';')
371 opts <- try tableOptions <|> [] <$ optional (char ';')
374 case lookup "tab" opts of
372 case lookup "tab" opts of
375− Just (T.uncons -> Just (c, _)) -> modifyState $ \st -> st{ tableTabChar = c }
373+ Just (T.uncons -> Just (c, _)) -> updateState $ \st -> st{ tableTabChar = c }
376− _ -> modifyState $ \st -> st{ tableTabChar = '\t' }
374+ _ -> updateState $ \st -> st{ tableTabChar = '\t' }
377 spaces
375 spaces
378 skipMany lexComment
376 skipMany lexComment
379 spaces
377 spaces
@@ -489,18 +487,18 @@ lexConditional mname = do
489 ifPart <- do
487 ifPart <- do
490 optional $ try $ char '\\' >> newline
488 optional $ try $ char '\\' >> newline
491 lexGroup
489 lexGroup
492− <|> do modifyState $ \s -> s{ afterConditional = True }
490+ <|> do updateState $ \s -> s{ afterConditional = True }
493 t <- manToken
491 t <- manToken
494− modifyState $ \s -> s{ afterConditional = False }
492+ updateState $ \s -> s{ afterConditional = False }
495 return t
493 return t
496 case mbtest of
494 case mbtest of
497 Nothing -> do
495 Nothing -> do
498− putState st -- reset state, so we don't record macros in skipped section
496+ setState st -- reset state, so we don't record macros in skipped section
499 report $ SkippedContent (T.cons '.' mname) pos
497 report $ SkippedContent (T.cons '.' mname) pos
500 return mempty
498 return mempty
501 Just True -> return ifPart
499 Just True -> return ifPart
502 Just False -> do
500 Just False -> do
503− putState st
501+ setState st
504 return mempty
502 return mempty
505
503
506expression :: PandocMonad m => RoffLexer m (Maybe Bool)
504expression :: PandocMonad m => RoffLexer m (Maybe Bool)
@@ -515,7 +513,7 @@ expression = do
515 _ -> Nothing
513 _ -> Nothing
516 where
514 where
517 returnValue v = do
515 returnValue v = do
518− modifyState $ \st -> st{ lastExpression = v }
516+ updateState $ \st -> st{ lastExpression = v }
519 return v
517 return v
520
518
521lexGroup :: PandocMonad m => RoffLexer m RoffTokens
519lexGroup :: PandocMonad m => RoffLexer m RoffTokens
@@ -536,7 +534,7 @@ lexIncludeFile args = do
536 result <- readFileFromDirs dirs $ T.unpack fp
534 result <- readFileFromDirs dirs $ T.unpack fp
537 case result of
535 case result of
538 Nothing -> report $ CouldNotLoadIncludeFile fp pos
536 Nothing -> report $ CouldNotLoadIncludeFile fp pos
539− Just s -> getInput >>= setInput . (s <>)
537+ Just s -> addToInput s
540 return mempty
538 return mempty
541 [] -> return mempty
539 [] -> return mempty
542
540
@@ -564,13 +562,13 @@ lexStringDef args = do -- string definition
564 (x:ys) -> do
562 (x:ys) -> do
565 let ts = singleTok $ TextLine (intercalate [RoffStr " " ] ys)
563 let ts = singleTok $ TextLine (intercalate [RoffStr " " ] ys)
566 let stringName = linePartsToText x
564 let stringName = linePartsToText x
567− modifyState $ \st ->
565+ updateState $ \st ->
568 st{ customMacros = M.insert stringName ts (customMacros st) }
566 st{ customMacros = M.insert stringName ts (customMacros st) }
569 return mempty
567 return mempty
570
568
571lexMacroDef :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens
569lexMacroDef :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens
572lexMacroDef args = do -- macro definition
570lexMacroDef args = do -- macro definition
573− modifyState $ \st -> st{ roffMode = CopyMode }
571+ updateState $ \st -> st{ roffMode = CopyMode }
574 (macroName, stopMacro) <-
572 (macroName, stopMacro) <-
575 case args of
573 case args of
576 (x : y : _) -> return (linePartsToText x, linePartsToText y)
574 (x : y : _) -> return (linePartsToText x, linePartsToText y)
@@ -584,7 +582,7 @@ lexMacroDef args = do -- macro definition
584 _ <- lexArgs
582 _ <- lexArgs
585 return ()
583 return ()
586 ts <- mconcat <$> manyTill manToken stop
584 ts <- mconcat <$> manyTill manToken stop
587− modifyState $ \st ->
585+ updateState $ \st ->
588 st{ customMacros = M.insert macroName ts (customMacros st)
586 st{ customMacros = M.insert macroName ts (customMacros st)
589 , roffMode = NormalMode }
587 , roffMode = NormalMode }
590 return mempty
588 return mempty
src/Text/Pandoc/Readers/TWiki.hs+7−27
@@ -28,22 +28,22 @@ import Text.Pandoc.Definition
28import Text.Pandoc.Options
28import Text.Pandoc.Options
29import Text.Pandoc.Parsing hiding (enclosed, nested)
29import Text.Pandoc.Parsing hiding (enclosed, nested)
30import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)
30import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)
31−import Text.Pandoc.Shared (crFilter, tshow)
31+import Text.Pandoc.Shared (tshow)
32import Text.Pandoc.XML (fromEntities)
32import Text.Pandoc.XML (fromEntities)
33
33
34-- | Read twiki from an input string and return a Pandoc document.
34-- | Read twiki from an input string and return a Pandoc document.
35−readTWiki :: PandocMonad m
35+readTWiki :: (PandocMonad m, ToSources a)
36 => ReaderOptions
36 => ReaderOptions
37− -> Text
37+ -> a
38 -> m Pandoc
38 -> m Pandoc
39readTWiki opts s = do
39readTWiki opts s = do
40+ let sources = ensureFinalNewlines 2 (toSources s)
40− res <- readWithM parseTWiki def{ stateOptions = opts }
41+ res <- readWithM parseTWiki def{ stateOptions = opts } sources
41− (crFilter s <> "\n\n")
42 case res of
42 case res of
43 Left e -> throwError e
43 Left e -> throwError e
44 Right d -> return d
44 Right d -> return d
45
45
46−type TWParser = ParserT Text ParserState
46+type TWParser = ParserT Sources ParserState
47
47
48--
48--
49-- utility functions
49-- utility functions
@@ -469,27 +469,7 @@ symbol :: PandocMonad m => TWParser m B.Inlines
469symbol = B.str <$> countChar 1 nonspaceChar
469symbol = B.str <$> countChar 1 nonspaceChar
470
470
471smart :: PandocMonad m => TWParser m B.Inlines
471smart :: PandocMonad m => TWParser m B.Inlines
472−smart = do
472+smart = smartPunctuation inline
473− guardEnabled Ext_smart
474− doubleQuoted <|> singleQuoted <|>
475− choice [ apostrophe
476− , dash
477− , ellipses
478− ]
479−
480−singleQuoted :: PandocMonad m => TWParser m B.Inlines
481−singleQuoted = try $ do
482− singleQuoteStart
483− withQuoteContext InSingleQuote
484− (B.singleQuoted . B.trimInlines . mconcat <$> many1Till inline singleQuoteEnd)
485−
486−doubleQuoted :: PandocMonad m => TWParser m B.Inlines
487−doubleQuoted = try $ do
488− doubleQuoteStart
489− contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
490− withQuoteContext InDoubleQuote (doubleQuoteEnd >>
491− return (B.doubleQuoted $ B.trimInlines contents))
492− <|> return (B.str "\8220" B.<> contents)
493
473
494link :: PandocMonad m => TWParser m B.Inlines
474link :: PandocMonad m => TWParser m B.Inlines
495link = try $ do
475link = try $ do
src/Text/Pandoc/Readers/Textile.hs+94−88
@@ -38,7 +38,8 @@ module Text.Pandoc.Readers.Textile ( readTextile) where
38import Control.Monad (guard, liftM)
38import Control.Monad (guard, liftM)
39import Control.Monad.Except (throwError)
39import Control.Monad.Except (throwError)
40import Data.Char (digitToInt, isUpper)
40import Data.Char (digitToInt, isUpper)
41−import Data.List (intersperse, transpose)
41+import Data.List (intersperse, transpose, foldl')
42+import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
42import Data.Text (Text)
43import Data.Text (Text)
43import qualified Data.Text as T
44import qualified Data.Text as T
44import Text.HTML.TagSoup (Tag (..), fromAttrib)
45import Text.HTML.TagSoup (Tag (..), fromAttrib)
@@ -52,30 +53,34 @@ import Text.Pandoc.Options
52import Text.Pandoc.Parsing
53import Text.Pandoc.Parsing
53import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag)
54import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag)
54import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
55import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
55−import Text.Pandoc.Shared (crFilter, trim, tshow)
56+import Text.Pandoc.Shared (trim, tshow)
56
57
57-- | Parse a Textile text and return a Pandoc document.
58-- | Parse a Textile text and return a Pandoc document.
58−readTextile :: PandocMonad m
59+readTextile :: (PandocMonad m, ToSources a)
59 => ReaderOptions -- ^ Reader options
60 => ReaderOptions -- ^ Reader options
60− -> Text -- ^ String to parse (assuming @'\n'@ line endings)
61+ -> a
61 -> m Pandoc
62 -> m Pandoc
62readTextile opts s = do
63readTextile opts s = do
64+ let sources = ensureFinalNewlines 2 (toSources s)
63− parsed <- readWithM parseTextile def{ stateOptions = opts }
65+ parsed <- readWithM parseTextile def{ stateOptions = opts } sources
64− (crFilter s <> "\n\n")
65 case parsed of
66 case parsed of
66 Right result -> return result
67 Right result -> return result
67 Left e -> throwError e
68 Left e -> throwError e
68
69
70+type TextileParser = ParserT Sources ParserState
69
71
70-- | Generate a Pandoc ADT from a textile document
72-- | Generate a Pandoc ADT from a textile document
71−parseTextile :: PandocMonad m => ParserT Text ParserState m Pandoc
73+parseTextile :: PandocMonad m => TextileParser m Pandoc
72parseTextile = do
74parseTextile = do
73 many blankline
75 many blankline
74 startPos <- getPosition
76 startPos <- getPosition
75 -- go through once just to get list of reference keys and notes
77 -- go through once just to get list of reference keys and notes
76 -- docMinusKeys is the raw document with blanks where the keys/notes were...
78 -- docMinusKeys is the raw document with blanks where the keys/notes were...
79+ let firstPassParser = do
80+ pos <- getPosition
77− let firstPassParser = noteBlock <|> lineClump
81+ t <- noteBlock <|> lineClump
82+ return (pos, t)
78− manyTill firstPassParser eof >>= setInput . T.concat
83+ manyTill firstPassParser eof >>= setInput . Sources
79 setPosition startPos
84 setPosition startPos
80 st' <- getState
85 st' <- getState
81 let reversedNotes = stateNotes st'
86 let reversedNotes = stateNotes st'
@@ -83,10 +88,10 @@ parseTextile = do
83 -- now parse it for real...
88 -- now parse it for real...
84 Pandoc nullMeta . B.toList <$> parseBlocks -- FIXME
89 Pandoc nullMeta . B.toList <$> parseBlocks -- FIXME
85
90
86−noteMarker :: PandocMonad m => ParserT Text ParserState m Text
91+noteMarker :: PandocMonad m => TextileParser m Text
87noteMarker = skipMany spaceChar >> string "fn" >> T.pack <$> manyTill digit (char '.')
92noteMarker = skipMany spaceChar >> string "fn" >> T.pack <$> manyTill digit (char '.')
88
93
89−noteBlock :: PandocMonad m => ParserT Text ParserState m Text
94+noteBlock :: PandocMonad m => TextileParser m Text
90noteBlock = try $ do
95noteBlock = try $ do
91 startPos <- getPosition
96 startPos <- getPosition
92 ref <- noteMarker
97 ref <- noteMarker
@@ -101,11 +106,11 @@ noteBlock = try $ do
101 return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n"
106 return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n"
102
107
103-- | Parse document blocks
108-- | Parse document blocks
104−parseBlocks :: PandocMonad m => ParserT Text ParserState m Blocks
109+parseBlocks :: PandocMonad m => TextileParser m Blocks
105parseBlocks = mconcat <$> manyTill block eof
110parseBlocks = mconcat <$> manyTill block eof
106
111
107-- | Block parsers list tried in definition order
112-- | Block parsers list tried in definition order
108−blockParsers :: PandocMonad m => [ParserT Text ParserState m Blocks]
113+blockParsers :: PandocMonad m => [TextileParser m Blocks]
109blockParsers = [ codeBlock
114blockParsers = [ codeBlock
110 , header
115 , header
111 , blockQuote
116 , blockQuote
@@ -120,22 +125,22 @@ blockParsers = [ codeBlock
120 ]
125 ]
121
126
122-- | Any block in the order of definition of blockParsers
127-- | Any block in the order of definition of blockParsers
123−block :: PandocMonad m => ParserT Text ParserState m Blocks
128+block :: PandocMonad m => TextileParser m Blocks
124block = do
129block = do
125 res <- choice blockParsers <?> "block"
130 res <- choice blockParsers <?> "block"
126 trace (T.take 60 $ tshow $ B.toList res)
131 trace (T.take 60 $ tshow $ B.toList res)
127 return res
132 return res
128
133
129−commentBlock :: PandocMonad m => ParserT Text ParserState m Blocks
134+commentBlock :: PandocMonad m => TextileParser m Blocks
130commentBlock = try $ do
135commentBlock = try $ do
131 string "###."
136 string "###."
132 manyTill anyLine blanklines
137 manyTill anyLine blanklines
133 return mempty
138 return mempty
134
139
135−codeBlock :: PandocMonad m => ParserT Text ParserState m Blocks
140+codeBlock :: PandocMonad m => TextileParser m Blocks
136codeBlock = codeBlockTextile <|> codeBlockHtml
141codeBlock = codeBlockTextile <|> codeBlockHtml
137
142
138−codeBlockTextile :: PandocMonad m => ParserT Text ParserState m Blocks
143+codeBlockTextile :: PandocMonad m => TextileParser m Blocks
139codeBlockTextile = try $ do
144codeBlockTextile = try $ do
140 string "bc." <|> string "pre."
145 string "bc." <|> string "pre."
141 extended <- option False (True <$ char '.')
146 extended <- option False (True <$ char '.')
@@ -155,7 +160,7 @@ trimTrailingNewlines :: Text -> Text
155trimTrailingNewlines = T.dropWhileEnd (=='\n')
160trimTrailingNewlines = T.dropWhileEnd (=='\n')
156
161
157-- | Code Blocks in Textile are between <pre> and </pre>
162-- | Code Blocks in Textile are between <pre> and </pre>
158−codeBlockHtml :: PandocMonad m => ParserT Text ParserState m Blocks
163+codeBlockHtml :: PandocMonad m => TextileParser m Blocks
159codeBlockHtml = try $ do
164codeBlockHtml = try $ do
160 (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True))
165 (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True))
161 result' <- T.pack <$> manyTill anyChar (htmlTag (tagClose (=="pre")))
166 result' <- T.pack <$> manyTill anyChar (htmlTag (tagClose (=="pre")))
@@ -173,7 +178,7 @@ codeBlockHtml = try $ do
173 return $ B.codeBlockWith (ident,classes,kvs) result'''
178 return $ B.codeBlockWith (ident,classes,kvs) result'''
174
179
175-- | Header of the form "hN. content" with N in 1..6
180-- | Header of the form "hN. content" with N in 1..6
176−header :: PandocMonad m => ParserT Text ParserState m Blocks
181+header :: PandocMonad m => TextileParser m Blocks
177header = try $ do
182header = try $ do
178 char 'h'
183 char 'h'
179 level <- digitToInt <$> oneOf "123456"
184 level <- digitToInt <$> oneOf "123456"
@@ -185,14 +190,14 @@ header = try $ do
185 return $ B.headerWith attr' level name
190 return $ B.headerWith attr' level name
186
191
187-- | Blockquote of the form "bq. content"
192-- | Blockquote of the form "bq. content"
188−blockQuote :: PandocMonad m => ParserT Text ParserState m Blocks
193+blockQuote :: PandocMonad m => TextileParser m Blocks
189blockQuote = try $ do
194blockQuote = try $ do
190 string "bq" >> attributes >> char '.' >> whitespace
195 string "bq" >> attributes >> char '.' >> whitespace
191 B.blockQuote <$> para
196 B.blockQuote <$> para
192
197
193-- Horizontal rule
198-- Horizontal rule
194
199
195−hrule :: PandocMonad m => ParserT Text st m Blocks
200+hrule :: PandocMonad m => TextileParser m Blocks
196hrule = try $ do
201hrule = try $ do
197 skipSpaces
202 skipSpaces
198 start <- oneOf "-*"
203 start <- oneOf "-*"
@@ -207,39 +212,39 @@ hrule = try $ do
207-- | Can be a bullet list or an ordered list. This implementation is
212-- | Can be a bullet list or an ordered list. This implementation is
208-- strict in the nesting, sublist must start at exactly "parent depth
213-- strict in the nesting, sublist must start at exactly "parent depth
209-- plus one"
214-- plus one"
210−anyList :: PandocMonad m => ParserT Text ParserState m Blocks
215+anyList :: PandocMonad m => TextileParser m Blocks
211anyList = try $ anyListAtDepth 1 <* blanklines
216anyList = try $ anyListAtDepth 1 <* blanklines
212
217
213-- | This allow one type of list to be nested into an other type,
218-- | This allow one type of list to be nested into an other type,
214-- provided correct nesting
219-- provided correct nesting
215−anyListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks
220+anyListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks
216anyListAtDepth depth = choice [ bulletListAtDepth depth,
221anyListAtDepth depth = choice [ bulletListAtDepth depth,
217 orderedListAtDepth depth,
222 orderedListAtDepth depth,
218 definitionList ]
223 definitionList ]
219
224
220-- | Bullet List of given depth, depth being the number of leading '*'
225-- | Bullet List of given depth, depth being the number of leading '*'
221−bulletListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks
226+bulletListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks
222bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth)
227bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth)
223
228
224-- | Bullet List Item of given depth, depth being the number of
229-- | Bullet List Item of given depth, depth being the number of
225-- leading '*'
230-- leading '*'
226−bulletListItemAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks
231+bulletListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks
227bulletListItemAtDepth = genericListItemAtDepth '*'
232bulletListItemAtDepth = genericListItemAtDepth '*'
228
233
229-- | Ordered List of given depth, depth being the number of
234-- | Ordered List of given depth, depth being the number of
230-- leading '#'
235-- leading '#'
231−orderedListAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks
236+orderedListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks
232orderedListAtDepth depth = try $ do
237orderedListAtDepth depth = try $ do
233 items <- many1 (orderedListItemAtDepth depth)
238 items <- many1 (orderedListItemAtDepth depth)
234 return $ B.orderedList items
239 return $ B.orderedList items
235
240
236-- | Ordered List Item of given depth, depth being the number of
241-- | Ordered List Item of given depth, depth being the number of
237-- leading '#'
242-- leading '#'
238−orderedListItemAtDepth :: PandocMonad m => Int -> ParserT Text ParserState m Blocks
243+orderedListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks
239orderedListItemAtDepth = genericListItemAtDepth '#'
244orderedListItemAtDepth = genericListItemAtDepth '#'
240
245
241-- | Common implementation of list items
246-- | Common implementation of list items
242−genericListItemAtDepth :: PandocMonad m => Char -> Int -> ParserT Text ParserState m Blocks
247+genericListItemAtDepth :: PandocMonad m => Char -> Int -> TextileParser m Blocks
243genericListItemAtDepth c depth = try $ do
248genericListItemAtDepth c depth = try $ do
244 count depth (char c) >> attributes >> whitespace
249 count depth (char c) >> attributes >> whitespace
245 contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|>
250 contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|>
@@ -249,25 +254,25 @@ genericListItemAtDepth c depth = try $ do
249 return $ contents <> sublist
254 return $ contents <> sublist
250
255
251-- | A definition list is a set of consecutive definition items
256-- | A definition list is a set of consecutive definition items
252−definitionList :: PandocMonad m => ParserT Text ParserState m Blocks
257+definitionList :: PandocMonad m => TextileParser m Blocks
253definitionList = try $ B.definitionList <$> many1 definitionListItem
258definitionList = try $ B.definitionList <$> many1 definitionListItem
254
259
255-- | List start character.
260-- | List start character.
256−listStart :: PandocMonad m => ParserT Text ParserState m ()
261+listStart :: PandocMonad m => TextileParser m ()
257listStart = genericListStart '*'
262listStart = genericListStart '*'
258 <|> () <$ genericListStart '#'
263 <|> () <$ genericListStart '#'
259 <|> () <$ definitionListStart
264 <|> () <$ definitionListStart
260
265
261−genericListStart :: PandocMonad m => Char -> ParserT Text st m ()
266+genericListStart :: PandocMonad m => Char -> TextileParser m ()
262genericListStart c = () <$ try (many1 (char c) >> whitespace)
267genericListStart c = () <$ try (many1 (char c) >> whitespace)
263
268
264−basicDLStart :: PandocMonad m => ParserT Text ParserState m ()
269+basicDLStart :: PandocMonad m => TextileParser m ()
265basicDLStart = do
270basicDLStart = do
266 char '-'
271 char '-'
267 whitespace
272 whitespace
268 notFollowedBy newline
273 notFollowedBy newline
269
274
270−definitionListStart :: PandocMonad m => ParserT Text ParserState m Inlines
275+definitionListStart :: PandocMonad m => TextileParser m Inlines
271definitionListStart = try $ do
276definitionListStart = try $ do
272 basicDLStart
277 basicDLStart
273 trimInlines . mconcat <$>
278 trimInlines . mconcat <$>
@@ -280,15 +285,15 @@ definitionListStart = try $ do
280-- the term defined, then spaces and ":=". The definition follows, on
285-- the term defined, then spaces and ":=". The definition follows, on
281-- the same single line, or spaned on multiple line, after a line
286-- the same single line, or spaned on multiple line, after a line
282-- break.
287-- break.
283−definitionListItem :: PandocMonad m => ParserT Text ParserState m (Inlines, [Blocks])
288+definitionListItem :: PandocMonad m => TextileParser m (Inlines, [Blocks])
284definitionListItem = try $ do
289definitionListItem = try $ do
285 term <- mconcat . intersperse B.linebreak <$> many1 definitionListStart
290 term <- mconcat . intersperse B.linebreak <$> many1 definitionListStart
286 def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef)
291 def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef)
287 return (term, def')
292 return (term, def')
288− where inlineDef :: PandocMonad m => ParserT Text ParserState m [Blocks]
293+ where inlineDef :: PandocMonad m => TextileParser m [Blocks]
289 inlineDef = liftM (\d -> [B.plain d])
294 inlineDef = liftM (\d -> [B.plain d])
290 $ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline
295 $ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline
291− multilineDef :: PandocMonad m => ParserT Text ParserState m [Blocks]
296+ multilineDef :: PandocMonad m => TextileParser m [Blocks]
292 multilineDef = try $ do
297 multilineDef = try $ do
293 optional whitespace >> newline
298 optional whitespace >> newline
294 s <- T.pack <$> many1Till anyChar (try (string "=:" >> newline))
299 s <- T.pack <$> many1Till anyChar (try (string "=:" >> newline))
@@ -299,7 +304,7 @@ definitionListItem = try $ do
299-- raw content
304-- raw content
300
305
301-- | A raw Html Block, optionally followed by blanklines
306-- | A raw Html Block, optionally followed by blanklines
302−rawHtmlBlock :: PandocMonad m => ParserT Text ParserState m Blocks
307+rawHtmlBlock :: PandocMonad m => TextileParser m Blocks
303rawHtmlBlock = try $ do
308rawHtmlBlock = try $ do
304 skipMany spaceChar
309 skipMany spaceChar
305 (_,b) <- htmlTag isBlockTag
310 (_,b) <- htmlTag isBlockTag
@@ -307,14 +312,14 @@ rawHtmlBlock = try $ do
307 return $ B.rawBlock "html" b
312 return $ B.rawBlock "html" b
308
313
309-- | Raw block of LaTeX content
314-- | Raw block of LaTeX content
310−rawLaTeXBlock' :: PandocMonad m => ParserT Text ParserState m Blocks
315+rawLaTeXBlock' :: PandocMonad m => TextileParser m Blocks
311rawLaTeXBlock' = do
316rawLaTeXBlock' = do
312 guardEnabled Ext_raw_tex
317 guardEnabled Ext_raw_tex
313 B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces)
318 B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces)
314
319
315
320
316-- | In textile, paragraphs are separated by blank lines.
321-- | In textile, paragraphs are separated by blank lines.
317−para :: PandocMonad m => ParserT Text ParserState m Blocks
322+para :: PandocMonad m => TextileParser m Blocks
318para = B.para . trimInlines . mconcat <$> many1 inline
323para = B.para . trimInlines . mconcat <$> many1 inline
319
324
320-- Tables
325-- Tables
@@ -325,7 +330,7 @@ toAlignment '>' = AlignRight
325toAlignment '=' = AlignCenter
330toAlignment '=' = AlignCenter
326toAlignment _ = AlignDefault
331toAlignment _ = AlignDefault
327
332
328−cellAttributes :: PandocMonad m => ParserT Text ParserState m (Bool, Alignment)
333+cellAttributes :: PandocMonad m => TextileParser m (Bool, Alignment)
329cellAttributes = try $ do
334cellAttributes = try $ do
330 isHeader <- option False (True <$ char '_')
335 isHeader <- option False (True <$ char '_')
331 -- we just ignore colspan and rowspan markers:
336 -- we just ignore colspan and rowspan markers:
@@ -338,7 +343,7 @@ cellAttributes = try $ do
338 return (isHeader, alignment)
343 return (isHeader, alignment)
339
344
340-- | A table cell spans until a pipe |
345-- | A table cell spans until a pipe |
341−tableCell :: PandocMonad m => ParserT Text ParserState m ((Bool, Alignment), Blocks)
346+tableCell :: PandocMonad m => TextileParser m ((Bool, Alignment), Blocks)
342tableCell = try $ do
347tableCell = try $ do
343 char '|'
348 char '|'
344 (isHeader, alignment) <- option (False, AlignDefault) cellAttributes
349 (isHeader, alignment) <- option (False, AlignDefault) cellAttributes
@@ -349,7 +354,7 @@ tableCell = try $ do
349 return ((isHeader, alignment), B.plain content)
354 return ((isHeader, alignment), B.plain content)
350
355
351-- | A table row is made of many table cells
356-- | A table row is made of many table cells
352−tableRow :: PandocMonad m => ParserT Text ParserState m [((Bool, Alignment), Blocks)]
357+tableRow :: PandocMonad m => TextileParser m [((Bool, Alignment), Blocks)]
353tableRow = try $ do
358tableRow = try $ do
354 -- skip optional row attributes
359 -- skip optional row attributes
355 optional $ try $ do
360 optional $ try $ do
@@ -359,7 +364,7 @@ tableRow = try $ do
359 many1 tableCell <* char '|' <* blankline
364 many1 tableCell <* char '|' <* blankline
360
365
361-- | A table with an optional header.
366-- | A table with an optional header.
362−table :: PandocMonad m => ParserT Text ParserState m Blocks
367+table :: PandocMonad m => TextileParser m Blocks
363table = try $ do
368table = try $ do
364 -- ignore table attributes
369 -- ignore table attributes
365 caption <- option mempty $ try $ do
370 caption <- option mempty $ try $ do
@@ -375,8 +380,9 @@ table = try $ do
375 (toprow:rest) | any (fst . fst) toprow ->
380 (toprow:rest) | any (fst . fst) toprow ->
376 (toprow, rest)
381 (toprow, rest)
377 _ -> (mempty, rawrows)
382 _ -> (mempty, rawrows)
378− let nbOfCols = maximum $ map length (headers:rows)
383+ let nbOfCols = maximum $ fmap length (headers :| rows)
384+ let aligns = map (maybe AlignDefault minimum . nonEmpty) $
379− let aligns = map minimum $ transpose $ map (map (snd . fst)) (headers:rows)
385+ transpose $ map (map (snd . fst)) (headers:rows)
380 let toRow = Row nullAttr . map B.simpleCell
386 let toRow = Row nullAttr . map B.simpleCell
381 toHeaderRow l = [toRow l | not (null l)]
387 toHeaderRow l = [toRow l | not (null l)]
382 return $ B.table (B.simpleCaption $ B.plain caption)
388 return $ B.table (B.simpleCaption $ B.plain caption)
@@ -386,7 +392,7 @@ table = try $ do
386 (TableFoot nullAttr [])
392 (TableFoot nullAttr [])
387
393
388-- | Ignore markers for cols, thead, tfoot.
394-- | Ignore markers for cols, thead, tfoot.
389−ignorableRow :: PandocMonad m => ParserT Text ParserState m ()
395+ignorableRow :: PandocMonad m => TextileParser m ()
390ignorableRow = try $ do
396ignorableRow = try $ do
391 char '|'
397 char '|'
392 oneOf ":^-~"
398 oneOf ":^-~"
@@ -395,7 +401,7 @@ ignorableRow = try $ do
395 _ <- anyLine
401 _ <- anyLine
396 return ()
402 return ()
397
403
398−explicitBlockStart :: PandocMonad m => Text -> ParserT Text ParserState m ()
404+explicitBlockStart :: PandocMonad m => Text -> TextileParser m ()
399explicitBlockStart name = try $ do
405explicitBlockStart name = try $ do
400 string (T.unpack name)
406 string (T.unpack name)
401 attributes
407 attributes
@@ -407,8 +413,8 @@ explicitBlockStart name = try $ do
407-- However, they can be used to set HTML/CSS attributes when needed.
413-- However, they can be used to set HTML/CSS attributes when needed.
408maybeExplicitBlock :: PandocMonad m
414maybeExplicitBlock :: PandocMonad m
409 => Text -- ^ block tag name
415 => Text -- ^ block tag name
410− -> ParserT Text ParserState m Blocks -- ^ implicit block
416+ -> TextileParser m Blocks -- ^ implicit block
411− -> ParserT Text ParserState m Blocks
417+ -> TextileParser m Blocks
412maybeExplicitBlock name blk = try $ do
418maybeExplicitBlock name blk = try $ do
413 optional $ explicitBlockStart name
419 optional $ explicitBlockStart name
414 blk
420 blk
@@ -421,11 +427,11 @@ maybeExplicitBlock name blk = try $ do
421
427
422
428
423-- | Any inline element
429-- | Any inline element
424−inline :: PandocMonad m => ParserT Text ParserState m Inlines
430+inline :: PandocMonad m => TextileParser m Inlines
425inline = choice inlineParsers <?> "inline"
431inline = choice inlineParsers <?> "inline"
426
432
427-- | Inline parsers tried in order
433-- | Inline parsers tried in order
428−inlineParsers :: PandocMonad m => [ParserT Text ParserState m Inlines]
434+inlineParsers :: PandocMonad m => [TextileParser m Inlines]
429inlineParsers = [ str
435inlineParsers = [ str
430 , whitespace
436 , whitespace
431 , endline
437 , endline
@@ -445,7 +451,7 @@ inlineParsers = [ str
445 ]
451 ]
446
452
447-- | Inline markups
453-- | Inline markups
448−inlineMarkup :: PandocMonad m => ParserT Text ParserState m Inlines
454+inlineMarkup :: PandocMonad m => TextileParser m Inlines
449inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
455inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
450 , simpleInline (string "**") B.strong
456 , simpleInline (string "**") B.strong
451 , simpleInline (string "__") B.emph
457 , simpleInline (string "__") B.emph
@@ -459,29 +465,29 @@ inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
459 ]
465 ]
460
466
461-- | Trademark, registered, copyright
467-- | Trademark, registered, copyright
462−mark :: PandocMonad m => ParserT Text st m Inlines
468+mark :: PandocMonad m => TextileParser m Inlines
463mark = try $ char '(' >> (try tm <|> try reg <|> copy)
469mark = try $ char '(' >> (try tm <|> try reg <|> copy)
464
470
465−reg :: PandocMonad m => ParserT Text st m Inlines
471+reg :: PandocMonad m => TextileParser m Inlines
466reg = do
472reg = do
467 oneOf "Rr"
473 oneOf "Rr"
468 char ')'
474 char ')'
469 return $ B.str "\174"
475 return $ B.str "\174"
470
476
471−tm :: PandocMonad m => ParserT Text st m Inlines
477+tm :: PandocMonad m => TextileParser m Inlines
472tm = do
478tm = do
473 oneOf "Tt"
479 oneOf "Tt"
474 oneOf "Mm"
480 oneOf "Mm"
475 char ')'
481 char ')'
476 return $ B.str "\8482"
482 return $ B.str "\8482"
477
483
478−copy :: PandocMonad m => ParserT Text st m Inlines
484+copy :: PandocMonad m => TextileParser m Inlines
479copy = do
485copy = do
480 oneOf "Cc"
486 oneOf "Cc"
481 char ')'
487 char ')'
482 return $ B.str "\169"
488 return $ B.str "\169"
483
489
484−note :: PandocMonad m => ParserT Text ParserState m Inlines
490+note :: PandocMonad m => TextileParser m Inlines
485note = try $ do
491note = try $ do
486 ref <- char '[' *> many1 digit <* char ']'
492 ref <- char '[' *> many1 digit <* char ']'
487 notes <- stateNotes <$> getState
493 notes <- stateNotes <$> getState
@@ -505,13 +511,13 @@ wordBoundaries :: [Char]
505wordBoundaries = markupChars <> stringBreakers
511wordBoundaries = markupChars <> stringBreakers
506
512
507-- | Parse a hyphened sequence of words
513-- | Parse a hyphened sequence of words
508−hyphenedWords :: PandocMonad m => ParserT Text ParserState m Text
514+hyphenedWords :: PandocMonad m => TextileParser m Text
509hyphenedWords = do
515hyphenedWords = do
510 x <- wordChunk
516 x <- wordChunk
511 xs <- many (try $ char '-' >> wordChunk)
517 xs <- many (try $ char '-' >> wordChunk)
512 return $ T.intercalate "-" (x:xs)
518 return $ T.intercalate "-" (x:xs)
513
519
514−wordChunk :: PandocMonad m => ParserT Text ParserState m Text
520+wordChunk :: PandocMonad m => TextileParser m Text
515wordChunk = try $ do
521wordChunk = try $ do
516 hd <- noneOf wordBoundaries
522 hd <- noneOf wordBoundaries
517 tl <- many ( noneOf wordBoundaries <|>
523 tl <- many ( noneOf wordBoundaries <|>
@@ -520,7 +526,7 @@ wordChunk = try $ do
520 return $ T.pack $ hd:tl
526 return $ T.pack $ hd:tl
521
527
522-- | Any string
528-- | Any string
523−str :: PandocMonad m => ParserT Text ParserState m Inlines
529+str :: PandocMonad m => TextileParser m Inlines
524str = do
530str = do
525 baseStr <- hyphenedWords
531 baseStr <- hyphenedWords
526 -- RedCloth compliance : if parsed word is uppercase and immediately
532 -- RedCloth compliance : if parsed word is uppercase and immediately
@@ -533,11 +539,11 @@ str = do
533 return $ B.str fullStr
539 return $ B.str fullStr
534
540
535-- | Some number of space chars
541-- | Some number of space chars
536−whitespace :: PandocMonad m => ParserT Text st m Inlines
542+whitespace :: PandocMonad m => TextileParser m Inlines
537whitespace = many1 spaceChar >> return B.space <?> "whitespace"
543whitespace = many1 spaceChar >> return B.space <?> "whitespace"
538
544
539-- | In Textile, an isolated endline character is a line break
545-- | In Textile, an isolated endline character is a line break
540−endline :: PandocMonad m => ParserT Text ParserState m Inlines
546+endline :: PandocMonad m => TextileParser m Inlines
541endline = try $ do
547endline = try $ do
542 newline
548 newline
543 notFollowedBy blankline
549 notFollowedBy blankline
@@ -545,18 +551,18 @@ endline = try $ do
545 notFollowedBy rawHtmlBlock
551 notFollowedBy rawHtmlBlock
546 return B.linebreak
552 return B.linebreak
547
553
548−rawHtmlInline :: PandocMonad m => ParserT Text ParserState m Inlines
554+rawHtmlInline :: PandocMonad m => TextileParser m Inlines
549rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
555rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
550
556
551-- | Raw LaTeX Inline
557-- | Raw LaTeX Inline
552−rawLaTeXInline' :: PandocMonad m => ParserT Text ParserState m Inlines
558+rawLaTeXInline' :: PandocMonad m => TextileParser m Inlines
553rawLaTeXInline' = try $ do
559rawLaTeXInline' = try $ do
554 guardEnabled Ext_raw_tex
560 guardEnabled Ext_raw_tex
555 B.rawInline "latex" <$> rawLaTeXInline
561 B.rawInline "latex" <$> rawLaTeXInline
556
562
557-- | Textile standard link syntax is "label":target. But we
563-- | Textile standard link syntax is "label":target. But we
558-- can also have ["label":target].
564-- can also have ["label":target].
559−link :: PandocMonad m => ParserT Text ParserState m Inlines
565+link :: PandocMonad m => TextileParser m Inlines
560link = try $ do
566link = try $ do
561 bracketed <- (True <$ char '[') <|> return False
567 bracketed <- (True <$ char '[') <|> return False
562 char '"' *> notFollowedBy (oneOf " \t\n\r")
568 char '"' *> notFollowedBy (oneOf " \t\n\r")
@@ -576,7 +582,7 @@ link = try $ do
576 else B.spanWith attr $ B.link url "" name'
582 else B.spanWith attr $ B.link url "" name'
577
583
578-- | image embedding
584-- | image embedding
579−image :: PandocMonad m => ParserT Text ParserState m Inlines
585+image :: PandocMonad m => TextileParser m Inlines
580image = try $ do
586image = try $ do
581 char '!' >> notFollowedBy space
587 char '!' >> notFollowedBy space
582 (ident, cls, kvs) <- attributes
588 (ident, cls, kvs) <- attributes
@@ -588,51 +594,51 @@ image = try $ do
588 char '!'
594 char '!'
589 return $ B.imageWith attr src alt (B.str alt)
595 return $ B.imageWith attr src alt (B.str alt)
590
596
591−escapedInline :: PandocMonad m => ParserT Text ParserState m Inlines
597+escapedInline :: PandocMonad m => TextileParser m Inlines
592escapedInline = escapedEqs <|> escapedTag
598escapedInline = escapedEqs <|> escapedTag
593
599
594−escapedEqs :: PandocMonad m => ParserT Text ParserState m Inlines
600+escapedEqs :: PandocMonad m => TextileParser m Inlines
595escapedEqs = B.str . T.pack <$>
601escapedEqs = B.str . T.pack <$>
596 try (string "==" *> manyTill anyChar' (try $ string "=="))
602 try (string "==" *> manyTill anyChar' (try $ string "=="))
597
603
598-- | literal text escaped btw <notextile> tags
604-- | literal text escaped btw <notextile> tags
599−escapedTag :: PandocMonad m => ParserT Text ParserState m Inlines
605+escapedTag :: PandocMonad m => TextileParser m Inlines
600escapedTag = B.str . T.pack <$>
606escapedTag = B.str . T.pack <$>
601 try (string "<notextile>" *>
607 try (string "<notextile>" *>
602 manyTill anyChar' (try $ string "</notextile>"))
608 manyTill anyChar' (try $ string "</notextile>"))
603
609
604-- | Any special symbol defined in wordBoundaries
610-- | Any special symbol defined in wordBoundaries
605−symbol :: PandocMonad m => ParserT Text ParserState m Inlines
611+symbol :: PandocMonad m => TextileParser m Inlines
606symbol = B.str . T.singleton <$> (notFollowedBy newline *>
612symbol = B.str . T.singleton <$> (notFollowedBy newline *>
607 notFollowedBy rawHtmlBlock *>
613 notFollowedBy rawHtmlBlock *>
608 oneOf wordBoundaries)
614 oneOf wordBoundaries)
609
615
610-- | Inline code
616-- | Inline code
611−code :: PandocMonad m => ParserT Text ParserState m Inlines
617+code :: PandocMonad m => TextileParser m Inlines
612code = code1 <|> code2
618code = code1 <|> code2
613
619
614-- any character except a newline before a blank line
620-- any character except a newline before a blank line
615−anyChar' :: PandocMonad m => ParserT Text ParserState m Char
621+anyChar' :: PandocMonad m => TextileParser m Char
616anyChar' =
622anyChar' =
617 satisfy (/='\n') <|>
623 satisfy (/='\n') <|>
618 try (char '\n' <* notFollowedBy blankline)
624 try (char '\n' <* notFollowedBy blankline)
619
625
620−code1 :: PandocMonad m => ParserT Text ParserState m Inlines
626+code1 :: PandocMonad m => TextileParser m Inlines
621code1 = B.code . T.pack <$> surrounded (char '@') anyChar'
627code1 = B.code . T.pack <$> surrounded (char '@') anyChar'
622
628
623−code2 :: PandocMonad m => ParserT Text ParserState m Inlines
629+code2 :: PandocMonad m => TextileParser m Inlines
624code2 = do
630code2 = do
625 htmlTag (tagOpen (=="tt") null)
631 htmlTag (tagOpen (=="tt") null)
626 B.code . T.pack <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt"))
632 B.code . T.pack <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt"))
627
633
628-- | Html / CSS attributes
634-- | Html / CSS attributes
629−attributes :: PandocMonad m => ParserT Text ParserState m Attr
635+attributes :: PandocMonad m => TextileParser m Attr
630−attributes = foldl (flip ($)) ("",[],[]) <$>
636+attributes = foldl' (flip ($)) ("",[],[]) <$>
631 try (do special <- option id specialAttribute
637 try (do special <- option id specialAttribute
632 attrs <- many attribute
638 attrs <- many attribute
633 return (special : attrs))
639 return (special : attrs))
634
640
635−specialAttribute :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr)
641+specialAttribute :: PandocMonad m => TextileParser m (Attr -> Attr)
636specialAttribute = do
642specialAttribute = do
637 alignStr <- ("center" <$ char '=') <|>
643 alignStr <- ("center" <$ char '=') <|>
638 ("justify" <$ try (string "<>")) <|>
644 ("justify" <$ try (string "<>")) <|>
@@ -641,11 +647,11 @@ specialAttribute = do
641 notFollowedBy spaceChar
647 notFollowedBy spaceChar
642 return $ addStyle $ T.pack $ "text-align:" ++ alignStr
648 return $ addStyle $ T.pack $ "text-align:" ++ alignStr
643
649
644−attribute :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr)
650+attribute :: PandocMonad m => TextileParser m (Attr -> Attr)
645attribute = try $
651attribute = try $
646 (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar
652 (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar
647
653
648−classIdAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr)
654+classIdAttr :: PandocMonad m => TextileParser m (Attr -> Attr)
649classIdAttr = try $ do -- (class class #id)
655classIdAttr = try $ do -- (class class #id)
650 char '('
656 char '('
651 ws <- T.words `fmap` T.pack <$> manyTill anyChar' (char ')')
657 ws <- T.words `fmap` T.pack <$> manyTill anyChar' (char ')')
@@ -657,7 +663,7 @@ classIdAttr = try $ do -- (class class #id)
657 classes'
663 classes'
658 -> return $ \(_,_,keyvals) -> ("",classes',keyvals)
664 -> return $ \(_,_,keyvals) -> ("",classes',keyvals)
659
665
660−styleAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr)
666+styleAttr :: PandocMonad m => TextileParser m (Attr -> Attr)
661styleAttr = do
667styleAttr = do
662 style <- try $ enclosed (char '{') (char '}') anyChar'
668 style <- try $ enclosed (char '{') (char '}') anyChar'
663 return $ addStyle $ T.pack style
669 return $ addStyle $ T.pack style
@@ -668,23 +674,23 @@ addStyle style (id',classes,keyvals) =
668 where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"]
674 where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"]
669 style' = style <> ";" <> T.concat [v | ("style",v) <- keyvals]
675 style' = style <> ";" <> T.concat [v | ("style",v) <- keyvals]
670
676
671−langAttr :: PandocMonad m => ParserT Text ParserState m (Attr -> Attr)
677+langAttr :: PandocMonad m => TextileParser m (Attr -> Attr)
672langAttr = do
678langAttr = do
673 lang <- try $ enclosed (char '[') (char ']') alphaNum
679 lang <- try $ enclosed (char '[') (char ']') alphaNum
674 return $ \(id',classes,keyvals) -> (id',classes,("lang",T.pack lang):keyvals)
680 return $ \(id',classes,keyvals) -> (id',classes,("lang",T.pack lang):keyvals)
675
681
676-- | Parses material surrounded by a parser.
682-- | Parses material surrounded by a parser.
677surrounded :: (PandocMonad m, Show t)
683surrounded :: (PandocMonad m, Show t)
678− => ParserT Text st m t -- ^ surrounding parser
684+ => ParserT Sources st m t -- ^ surrounding parser
679− -> ParserT Text st m a -- ^ content parser (to be used repeatedly)
685+ -> ParserT Sources st m a -- ^ content parser (to be used repeatedly)
680− -> ParserT Text st m [a]
686+ -> ParserT Sources st m [a]
681surrounded border =
687surrounded border =
682 enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border)
688 enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border)
683
689
684simpleInline :: PandocMonad m
690simpleInline :: PandocMonad m
685− => ParserT Text ParserState m t -- ^ surrounding parser
691+ => TextileParser m t -- ^ surrounding parser
686 -> (Inlines -> Inlines) -- ^ Inline constructor
692 -> (Inlines -> Inlines) -- ^ Inline constructor
687− -> ParserT Text ParserState m Inlines -- ^ content parser (to be used repeatedly)
693+ -> TextileParser m Inlines -- ^ content parser (to be used repeatedly)
688simpleInline border construct = try $ do
694simpleInline border construct = try $ do
689 notAfterString
695 notAfterString
690 border *> notFollowedBy (oneOf " \t\n\r")
696 border *> notFollowedBy (oneOf " \t\n\r")
@@ -698,7 +704,7 @@ simpleInline border construct = try $ do
698 then body
704 then body
699 else B.spanWith attr body
705 else B.spanWith attr body
700
706
701−groupedInlineMarkup :: PandocMonad m => ParserT Text ParserState m Inlines
707+groupedInlineMarkup :: PandocMonad m => TextileParser m Inlines
702groupedInlineMarkup = try $ do
708groupedInlineMarkup = try $ do
703 char '['
709 char '['
704 sp1 <- option mempty $ B.space <$ whitespace
710 sp1 <- option mempty $ B.space <$ whitespace
@@ -707,5 +713,5 @@ groupedInlineMarkup = try $ do
707 char ']'
713 char ']'
708 return $ sp1 <> result <> sp2
714 return $ sp1 <> result <> sp2
709
715
710−eof' :: Monad m => ParserT Text s m Char
716+eof' :: Monad m => ParserT Sources s m Char
711eof' = '\n' <$ eof
717eof' = '\n' <$ eof
src/Text/Pandoc/Readers/TikiWiki.hs+6−6
@@ -30,23 +30,23 @@ import Text.Pandoc.Definition
30import Text.Pandoc.Logging (Verbosity (..))
30import Text.Pandoc.Logging (Verbosity (..))
31import Text.Pandoc.Options
31import Text.Pandoc.Options
32import Text.Pandoc.Parsing hiding (enclosed, nested)
32import Text.Pandoc.Parsing hiding (enclosed, nested)
33−import Text.Pandoc.Shared (crFilter, safeRead)
33+import Text.Pandoc.Shared (safeRead)
34import Text.Pandoc.XML (fromEntities)
34import Text.Pandoc.XML (fromEntities)
35import Text.Printf (printf)
35import Text.Printf (printf)
36
36
37-- | Read TikiWiki from an input string and return a Pandoc document.
37-- | Read TikiWiki from an input string and return a Pandoc document.
38−readTikiWiki :: PandocMonad m
38+readTikiWiki :: (PandocMonad m, ToSources a)
39 => ReaderOptions
39 => ReaderOptions
40− -> Text
40+ -> a
41 -> m Pandoc
41 -> m Pandoc
42readTikiWiki opts s = do
42readTikiWiki opts s = do
43+ let sources = ensureFinalNewlines 2 (toSources s)
43− res <- readWithM parseTikiWiki def{ stateOptions = opts }
44+ res <- readWithM parseTikiWiki def{ stateOptions = opts } sources
44− (crFilter s <> "\n\n")
45 case res of
45 case res of
46 Left e -> throwError e
46 Left e -> throwError e
47 Right d -> return d
47 Right d -> return d
48
48
49−type TikiWikiParser = ParserT Text ParserState
49+type TikiWikiParser = ParserT Sources ParserState
50
50
51--
51--
52-- utility functions
52-- utility functions
src/Text/Pandoc/Readers/Txt2Tags.hs+47−21
@@ -1,4 +1,5 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE LambdaCase #-}
2{- |
3{- |
3 Module : Text.Pandoc.Readers.Txt2Tags
4 Module : Text.Pandoc.Readers.Txt2Tags
4 Copyright : Copyright (C) 2014 Matthew Pickering
5 Copyright : Copyright (C) 2014 Matthew Pickering
@@ -19,6 +20,7 @@ import Control.Monad.Except (catchError, throwError)
19import Control.Monad.Reader (Reader, asks, runReader)
20import Control.Monad.Reader (Reader, asks, runReader)
20import Data.Default
21import Data.Default
21import Data.List (intercalate, transpose)
22import Data.List (intercalate, transpose)
23+import Data.List.NonEmpty (nonEmpty)
22import Data.Maybe (fromMaybe)
24import Data.Maybe (fromMaybe)
23import Data.Text (Text)
25import Data.Text (Text)
24import qualified Data.Text as T
26import qualified Data.Text as T
@@ -31,9 +33,9 @@ import Data.Time (defaultTimeLocale)
31import Text.Pandoc.Definition
33import Text.Pandoc.Definition
32import Text.Pandoc.Options
34import Text.Pandoc.Options
33import Text.Pandoc.Parsing hiding (space, spaces, uri)
35import Text.Pandoc.Parsing hiding (space, spaces, uri)
34−import Text.Pandoc.Shared (compactify, compactifyDL, crFilter, escapeURI)
36+import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI)
35
37
36−type T2T = ParserT Text ParserState (Reader T2TMeta)
38+type T2T = ParserT Sources ParserState (Reader T2TMeta)
37
39
38-- | An object for the T2T macros meta information
40-- | An object for the T2T macros meta information
39-- the contents of each field is simply substituted verbatim into the file
41-- the contents of each field is simply substituted verbatim into the file
@@ -53,25 +55,28 @@ getT2TMeta = do
53 inps <- P.getInputFiles
55 inps <- P.getInputFiles
54 outp <- fromMaybe "" <$> P.getOutputFile
56 outp <- fromMaybe "" <$> P.getOutputFile
55 curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime
57 curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime
56− let getModTime = fmap (formatTime defaultTimeLocale "%T") .
57− P.getModificationTime
58− curMtime <- case inps of
58+ curMtime <- catchError
59+ (mapM P.getModificationTime inps >>=
60+ (\case
61+ Nothing ->
59− [] -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime
62+ formatTime defaultTimeLocale "%T" <$> P.getZonedTime
60− _ -> catchError
63+ Just ts -> return $
61− (maximum <$> mapM getModTime inps)
64+ formatTime defaultTimeLocale "%T" $ maximum ts)
65+ . nonEmpty)
62− (const (return ""))
66+ (const (return ""))
63− return $ T2TMeta (T.pack curDate) (T.pack curMtime) (intercalate ", " inps) outp
67+ return $ T2TMeta (T.pack curDate) (T.pack curMtime)
68+ (intercalate ", " inps) outp
64
69
65-- | Read Txt2Tags from an input string returning a Pandoc document
70-- | Read Txt2Tags from an input string returning a Pandoc document
66−readTxt2Tags :: PandocMonad m
71+readTxt2Tags :: (PandocMonad m, ToSources a)
67 => ReaderOptions
72 => ReaderOptions
68− -> Text
73+ -> a
69 -> m Pandoc
74 -> m Pandoc
70readTxt2Tags opts s = do
75readTxt2Tags opts s = do
76+ let sources = ensureFinalNewlines 2 (toSources s)
71 meta <- getT2TMeta
77 meta <- getT2TMeta
72 let parsed = flip runReader meta $
78 let parsed = flip runReader meta $
73− readWithM parseT2T (def {stateOptions = opts}) $
79+ readWithM parseT2T (def {stateOptions = opts}) sources
74− crFilter s <> "\n\n"
75 case parsed of
80 case parsed of
76 Right result -> return result
81 Right result -> return result
77 Left e -> throwError e
82 Left e -> throwError e
@@ -261,9 +266,9 @@ table = try $ do
261 rows <- many1 (many commentLine *> tableRow)
266 rows <- many1 (many commentLine *> tableRow)
262 let columns = transpose rows
267 let columns = transpose rows
263 let ncolumns = length columns
268 let ncolumns = length columns
264− let aligns = map (foldr1 findAlign . map fst) columns
269+ let aligns = map (fromMaybe AlignDefault . foldr findAlign Nothing) columns
265 let rows' = map (map snd) rows
270 let rows' = map (map snd) rows
266− let size = maximum (map length rows')
271+ let size = maybe 0 maximum $ nonEmpty $ map length rows'
267 let rowsPadded = map (pad size) rows'
272 let rowsPadded = map (pad size) rows'
268 let headerPadded = if null tableHeader then mempty else pad size tableHeader
273 let headerPadded = if null tableHeader then mempty else pad size tableHeader
269 let toRow = Row nullAttr . map B.simpleCell
274 let toRow = Row nullAttr . map B.simpleCell
@@ -278,10 +283,11 @@ pad :: (Monoid a) => Int -> [a] -> [a]
278pad n xs = xs ++ replicate (n - length xs) mempty
283pad n xs = xs ++ replicate (n - length xs) mempty
279
284
280
285
281−findAlign :: Alignment -> Alignment -> Alignment
286+findAlign :: (Alignment, a) -> Maybe Alignment -> Maybe Alignment
282−findAlign x y
287+findAlign (x,_) (Just y)
283− | x == y = x
288+ | x == y = Just x
284− | otherwise = AlignDefault
289+ | otherwise = Just AlignDefault
290+findAlign (x,_) Nothing = Just x
285
291
286headerRow :: T2T [(Alignment, Blocks)]
292headerRow :: T2T [(Alignment, Blocks)]
287headerRow = genericRow (string "||")
293headerRow = genericRow (string "||")
@@ -472,9 +478,29 @@ macro = try $ do
472-- raw URLs in text are automatically linked
478-- raw URLs in text are automatically linked
473url :: T2T Inlines
479url :: T2T Inlines
474url = try $ do
480url = try $ do
475− (rawUrl, escapedUrl) <- try uri <|> emailAddress
481+ (rawUrl, escapedUrl) <- try uri <|> emailAddress'
476 return $ B.link rawUrl "" (B.str escapedUrl)
482 return $ B.link rawUrl "" (B.str escapedUrl)
477
483
484+emailAddress' :: T2T (Text, Text)
485+emailAddress' = do
486+ (base, mailURI) <- emailAddress
487+ query <- option "" emailQuery
488+ return (base <> query, mailURI <> query)
489+
490+emailQuery :: T2T Text
491+emailQuery = do
492+ char '?'
493+ parts <- kv `sepBy1` (char '&')
494+ return $ "?" <> T.intercalate "&" parts
495+
496+kv :: T2T Text
497+kv = do
498+ k <- T.pack <$> many1 alphaNum
499+ char '='
500+ let vchar = alphaNum <|> try (oneOf "%._/~:,=$@&+-;*" <* lookAhead alphaNum)
501+ v <- T.pack <$> many1 vchar
502+ return (k <> "=" <> v)
503+
478uri :: T2T (Text, Text)
504uri :: T2T (Text, Text)
479uri = try $ do
505uri = try $ do
480 address <- t2tURI
506 address <- t2tURI
src/Text/Pandoc/Readers/Vimwiki.hs+12−7
@@ -74,23 +74,28 @@ import Text.Pandoc.Parsing (ParserState, ParserT, blanklines, emailAddress,
74 many1Till, orderedListMarker, readWithM,
74 many1Till, orderedListMarker, readWithM,
75 registerHeader, spaceChar, stateMeta,
75 registerHeader, spaceChar, stateMeta,
76 stateOptions, uri, manyTillChar, manyChar, textStr,
76 stateOptions, uri, manyTillChar, manyChar, textStr,
77− many1Char, countChar, many1TillChar)
77+ many1Char, countChar, many1TillChar,
78+ alphaNum, anyChar, char, newline, noneOf, oneOf,
79+ space, spaces, string)
80+import Text.Pandoc.Sources (ToSources(..), Sources)
78−import Text.Pandoc.Shared (crFilter, splitTextBy, stringify, stripFirstAndLast,
81+import Text.Pandoc.Shared (splitTextBy, stringify, stripFirstAndLast,
79 isURI, tshow)
82 isURI, tshow)
80−import Text.Parsec.Char (alphaNum, anyChar, char, newline, noneOf, oneOf, space,
81− spaces, string)
82import Text.Parsec.Combinator (between, choice, eof, lookAhead, many1,
83import Text.Parsec.Combinator (between, choice, eof, lookAhead, many1,
83 manyTill, notFollowedBy, option, skipMany1)
84 manyTill, notFollowedBy, option, skipMany1)
84import Text.Parsec.Prim (getState, many, try, updateState, (<|>))
85import Text.Parsec.Prim (getState, many, try, updateState, (<|>))
85
86
86−readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
87+readVimwiki :: (PandocMonad m, ToSources a)
88+ => ReaderOptions
89+ -> a
90+ -> m Pandoc
87readVimwiki opts s = do
91readVimwiki opts s = do
92+ let sources = toSources s
88− res <- readWithM parseVimwiki def{ stateOptions = opts } $ crFilter s
93+ res <- readWithM parseVimwiki def{ stateOptions = opts } sources
89 case res of
94 case res of
90 Left e -> throwError e
95 Left e -> throwError e
91 Right result -> return result
96 Right result -> return result
92
97
93−type VwParser = ParserT Text ParserState
98+type VwParser = ParserT Sources ParserState
94
99
95
100
96-- constants
101-- constants
src/Text/Pandoc/SelfContained.hs+4−7
@@ -16,7 +16,6 @@ the HTML using data URIs.
16module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where
16module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where
17import Codec.Compression.GZip as Gzip
17import Codec.Compression.GZip as Gzip
18import Control.Applicative ((<|>))
18import Control.Applicative ((<|>))
19−import Control.Monad.Except (throwError)
20import Control.Monad.Trans (lift)
19import Control.Monad.Trans (lift)
21import Data.ByteString (ByteString)
20import Data.ByteString (ByteString)
22import Data.ByteString.Base64
21import Data.ByteString.Base64
@@ -29,7 +28,6 @@ import System.FilePath (takeDirectory, takeExtension, (</>))
29import Text.HTML.TagSoup
28import Text.HTML.TagSoup
30import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem,
29import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem,
31 getInputFiles, report, setInputFiles)
30 getInputFiles, report, setInputFiles)
32−import Text.Pandoc.Error
33import Text.Pandoc.Logging
31import Text.Pandoc.Logging
34import Text.Pandoc.MIME (MimeType)
32import Text.Pandoc.MIME (MimeType)
35import Text.Pandoc.Shared (isURI, renderTags', trim)
33import Text.Pandoc.Shared (isURI, renderTags', trim)
@@ -244,11 +242,10 @@ getData mimetype src
244 let raw' = if ext `elem` [".gz", ".svgz"]
242 let raw' = if ext `elem` [".gz", ".svgz"]
245 then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw]
243 then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw]
246 else raw
244 else raw
247− mime <- case (mimetype, respMime) of
245+ let mime = case (mimetype, respMime) of
248− ("",Nothing) -> throwError $ PandocSomeError
246+ ("",Nothing) -> "application/octet-stream"
249− $ "Could not determine mime type for `" <> src <> "'"
250− (x, Nothing) -> return x
247+ (x, Nothing) -> x
251− (_, Just x ) -> return x
248+ (_, Just x ) -> x
252 result <- if "text/css" `T.isPrefixOf` mime
249 result <- if "text/css" `T.isPrefixOf` mime
253 then do
250 then do
254 oldInputs <- getInputFiles
251 oldInputs <- getInputFiles
src/Text/Pandoc/Shared.hs+41−64
@@ -25,11 +25,7 @@ module Text.Pandoc.Shared (
25 ordNub,
25 ordNub,
26 findM,
26 findM,
27 -- * Text processing
27 -- * Text processing
28− ToString (..),
29− ToText (..),
30 tshow,
28 tshow,
31− backslashEscapes,
32− escapeStringUsing,
33 elemText,
29 elemText,
34 notElemText,
30 notElemText,
35 stripTrailingNewlines,
31 stripTrailingNewlines,
@@ -95,7 +91,7 @@ module Text.Pandoc.Shared (
95 safeRead,
91 safeRead,
96 safeStrRead,
92 safeStrRead,
97 -- * User data directory
93 -- * User data directory
98− defaultUserDataDirs,
94+ defaultUserDataDir,
99 -- * Version
95 -- * Version
100 pandocVersion
96 pandocVersion
101 ) where
97 ) where
@@ -109,7 +105,7 @@ import qualified Data.Bifunctor as Bifunctor
109import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum,
105import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum,
110 generalCategory, GeneralCategory(NonSpacingMark,
106 generalCategory, GeneralCategory(NonSpacingMark,
111 SpacingCombiningMark, EnclosingMark, ConnectorPunctuation))
107 SpacingCombiningMark, EnclosingMark, ConnectorPunctuation))
112−import Data.List (find, intercalate, intersperse, sortOn)
108+import Data.List (find, intercalate, intersperse, sortOn, foldl')
113import qualified Data.Map as M
109import qualified Data.Map as M
114import Data.Maybe (mapMaybe, fromMaybe)
110import Data.Maybe (mapMaybe, fromMaybe)
115import Data.Monoid (Any (..))
111import Data.Monoid (Any (..))
@@ -127,7 +123,7 @@ import Text.HTML.TagSoup (RenderOptions (..), Tag (..), renderOptions,
127import Text.Pandoc.Builder (Blocks, Inlines, ToMetaValue (..))
123import Text.Pandoc.Builder (Blocks, Inlines, ToMetaValue (..))
128import qualified Text.Pandoc.Builder as B
124import qualified Text.Pandoc.Builder as B
129import Data.Time
125import Data.Time
130−import Text.Pandoc.Asciify (toAsciiChar)
126+import Text.Pandoc.Asciify (toAsciiText)
131import Text.Pandoc.Definition
127import Text.Pandoc.Definition
132import Text.Pandoc.Extensions (Extensions, Extension(..), extensionEnabled)
128import Text.Pandoc.Extensions (Extensions, Extension(..), extensionEnabled)
133import Text.Pandoc.Generic (bottomUp)
129import Text.Pandoc.Generic (bottomUp)
@@ -157,12 +153,20 @@ splitTextBy isSep t
157 in first : splitTextBy isSep (T.dropWhile isSep rest)
153 in first : splitTextBy isSep (T.dropWhile isSep rest)
158
154
159splitTextByIndices :: [Int] -> T.Text -> [T.Text]
155splitTextByIndices :: [Int] -> T.Text -> [T.Text]
160−splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns))
156+splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) . T.unpack
161 where
157 where
162− splitTextByRelIndices [] t = [t]
158+ splitTextByRelIndices [] cs = [T.pack cs]
163− splitTextByRelIndices (x:xs) t =
159+ splitTextByRelIndices (x:xs) cs =
164− let (first, rest) = T.splitAt x t
160+ let (first, rest) = splitAt' x cs
165− in first : splitTextByRelIndices xs rest
161+ in T.pack first : splitTextByRelIndices xs rest
162+
163+-- Note: don't replace this with T.splitAt, which is not sensitive
164+-- to character widths!
165+splitAt' :: Int -> [Char] -> ([Char],[Char])
166+splitAt' _ [] = ([],[])
167+splitAt' n xs | n <= 0 = ([],xs)
168+splitAt' n (x:xs) = (x:ys,zs)
169+ where (ys,zs) = splitAt' (n - charWidth x) xs
166
170
167ordNub :: (Ord a) => [a] -> [a]
171ordNub :: (Ord a) => [a] -> [a]
168ordNub l = go Set.empty l
172ordNub l = go Set.empty l
@@ -183,38 +187,9 @@ findM p = foldr go (pure Nothing)
183-- Text processing
187-- Text processing
184--
188--
185
189
186−class ToString a where
187− toString :: a -> String
188−
189−instance ToString String where
190− toString = id
191−
192−instance ToString T.Text where
193− toString = T.unpack
194−
195−class ToText a where
196− toText :: a -> T.Text
197−
198−instance ToText String where
199− toText = T.pack
200−
201−instance ToText T.Text where
202− toText = id
203−
204tshow :: Show a => a -> T.Text
190tshow :: Show a => a -> T.Text
205tshow = T.pack . show
191tshow = T.pack . show
206
192
207−-- | Returns an association list of backslash escapes for the
208−-- designated characters.
209−backslashEscapes :: [Char] -- ^ list of special characters to escape
210− -> [(Char, T.Text)]
211−backslashEscapes = map (\ch -> (ch, T.pack ['\\',ch]))
212−
213−-- | Escape a string of characters, using an association list of
214−-- characters and strings.
215−escapeStringUsing :: [(Char, T.Text)] -> T.Text -> T.Text
216−escapeStringUsing tbl = T.concatMap $ \c -> fromMaybe (T.singleton c) $ lookup c tbl
217−
218-- | @True@ exactly when the @Char@ appears in the @Text@.
193-- | @True@ exactly when the @Char@ appears in the @Text@.
219elemText :: Char -> T.Text -> Bool
194elemText :: Char -> T.Text -> Bool
220elemText c = T.any (== c)
195elemText c = T.any (== c)
@@ -323,6 +298,7 @@ tabFilter tabStop = T.unlines . map go . T.lines
323 (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ")
298 (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ")
324 <> go (T.drop 1 s2)
299 <> go (T.drop 1 s2)
325
300
301+{-# DEPRECATED crFilter "readers filter crs automatically" #-}
326-- | Strip out DOS line endings.
302-- | Strip out DOS line endings.
327crFilter :: T.Text -> T.Text
303crFilter :: T.Text -> T.Text
328crFilter = T.filter (/= '\r')
304crFilter = T.filter (/= '\r')
@@ -464,22 +440,20 @@ plainToPara :: Block -> Block
464plainToPara (Plain ils) = Para ils
440plainToPara (Plain ils) = Para ils
465plainToPara x = x
441plainToPara x = x
466
442
443+
467-- | Like @compactify@, but acts on items of definition lists.
444-- | Like @compactify@, but acts on items of definition lists.
468compactifyDL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])]
445compactifyDL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])]
469compactifyDL items =
446compactifyDL items =
470− let defs = concatMap snd items
447+ case reverse items of
448+ ((t,ds):ys) ->
471− in case reverse (concatMap B.toList defs) of
449+ case reverse (map (reverse . B.toList) ds) of
472− (Para x:xs)
473− | not (any isPara xs) ->
450+ ((Para x:xs) : zs) | not (any isPara xs) ->
474− let (t,ds) = last items
451+ reverse ys ++
475− lastDef = B.toList $ last ds
452+ [(t, reverse (map B.fromList zs) ++
476− ds' = init ds ++
453+ [B.fromList (reverse (Plain x:xs))])]
477− if null lastDef
478− then [B.fromList lastDef]
479− else [B.fromList $ init lastDef ++ [Plain x]]
480− in init items ++ [(t, ds')]
481− | otherwise -> items
454+ _ -> items
482− _ -> items
455+ _ -> items
456+
483
457
484-- | Combine a list of lines by adding hard linebreaks.
458-- | Combine a list of lines by adding hard linebreaks.
485combineLines :: [[Inline]] -> [Inline]
459combineLines :: [[Inline]] -> [Inline]
@@ -513,7 +487,7 @@ inlineListToIdentifier exts =
513 | otherwise = T.dropWhile (not . isAlpha)
487 | otherwise = T.dropWhile (not . isAlpha)
514 filterAscii
488 filterAscii
515 | extensionEnabled Ext_ascii_identifiers exts
489 | extensionEnabled Ext_ascii_identifiers exts
516− = T.pack . mapMaybe toAsciiChar . T.unpack
490+ = toAsciiText
517 | otherwise = id
491 | otherwise = id
518 toIdent
492 toIdent
519 | extensionEnabled Ext_gfm_auto_identifiers exts =
493 | extensionEnabled Ext_gfm_auto_identifiers exts =
@@ -842,7 +816,7 @@ mapLeft = Bifunctor.first
842-- > collapseFilePath "parent/foo/.." == "parent"
816-- > collapseFilePath "parent/foo/.." == "parent"
843-- > collapseFilePath "/parent/foo/../../bar" == "/bar"
817-- > collapseFilePath "/parent/foo/../../bar" == "/bar"
844collapseFilePath :: FilePath -> FilePath
818collapseFilePath :: FilePath -> FilePath
845−collapseFilePath = Posix.joinPath . reverse . foldl go [] . splitDirectories
819+collapseFilePath = Posix.joinPath . reverse . foldl' go [] . splitDirectories
846 where
820 where
847 go rs "." = rs
821 go rs "." = rs
848 go r@(p:rs) ".." = case p of
822 go r@(p:rs) ".." = case p of
@@ -879,7 +853,6 @@ filteredFilesFromArchive zf f =
879 fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString)
853 fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString)
880 fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e)
854 fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e)
881
855
882−
883--
856--
884-- IANA URIs
857-- IANA URIs
885--
858--
@@ -1012,12 +985,16 @@ safeStrRead s = case reads s of
1012--
985--
1013
986
1014-- | Return appropriate user data directory for platform. We use
987-- | Return appropriate user data directory for platform. We use
1015−-- XDG_DATA_HOME (or its default value), but fall back to the
988+-- XDG_DATA_HOME (or its default value), but for backwards compatibility,
1016−-- legacy user data directory ($HOME/.pandoc on *nix) if this is
989+-- we fall back to the legacy user data directory ($HOME/.pandoc on *nix)
1017−-- missing.
990+-- if the XDG_DATA_HOME is missing and this exists. If neither directory
1018−defaultUserDataDirs :: IO [FilePath]
991+-- is present, we return the XDG data directory.
1019−defaultUserDataDirs = E.catch (do
992+defaultUserDataDir :: IO FilePath
993+defaultUserDataDir = do
1020 xdgDir <- getXdgDirectory XdgData "pandoc"
994 xdgDir <- getXdgDirectory XdgData "pandoc"
1021 legacyDir <- getAppUserDataDirectory "pandoc"
995 legacyDir <- getAppUserDataDirectory "pandoc"
1022− return $ ordNub [xdgDir, legacyDir])
996+ xdgExists <- doesDirectoryExist xdgDir
1023− (\(_ :: E.SomeException) -> return [])
997+ legacyDirExists <- doesDirectoryExist legacyDir
998+ if not xdgExists && legacyDirExists
999+ then return legacyDir
1000+ else return xdgDir
src/Text/Pandoc/Sources.hsnew file+195
@@ -0,0 +1,195 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE OverloadedStrings #-}
6+{- |
7+ Module : Text.Pandoc.Sources
8+ Copyright : Copyright (C) 2021 John MacFarlane
9+ License : GNU GPL, version 2 or above
10+
11+ Maintainer : John MacFarlane <jgm@berkeley.edu>
12+ Stability : alpha
13+ Portability : portable
14+
15+Defines Sources object to be used as input to pandoc parsers and redefines Char
16+parsers so they get source position information from it.
17+-}
18+
19+module Text.Pandoc.Sources
20+ ( Sources(..)
21+ , ToSources(..)
22+ , UpdateSourcePos(..)
23+ , sourcesToText
24+ , initialSourceName
25+ , addToSources
26+ , ensureFinalNewlines
27+ , addToInput
28+ , satisfy
29+ , oneOf
30+ , noneOf
31+ , anyChar
32+ , char
33+ , string
34+ , newline
35+ , space
36+ , spaces
37+ , letter
38+ , digit
39+ , hexDigit
40+ , alphaNum
41+ )
42+where
43+import qualified Text.Parsec as P
44+import Text.Parsec (Stream(..), ParsecT)
45+import Text.Parsec.Pos as P
46+import Data.Text (Text)
47+import qualified Data.Text as T
48+import Data.Char (isSpace, isLetter, isAlphaNum, isDigit, isHexDigit)
49+import Data.String (IsString(..))
50+import qualified Data.List.NonEmpty as NonEmpty
51+
52+-- | A list of inputs labeled with source positions. It is assumed
53+-- that the 'Text's have @\n@ line endings.
54+newtype Sources = Sources { unSources :: [(SourcePos, Text)] }
55+ deriving (Show, Semigroup, Monoid)
56+
57+instance Monad m => Stream Sources m Char where
58+ uncons (Sources []) = return Nothing
59+ uncons (Sources ((pos,t):rest)) =
60+ case T.uncons t of
61+ Nothing -> uncons (Sources rest)
62+ Just (c,t') -> return $ Just (c, Sources ((pos,t'):rest))
63+
64+instance IsString Sources where
65+ fromString s = Sources [(P.initialPos "", T.pack (filter (/='\r') s))]
66+
67+class ToSources a where
68+ toSources :: a -> Sources
69+
70+instance ToSources Text where
71+ toSources t = Sources [(P.initialPos "", T.filter (/='\r') t)]
72+
73+instance ToSources [(FilePath, Text)] where
74+ toSources = Sources
75+ . map (\(fp,t) ->
76+ (P.initialPos fp, T.snoc (T.filter (/='\r') t) '\n'))
77+
78+instance ToSources Sources where
79+ toSources = id
80+
81+sourcesToText :: Sources -> Text
82+sourcesToText (Sources xs) = mconcat $ map snd xs
83+
84+addToSources :: Monad m => SourcePos -> Text -> ParsecT Sources u m ()
85+addToSources pos t = do
86+ curpos <- P.getPosition
87+ Sources xs <- P.getInput
88+ let xs' = case xs of
89+ [] -> []
90+ ((_,t'):rest) -> (curpos,t'):rest
91+ P.setInput $ Sources ((pos, T.filter (/='\r') t):xs')
92+
93+ensureFinalNewlines :: Int -- ^ number of trailing newlines
94+ -> Sources
95+ -> Sources
96+ensureFinalNewlines n (Sources xs) =
97+ case NonEmpty.nonEmpty xs of
98+ Nothing -> Sources [(initialPos "", T.replicate n "\n")]
99+ Just lst ->
100+ case NonEmpty.last lst of
101+ (spos, t) ->
102+ case T.length (T.takeWhileEnd (=='\n') t) of
103+ len | len >= n -> Sources xs
104+ | otherwise -> Sources (NonEmpty.init lst ++
105+ [(spos,
106+ t <> T.replicate (n - len) "\n")])
107+
108+class UpdateSourcePos s c where
109+ updateSourcePos :: SourcePos -> c -> s -> SourcePos
110+
111+instance UpdateSourcePos Text Char where
112+ updateSourcePos pos c _ = updatePosChar pos c
113+
114+instance UpdateSourcePos Sources Char where
115+ updateSourcePos pos c sources =
116+ case sources of
117+ Sources [] -> updatePosChar pos c
118+ Sources ((_,t):(pos',_):_)
119+ | T.null t -> pos'
120+ Sources _ ->
121+ case c of
122+ '\n' -> incSourceLine (setSourceColumn pos 1) 1
123+ '\t' -> incSourceColumn pos (4 - ((sourceColumn pos - 1) `mod` 4))
124+ _ -> incSourceColumn pos 1
125+
126+-- | Get name of first source in 'Sources'.
127+initialSourceName :: Sources -> FilePath
128+initialSourceName (Sources []) = ""
129+initialSourceName (Sources ((pos,_):_)) = sourceName pos
130+
131+-- | Add some text to the beginning of the input sources.
132+-- This simplifies code that expands macros.
133+addToInput :: Monad m => Text -> ParsecT Sources u m ()
134+addToInput t = do
135+ Sources xs <- P.getInput
136+ case xs of
137+ [] -> P.setInput $ Sources [(initialPos "",t)]
138+ (pos,t'):rest -> P.setInput $ Sources ((pos, t <> t'):rest)
139+
140+-- We need to redefine the parsers in Text.Parsec.Char so that they
141+-- update source positions properly from the Sources stream.
142+
143+satisfy :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
144+ => (Char -> Bool) -> ParsecT s u m Char
145+satisfy f = P.tokenPrim show updateSourcePos matcher
146+ where
147+ matcher c = if f c then Just c else Nothing
148+
149+oneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
150+ => [Char] -> ParsecT s u m Char
151+oneOf cs = satisfy (`elem` cs)
152+
153+noneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
154+ => [Char] -> ParsecT s u m Char
155+noneOf cs = satisfy (`notElem` cs)
156+
157+anyChar :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
158+ => ParsecT s u m Char
159+anyChar = satisfy (const True)
160+
161+char :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
162+ => Char -> ParsecT s u m Char
163+char c = satisfy (== c)
164+
165+string :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
166+ => [Char] -> ParsecT s u m [Char]
167+string = mapM char
168+
169+newline :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
170+ => ParsecT s u m Char
171+newline = satisfy (== '\n')
172+
173+space :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
174+ => ParsecT s u m Char
175+space = satisfy isSpace
176+
177+spaces :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
178+ => ParsecT s u m ()
179+spaces = P.skipMany space P.<?> "white space"
180+
181+letter :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
182+ => ParsecT s u m Char
183+letter = satisfy isLetter
184+
185+alphaNum :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
186+ => ParsecT s u m Char
187+alphaNum = satisfy isAlphaNum
188+
189+digit :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
190+ => ParsecT s u m Char
191+digit = satisfy isDigit
192+
193+hexDigit :: (Monad m, Stream s m Char, UpdateSourcePos s Char)
194+ => ParsecT s u m Char
195+hexDigit = satisfy isHexDigit
src/Text/Pandoc/Templates.hs+4
@@ -34,6 +34,7 @@ import Control.Monad.Except (catchError, throwError)
34import Data.Text (Text)
34import Data.Text (Text)
35import qualified Data.Text as T
35import qualified Data.Text as T
36import Text.Pandoc.Error
36import Text.Pandoc.Error
37+import System.IO.Error (isDoesNotExistError)
37
38
38-- | Wrap a Monad in this if you want partials to
39-- | Wrap a Monad in this if you want partials to
39-- be taken only from the default data files.
40-- be taken only from the default data files.
@@ -70,6 +71,9 @@ getTemplate tp = UTF8.toText <$>
70 PandocResourceNotFound _ ->
71 PandocResourceNotFound _ ->
71 -- see #5987 on reason for takeFileName
72 -- see #5987 on reason for takeFileName
72 readDataFile ("templates" </> takeFileName tp)
73 readDataFile ("templates" </> takeFileName tp)
74+ PandocIOError _ ioe | isDoesNotExistError ioe ->
75+ -- see #5987 on reason for takeFileName
76+ readDataFile ("templates" </> takeFileName tp)
73 _ -> throwError e))
77 _ -> throwError e))
74
78
75-- | Get default template for the specified writer.
79-- | Get default template for the specified writer.
src/Text/Pandoc/Writers.hs+3−3
@@ -81,6 +81,7 @@ import Data.Aeson
81import qualified Data.ByteString.Lazy as BL
81import qualified Data.ByteString.Lazy as BL
82import Data.Text (Text)
82import Data.Text (Text)
83import qualified Data.Text as T
83import qualified Data.Text as T
84+import Text.Pandoc.Shared (tshow)
84import Text.Pandoc.Class
85import Text.Pandoc.Class
85import Text.Pandoc.Definition
86import Text.Pandoc.Definition
86import Text.Pandoc.Options
87import Text.Pandoc.Options
@@ -122,7 +123,6 @@ import Text.Pandoc.Writers.Texinfo
122import Text.Pandoc.Writers.Textile
123import Text.Pandoc.Writers.Textile
123import Text.Pandoc.Writers.XWiki
124import Text.Pandoc.Writers.XWiki
124import Text.Pandoc.Writers.ZimWiki
125import Text.Pandoc.Writers.ZimWiki
125−import Text.Parsec.Error
126
126
127data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text)
127data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text)
128 | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString)
128 | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString)
@@ -196,8 +196,8 @@ writers = [
196getWriter :: PandocMonad m => Text -> m (Writer m, Extensions)
196getWriter :: PandocMonad m => Text -> m (Writer m, Extensions)
197getWriter s =
197getWriter s =
198 case parseFormatSpec s of
198 case parseFormatSpec s of
199− Left e -> throwError $ PandocAppError
199+ Left e -> throwError $ PandocAppError $
200− $ T.intercalate "\n" [T.pack m | Message m <- errorMessages e]
200+ "Error parsing writer format " <> tshow s <> ": " <> tshow e
201 Right (writerName, extsToEnable, extsToDisable) ->
201 Right (writerName, extsToEnable, extsToDisable) ->
202 case lookup writerName writers of
202 case lookup writerName writers of
203 Nothing -> throwError $
203 Nothing -> throwError $
src/Text/Pandoc/Writers/AsciiDoc.hs+10−4
@@ -22,6 +22,7 @@ module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc, writeAsciiDoctor) where
22import Control.Monad.State.Strict
22import Control.Monad.State.Strict
23import Data.Char (isPunctuation, isSpace)
23import Data.Char (isPunctuation, isSpace)
24import Data.List (intercalate, intersperse)
24import Data.List (intercalate, intersperse)
25+import Data.List.NonEmpty (NonEmpty(..))
25import Data.Maybe (fromMaybe, isJust)
26import Data.Maybe (fromMaybe, isJust)
26import qualified Data.Set as Set
27import qualified Data.Set as Set
27import qualified Data.Text as T
28import qualified Data.Text as T
@@ -104,8 +105,11 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do
104
105
105-- | Escape special characters for AsciiDoc.
106-- | Escape special characters for AsciiDoc.
106escapeString :: Text -> Text
107escapeString :: Text -> Text
107−escapeString = escapeStringUsing escs
108+escapeString t
108− where escs = backslashEscapes "{"
109+ | T.any (== '{') t = T.concatMap escChar t
110+ | otherwise = t
111+ where escChar '{' = "\\{"
112+ escChar c = T.singleton c
109
113
110-- | Ordered list start parser for use in Para below.
114-- | Ordered list start parser for use in Para below.
111olMarker :: Parser Text ParserState Char
115olMarker :: Parser Text ParserState Char
@@ -274,7 +278,7 @@ blockToAsciiDoc opts block@(Table _ blkCapt specs thead tbody tfoot) = do
274 let colwidth = if writerWrapText opts == WrapAuto
278 let colwidth = if writerWrapText opts == WrapAuto
275 then writerColumns opts
279 then writerColumns opts
276 else 100000
280 else 100000
277− let maxwidth = maximum $ map offset (head':rows')
281+ let maxwidth = maximum $ fmap offset (head' :| rows')
278 let body = if maxwidth > colwidth then vsep rows' else vcat rows'
282 let body = if maxwidth > colwidth then vsep rows' else vcat rows'
279 let border = separator <> text "==="
283 let border = separator <> text "==="
280 return $
284 return $
@@ -495,7 +499,9 @@ inlineToAsciiDoc opts (Quoted qt lst) = do
495 | otherwise -> [Str "``"] ++ lst ++ [Str "''"]
499 | otherwise -> [Str "``"] ++ lst ++ [Str "''"]
496inlineToAsciiDoc _ (Code _ str) = do
500inlineToAsciiDoc _ (Code _ str) = do
497 isAsciidoctor <- gets asciidoctorVariant
501 isAsciidoctor <- gets asciidoctorVariant
498− let contents = literal (escapeStringUsing (backslashEscapes "`") str)
502+ let escChar '`' = "\\'"
503+ escChar c = T.singleton c
504+ let contents = literal (T.concatMap escChar str)
499 return $
505 return $
500 if isAsciidoctor
506 if isAsciidoctor
501 then text "`+" <> contents <> "+`"
507 then text "`+" <> contents <> "+`"
src/Text/Pandoc/Writers/BibTeX.hs+1−1
@@ -43,7 +43,7 @@ writeBibTeX' :: PandocMonad m => Variant -> WriterOptions -> Pandoc -> m Text
43writeBibTeX' variant opts (Pandoc meta _) = do
43writeBibTeX' variant opts (Pandoc meta _) = do
44 let mblang = case lookupMetaString "lang" meta of
44 let mblang = case lookupMetaString "lang" meta of
45 "" -> Nothing
45 "" -> Nothing
46− t -> Just $ parseLang t
46+ t -> either (const Nothing) Just $ parseLang t
47 let refs = case lookupMeta "references" meta of
47 let refs = case lookupMeta "references" meta of
48 Just (MetaList xs) -> mapMaybe metaValueToReference xs
48 Just (MetaList xs) -> mapMaybe metaValueToReference xs
49 _ -> []
49 _ -> []
src/Text/Pandoc/Writers/ConTeXt.hs+44−38
@@ -20,7 +20,7 @@ import Data.Maybe (mapMaybe)
20import Data.Text (Text)
20import Data.Text (Text)
21import qualified Data.Text as T
21import qualified Data.Text as T
22import Network.URI (unEscapeString)
22import Network.URI (unEscapeString)
23−import Text.Pandoc.BCP47
23+import Text.Collate.Lang (Lang(..))
24import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
24import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
25import Text.Pandoc.Definition
25import Text.Pandoc.Definition
26import Text.Pandoc.ImageSize
26import Text.Pandoc.ImageSize
@@ -177,8 +177,12 @@ blockToConTeXt (Para lst) = do
177 contents <- inlineListToConTeXt lst
177 contents <- inlineListToConTeXt lst
178 return $ contents <> blankline
178 return $ contents <> blankline
179blockToConTeXt (LineBlock lns) = do
179blockToConTeXt (LineBlock lns) = do
180+ let emptyToBlankline doc = if isEmpty doc
181+ then blankline
182+ else doc
180− doclines <- nowrap . vcat <$> mapM inlineListToConTeXt lns
183+ doclines <- mapM inlineListToConTeXt lns
184+ let contextLines = vcat . map emptyToBlankline $ doclines
181− return $ "\\startlines" $$ doclines $$ "\\stoplines" <> blankline
185+ return $ "\\startlines" $$ contextLines $$ "\\stoplines" <> blankline
182blockToConTeXt (BlockQuote lst) = do
186blockToConTeXt (BlockQuote lst) = do
183 contents <- blockListToConTeXt lst
187 contents <- blockListToConTeXt lst
184 return $ "\\startblockquote" $$ nest 0 contents $$ "\\stopblockquote" <> blankline
188 return $ "\\startblockquote" $$ nest 0 contents $$ "\\stopblockquote" <> blankline
@@ -228,13 +232,7 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do
228 Period -> "stopper=."
232 Period -> "stopper=."
229 OneParen -> "stopper=)"
233 OneParen -> "stopper=)"
230 TwoParens -> "left=(,stopper=)"
234 TwoParens -> "left=(,stopper=)"
231− let width = maximum $ map T.length $ take (length contents)
232− (orderedListMarkers (start, style', delim))
233− let width' = (toEnum width + 1) / 2
234− let width'' = if width' > (1.5 :: Double)
235− then "width=" <> tshow width' <> "em"
236− else ""
237− let specs2Items = filter (not . T.null) [start', delim', width'']
235+ let specs2Items = filter (not . T.null) [start', delim']
238 let specs2 = if null specs2Items
236 let specs2 = if null specs2Items
239 then ""
237 then ""
240 else "[" <> T.intercalate "," specs2Items <> "]"
238 else "[" <> T.intercalate "," specs2Items <> "]"
@@ -248,8 +246,8 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do
248 UpperAlpha -> 'A') :
246 UpperAlpha -> 'A') :
249 if isTightList lst then ",packed]" else "]"
247 if isTightList lst then ",packed]" else "]"
250 let specs = T.pack style'' <> specs2
248 let specs = T.pack style'' <> specs2
251− return $ "\\startitemize" <> literal specs $$ vcat contents $$
249+ return $ "\\startenumerate" <> literal specs $$ vcat contents $$
252− "\\stopitemize" <> blankline
250+ "\\stopenumerate" <> blankline
253blockToConTeXt (DefinitionList lst) =
251blockToConTeXt (DefinitionList lst) =
254 liftM vcat $ mapM defListItemToConTeXt lst
252 liftM vcat $ mapM defListItemToConTeXt lst
255blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline
253blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline
@@ -432,9 +430,13 @@ inlineToConTeXt (Link _ txt (src, _)) = do
432 put $ st {stNextRef = next + 1}
430 put $ st {stNextRef = next + 1}
433 let ref = "url" <> tshow next
431 let ref = "url" <> tshow next
434 contents <- inlineListToConTeXt txt
432 contents <- inlineListToConTeXt txt
433+ let escChar '#' = "\\#"
434+ escChar '%' = "\\%"
435+ escChar c = T.singleton c
436+ let escContextURL = T.concatMap escChar
435 return $ "\\useURL"
437 return $ "\\useURL"
436 <> brackets (literal ref)
438 <> brackets (literal ref)
437− <> brackets (literal $ escapeStringUsing [('#',"\\#"),('%',"\\%")] src)
439+ <> brackets (literal $ escContextURL src)
438 <> (if isAutolink
440 <> (if isAutolink
439 then empty
441 then empty
440 else brackets empty <> brackets contents)
442 else brackets empty <> brackets contents)
@@ -477,7 +479,7 @@ inlineToConTeXt (Note contents) = do
477 then literal "\\footnote{" <> nest 2 (chomp contents') <> char '}'
479 then literal "\\footnote{" <> nest 2 (chomp contents') <> char '}'
478 else literal "\\startbuffer " <> nest 2 (chomp contents') <>
480 else literal "\\startbuffer " <> nest 2 (chomp contents') <>
479 literal "\\stopbuffer\\footnote{\\getbuffer}"
481 literal "\\stopbuffer\\footnote{\\getbuffer}"
480−inlineToConTeXt (Span (_,_,kvs) ils) = do
482+inlineToConTeXt (Span (ident,_,kvs) ils) = do
481 mblang <- fromBCP47 (lookup "lang" kvs)
483 mblang <- fromBCP47 (lookup "lang" kvs)
482 let wrapDir txt = case lookup "dir" kvs of
484 let wrapDir txt = case lookup "dir" kvs of
483 Just "rtl" -> braces $ "\\righttoleft " <> txt
485 Just "rtl" -> braces $ "\\righttoleft " <> txt
@@ -487,7 +489,11 @@ inlineToConTeXt (Span (_,_,kvs) ils) = do
487 Just lng -> braces ("\\language" <>
489 Just lng -> braces ("\\language" <>
488 brackets (literal lng) <> txt)
490 brackets (literal lng) <> txt)
489 Nothing -> txt
491 Nothing -> txt
492+ addReference =
493+ if T.null ident
494+ then id
495+ else (("\\reference" <> brackets (literal ident) <> "{}") <>)
490− wrapLang . wrapDir <$> inlineListToConTeXt ils
496+ addReference . wrapLang . wrapDir <$> inlineListToConTeXt ils
491
497
492-- | Craft the section header, inserting the section reference, if supplied.
498-- | Craft the section header, inserting the section reference, if supplied.
493sectionHeader :: PandocMonad m
499sectionHeader :: PandocMonad m
@@ -549,26 +555,26 @@ fromBCP47 mbs = fromBCP47' <$> toLang mbs
549-- https://tools.ietf.org/html/bcp47#section-2.1
555-- https://tools.ietf.org/html/bcp47#section-2.1
550-- http://wiki.contextgarden.net/Language_Codes
556-- http://wiki.contextgarden.net/Language_Codes
551fromBCP47' :: Maybe Lang -> Maybe Text
557fromBCP47' :: Maybe Lang -> Maybe Text
552−fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy"
558+fromBCP47' (Just (Lang "ar" _ (Just "SY") _ _ _)) = Just "ar-sy"
553−fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq"
559+fromBCP47' (Just (Lang "ar" _ (Just "IQ") _ _ _)) = Just "ar-iq"
554−fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo"
560+fromBCP47' (Just (Lang "ar" _ (Just "JO") _ _ _)) = Just "ar-jo"
555−fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb"
561+fromBCP47' (Just (Lang "ar" _ (Just "LB") _ _ _)) = Just "ar-lb"
556−fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz"
562+fromBCP47' (Just (Lang "ar" _ (Just "DZ") _ _ _)) = Just "ar-dz"
557−fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma"
563+fromBCP47' (Just (Lang "ar" _ (Just "MA") _ _ _)) = Just "ar-ma"
558−fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo"
564+fromBCP47' (Just (Lang "de" _ _ ["1901"] _ _)) = Just "deo"
559−fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de"
565+fromBCP47' (Just (Lang "de" _ (Just "DE") _ _ _)) = Just "de-de"
560−fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at"
566+fromBCP47' (Just (Lang "de" _ (Just "AT") _ _ _)) = Just "de-at"
561−fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch"
567+fromBCP47' (Just (Lang "de" _ (Just "CH") _ _ _)) = Just "de-ch"
562−fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr"
568+fromBCP47' (Just (Lang "el" _ _ ["poly"] _ _)) = Just "agr"
563−fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us"
569+fromBCP47' (Just (Lang "en" _ (Just "US") _ _ _)) = Just "en-us"
564−fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb"
570+fromBCP47' (Just (Lang "en" _ (Just "GB") _ _ _)) = Just "en-gb"
565−fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr"
571+fromBCP47' (Just (Lang "grc"_ _ _ _ _)) = Just "agr"
566−fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr"
572+fromBCP47' (Just (Lang "el" _ _ _ _ _)) = Just "gr"
567−fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba"
573+fromBCP47' (Just (Lang "eu" _ _ _ _ _)) = Just "ba"
568−fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il"
574+fromBCP47' (Just (Lang "he" _ _ _ _ _)) = Just "il"
569−fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja"
575+fromBCP47' (Just (Lang "jp" _ _ _ _ _)) = Just "ja"
570−fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua"
576+fromBCP47' (Just (Lang "uk" _ _ _ _ _)) = Just "ua"
571−fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn"
577+fromBCP47' (Just (Lang "vi" _ _ _ _ _)) = Just "vn"
572−fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn"
578+fromBCP47' (Just (Lang "zh" _ _ _ _ _)) = Just "cn"
573−fromBCP47' (Just (Lang l _ _ _) ) = Just l
579+fromBCP47' (Just (Lang l _ _ _ _ _)) = Just l
574−fromBCP47' Nothing = Nothing
580+fromBCP47' Nothing = Nothing
src/Text/Pandoc/Writers/CslJson.hs+4−3
@@ -34,15 +34,16 @@ import Control.Monad.Identity
34import Citeproc.Locale (getLocale)
34import Citeproc.Locale (getLocale)
35import Citeproc.CslJson
35import Citeproc.CslJson
36import Text.Pandoc.Options (WriterOptions)
36import Text.Pandoc.Options (WriterOptions)
37−import Data.Maybe (mapMaybe)
37+import Data.Maybe (mapMaybe, fromMaybe)
38import Data.Aeson.Encode.Pretty (Config (..), Indent (Spaces),
38import Data.Aeson.Encode.Pretty (Config (..), Indent (Spaces),
39 NumberFormat (Generic),
39 NumberFormat (Generic),
40 defConfig, encodePretty')
40 defConfig, encodePretty')
41
41
42writeCslJson :: PandocMonad m => WriterOptions -> Pandoc -> m Text
42writeCslJson :: PandocMonad m => WriterOptions -> Pandoc -> m Text
43writeCslJson _opts (Pandoc meta _) = do
43writeCslJson _opts (Pandoc meta _) = do
44− let lang = maybe (Lang "en" (Just "US")) parseLang
44+ let lang = fromMaybe (Lang "en" Nothing (Just "US") [] [] [])
45− (lookupMeta "lang" meta >>= metaValueToText)
45+ (lookupMeta "lang" meta >>= metaValueToText >>=
46+ either (const Nothing) Just . parseLang)
46 locale <- case getLocale lang of
47 locale <- case getLocale lang of
47 Left e -> throwError $ PandocCiteprocError e
48 Left e -> throwError $ PandocCiteprocError e
48 Right l -> return l
49 Right l -> return l
src/Text/Pandoc/Writers/Docbook.hs+50−7
@@ -168,7 +168,7 @@ blockToDocbook :: PandocMonad m => WriterOptions -> Block -> DB m (Doc Text)
168blockToDocbook _ Null = return empty
168blockToDocbook _ Null = return empty
169-- Add ids to paragraphs in divs with ids - this is needed for
169-- Add ids to paragraphs in divs with ids - this is needed for
170-- pandoc-citeproc to get link anchors in bibliographies:
170-- pandoc-citeproc to get link anchors in bibliographies:
171−blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do
171+blockToDocbook opts (Div (id',"section":_,_) (Header lvl (_,_,attrs) ils : xs)) = do
172 version <- ask
172 version <- ask
173 -- Docbook doesn't allow sections with no content, so insert some if needed
173 -- Docbook doesn't allow sections with no content, so insert some if needed
174 let bs = if null xs
174 let bs = if null xs
@@ -188,15 +188,17 @@ blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do
188 -- standalone documents will include them in the template.
188 -- standalone documents will include them in the template.
189 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
189 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
190 else []
190 else []
191+
192+ -- Populate miscAttr with Header.Attr.attributes, filtering out non-valid DocBook section attributes, id, and xml:id
193+ miscAttr = filter (isSectionAttr version) attrs
191− attribs = nsAttr <> idAttr
194+ attribs = nsAttr <> idAttr <> miscAttr
192 title' <- inlinesToDocbook opts ils
195 title' <- inlinesToDocbook opts ils
193 contents <- blocksToDocbook opts bs
196 contents <- blocksToDocbook opts bs
194 return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents
197 return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents
195blockToDocbook opts (Div (ident,classes,_) bs) = do
198blockToDocbook opts (Div (ident,classes,_) bs) = do
196 version <- ask
199 version <- ask
197 let identAttribs = [(idName version, ident) | not (T.null ident)]
200 let identAttribs = [(idName version, ident) | not (T.null ident)]
198− admonitions = ["attention","caution","danger","error","hint",
199− "important","note","tip","warning"]
201+ admonitions = ["caution","danger","important","note","tip","warning"]
200 case classes of
202 case classes of
201 (l:_) | l `elem` admonitions -> do
203 (l:_) | l `elem` admonitions -> do
202 let (mTitleBs, bodyBs) =
204 let (mTitleBs, bodyBs) =
@@ -250,17 +252,18 @@ blockToDocbook opts (LineBlock lns) =
250 blockToDocbook opts $ linesToPara lns
252 blockToDocbook opts $ linesToPara lns
251blockToDocbook opts (BlockQuote blocks) =
253blockToDocbook opts (BlockQuote blocks) =
252 inTagsIndented "blockquote" <$> blocksToDocbook opts blocks
254 inTagsIndented "blockquote" <$> blocksToDocbook opts blocks
253−blockToDocbook _ (CodeBlock (_,classes,_) str) = return $
255+blockToDocbook opts (CodeBlock (_,classes,_) str) = return $
254 literal ("<programlisting" <> lang <> ">") <> cr <>
256 literal ("<programlisting" <> lang <> ">") <> cr <>
255 flush (literal (escapeStringForXML str) <> cr <> literal "</programlisting>")
257 flush (literal (escapeStringForXML str) <> cr <> literal "</programlisting>")
256 where lang = if null langs
258 where lang = if null langs
257 then ""
259 then ""
258 else " language=\"" <> escapeStringForXML (head langs) <>
260 else " language=\"" <> escapeStringForXML (head langs) <>
259 "\""
261 "\""
262+ syntaxMap = writerSyntaxMap opts
260− isLang l = T.toLower l `elem` map T.toLower languages
263+ isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap)
261 langsFrom s = if isLang s
264 langsFrom s = if isLang s
262 then [s]
265 then [s]
263− else languagesByExtension . T.toLower $ s
266+ else (languagesByExtension syntaxMap) . T.toLower $ s
264 langs = concatMap langsFrom classes
267 langs = concatMap langsFrom classes
265blockToDocbook opts (BulletList lst) = do
268blockToDocbook opts (BulletList lst) = do
266 let attribs = [("spacing", "compact") | isTightList lst]
269 let attribs = [("spacing", "compact") | isTightList lst]
@@ -451,3 +454,43 @@ idAndRole (id',cls,_) = ident <> role
451 where
454 where
452 ident = [("id", id') | not (T.null id')]
455 ident = [("id", id') | not (T.null id')]
453 role = [("role", T.unwords cls) | not (null cls)]
456 role = [("role", T.unwords cls) | not (null cls)]
457+
458+isSectionAttr :: DocBookVersion -> (Text, Text) -> Bool
459+isSectionAttr _ ("label",_) = True
460+isSectionAttr _ ("status",_) = True
461+isSectionAttr DocBook5 ("annotations",_) = True
462+isSectionAttr DocBook5 ("dir","ltr") = True
463+isSectionAttr DocBook5 ("dir","rtl") = True
464+isSectionAttr DocBook5 ("dir","lro") = True
465+isSectionAttr DocBook5 ("dir","rlo") = True
466+isSectionAttr _ ("remap",_) = True
467+isSectionAttr _ ("revisionflag","changed") = True
468+isSectionAttr _ ("revisionflag","added") = True
469+isSectionAttr _ ("revisionflag","deleted") = True
470+isSectionAttr _ ("revisionflag","off") = True
471+isSectionAttr _ ("role",_) = True
472+isSectionAttr DocBook5 ("version",_) = True
473+isSectionAttr DocBook5 ("xml:base",_) = True
474+isSectionAttr DocBook5 ("xml:lang",_) = True
475+isSectionAttr _ ("xreflabel",_) = True
476+isSectionAttr DocBook5 ("linkend",_) = True
477+isSectionAttr DocBook5 ("linkends",_) = True
478+isSectionAttr DocBook5 ("xlink:actuate",_) = True
479+isSectionAttr DocBook5 ("xlink:arcrole",_) = True
480+isSectionAttr DocBook5 ("xlink:from",_) = True
481+isSectionAttr DocBook5 ("xlink:href",_) = True
482+isSectionAttr DocBook5 ("xlink:label",_) = True
483+isSectionAttr DocBook5 ("xlink:role",_) = True
484+isSectionAttr DocBook5 ("xlink:show",_) = True
485+isSectionAttr DocBook5 ("xlink:title",_) = True
486+isSectionAttr DocBook5 ("xlink:to",_) = True
487+isSectionAttr DocBook5 ("xlink:type",_) = True
488+isSectionAttr DocBook4 ("arch",_) = True
489+isSectionAttr DocBook4 ("condition",_) = True
490+isSectionAttr DocBook4 ("conformance",_) = True
491+isSectionAttr DocBook4 ("lang",_) = True
492+isSectionAttr DocBook4 ("os",_) = True
493+isSectionAttr DocBook4 ("revision",_) = True
494+isSectionAttr DocBook4 ("security",_) = True
495+isSectionAttr DocBook4 ("vendor",_) = True
496+isSectionAttr _ (_,_) = False
src/Text/Pandoc/Writers/Docx.hs+60−244
@@ -22,7 +22,6 @@ import Control.Applicative ((<|>))
22import Control.Monad.Except (catchError, throwError)
22import Control.Monad.Except (catchError, throwError)
23import Control.Monad.Reader
23import Control.Monad.Reader
24import Control.Monad.State.Strict
24import Control.Monad.State.Strict
25−import qualified Data.ByteString as B
26import qualified Data.ByteString.Lazy as BL
25import qualified Data.ByteString.Lazy as BL
27import Data.Char (isSpace, isLetter)
26import Data.Char (isSpace, isLetter)
28import Data.List (intercalate, isPrefixOf, isSuffixOf)
27import Data.List (intercalate, isPrefixOf, isSuffixOf)
@@ -36,9 +35,9 @@ import qualified Data.Text.Lazy as TL
36import Data.Time.Clock.POSIX
35import Data.Time.Clock.POSIX
37import Data.Digest.Pure.SHA (sha1, showDigest)
36import Data.Digest.Pure.SHA (sha1, showDigest)
38import Skylighting
37import Skylighting
39−import System.Random (randomRs, mkStdGen)
40−import Text.Pandoc.BCP47 (getLang, renderLang)
38+import Text.Collate.Lang (renderLang)
41−import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
39+import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang, translateTerm)
40+import qualified Text.Pandoc.Translations as Term
42import qualified Text.Pandoc.Class.PandocMonad as P
41import qualified Text.Pandoc.Class.PandocMonad as P
43import Data.Time
42import Data.Time
44import Text.Pandoc.UTF8 (fromTextLazy)
43import Text.Pandoc.UTF8 (fromTextLazy)
@@ -48,123 +47,25 @@ import Text.Pandoc.Highlighting (highlight)
48import Text.Pandoc.Error
47import Text.Pandoc.Error
49import Text.Pandoc.ImageSize
48import Text.Pandoc.ImageSize
50import Text.Pandoc.Logging
49import Text.Pandoc.Logging
51−import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType,
50+import Text.Pandoc.MIME (extensionFromMimeType, getMimeType, getMimeTypeDef)
52− getMimeTypeDef)
53import Text.Pandoc.Options
51import Text.Pandoc.Options
54import Text.Pandoc.Writers.Docx.StyleMap
52import Text.Pandoc.Writers.Docx.StyleMap
53+import Text.Pandoc.Writers.Docx.Table
54+import Text.Pandoc.Writers.Docx.Types
55import Text.Pandoc.Shared
55import Text.Pandoc.Shared
56import Text.Pandoc.Walk
56import Text.Pandoc.Walk
57+import qualified Text.Pandoc.Writers.GridTable as Grid
57import Text.Pandoc.Writers.Math
58import Text.Pandoc.Writers.Math
58import Text.Pandoc.Writers.Shared
59import Text.Pandoc.Writers.Shared
59−import Text.Printf (printf)
60import Text.TeXMath
60import Text.TeXMath
61import Text.Pandoc.Writers.OOXML
61import Text.Pandoc.Writers.OOXML
62import Text.Pandoc.XML.Light as XML
62import Text.Pandoc.XML.Light as XML
63import Data.Generics (mkT, everywhere)
63import Data.Generics (mkT, everywhere)
64
64
65−data ListMarker = NoMarker
66− | BulletMarker
67− | NumberMarker ListNumberStyle ListNumberDelim Int
68− deriving (Show, Read, Eq, Ord)
69−
70−listMarkerToId :: ListMarker -> Text
71−listMarkerToId NoMarker = "990"
72−listMarkerToId BulletMarker = "991"
73−listMarkerToId (NumberMarker sty delim n) = T.pack $
74− '9' : '9' : styNum : delimNum : show n
75− where styNum = case sty of
76− DefaultStyle -> '2'
77− Example -> '3'
78− Decimal -> '4'
79− LowerRoman -> '5'
80− UpperRoman -> '6'
81− LowerAlpha -> '7'
82− UpperAlpha -> '8'
83− delimNum = case delim of
84− DefaultDelim -> '0'
85− Period -> '1'
86− OneParen -> '2'
87− TwoParens -> '3'
88−
89−data EnvProps = EnvProps{ styleElement :: Maybe Element
90− , otherElements :: [Element]
91− }
92−
93−instance Semigroup EnvProps where
94− EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es')
95−
96−instance Monoid EnvProps where
97− mempty = EnvProps Nothing []
98− mappend = (<>)
99−
100squashProps :: EnvProps -> [Element]
65squashProps :: EnvProps -> [Element]
101squashProps (EnvProps Nothing es) = es
66squashProps (EnvProps Nothing es) = es
102squashProps (EnvProps (Just e) es) = e : es
67squashProps (EnvProps (Just e) es) = e : es
103
68
104−data WriterEnv = WriterEnv{ envTextProperties :: EnvProps
105− , envParaProperties :: EnvProps
106− , envRTL :: Bool
107− , envListLevel :: Int
108− , envListNumId :: Int
109− , envInDel :: Bool
110− , envChangesAuthor :: Text
111− , envChangesDate :: Text
112− , envPrintWidth :: Integer
113− }
114−
115−defaultWriterEnv :: WriterEnv
116−defaultWriterEnv = WriterEnv{ envTextProperties = mempty
117− , envParaProperties = mempty
118− , envRTL = False
119− , envListLevel = -1
120− , envListNumId = 1
121− , envInDel = False
122− , envChangesAuthor = "unknown"
123− , envChangesDate = "1969-12-31T19:00:00Z"
124− , envPrintWidth = 1
125− }
126−
127−data WriterState = WriterState{
128− stFootnotes :: [Element]
129− , stComments :: [([(Text, Text)], [Inline])]
130− , stSectionIds :: Set.Set Text
131− , stExternalLinks :: M.Map Text Text
132− , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString)
133− , stLists :: [ListMarker]
134− , stInsId :: Int
135− , stDelId :: Int
136− , stStyleMaps :: StyleMaps
137− , stFirstPara :: Bool
138− , stInTable :: Bool
139− , stInList :: Bool
140− , stTocTitle :: [Inline]
141− , stDynamicParaProps :: Set.Set ParaStyleName
142− , stDynamicTextProps :: Set.Set CharStyleName
143− , stCurId :: Int
144− }
145−
146−defaultWriterState :: WriterState
147−defaultWriterState = WriterState{
148− stFootnotes = defaultFootnotes
149− , stComments = []
150− , stSectionIds = Set.empty
151− , stExternalLinks = M.empty
152− , stImages = M.empty
153− , stLists = [NoMarker]
154− , stInsId = 1
155− , stDelId = 1
156− , stStyleMaps = StyleMaps M.empty M.empty
157− , stFirstPara = False
158− , stInTable = False
159− , stInList = False
160− , stTocTitle = [Str "Table of Contents"]
161− , stDynamicParaProps = Set.empty
162− , stDynamicTextProps = Set.empty
163− , stCurId = 20
164− }
165−
166−type WS m = ReaderT WriterEnv (StateT WriterState m)
167−
168renumIdMap :: Int -> [Element] -> M.Map Text Text
69renumIdMap :: Int -> [Element] -> M.Map Text Text
169renumIdMap _ [] = M.empty
70renumIdMap _ [] = M.empty
170renumIdMap n (e:es)
71renumIdMap n (e:es)
@@ -569,12 +470,27 @@ writeDocx opts doc = do
569 -- adds references to footnotes or endnotes we don't have...
470 -- adds references to footnotes or endnotes we don't have...
570 -- we do, however, copy some settings over from reference
471 -- we do, however, copy some settings over from reference
571 let settingsPath = "word/settings.xml"
472 let settingsPath = "word/settings.xml"
473+ settingsList = [ "zoom"
474+ , "embedSystemFonts"
475+ , "doNotTrackMoves"
476+ , "defaultTabStop"
477+ , "drawingGridHorizontalSpacing"
478+ , "drawingGridVerticalSpacing"
479+ , "displayHorizontalDrawingGridEvery"
480+ , "displayVerticalDrawingGridEvery"
481+ , "characterSpacingControl"
482+ , "savePreviewPicture"
483+ , "mathPr"
484+ , "themeFontLang"
485+ , "decimalSymbol"
486+ , "listSeparator"
572− settingsList = [ "w:autoHyphenation"
487+ , "autoHyphenation"
573− , "w:consecutiveHyphenLimit"
488+ , "consecutiveHyphenLimit"
574− , "w:hyphenationZone"
489+ , "hyphenationZone"
575− , "w:doNotHyphenateCap"
490+ , "doNotHyphenateCap"
576− , "w:evenAndOddHeaders"
491+ , "evenAndOddHeaders"
577− , "w:proofState"
492+ , "proofState"
493+ , "compat"
578 ]
494 ]
579 settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime settingsList
495 settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime settingsList
580
496
@@ -677,16 +593,17 @@ copyChildren :: (PandocMonad m)
677copyChildren refArchive distArchive path timestamp elNames = do
593copyChildren refArchive distArchive path timestamp elNames = do
678 ref <- parseXml refArchive distArchive path
594 ref <- parseXml refArchive distArchive path
679 dist <- parseXml distArchive distArchive path
595 dist <- parseXml distArchive distArchive path
596+ let elsToCopy =
597+ map cleanElem $ filterChildrenName (\e -> qName e `elem` elNames) ref
598+ let elsToKeep =
599+ [e | Elem e <- elContent dist, not (any (hasSameNameAs e) elsToCopy)]
680 return $ toEntry path timestamp $ renderXml dist{
600 return $ toEntry path timestamp $ renderXml dist{
681− elContent = elContent dist ++ copyContent ref
601+ elContent = map Elem elsToKeep ++ map Elem elsToCopy
682 }
602 }
683 where
603 where
684− strName QName{qName=name, qPrefix=prefix}
604+ hasSameNameAs (Element {elName = n1}) (Element {elName = n2}) =
685− | Just p <- prefix = p <> ":" <> name
605+ qName n1 == qName n2
686− | otherwise = name
687− shouldCopy = (`elem` elNames) . strName
688− cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}}
606+ cleanElem el@Element{elName=name} = el{elName=name{qURI=Nothing}}
689− copyContent = map cleanElem . filterChildrenName shouldCopy
690
607
691-- this is the lowest number used for a list numId
608-- this is the lowest number used for a list numId
692baseListId :: Int
609baseListId :: Int
@@ -695,8 +612,7 @@ baseListId = 1000
695mkNumbering :: [ListMarker] -> [Element]
612mkNumbering :: [ListMarker] -> [Element]
696mkNumbering lists =
613mkNumbering lists =
697 elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
614 elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
698− where elts = zipWith mkAbstractNum (ordNub lists) $
615+ where elts = map mkAbstractNum (ordNub lists)
699− randomRs (0x10000000, 0xFFFFFFFF) $ mkStdGen 1848
700
616
701maxListLevel :: Int
617maxListLevel :: Int
702maxListLevel = 8
618maxListLevel = 8
@@ -713,10 +629,10 @@ mkNum marker numid =
713 $ mknode "w:startOverride" [("w:val",tshow start)] ())
629 $ mknode "w:startOverride" [("w:val",tshow start)] ())
714 [0..maxListLevel]
630 [0..maxListLevel]
715
631
716−mkAbstractNum :: ListMarker -> Integer -> Element
632+mkAbstractNum :: ListMarker -> Element
717−mkAbstractNum marker nsid =
633+mkAbstractNum marker =
718 mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
634 mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
719− $ mknode "w:nsid" [("w:val", T.pack $ printf "%8x" nsid)] ()
635+ $ mknode "w:nsid" [("w:val", "A" <> listMarkerToId marker)] ()
720 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
636 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
721 : map (mkLvl marker)
637 : map (mkLvl marker)
722 [0..maxListLevel]
638 [0..maxListLevel]
@@ -860,12 +776,6 @@ separateTables (x@Table{}:xs@(Table{}:_)) =
860 x : RawBlock (Format "openxml") "<w:p />" : separateTables xs
776 x : RawBlock (Format "openxml") "<w:p />" : separateTables xs
861separateTables (x:xs) = x : separateTables xs
777separateTables (x:xs) = x : separateTables xs
862
778
863−pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element
864−pStyleM styleName = do
865− pStyleMap <- gets (smParaStyle . stStyleMaps)
866− let sty' = getStyleIdFromName styleName pStyleMap
867− return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] ()
868−
869rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element
779rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element
870rStyleM styleName = do
780rStyleM styleName = do
871 cStyleMap <- gets (smCharStyle . stStyleMaps)
781 cStyleMap <- gets (smCharStyle . stStyleMaps)
@@ -945,14 +855,28 @@ blockToOpenXML' opts (Plain lst) = do
945-- title beginning with fig: indicates that the image is a figure
855-- title beginning with fig: indicates that the image is a figure
946blockToOpenXML' opts (Para [Image attr alt (src,T.stripPrefix "fig:" -> Just tit)]) = do
856blockToOpenXML' opts (Para [Image attr alt (src,T.stripPrefix "fig:" -> Just tit)]) = do
947 setFirstPara
857 setFirstPara
858+ fignum <- gets stNextFigureNum
859+ unless (null alt) $ modify $ \st -> st{ stNextFigureNum = fignum + 1 }
860+ let figid = "fig" <> tshow fignum
861+ figname <- translateTerm Term.Figure
948 prop <- pStyleM $
862 prop <- pStyleM $
949 if null alt
863 if null alt
950 then "Figure"
864 then "Figure"
951 else "Captioned Figure"
865 else "Captioned Figure"
952 paraProps <- local (\env -> env { envParaProperties = EnvProps (Just prop) [] <> envParaProperties env }) (getParaProps False)
866 paraProps <- local (\env -> env { envParaProperties = EnvProps (Just prop) [] <> envParaProperties env }) (getParaProps False)
953 contents <- inlinesToOpenXML opts [Image attr alt (src,tit)]
867 contents <- inlinesToOpenXML opts [Image attr alt (src,tit)]
868+ captionNode <- if null alt
869+ then return []
954− captionNode <- withParaPropM (pStyleM "Image Caption")
870+ else withParaPropM (pStyleM "Image Caption")
955− $ blockToOpenXML opts (Para alt)
871+ $ blockToOpenXML opts
872+ (Para $ Span (figid,[],[])
873+ [Str (figname <> "\160"),
874+ RawInline (Format "openxml")
875+ ("<w:fldSimple w:instr=\"SEQ Figure"
876+ <> " \\* ARABIC \"><w:r><w:t>"
877+ <> tshow fignum
878+ <> "</w:t></w:r></w:fldSimple>"),
879+ Str ":", Space] : alt)
956 return $
880 return $
957 Elem (mknode "w:p" [] (map Elem paraProps ++ contents))
881 Elem (mknode "w:p" [] (map Elem paraProps ++ contents))
958 : captionNode
882 : captionNode
@@ -997,78 +921,9 @@ blockToOpenXML' _ HorizontalRule = do
997 $ mknode "v:rect" [("style","width:0;height:1.5pt"),
921 $ mknode "v:rect" [("style","width:0;height:1.5pt"),
998 ("o:hralign","center"),
922 ("o:hralign","center"),
999 ("o:hrstd","t"),("o:hr","t")] () ]
923 ("o:hrstd","t"),("o:hr","t")] () ]
1000−blockToOpenXML' opts (Table _ blkCapt specs thead tbody tfoot) = do
924+blockToOpenXML' opts (Table attr caption colspecs thead tbodies tfoot) =
1001− let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
1002− setFirstPara
1003− modify $ \s -> s { stInTable = True }
1004− let captionStr = stringify caption
1005− caption' <- if null caption
1006− then return []
1007− else withParaPropM (pStyleM "Table Caption")
1008− $ blockToOpenXML opts (Para caption)
1009− let alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] ()
1010− -- Table cells require a <w:p> element, even an empty one!
1011− -- Not in the spec but in Word 2007, 2010. See #4953. And
1012− -- apparently the last element must be a <w:p>, see #6983.
1013− let cellToOpenXML (al, cell) = do
1014− es <- withParaProp (alignmentFor al) $ blocksToOpenXML opts cell
925+ tableToOpenXML (blocksToOpenXML opts)
1015− return $
926+ (Grid.toTable attr caption colspecs thead tbodies tfoot)
1016− case reverse (onlyElems es) of
1017− b:e:_ | qName (elName b) == "bookmarkEnd"
1018− , qName (elName e) == "p" -> es
1019− e:_ | qName (elName e) == "p" -> es
1020− _ -> es ++ [Elem $ mknode "w:p" [] ()]
1021− headers' <- mapM cellToOpenXML $ zip aligns headers
1022− rows' <- mapM (mapM cellToOpenXML . zip aligns) rows
1023− compactStyle <- pStyleM "Compact"
1024− let emptyCell' = [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]]
1025− let mkcell contents = mknode "w:tc" []
1026− $ if null contents
1027− then emptyCell'
1028− else contents
1029− let mkrow cells =
1030− mknode "w:tr" [] $
1031− map mkcell cells
1032− let textwidth = 7920 -- 5.5 in in twips, 1/20 pt
1033− let fullrow = 5000 -- 100% specified in pct
1034− let rowwidth = fullrow * sum widths
1035− let mkgridcol w = mknode "w:gridCol"
1036− [("w:w", tshow (floor (textwidth * w) :: Integer))] ()
1037− let hasHeader = not $ all null headers
1038− modify $ \s -> s { stInTable = False }
1039− -- for compatibility with Word <= 2007, we include a val with a bitmask
1040− -- 0×0020 Apply first row conditional formatting
1041− -- 0×0040 Apply last row conditional formatting
1042− -- 0×0080 Apply first column conditional formatting
1043− -- 0×0100 Apply last column conditional formatting
1044− -- 0×0200 Do not apply row banding conditional formatting
1045− -- 0×0400 Do not apply column banding conditional formattin
1046− let tblLookVal :: Int
1047− tblLookVal = if hasHeader then 0x20 else 0
1048− return $
1049− caption' ++
1050− [Elem $
1051− mknode "w:tbl" []
1052− ( mknode "w:tblPr" []
1053− ( mknode "w:tblStyle" [("w:val","Table")] () :
1054− mknode "w:tblW" [("w:type", "pct"), ("w:w", tshow rowwidth)] () :
1055− mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0")
1056− ,("w:lastRow","0")
1057− ,("w:firstColumn","0")
1058− ,("w:lastColumn","0")
1059− ,("w:noHBand","0")
1060− ,("w:noVBand","0")
1061− ,("w:val", T.pack $ printf "%04x" tblLookVal)
1062− ] () :
1063− [ mknode "w:tblCaption" [("w:val", captionStr)] ()
1064− | not (null caption) ] )
1065− : mknode "w:tblGrid" []
1066− (if all (==0) widths
1067− then []
1068− else map mkgridcol widths)
1069− : [ mkrow headers' | hasHeader ] ++
1070− map mkrow rows'
1071− )]
1072blockToOpenXML' opts el
927blockToOpenXML' opts el
1073 | BulletList lst <- el = addOpenXMLList BulletMarker lst
928 | BulletList lst <- el = addOpenXMLList BulletMarker lst
1074 | OrderedList (start, numstyle, numdelim) lst <- el
929 | OrderedList (start, numstyle, numdelim) lst <- el
@@ -1123,13 +978,6 @@ listItemToOpenXML opts numid (first:rest) = do
1123 modify $ \st -> st{ stInList = oldInList }
978 modify $ \st -> st{ stInList = oldInList }
1124 return $ first'' ++ rest''
979 return $ first'' ++ rest''
1125
980
1126−alignmentToString :: Alignment -> Text
1127−alignmentToString alignment = case alignment of
1128− AlignLeft -> "left"
1129− AlignRight -> "right"
1130− AlignCenter -> "center"
1131− AlignDefault -> "left"
1132−
1133-- | Convert a list of inline elements to OpenXML.
981-- | Convert a list of inline elements to OpenXML.
1134inlinesToOpenXML :: PandocMonad m => WriterOptions -> [Inline] -> WS m [Content]
982inlinesToOpenXML :: PandocMonad m => WriterOptions -> [Inline] -> WS m [Content]
1135inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst
983inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst
@@ -1140,10 +988,6 @@ withNumId numid = local $ \env -> env{ envListNumId = numid }
1140asList :: (PandocMonad m) => WS m a -> WS m a
988asList :: (PandocMonad m) => WS m a -> WS m a
1141asList = local $ \env -> env{ envListLevel = envListLevel env + 1 }
989asList = local $ \env -> env{ envListLevel = envListLevel env + 1 }
1142
990
1143−isStyle :: Element -> Bool
1144−isStyle e = isElem [] "w" "rStyle" e ||
1145− isElem [] "w" "pStyle" e
1146−
1147getTextProps :: (PandocMonad m) => WS m [Element]
991getTextProps :: (PandocMonad m) => WS m [Element]
1148getTextProps = do
992getTextProps = do
1149 props <- asks envTextProperties
993 props <- asks envTextProperties
@@ -1172,16 +1016,6 @@ getParaProps displayMathPara = do
1172 [] -> []
1016 [] -> []
1173 ps -> [mknode "w:pPr" [] ps]
1017 ps -> [mknode "w:pPr" [] ps]
1174
1018
1175−withParaProp :: PandocMonad m => Element -> WS m a -> WS m a
1176−withParaProp d p =
1177− local (\env -> env {envParaProperties = ep <> envParaProperties env}) p
1178− where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d]
1179−
1180−withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a
1181−withParaPropM md p = do
1182− d <- md
1183− withParaProp d p
1184−
1185formattedString :: PandocMonad m => Text -> WS m [Element]
1019formattedString :: PandocMonad m => Text -> WS m [Element]
1186formattedString str =
1020formattedString str =
1187 -- properly handle soft hyphens
1021 -- properly handle soft hyphens
@@ -1202,9 +1036,6 @@ formattedRun els = do
1202 props <- getTextProps
1036 props <- getTextProps
1203 return [ mknode "w:r" [] $ props ++ els ]
1037 return [ mknode "w:r" [] $ props ++ els ]
1204
1038
1205−setFirstPara :: PandocMonad m => WS m ()
1206−setFirstPara = modify $ \s -> s { stFirstPara = True }
1207−
1208-- | Convert an inline element to OpenXML.
1039-- | Convert an inline element to OpenXML.
1209inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Content]
1040inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Content]
1210inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il
1041inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il
@@ -1473,6 +1304,7 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
1473 Just Eps -> ".eps"
1304 Just Eps -> ".eps"
1474 Just Svg -> ".svg"
1305 Just Svg -> ".svg"
1475 Just Emf -> ".emf"
1306 Just Emf -> ".emf"
1307+ Just Tiff -> ".tiff"
1476 Nothing -> ""
1308 Nothing -> ""
1477 imgpath = "media/" <> ident <> imgext
1309 imgpath = "media/" <> ident <> imgext
1478 mbMimeType = mt <|> getMimeType (T.unpack imgpath)
1310 mbMimeType = mt <|> getMimeType (T.unpack imgpath)
@@ -1496,22 +1328,6 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
1496br :: Element
1328br :: Element
1497br = mknode "w:r" [] [mknode "w:br" [] ()]
1329br = mknode "w:r" [] [mknode "w:br" [] ()]
1498
1330
1499−-- Word will insert these footnotes into the settings.xml file
1500−-- (whether or not they're visible in the document). If they're in the
1501−-- file, but not in the footnotes.xml file, it will produce
1502−-- problems. So we want to make sure we insert them into our document.
1503−defaultFootnotes :: [Element]
1504−defaultFootnotes = [ mknode "w:footnote"
1505− [("w:type", "separator"), ("w:id", "-1")]
1506− [ mknode "w:p" []
1507− [mknode "w:r" []
1508− [ mknode "w:separator" [] ()]]]
1509− , mknode "w:footnote"
1510− [("w:type", "continuationSeparator"), ("w:id", "0")]
1511− [ mknode "w:p" []
1512− [ mknode "w:r" []
1513− [ mknode "w:continuationSeparator" [] ()]]]]
1514−
1515
1331
1516withDirection :: PandocMonad m => WS m a -> WS m a
1332withDirection :: PandocMonad m => WS m a -> WS m a
1517withDirection x = do
1333withDirection x = do
src/Text/Pandoc/Writers/Docx/Table.hsnew file+227
@@ -0,0 +1,227 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{- |
5+Module : Text.Pandoc.Writers.Docx.Table
6+Copyright : Copyright (C) 2012-2021 John MacFarlane
7+License : GNU GPL, version 2 or above
8+Maintainer : John MacFarlane <jgm@berkeley.edu>
9+
10+Conversion of table blocks to docx.
11+-}
12+module Text.Pandoc.Writers.Docx.Table
13+ ( tableToOpenXML
14+ ) where
15+
16+import Control.Monad.State.Strict
17+import Data.Array
18+import Data.Text (Text)
19+import Text.Pandoc.Definition
20+import Text.Pandoc.Class.PandocMonad (PandocMonad, translateTerm)
21+import Text.Pandoc.Writers.Docx.Types
22+import Text.Pandoc.Shared
23+import Text.Printf (printf)
24+import Text.Pandoc.Writers.GridTable hiding (Table)
25+import Text.Pandoc.Writers.OOXML
26+import Text.Pandoc.XML.Light as XML hiding (Attr)
27+import qualified Data.Text as T
28+import qualified Text.Pandoc.Translations as Term
29+import qualified Text.Pandoc.Writers.GridTable as Grid
30+
31+tableToOpenXML :: PandocMonad m
32+ => ([Block] -> WS m [Content])
33+ -> Grid.Table
34+ -> WS m [Content]
35+tableToOpenXML blocksToOpenXML gridTable = do
36+ setFirstPara
37+ let (Grid.Table (ident,_,_) caption colspecs _rowheads thead tbodies tfoot) =
38+ gridTable
39+ let (Caption _maybeShortCaption captionBlocks) = caption
40+ tablenum <- gets stNextTableNum
41+ unless (null captionBlocks) $
42+ modify $ \st -> st{ stNextTableNum = tablenum + 1 }
43+ let tableid = if T.null ident
44+ then "table" <> tshow tablenum
45+ else ident
46+ tablename <- translateTerm Term.Table
47+ let captionStr = stringify captionBlocks
48+ let aligns = map fst $ elems colspecs
49+ captionXml <- if null captionBlocks
50+ then return []
51+ else withParaPropM (pStyleM "Table Caption")
52+ $ blocksToOpenXML
53+ $ addLabel tableid tablename tablenum captionBlocks
54+ -- We set "in table" after processing the caption, because we don't
55+ -- want the "Table Caption" style to be overwritten with "Compact".
56+ modify $ \s -> s { stInTable = True }
57+ head' <- cellGridToOpenXML blocksToOpenXML HeadRow aligns thead
58+ bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow aligns) tbodies
59+ foot' <- cellGridToOpenXML blocksToOpenXML FootRow aligns tfoot
60+
61+ let hasHeader = not . null . indices . partRowAttrs $ thead
62+ let hasFooter = not . null . indices . partRowAttrs $ tfoot
63+ -- for compatibility with Word <= 2007, we include a val with a bitmask
64+ -- 0×0020 Apply first row conditional formatting
65+ -- 0×0040 Apply last row conditional formatting
66+ -- 0×0080 Apply first column conditional formatting
67+ -- 0×0100 Apply last column conditional formatting
68+ -- 0×0200 Do not apply row banding conditional formatting
69+ -- 0×0400 Do not apply column banding conditional formattin
70+ let tblLookVal = if hasHeader then (0x20 :: Int) else 0
71+ let (gridCols, tblWattr) = tableLayout (elems colspecs)
72+ let tbl = mknode "w:tbl" []
73+ ( mknode "w:tblPr" []
74+ ( mknode "w:tblStyle" [("w:val","Table")] () :
75+ mknode "w:tblW" tblWattr () :
76+ mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0")
77+ ,("w:lastRow",if hasFooter then "1" else "0")
78+ ,("w:firstColumn","0")
79+ ,("w:lastColumn","0")
80+ ,("w:noHBand","0")
81+ ,("w:noVBand","0")
82+ ,("w:val", T.pack $ printf "%04x" tblLookVal)
83+ ] () :
84+ [ mknode "w:tblCaption" [("w:val", captionStr)] ()
85+ | not (T.null captionStr) ]
86+ )
87+ : mknode "w:tblGrid" [] gridCols
88+ : head' ++ mconcat bodies ++ foot'
89+ )
90+ modify $ \s -> s { stInTable = False }
91+ return $ captionXml ++ [Elem tbl]
92+
93+addLabel :: Text -> Text -> Int -> [Block] -> [Block]
94+addLabel tableid tablename tablenum bs =
95+ case bs of
96+ (Para ils : rest) -> Para (label : Space : ils) : rest
97+ (Plain ils : rest) -> Plain (label : Space : ils) : rest
98+ _ -> Para [label] : bs
99+ where
100+ label = Span (tableid,[],[])
101+ [Str (tablename <> "\160"),
102+ RawInline (Format "openxml")
103+ ("<w:fldSimple w:instr=\"SEQ Table"
104+ <> " \\* ARABIC \"><w:r><w:t>"
105+ <> tshow tablenum
106+ <> "</w:t></w:r></w:fldSimple>"),
107+ Str ":"]
108+
109+-- | Parts of a table
110+data RowType = HeadRow | BodyRow | FootRow
111+
112+alignmentToString :: Alignment -> Text
113+alignmentToString = \case
114+ AlignLeft -> "left"
115+ AlignRight -> "right"
116+ AlignCenter -> "center"
117+ AlignDefault -> "left"
118+
119+tableLayout :: [ColSpec] -> ([Element], [(Text, Text)])
120+tableLayout specs =
121+ let
122+ textwidth = 7920 -- 5.5 in in twips (1 twip == 1/20 pt)
123+ fullrow = 5000 -- 100% specified in pct (1 pct == 1/50th of a percent)
124+ ncols = length specs
125+ getWidth = \case
126+ ColWidth n -> n
127+ _ -> 0
128+ widths = map (getWidth . snd) specs
129+ rowwidth = round (fullrow * sum widths) :: Int
130+ widthToTwips w = floor (textwidth * w) :: Int
131+ mkGridCol w = mknode "w:gridCol" [("w:w", tshow (widthToTwips w))] ()
132+ in if all (== 0) widths
133+ then ( replicate ncols $ mkGridCol (1.0 / fromIntegral ncols)
134+ , [ ("w:type", "auto"), ("w:w", "0")])
135+ else ( map mkGridCol widths
136+ , [ ("w:type", "pct"), ("w:w", tshow rowwidth) ])
137+
138+cellGridToOpenXML :: PandocMonad m
139+ => ([Block] -> WS m [Content])
140+ -> RowType
141+ -> [Alignment]
142+ -> Part
143+ -> WS m [Element]
144+cellGridToOpenXML blocksToOpenXML rowType aligns part@(Part _ cellArray _) =
145+ if null (elems cellArray)
146+ then return mempty
147+ else mapM (rowToOpenXML blocksToOpenXML) $
148+ partToRows rowType aligns part
149+
150+data OOXMLCell
151+ = OOXMLCell Attr Alignment RowSpan ColSpan [Block]
152+ | OOXMLCellMerge ColSpan
153+
154+data OOXMLRow = OOXMLRow RowType Attr [OOXMLCell]
155+
156+partToRows :: RowType -> [Alignment] -> Part -> [OOXMLRow]
157+partToRows rowType aligns part =
158+ let
159+ toOOXMLCell :: Alignment -> RowIndex -> ColIndex -> GridCell -> [OOXMLCell]
160+ toOOXMLCell columnAlign ridx cidx = \case
161+ ContentCell attr align rowspan colspan blocks ->
162+ -- Respect non-default, cell specific alignment.
163+ let align' = case align of
164+ AlignDefault -> columnAlign
165+ _ -> align
166+ in [OOXMLCell attr align' rowspan colspan blocks]
167+ ContinuationCell idx'@(ridx',cidx') | ridx /= ridx', cidx == cidx' ->
168+ case (partCellArray part)!idx' of
169+ (ContentCell _ _ _ colspan _) -> [OOXMLCellMerge colspan]
170+ x -> error $ "Content cell expected, got, " ++ show x ++
171+ " at index " ++ show idx'
172+ _ -> mempty
173+ mkRow :: (RowIndex, Attr) -> OOXMLRow
174+ mkRow (ridx, attr) = OOXMLRow rowType attr
175+ . mconcat
176+ . zipWith (\align -> uncurry $ toOOXMLCell align ridx)
177+ aligns
178+ . assocs
179+ . rowArray ridx
180+ $ partCellArray part
181+ in map mkRow $ assocs (partRowAttrs part)
182+
183+rowToOpenXML :: PandocMonad m
184+ => ([Block] -> WS m [Content])
185+ -> OOXMLRow
186+ -> WS m Element
187+rowToOpenXML blocksToOpenXML (OOXMLRow rowType _attr cells) = do
188+ xmlcells <- mapM (ooxmlCellToOpenXML blocksToOpenXML) cells
189+ let addTrPr = case rowType of
190+ HeadRow -> (mknode "w:trPr" []
191+ [mknode "w:tblHeader" [("w:val", "true")] ()] :)
192+ BodyRow -> id
193+ FootRow -> id
194+ return $ mknode "w:tr" [] (addTrPr xmlcells)
195+
196+ooxmlCellToOpenXML :: PandocMonad m
197+ => ([Block] -> WS m [Content])
198+ -> OOXMLCell
199+ -> WS m Element
200+ooxmlCellToOpenXML blocksToOpenXML = \case
201+ OOXMLCellMerge (ColSpan colspan) -> do
202+ return $ mknode "w:tc" []
203+ [ mknode "w:tcPr" [] [ mknode "w:gridSpan" [("w:val", tshow colspan)] ()
204+ , mknode "w:vMerge" [("w:val", "continue")] () ]
205+ , mknode "w:p" [] [mknode "w:pPr" [] ()]]
206+ OOXMLCell _attr align rowspan (ColSpan colspan) contents -> do
207+ compactStyle <- pStyleM "Compact"
208+ es <- withParaProp (alignmentFor align) $ blocksToOpenXML contents
209+ -- Table cells require a <w:p> element, even an empty one!
210+ -- Not in the spec but in Word 2007, 2010. See #4953. And
211+ -- apparently the last element must be a <w:p>, see #6983.
212+ return . mknode "w:tc" [] $
213+ Elem
214+ (mknode "w:tcPr" [] ([ mknode "w:gridSpan" [("w:val", tshow colspan)] ()
215+ | colspan > 1] ++
216+ [ mknode "w:vMerge" [("w:val", "restart")] ()
217+ | rowspan > RowSpan 1 ])) :
218+ if null contents
219+ then [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]]
220+ else case reverse (onlyElems es) of
221+ b:e:_ | qName (elName b) == "bookmarkEnd" -- y tho?
222+ , qName (elName e) == "p" -> es
223+ e:_ | qName (elName e) == "p" -> es
224+ _ -> es ++ [Elem $ mknode "w:p" [] ()]
225+
226+alignmentFor :: Alignment -> Element
227+alignmentFor al = mknode "w:jc" [("w:val",alignmentToString al)] ()
src/Text/Pandoc/Writers/Docx/Types.hsnew file+185
@@ -0,0 +1,185 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+Module : Text.Pandoc.Writers.Docx
4+Copyright : Copyright (C) 2012-2021 John MacFarlane
5+License : GNU GPL, version 2 or above
6+Maintainer : John MacFarlane <jgm@berkeley.edu>
7+
8+Conversion of table blocks to docx.
9+-}
10+module Text.Pandoc.Writers.Docx.Types
11+ ( EnvProps (..)
12+ , WriterEnv (..)
13+ , defaultWriterEnv
14+ , WriterState (..)
15+ , defaultWriterState
16+ , WS
17+ , ListMarker (..)
18+ , listMarkerToId
19+ , pStyleM
20+ , isStyle
21+ , setFirstPara
22+ , withParaProp
23+ , withParaPropM
24+ ) where
25+
26+import Control.Applicative ((<|>))
27+import Control.Monad.Reader
28+import Control.Monad.State.Strict
29+import Data.Text (Text)
30+import Text.Pandoc.Class.PandocMonad (PandocMonad)
31+import Text.Pandoc.Definition
32+import Text.Pandoc.MIME (MimeType)
33+import Text.Pandoc.Writers.Docx.StyleMap
34+import Text.Pandoc.Writers.OOXML
35+import Text.Pandoc.XML.Light as XML
36+import qualified Data.ByteString as B
37+import qualified Data.Map as M
38+import qualified Data.Set as Set
39+import qualified Data.Text as T
40+
41+data ListMarker = NoMarker
42+ | BulletMarker
43+ | NumberMarker ListNumberStyle ListNumberDelim Int
44+ deriving (Show, Read, Eq, Ord)
45+
46+listMarkerToId :: ListMarker -> Text
47+listMarkerToId NoMarker = "990"
48+listMarkerToId BulletMarker = "991"
49+listMarkerToId (NumberMarker sty delim n) = T.pack $
50+ '9' : '9' : styNum : delimNum : show n
51+ where styNum = case sty of
52+ DefaultStyle -> '2'
53+ Example -> '3'
54+ Decimal -> '4'
55+ LowerRoman -> '5'
56+ UpperRoman -> '6'
57+ LowerAlpha -> '7'
58+ UpperAlpha -> '8'
59+ delimNum = case delim of
60+ DefaultDelim -> '0'
61+ Period -> '1'
62+ OneParen -> '2'
63+ TwoParens -> '3'
64+
65+
66+data EnvProps = EnvProps{ styleElement :: Maybe Element
67+ , otherElements :: [Element]
68+ }
69+
70+instance Semigroup EnvProps where
71+ EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es')
72+
73+instance Monoid EnvProps where
74+ mempty = EnvProps Nothing []
75+ mappend = (<>)
76+
77+data WriterEnv = WriterEnv
78+ { envTextProperties :: EnvProps
79+ , envParaProperties :: EnvProps
80+ , envRTL :: Bool
81+ , envListLevel :: Int
82+ , envListNumId :: Int
83+ , envInDel :: Bool
84+ , envChangesAuthor :: Text
85+ , envChangesDate :: Text
86+ , envPrintWidth :: Integer
87+ }
88+
89+defaultWriterEnv :: WriterEnv
90+defaultWriterEnv = WriterEnv
91+ { envTextProperties = mempty
92+ , envParaProperties = mempty
93+ , envRTL = False
94+ , envListLevel = -1
95+ , envListNumId = 1
96+ , envInDel = False
97+ , envChangesAuthor = "unknown"
98+ , envChangesDate = "1969-12-31T19:00:00Z"
99+ , envPrintWidth = 1
100+ }
101+
102+
103+data WriterState = WriterState{
104+ stFootnotes :: [Element]
105+ , stComments :: [([(Text, Text)], [Inline])]
106+ , stSectionIds :: Set.Set Text
107+ , stExternalLinks :: M.Map Text Text
108+ , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString)
109+ , stLists :: [ListMarker]
110+ , stInsId :: Int
111+ , stDelId :: Int
112+ , stStyleMaps :: StyleMaps
113+ , stFirstPara :: Bool
114+ , stInTable :: Bool
115+ , stInList :: Bool
116+ , stTocTitle :: [Inline]
117+ , stDynamicParaProps :: Set.Set ParaStyleName
118+ , stDynamicTextProps :: Set.Set CharStyleName
119+ , stCurId :: Int
120+ , stNextFigureNum :: Int
121+ , stNextTableNum :: Int
122+ }
123+
124+defaultWriterState :: WriterState
125+defaultWriterState = WriterState{
126+ stFootnotes = defaultFootnotes
127+ , stComments = []
128+ , stSectionIds = Set.empty
129+ , stExternalLinks = M.empty
130+ , stImages = M.empty
131+ , stLists = [NoMarker]
132+ , stInsId = 1
133+ , stDelId = 1
134+ , stStyleMaps = StyleMaps M.empty M.empty
135+ , stFirstPara = False
136+ , stInTable = False
137+ , stInList = False
138+ , stTocTitle = [Str "Table of Contents"]
139+ , stDynamicParaProps = Set.empty
140+ , stDynamicTextProps = Set.empty
141+ , stCurId = 20
142+ , stNextFigureNum = 1
143+ , stNextTableNum = 1
144+ }
145+
146+setFirstPara :: PandocMonad m => WS m ()
147+setFirstPara = modify $ \s -> s { stFirstPara = True }
148+
149+type WS m = ReaderT WriterEnv (StateT WriterState m)
150+
151+-- Word will insert these footnotes into the settings.xml file
152+-- (whether or not they're visible in the document). If they're in the
153+-- file, but not in the footnotes.xml file, it will produce
154+-- problems. So we want to make sure we insert them into our document.
155+defaultFootnotes :: [Element]
156+defaultFootnotes = [ mknode "w:footnote"
157+ [("w:type", "separator"), ("w:id", "-1")]
158+ [ mknode "w:p" []
159+ [mknode "w:r" []
160+ [ mknode "w:separator" [] ()]]]
161+ , mknode "w:footnote"
162+ [("w:type", "continuationSeparator"), ("w:id", "0")]
163+ [ mknode "w:p" []
164+ [ mknode "w:r" []
165+ [ mknode "w:continuationSeparator" [] ()]]]]
166+
167+pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element
168+pStyleM styleName = do
169+ pStyleMap <- gets (smParaStyle . stStyleMaps)
170+ let sty' = getStyleIdFromName styleName pStyleMap
171+ return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] ()
172+
173+withParaProp :: PandocMonad m => Element -> WS m a -> WS m a
174+withParaProp d p =
175+ local (\env -> env {envParaProperties = ep <> envParaProperties env}) p
176+ where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d]
177+
178+withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a
179+withParaPropM md p = do
180+ d <- md
181+ withParaProp d p
182+
183+isStyle :: Element -> Bool
184+isStyle e = isElem [] "w" "rStyle" e ||
185+ isElem [] "w" "pStyle" e
src/Text/Pandoc/Writers/DokuWiki.hs+3−1
@@ -27,6 +27,7 @@ import Control.Monad.Reader (ReaderT, asks, local, runReaderT)
27import Control.Monad.State.Strict (StateT, evalStateT)
27import Control.Monad.State.Strict (StateT, evalStateT)
28import Data.Default (Default (..))
28import Data.Default (Default (..))
29import Data.List (transpose)
29import Data.List (transpose)
30+import Data.List.NonEmpty (nonEmpty)
30import Data.Text (Text)
31import Data.Text (Text)
31import qualified Data.Text as T
32import qualified Data.Text as T
32import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
33import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
@@ -172,7 +173,8 @@ blockToDokuWiki opts (Table _ blkCapt specs thead tbody tfoot) = do
172 then return []
173 then return []
173 else zipWithM (tableItemToDokuWiki opts) aligns headers
174 else zipWithM (tableItemToDokuWiki opts) aligns headers
174 rows' <- mapM (zipWithM (tableItemToDokuWiki opts) aligns) rows
175 rows' <- mapM (zipWithM (tableItemToDokuWiki opts) aligns) rows
175− let widths = map (maximum . map T.length) $ transpose (headers':rows')
176+ let widths = map (maybe 0 maximum . nonEmpty . map T.length)
177+ $ transpose (headers':rows')
176 let padTo (width, al) s =
178 let padTo (width, al) s =
177 case width - T.length s of
179 case width - T.length s of
178 x | x > 0 ->
180 x | x > 0 ->
src/Text/Pandoc/Writers/EPUB.hs+3−3
@@ -991,12 +991,12 @@ metadataElement version md currentTime =
991 showDateTimeISO8601 currentTime | version == EPUB3 ]
991 showDateTimeISO8601 currentTime | version == EPUB3 ]
992 belongsToCollectionNodes =
992 belongsToCollectionNodes =
993 maybe []
993 maybe []
994− (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-id-1")] $ belongsToCollection )
994+ (\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-collection-1")] $ belongsToCollection )
995 :
995 :
996− [unode "meta" ! [("refines", "#epub-id-1"), ("property", "collection-type")] $ ("series" :: Text) ])
996+ [unode "meta" ! [("refines", "#epub-collection-1"), ("property", "collection-type")] $ ("series" :: Text) ])
997 (epubBelongsToCollection md)++
997 (epubBelongsToCollection md)++
998 maybe []
998 maybe []
999− (\groupPosition -> [unode "meta" ! [("refines", "#epub-id-1"), ("property", "group-position")] $ groupPosition ])
999+ (\groupPosition -> [unode "meta" ! [("refines", "#epub-collection-1"), ("property", "group-position")] $ groupPosition ])
1000 (epubGroupPosition md)
1000 (epubGroupPosition md)
1001 dcTag n s = unode ("dc:" <> n) s
1001 dcTag n s = unode ("dc:" <> n) s
1002 dcTag' n s = [dcTag n s]
1002 dcTag' n s = [dcTag n s]
src/Text/Pandoc/Writers/GridTable.hsnew file+157
@@ -0,0 +1,157 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE TupleSections #-}
5+
6+{- |
7+Module : Text.Pandoc.Writers.GridTable
8+Copyright : © 2020-2021 Albert Krewinkel
9+License : GNU GPL, version 2 or above
10+
11+Maintainer : Albert Krewinkel <albert@zeitkraut.de>
12+
13+Grid representation of pandoc tables.
14+-}
15+module Text.Pandoc.Writers.GridTable
16+ ( Table (..)
17+ , GridCell (..)
18+ , RowIndex (..)
19+ , ColIndex (..)
20+ , CellIndex
21+ , Part (..)
22+ , toTable
23+ , rowArray
24+ ) where
25+
26+import Control.Monad (forM_)
27+import Control.Monad.ST
28+import Data.Array
29+import Data.Array.MArray
30+import Data.Array.ST
31+import Data.Maybe (listToMaybe)
32+import Data.STRef
33+import Text.Pandoc.Definition hiding (Table)
34+import qualified Text.Pandoc.Builder as B
35+
36+-- | A grid cell contains either a real table cell, or is the
37+-- continuation of a column or row-spanning cell. In the latter case,
38+-- the index of the continued cell is provided.
39+data GridCell
40+ = ContentCell Attr Alignment RowSpan ColSpan [Block]
41+ | ContinuationCell CellIndex
42+ deriving (Show)
43+
44+-- | Row index in a table part.
45+newtype RowIndex = RowIndex Int deriving (Enum, Eq, Ix, Ord, Show)
46+-- | Column index in a table part.
47+newtype ColIndex = ColIndex Int deriving (Enum, Eq, Ix, Ord, Show)
48+
49+-- | Index to a cell in a table part.
50+type CellIndex = (RowIndex, ColIndex)
51+
52+-- | Cells are placed on a grid. Row attributes are stored in a separate
53+-- array.
54+data Part = Part
55+ { partAttr :: Attr
56+ , partCellArray :: Array (RowIndex,ColIndex) GridCell
57+ , partRowAttrs :: Array RowIndex Attr
58+ }
59+
60+data Table = Table
61+ { tableAttr :: Attr
62+ , tableCaption :: Caption
63+ , tableColSpecs :: Array ColIndex ColSpec
64+ , tableRowHeads :: RowHeadColumns
65+ , tableHead :: Part
66+ , tableBodies :: [Part]
67+ , tableFoot :: Part
68+ }
69+
70+toTable
71+ :: B.Attr
72+ -> B.Caption
73+ -> [B.ColSpec]
74+ -> B.TableHead
75+ -> [B.TableBody]
76+ -> B.TableFoot
77+ -> Table
78+toTable attr caption colSpecs thead tbodies tfoot =
79+ Table attr caption colSpecs' rowHeads thGrid tbGrids tfGrid
80+ where
81+ colSpecs' = listArray (ColIndex 1, ColIndex $ length colSpecs) colSpecs
82+ rowHeads = case listToMaybe tbodies of
83+ Nothing -> RowHeadColumns 0
84+ Just (TableBody _attr rowHeadCols _headerRows _rows) -> rowHeadCols
85+ thGrid = let (TableHead headAttr rows) = thead
86+ in rowsToPart headAttr rows
87+ tbGrids = map bodyToGrid tbodies
88+ tfGrid = let (TableFoot footAttr rows) = tfoot
89+ in rowsToPart footAttr rows
90+ bodyToGrid (TableBody bodyAttr _rowHeadCols headRows rows) =
91+ rowsToPart bodyAttr (headRows ++ rows)
92+
93+data BuilderCell
94+ = FilledCell GridCell
95+ | FreeCell
96+
97+fromBuilderCell :: BuilderCell -> GridCell
98+fromBuilderCell = \case
99+ FilledCell c -> c
100+ FreeCell -> error "Found an unassigned cell."
101+
102+rowsToPart :: Attr -> [B.Row] -> Part
103+rowsToPart attr = \case
104+ [] -> Part
105+ attr
106+ (listArray ((RowIndex 1, ColIndex 1), (RowIndex 0, ColIndex 0)) [])
107+ (listArray (RowIndex 1, RowIndex 0) [])
108+ rows@(Row _attr firstRow:_) ->
109+ let nrows = length rows
110+ ncols = sum $ map (\(Cell _ _ _ (ColSpan cs) _) -> cs) firstRow
111+ gbounds = ((RowIndex 1, ColIndex 1), (RowIndex nrows, ColIndex ncols))
112+ mutableGrid :: ST s (STArray s CellIndex GridCell)
113+ mutableGrid = do
114+ grid <- newArray gbounds FreeCell
115+ ridx <- newSTRef (RowIndex 1)
116+ forM_ rows $ \(Row _attr cells) -> do
117+ cidx <- newSTRef (ColIndex 1)
118+ forM_ cells $ \(Cell cellAttr align rs cs blks) -> do
119+ ridx' <- readSTRef ridx
120+ let nextFreeInRow colindex@(ColIndex c) = do
121+ readArray grid (ridx', colindex) >>= \case
122+ FreeCell -> pure colindex
123+ _ -> nextFreeInRow $ ColIndex (c + 1)
124+ cidx' <- readSTRef cidx >>= nextFreeInRow
125+ writeArray grid (ridx', cidx') . FilledCell $
126+ ContentCell cellAttr align rs cs blks
127+ forM_ (continuationIndices ridx' cidx' rs cs) $ \idx -> do
128+ writeArray grid idx . FilledCell $
129+ ContinuationCell (ridx', cidx')
130+ -- go to new column
131+ writeSTRef cidx cidx'
132+ -- go to next row
133+ modifySTRef ridx (incrRowIndex 1)
134+ -- Swap BuilderCells with normal GridCells.
135+ mapArray fromBuilderCell grid
136+ in Part
137+ { partCellArray = runSTArray mutableGrid
138+ , partRowAttrs = listArray (RowIndex 1, RowIndex nrows) $
139+ map (\(Row rowAttr _) -> rowAttr) rows
140+ , partAttr = attr
141+ }
142+
143+continuationIndices :: RowIndex -> ColIndex -> RowSpan -> ColSpan -> [CellIndex]
144+continuationIndices (RowIndex ridx) (ColIndex cidx) rowspan colspan =
145+ let (RowSpan rs) = rowspan
146+ (ColSpan cs) = colspan
147+ in [ (RowIndex r, ColIndex c) | r <- [ridx..(ridx + rs - 1)]
148+ , c <- [cidx..(cidx + cs - 1)]
149+ , (r, c) /= (ridx, cidx)]
150+
151+rowArray :: RowIndex -> Array CellIndex GridCell -> Array ColIndex GridCell
152+rowArray ridx grid =
153+ let ((_minRidx, minCidx), (_maxRidx, maxCidx)) = bounds grid
154+ in ixmap (minCidx, maxCidx) (ridx,) grid
155+
156+incrRowIndex :: RowSpan -> RowIndex -> RowIndex
157+incrRowIndex (RowSpan n) (RowIndex r) = RowIndex $ r + n
src/Text/Pandoc/Writers/HTML.hs+107−34
@@ -26,13 +26,18 @@ module Text.Pandoc.Writers.HTML (
26 writeSlideous,
26 writeSlideous,
27 writeDZSlides,
27 writeDZSlides,
28 writeRevealJs,
28 writeRevealJs,
29− tagWithAttributes
29+ tagWithAttributes,
30+ inlineToHtml,
31+ blockToHtml,
32+ WriterState(..),
33+ defaultWriterState
30 ) where
34 ) where
35+
31import Control.Monad.State.Strict
36import Control.Monad.State.Strict
32import Data.Char (ord)
37import Data.Char (ord)
33−import Data.List (intercalate, intersperse, partition, delete, (\\))
38+import Data.List (intercalate, intersperse, partition, delete, (\\), foldl')
34import Data.List.NonEmpty (NonEmpty((:|)))
39import Data.List.NonEmpty (NonEmpty((:|)))
35−import Data.Maybe (fromMaybe, isJust, isNothing, mapMaybe)
40+import Data.Maybe (fromMaybe, isJust, isNothing)
36import qualified Data.Set as Set
41import qualified Data.Set as Set
37import Data.Text (Text)
42import Data.Text (Text)
38import qualified Data.Text as T
43import qualified Data.Text as T
@@ -344,6 +349,52 @@ pandocToHtml opts (Pandoc meta blocks) = do
344 PlainMath -> defField "displaymath-css" True
349 PlainMath -> defField "displaymath-css" True
345 WebTeX _ -> defField "displaymath-css" True
350 WebTeX _ -> defField "displaymath-css" True
346 _ -> id) .
351 _ -> id) .
352+ (if slideVariant == RevealJsSlides
353+ then -- set boolean options explicitly, since
354+ -- template can't distinguish False/undefined
355+ defField "controls" True .
356+ defField "controlsTutorial" True .
357+ defField "controlsLayout" ("bottom-right" :: Text) .
358+ defField "controlsBackArrows" ("faded" :: Text) .
359+ defField "progress" True .
360+ defField "slideNumber" False .
361+ defField "showSlideNumber" ("all" :: Text) .
362+ defField "hashOneBasedIndex" False .
363+ defField "hash" False .
364+ defField "respondToHashChanges" True .
365+ defField "history" False .
366+ defField "keyboard" True .
367+ defField "overview" True .
368+ defField "disableLayout" False .
369+ defField "center" True .
370+ defField "touch" True .
371+ defField "loop" False .
372+ defField "rtl" False .
373+ defField "navigationMode" ("default" :: Text) .
374+ defField "shuffle" False .
375+ defField "fragments" True .
376+ defField "fragmentInURL" True .
377+ defField "embedded" False .
378+ defField "help" True .
379+ defField "pause" True .
380+ defField "showNotes" False .
381+ defField "autoPlayMedia" ("null" :: Text) .
382+ defField "preloadIframes" ("null" :: Text) .
383+ defField "autoSlide" ("0" :: Text) .
384+ defField "autoSlideStoppable" True .
385+ defField "autoSlideMethod" ("null" :: Text) .
386+ defField "defaultTiming" ("null" :: Text) .
387+ defField "mouseWheel" False .
388+ defField "display" ("block" :: Text) .
389+ defField "hideInactiveCursor" True .
390+ defField "hideCursorTime" ("5000" :: Text) .
391+ defField "previewLinks" False .
392+ defField "transition" ("slide" :: Text) .
393+ defField "transitionSpeed" ("default" :: Text) .
394+ defField "backgroundTransition" ("fade" :: Text) .
395+ defField "viewDistance" ("3" :: Text) .
396+ defField "mobileViewDistance" ("2" :: Text)
397+ else id) .
347 defField "document-css" (isNothing mCss && slideVariant == NoSlides) .
398 defField "document-css" (isNothing mCss && slideVariant == NoSlides) .
348 defField "quotes" (stQuotes st) .
399 defField "quotes" (stQuotes st) .
349 -- for backwards compatibility we populate toc
400 -- for backwards compatibility we populate toc
@@ -544,30 +595,35 @@ tagWithAttributes opts html5 selfClosing tagname attr =
544
595
545addAttrs :: PandocMonad m
596addAttrs :: PandocMonad m
546 => WriterOptions -> Attr -> Html -> StateT WriterState m Html
597 => WriterOptions -> Attr -> Html -> StateT WriterState m Html
547−addAttrs opts attr h = foldl (!) h <$> attrsToHtml opts attr
598+addAttrs opts attr h = foldl' (!) h <$> attrsToHtml opts attr
548
599
549toAttrs :: PandocMonad m
600toAttrs :: PandocMonad m
550 => [(Text, Text)] -> StateT WriterState m [Attribute]
601 => [(Text, Text)] -> StateT WriterState m [Attribute]
551toAttrs kvs = do
602toAttrs kvs = do
552 html5 <- gets stHtml5
603 html5 <- gets stHtml5
553 mbEpubVersion <- gets stEPUBVersion
604 mbEpubVersion <- gets stEPUBVersion
554− return $ mapMaybe (\(x,y) ->
605+ reverse . snd <$> foldM (go html5 mbEpubVersion) (Set.empty, []) kvs
555− if html5
606+ where
556− then
607+ go html5 mbEpubVersion (keys, attrs) (k,v) = do
608+ if k `Set.member` keys
609+ then do
610+ report $ DuplicateAttribute k v
611+ return (keys, attrs)
612+ else return (Set.insert k keys, addAttr html5 mbEpubVersion k v attrs)
613+ addAttr html5 mbEpubVersion x y
614+ | html5
557− if x `Set.member` (html5Attributes <> rdfaAttributes)
615+ = if x `Set.member` (html5Attributes <> rdfaAttributes)
558− || T.any (== ':') x -- e.g. epub: namespace
616+ || T.any (== ':') x -- e.g. epub: namespace
559− || "data-" `T.isPrefixOf` x
617+ || "data-" `T.isPrefixOf` x
560− || "aria-" `T.isPrefixOf` x
618+ || "aria-" `T.isPrefixOf` x
561− then Just $ customAttribute (textTag x) (toValue y)
619+ then (customAttribute (textTag x) (toValue y) :)
562− else Just $ customAttribute (textTag ("data-" <> x))
620+ else (customAttribute (textTag ("data-" <> x)) (toValue y) :)
563− (toValue y)
564− else
565− if mbEpubVersion == Just EPUB2 &&
621+ | mbEpubVersion == Just EPUB2
566− not (x `Set.member` (html4Attributes <> rdfaAttributes) ||
622+ , not (x `Set.member` (html4Attributes <> rdfaAttributes) ||
567− "xml:" `T.isPrefixOf` x)
623+ "xml:" `T.isPrefixOf` x)
568− then Nothing
624+ = id
625+ | otherwise
569− else Just $ customAttribute (textTag x) (toValue y))
626+ = (customAttribute (textTag x) (toValue y) :)
570− kvs
571
627
572attrsToHtml :: PandocMonad m
628attrsToHtml :: PandocMonad m
573 => WriterOptions -> Attr -> StateT WriterState m [Attribute]
629 => WriterOptions -> Attr -> StateT WriterState m [Attribute]
@@ -608,17 +664,20 @@ dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height
608figure :: PandocMonad m
664figure :: PandocMonad m
609 => WriterOptions -> Attr -> [Inline] -> (Text, Text)
665 => WriterOptions -> Attr -> [Inline] -> (Text, Text)
610 -> StateT WriterState m Html
666 -> StateT WriterState m Html
611−figure opts attr txt (s,tit) = do
667+figure opts attr@(_, _, attrList) txt (s,tit) = do
612 html5 <- gets stHtml5
668 html5 <- gets stHtml5
613 -- Screen-readers will normally read the @alt@ text and the figure; we
669 -- Screen-readers will normally read the @alt@ text and the figure; we
614 -- want to avoid them reading the same text twice. With HTML5 we can
670 -- want to avoid them reading the same text twice. With HTML5 we can
615 -- use aria-hidden for the caption; with HTML4, we use an empty
671 -- use aria-hidden for the caption; with HTML4, we use an empty
616 -- alt-text instead.
672 -- alt-text instead.
673+ -- When the alt text differs from the caption both should be read.
617 let alt = if html5 then txt else [Str ""]
674 let alt = if html5 then txt else [Str ""]
618 let tocapt = if html5
675 let tocapt = if html5
619− then H5.figcaption !
676+ then (H5.figcaption !) $
677+ if isJust (lookup "alt" attrList)
678+ then mempty
620− H5.customAttribute (textTag "aria-hidden")
679+ else H5.customAttribute (textTag "aria-hidden")
621− (toValue @Text "true")
680+ (toValue @Text "true")
622 else H.p ! A.class_ "caption"
681 else H.p ! A.class_ "caption"
623 img <- inlineToHtml opts (Image attr alt (s,tit))
682 img <- inlineToHtml opts (Image attr alt (s,tit))
624 capt <- if null txt
683 capt <- if null txt
@@ -765,9 +824,10 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do
765 lookup "entry-spacing" kvs' >>=
824 lookup "entry-spacing" kvs' >>=
766 safeRead }
825 safeRead }
767 let isCslBibEntry = "csl-entry" `elem` classes
826 let isCslBibEntry = "csl-entry" `elem` classes
768− let kvs = [(k,v) | (k,v) <- kvs', k /= "width"] ++
827+ let kvs = [(k,v) | (k,v) <- kvs'
828+ , k /= "width" || "column" `notElem` classes] ++
769− [("style", "width:" <> w <> ";") | "column" `elem` classes,
829+ [("style", "width:" <> w <> ";") | "column" `elem` classes
770− ("width", w) <- kvs'] ++
830+ , ("width", w) <- kvs'] ++
771 [("role", "doc-bibliography") | isCslBibBody && html5] ++
831 [("role", "doc-bibliography") | isCslBibBody && html5] ++
772 [("role", "doc-biblioentry") | isCslBibEntry && html5]
832 [("role", "doc-biblioentry") | isCslBibEntry && html5]
773 let speakerNotes = "notes" `elem` classes
833 let speakerNotes = "notes" `elem` classes
@@ -877,7 +937,7 @@ blockToHtml opts (BlockQuote blocks) = do
877 else do
937 else do
878 contents <- blockListToHtml opts blocks
938 contents <- blockListToHtml opts blocks
879 return $ H.blockquote $ nl opts >> contents >> nl opts
939 return $ H.blockquote $ nl opts >> contents >> nl opts
880−blockToHtml opts (Header level attr@(_,classes,kvs) lst) = do
940+blockToHtml opts (Header level (ident,classes,kvs) lst) = do
881 contents <- inlineListToHtml opts lst
941 contents <- inlineListToHtml opts lst
882 let secnum = fromMaybe mempty $ lookup "number" kvs
942 let secnum = fromMaybe mempty $ lookup "number" kvs
883 let contents' = if writerNumberSections opts && not (T.null secnum)
943 let contents' = if writerNumberSections opts && not (T.null secnum)
@@ -885,7 +945,13 @@ blockToHtml opts (Header level attr@(_,classes,kvs) lst) = do
885 then (H.span ! A.class_ "header-section-number"
945 then (H.span ! A.class_ "header-section-number"
886 $ toHtml secnum) >> strToHtml " " >> contents
946 $ toHtml secnum) >> strToHtml " " >> contents
887 else contents
947 else contents
948+ html5 <- gets stHtml5
949+ let kvs' = if html5
950+ then kvs
951+ else [ (k, v) | (k, v) <- kvs
952+ , k `elem` (["lang", "dir", "title", "style"
953+ , "align"] ++ intrinsicEventsHTML4)]
888− addAttrs opts attr
954+ addAttrs opts (ident,classes,kvs')
889 $ case level of
955 $ case level of
890 1 -> H.h1 contents'
956 1 -> H.h1 contents'
891 2 -> H.h2 contents'
957 2 -> H.h2 contents'
@@ -921,7 +987,7 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
921 numstyle']
987 numstyle']
922 else [])
988 else [])
923 l <- ordList opts contents
989 l <- ordList opts contents
924− return $ foldl (!) l attribs
990+ return $ foldl' (!) l attribs
925blockToHtml opts (DefinitionList lst) = do
991blockToHtml opts (DefinitionList lst) = do
926 contents <- mapM (\(term, defs) ->
992 contents <- mapM (\(term, defs) ->
927 do term' <- liftM H.dt $ inlineListToHtml opts term
993 do term' <- liftM H.dt $ inlineListToHtml opts term
@@ -1375,7 +1441,7 @@ inlineToHtml opts inline = do
1375 return $ if T.null tit
1441 return $ if T.null tit
1376 then link'
1442 then link'
1377 else link' ! A.title (toValue tit)
1443 else link' ! A.title (toValue tit)
1378− (Image attr txt (s,tit)) -> do
1444+ (Image attr@(_, _, attrList) txt (s, tit)) -> do
1379 let alternate = stringify txt
1445 let alternate = stringify txt
1380 slideVariant <- gets stSlideVariant
1446 slideVariant <- gets stSlideVariant
1381 let isReveal = slideVariant == RevealJsSlides
1447 let isReveal = slideVariant == RevealJsSlides
@@ -1388,7 +1454,8 @@ inlineToHtml opts inline = do
1388 [A.title $ toValue tit | not (T.null tit)] ++
1454 [A.title $ toValue tit | not (T.null tit)] ++
1389 attrs
1455 attrs
1390 imageTag = (if html5 then H5.img else H.img
1456 imageTag = (if html5 then H5.img else H.img
1391− , [A.alt $ toValue alternate | not (null txt)] )
1457+ , [A.alt $ toValue alternate | not (null txt) &&
1458+ isNothing (lookup "alt" attrList)] )
1392 mediaTag tg fallbackTxt =
1459 mediaTag tg fallbackTxt =
1393 let linkTxt = if null txt
1460 let linkTxt = if null txt
1394 then fallbackTxt
1461 then fallbackTxt
@@ -1402,7 +1469,7 @@ inlineToHtml opts inline = do
1402 Just "audio" -> mediaTag H5.audio "Audio"
1469 Just "audio" -> mediaTag H5.audio "Audio"
1403 Just _ -> (H5.embed, [])
1470 Just _ -> (H5.embed, [])
1404 _ -> imageTag
1471 _ -> imageTag
1405− return $ foldl (!) tag $ attributes ++ specAttrs
1472+ return $ foldl' (!) tag $ attributes ++ specAttrs
1406 -- note: null title included, as in Markdown.pl
1473 -- note: null title included, as in Markdown.pl
1407 (Note contents) -> do
1474 (Note contents) -> do
1408 notes <- gets stNotes
1475 notes <- gets stNotes
@@ -1521,6 +1588,12 @@ allowsMathEnvironments MathML = True
1521allowsMathEnvironments (WebTeX _) = True
1588allowsMathEnvironments (WebTeX _) = True
1522allowsMathEnvironments _ = False
1589allowsMathEnvironments _ = False
1523
1590
1591+-- | List of intrinsic event attributes allowed on all elements in HTML4.
1592+intrinsicEventsHTML4 :: [Text]
1593+intrinsicEventsHTML4 =
1594+ [ "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover"
1595+ , "onmouseout", "onmouseout", "onkeypress", "onkeydown", "onkeyup"]
1596+
1524isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool
1597isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool
1525isRawHtml f = do
1598isRawHtml f = do
1526 html5 <- gets stHtml5
1599 html5 <- gets stHtml5
src/Text/Pandoc/Writers/Haddock.hs+13−2
@@ -15,6 +15,7 @@ Haddock: <http://www.haskell.org/haddock/doc/html/>
15-}
15-}
16module Text.Pandoc.Writers.Haddock (writeHaddock) where
16module Text.Pandoc.Writers.Haddock (writeHaddock) where
17import Control.Monad.State.Strict
17import Control.Monad.State.Strict
18+import Data.Char (isAlphaNum)
18import Data.Default
19import Data.Default
19import Data.Text (Text)
20import Data.Text (Text)
20import qualified Data.Text as T
21import qualified Data.Text as T
@@ -71,8 +72,18 @@ notesToHaddock opts notes =
71
72
72-- | Escape special characters for Haddock.
73-- | Escape special characters for Haddock.
73escapeString :: Text -> Text
74escapeString :: Text -> Text
74−escapeString = escapeStringUsing haddockEscapes
75+escapeString t
75− where haddockEscapes = backslashEscapes "\\/'`\"@<"
76+ | T.all isAlphaNum t = t
77+ | otherwise = T.concatMap escChar t
78+ where
79+ escChar '\\' = "\\\\"
80+ escChar '/' = "\\/"
81+ escChar '\'' = "\\'"
82+ escChar '`' = "\\`"
83+ escChar '"' = "\\\""
84+ escChar '@' = "\\@"
85+ escChar '<' = "\\<"
86+ escChar c = T.singleton c
76
87
77-- | Convert Pandoc block element to haddock.
88-- | Convert Pandoc block element to haddock.
78blockToHaddock :: PandocMonad m
89blockToHaddock :: PandocMonad m
src/Text/Pandoc/Writers/JATS.hs+67−44
@@ -1,6 +1,7 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ViewPatterns #-}
4+{-# LANGUAGE ViewPatterns #-}
4{- |
5{- |
5 Module : Text.Pandoc.Writers.JATS
6 Module : Text.Pandoc.Writers.JATS
6 Copyright : 2017-2021 John MacFarlane
7 Copyright : 2017-2021 John MacFarlane
@@ -20,12 +21,13 @@ module Text.Pandoc.Writers.JATS
20 , writeJatsPublishing
21 , writeJatsPublishing
21 , writeJatsArticleAuthoring
22 , writeJatsArticleAuthoring
22 ) where
23 ) where
24+import Control.Applicative ((<|>))
23import Control.Monad.Reader
25import Control.Monad.Reader
24import Control.Monad.State
26import Control.Monad.State
25import Data.Generics (everywhere, mkT)
27import Data.Generics (everywhere, mkT)
26import Data.List (partition)
28import Data.List (partition)
27import qualified Data.Map as M
29import qualified Data.Map as M
28−import Data.Maybe (fromMaybe)
30+import Data.Maybe (fromMaybe, listToMaybe)
29import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime)
31import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime)
30import qualified Data.Text as T
32import qualified Data.Text as T
31import Data.Text (Text)
33import Data.Text (Text)
@@ -80,7 +82,7 @@ writeJats tagSet opts d = do
80 let environment = JATSEnv
82 let environment = JATSEnv
81 { jatsTagSet = tagSet
83 { jatsTagSet = tagSet
82 , jatsInlinesWriter = inlinesToJATS
84 , jatsInlinesWriter = inlinesToJATS
83− , jatsBlockWriter = blockToJATS
85+ , jatsBlockWriter = wrappedBlocksToJATS
84 , jatsReferences = refs
86 , jatsReferences = refs
85 }
87 }
86 let initialState = JATSState { jatsNotes = [] }
88 let initialState = JATSState { jatsNotes = [] }
@@ -162,11 +164,9 @@ wrappedBlocksToJATS needsWrap opts =
162 wrappedBlockToJATS b = do
164 wrappedBlockToJATS b = do
163 inner <- blockToJATS opts b
165 inner <- blockToJATS opts b
164 return $
166 return $
165− if needsWrap b || isBlockQuote b -- see #7041
167+ if needsWrap b
166 then inTags True "p" [("specific-use","wrapper")] inner
168 then inTags True "p" [("specific-use","wrapper")] inner
167 else inner
169 else inner
168− isBlockQuote (BlockQuote _) = True
169− isBlockQuote _ = False
170
170
171-- | Auxiliary function to convert Plain block to Para.
171-- | Auxiliary function to convert Plain block to Para.
172plainToPara :: Block -> Block
172plainToPara :: Block -> Block
@@ -225,33 +225,36 @@ imageMimeType src kvs =
225 (T.drop 1 . T.dropWhile (/='/') <$> mbMT)
225 (T.drop 1 . T.dropWhile (/='/') <$> mbMT)
226 in (maintype, subtype)
226 in (maintype, subtype)
227
227
228−languageFor :: [Text] -> Text
228+languageFor :: WriterOptions -> [Text] -> Text
229−languageFor classes =
229+languageFor opts classes =
230 case langs of
230 case langs of
231 (l:_) -> escapeStringForXML l
231 (l:_) -> escapeStringForXML l
232 [] -> ""
232 [] -> ""
233+ where
234+ syntaxMap = writerSyntaxMap opts
233− where isLang l = T.toLower l `elem` map T.toLower languages
235+ isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap)
234 langsFrom s = if isLang s
236 langsFrom s = if isLang s
235 then [s]
237 then [s]
236− else languagesByExtension . T.toLower $ s
238+ else (languagesByExtension syntaxMap) . T.toLower $ s
237 langs = concatMap langsFrom classes
239 langs = concatMap langsFrom classes
238
240
239−codeAttr :: Attr -> (Text, [(Text, Text)])
241+codeAttr :: WriterOptions -> Attr -> (Text, [(Text, Text)])
240−codeAttr (ident,classes,kvs) = (lang, attr)
242+codeAttr opts (ident,classes,kvs) = (lang, attr)
241 where
243 where
242− attr = [("id",ident) | not (T.null ident)] ++
244+ attr = [("id", escapeNCName ident) | not (T.null ident)] ++
243 [("language",lang) | not (T.null lang)] ++
245 [("language",lang) | not (T.null lang)] ++
244 [(k,v) | (k,v) <- kvs, k `elem` ["code-type",
246 [(k,v) | (k,v) <- kvs, k `elem` ["code-type",
245 "code-version", "executable",
247 "code-version", "executable",
246 "language-version", "orientation",
248 "language-version", "orientation",
247 "platforms", "position", "specific-use"]]
249 "platforms", "position", "specific-use"]]
248− lang = languageFor classes
250+ lang = languageFor opts classes
249
251
250-- | Convert a Pandoc block element to JATS.
252-- | Convert a Pandoc block element to JATS.
251blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text)
253blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text)
252blockToJATS _ Null = return empty
254blockToJATS _ Null = return empty
253blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do
255blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do
254− let idAttr = [("id", writerIdentifierPrefix opts <> id') | not (T.null id')]
256+ let idAttr = [ ("id", writerIdentifierPrefix opts <> escapeNCName id')
257+ | not (T.null id')]
255 let otherAttrs = ["sec-type", "specific-use"]
258 let otherAttrs = ["sec-type", "specific-use"]
256 let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs]
259 let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs]
257 title' <- inlinesToJATS opts ils
260 title' <- inlinesToJATS opts ils
@@ -260,7 +263,7 @@ blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do
260 inTagsSimple "title" title' $$ contents
263 inTagsSimple "title" title' $$ contents
261-- Bibliography reference:
264-- Bibliography reference:
262blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident =
265blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident =
263− inTags True "ref" [("id", ident)] .
266+ inTags True "ref" [("id", escapeNCName ident)] .
264 inTagsSimple "mixed-citation" <$>
267 inTagsSimple "mixed-citation" <$>
265 inlinesToJATS opts lst
268 inlinesToJATS opts lst
266blockToJATS opts (Div ("refs",_,_) xs) = do
269blockToJATS opts (Div ("refs",_,_) xs) = do
@@ -271,14 +274,14 @@ blockToJATS opts (Div ("refs",_,_) xs) = do
271 return $ inTagsIndented "ref-list" contents
274 return $ inTagsIndented "ref-list" contents
272blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do
275blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do
273 contents <- blocksToJATS opts bs
276 contents <- blocksToJATS opts bs
274− let attr = [("id", ident) | not (T.null ident)] ++
277+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
275 [("xml:lang",l) | ("lang",l) <- kvs] ++
278 [("xml:lang",l) | ("lang",l) <- kvs] ++
276 [(k,v) | (k,v) <- kvs, k `elem` ["specific-use",
279 [(k,v) | (k,v) <- kvs, k `elem` ["specific-use",
277 "content-type", "orientation", "position"]]
280 "content-type", "orientation", "position"]]
278 return $ inTags True cls attr contents
281 return $ inTags True cls attr contents
279blockToJATS opts (Div (ident,_,kvs) bs) = do
282blockToJATS opts (Div (ident,_,kvs) bs) = do
280 contents <- blocksToJATS opts bs
283 contents <- blocksToJATS opts bs
281− let attr = [("id", ident) | not (T.null ident)] ++
284+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
282 [("xml:lang",l) | ("lang",l) <- kvs] ++
285 [("xml:lang",l) | ("lang",l) <- kvs] ++
283 [(k,v) | (k,v) <- kvs, k `elem` ["specific-use",
286 [(k,v) | (k,v) <- kvs, k `elem` ["specific-use",
284 "content-type", "orientation", "position"]]
287 "content-type", "orientation", "position"]]
@@ -296,7 +299,7 @@ blockToJATS opts (Para [Image (ident,_,kvs) txt
296 let capt = if null txt
299 let capt = if null txt
297 then empty
300 then empty
298 else inTagsSimple "caption" $ inTagsSimple "p" alt
301 else inTagsSimple "caption" $ inTagsSimple "p" alt
299− let attr = [("id", ident) | not (T.null ident)] ++
302+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
300 [(k,v) | (k,v) <- kvs, k `elem` ["fig-type", "orientation",
303 [(k,v) | (k,v) <- kvs, k `elem` ["fig-type", "orientation",
301 "position", "specific-use"]]
304 "position", "specific-use"]]
302 let graphicattr = [("mimetype",maintype),
305 let graphicattr = [("mimetype",maintype),
@@ -307,7 +310,7 @@ blockToJATS opts (Para [Image (ident,_,kvs) txt
307 capt $$ selfClosingTag "graphic" graphicattr
310 capt $$ selfClosingTag "graphic" graphicattr
308blockToJATS _ (Para [Image (ident,_,kvs) _ (src, tit)]) = do
311blockToJATS _ (Para [Image (ident,_,kvs) _ (src, tit)]) = do
309 let (maintype, subtype) = imageMimeType src kvs
312 let (maintype, subtype) = imageMimeType src kvs
310− let attr = [("id", ident) | not (T.null ident)] ++
313+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
311 [("mimetype", maintype),
314 [("mimetype", maintype),
312 ("mime-subtype", subtype),
315 ("mime-subtype", subtype),
313 ("xlink:href", src)] ++
316 ("xlink:href", src)] ++
@@ -323,13 +326,16 @@ blockToJATS opts (LineBlock lns) =
323 blockToJATS opts $ linesToPara lns
326 blockToJATS opts $ linesToPara lns
324blockToJATS opts (BlockQuote blocks) = do
327blockToJATS opts (BlockQuote blocks) = do
325 tagSet <- asks jatsTagSet
328 tagSet <- asks jatsTagSet
326− let blocksToJats' = if tagSet == TagSetArticleAuthoring
329+ let needsWrap = if tagSet == TagSetArticleAuthoring
327− then wrappedBlocksToJATS (not . isPara)
330+ then not . isPara
328− else blocksToJATS
331+ else \case
332+ Header{} -> True
333+ HorizontalRule -> True
334+ _ -> False
329− inTagsIndented "disp-quote" <$> blocksToJats' opts blocks
335+ inTagsIndented "disp-quote" <$> wrappedBlocksToJATS needsWrap opts blocks
330−blockToJATS _ (CodeBlock a str) = return $
336+blockToJATS opts (CodeBlock a str) = return $
331 inTags False tag attr (flush (text (T.unpack $ escapeStringForXML str)))
337 inTags False tag attr (flush (text (T.unpack $ escapeStringForXML str)))
332− where (lang, attr) = codeAttr a
338+ where (lang, attr) = codeAttr opts a
333 tag = if T.null lang then "preformat" else "code"
339 tag = if T.null lang then "preformat" else "code"
334blockToJATS _ (BulletList []) = return empty
340blockToJATS _ (BulletList []) = return empty
335blockToJATS opts (BulletList lst) =
341blockToJATS opts (BulletList lst) =
@@ -409,9 +415,9 @@ inlineToJATS opts (Quoted SingleQuote lst) = do
409inlineToJATS opts (Quoted DoubleQuote lst) = do
415inlineToJATS opts (Quoted DoubleQuote lst) = do
410 contents <- inlinesToJATS opts lst
416 contents <- inlinesToJATS opts lst
411 return $ char '“' <> contents <> char '”'
417 return $ char '“' <> contents <> char '”'
412−inlineToJATS _ (Code a str) =
418+inlineToJATS opts (Code a str) =
413 return $ inTags False tag attr $ literal (escapeStringForXML str)
419 return $ inTags False tag attr $ literal (escapeStringForXML str)
414− where (lang, attr) = codeAttr a
420+ where (lang, attr) = codeAttr opts a
415 tag = if T.null lang then "monospace" else "code"
421 tag = if T.null lang then "monospace" else "code"
416inlineToJATS _ il@(RawInline f x)
422inlineToJATS _ il@(RawInline f x)
417 | f == "jats" = return $ literal x
423 | f == "jats" = return $ literal x
@@ -434,7 +440,8 @@ inlineToJATS opts (Note contents) = do
434 let notenum = case notes of
440 let notenum = case notes of
435 (n, _):_ -> n + 1
441 (n, _):_ -> n + 1
436 [] -> 1
442 [] -> 1
437− thenote <- inTags True "fn" [("id","fn" <> tshow notenum)]
443+ thenote <- inTags True "fn" [("id", "fn" <> tshow notenum)]
444+ . (inTagsSimple "label" (literal $ tshow notenum) <>)
438 <$> wrappedBlocksToJATS (not . isPara) opts
445 <$> wrappedBlocksToJATS (not . isPara) opts
439 (walk demoteHeaderAndRefs contents)
446 (walk demoteHeaderAndRefs contents)
440 modify $ \st -> st{ jatsNotes = (notenum, thenote) : notes }
447 modify $ \st -> st{ jatsNotes = (notenum, thenote) : notes }
@@ -442,18 +449,34 @@ inlineToJATS opts (Note contents) = do
442 ("rid", "fn" <> tshow notenum)]
449 ("rid", "fn" <> tshow notenum)]
443 $ text (show notenum)
450 $ text (show notenum)
444inlineToJATS opts (Cite _ lst) =
451inlineToJATS opts (Cite _ lst) =
445− -- TODO revisit this after examining the jats.csl pipeline
446 inlinesToJATS opts lst
452 inlinesToJATS opts lst
447−inlineToJATS opts (Span ("",_,[]) ils) = inlinesToJATS opts ils
453+inlineToJATS opts (Span (ident,classes,kvs) ils) = do
448−inlineToJATS opts (Span (ident,_,kvs) ils) = do
449 contents <- inlinesToJATS opts ils
454 contents <- inlinesToJATS opts ils
450− let attr = [("id",ident) | not (T.null ident)] ++
455+ let commonAttr = [("id", escapeNCName ident) | not (T.null ident)] ++
451− [("xml:lang",l) | ("lang",l) <- kvs] ++
456+ [("xml:lang",l) | ("lang",l) <- kvs] ++
452− [(k,v) | (k,v) <- kvs
453− , k `elem` ["content-type", "rationale",
454− "rid", "specific-use"]]
457+ [(k,v) | (k,v) <- kvs, k `elem` ["alt", "specific-use"]]
455− return $ selfClosingTag "milestone-start" attr <> contents <>
458+ -- A named-content element is a good fit for spans, but requires a
456− selfClosingTag "milestone-end" []
459+ -- content-type attribute to be present. We use either the explicit
460+ -- attribute or the first class as content type. If neither is
461+ -- available, then we fall back to using a @styled-content@ element.
462+ let (tag, specificAttr) =
463+ case lookup "content-type" kvs <|> listToMaybe classes of
464+ Just ct -> ( "named-content"
465+ , ("content-type", ct) :
466+ [(k, v) | (k, v) <- kvs
467+ , k `elem` ["rid", "vocab", "vocab-identifier",
468+ "vocab-term", "vocab-term-identifier"]])
469+ -- Fall back to styled-content
470+ Nothing -> ("styled-content"
471+ , [(k, v) | (k,v) <- kvs
472+ , k `elem` ["style", "style-type", "style-detail",
473+ "toggle"]])
474+ let attr = commonAttr ++ specificAttr
475+ -- unwrap if wrapping element would have no attributes
476+ return $
477+ if null attr
478+ then contents
479+ else inTags False tag attr contents
457inlineToJATS _ (Math t str) = do
480inlineToJATS _ (Math t str) = do
458 let addPref (Xml.Attr q v)
481 let addPref (Xml.Attr q v)
459 | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v
482 | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v
@@ -488,9 +511,9 @@ inlineToJATS _ (Link _attr [Str t] (T.stripPrefix "mailto:" -> Just email, _))
488 return $ inTagsSimple "email" $ literal (escapeStringForXML email)
511 return $ inTagsSimple "email" $ literal (escapeStringForXML email)
489inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do
512inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do
490 let attr = mconcat
513 let attr = mconcat
491− [ [("id", ident) | not (T.null ident)]
514+ [ [("id", escapeNCName ident) | not (T.null ident)]
492 , [("alt", stringify txt) | not (null txt)]
515 , [("alt", stringify txt) | not (null txt)]
493− , [("rid", src)]
516+ , [("rid", escapeNCName src)]
494 , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]]
517 , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]]
495 , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src]
518 , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src]
496 ]
519 ]
@@ -500,7 +523,7 @@ inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do
500 contents <- inlinesToJATS opts txt
523 contents <- inlinesToJATS opts txt
501 return $ inTags False "xref" attr contents
524 return $ inTags False "xref" attr contents
502inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do
525inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do
503− let attr = [("id", ident) | not (T.null ident)] ++
526+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
504 [("ext-link-type", "uri"),
527 [("ext-link-type", "uri"),
505 ("xlink:href", src)] ++
528 ("xlink:href", src)] ++
506 [("xlink:title", tit) | not (T.null tit)] ++
529 [("xlink:title", tit) | not (T.null tit)] ++
@@ -518,7 +541,7 @@ inlineToJATS _ (Image (ident,_,kvs) _ (src, tit)) = do
518 let subtype = fromMaybe "" $
541 let subtype = fromMaybe "" $
519 lookup "mime-subtype" kvs `mplus`
542 lookup "mime-subtype" kvs `mplus`
520 (T.drop 1 . T.dropWhile (/='/') <$> mbMT)
543 (T.drop 1 . T.dropWhile (/='/') <$> mbMT)
521− let attr = [("id", ident) | not (T.null ident)] ++
544+ let attr = [("id", escapeNCName ident) | not (T.null ident)] ++
522 [("mimetype", maintype),
545 [("mimetype", maintype),
523 ("mime-subtype", subtype),
546 ("mime-subtype", subtype),
524 ("xlink:href", src)] ++
547 ("xlink:href", src)] ++
src/Text/Pandoc/Writers/JATS/References.hs+3−2
@@ -29,7 +29,7 @@ import Text.Pandoc.Builder (Inlines)
29import Text.Pandoc.Options (WriterOptions)
29import Text.Pandoc.Options (WriterOptions)
30import Text.Pandoc.Shared (tshow)
30import Text.Pandoc.Shared (tshow)
31import Text.Pandoc.Writers.JATS.Types
31import Text.Pandoc.Writers.JATS.Types
32−import Text.Pandoc.XML (escapeStringForXML, inTags)
32+import Text.Pandoc.XML (escapeNCName, escapeStringForXML, inTags)
33import qualified Data.Text as T
33import qualified Data.Text as T
34
34
35referencesToJATS :: PandocMonad m
35referencesToJATS :: PandocMonad m
@@ -46,7 +46,8 @@ referenceToJATS :: PandocMonad m
46referenceToJATS _opts ref = do
46referenceToJATS _opts ref = do
47 let refType = referenceType ref
47 let refType = referenceType ref
48 let pubType = [("publication-type", refType) | not (T.null refType)]
48 let pubType = [("publication-type", refType) | not (T.null refType)]
49+ let ident = escapeNCName $ "ref-" <> unItemId (referenceId ref)
49− let wrap = inTags True "ref" [("id", "ref-" <> unItemId (referenceId ref))]
50+ let wrap = inTags True "ref" [("id", ident)]
50 . inTags True "element-citation" pubType
51 . inTags True "element-citation" pubType
51 return . wrap . vcat $
52 return . wrap . vcat $
52 [ authors
53 [ authors
src/Text/Pandoc/Writers/JATS/Table.hs+25−5
@@ -24,7 +24,7 @@ import Text.Pandoc.Definition
24import Text.Pandoc.Options (WriterOptions)
24import Text.Pandoc.Options (WriterOptions)
25import Text.Pandoc.Shared (tshow)
25import Text.Pandoc.Shared (tshow)
26import Text.Pandoc.Writers.JATS.Types
26import Text.Pandoc.Writers.JATS.Types
27−import Text.Pandoc.XML (inTags, inTagsIndented, selfClosingTag)
27+import Text.Pandoc.XML (escapeNCName, inTags, inTagsIndented, selfClosingTag)
28import qualified Data.Text as T
28import qualified Data.Text as T
29import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
29import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
30
30
@@ -34,13 +34,19 @@ tableToJATS :: PandocMonad m
34 -> JATS m (Doc Text)
34 -> JATS m (Doc Text)
35tableToJATS opts (Ann.Table attr caption colspecs thead tbodies tfoot) = do
35tableToJATS opts (Ann.Table attr caption colspecs thead tbodies tfoot) = do
36 let (Caption _maybeShortCaption captionBlocks) = caption
36 let (Caption _maybeShortCaption captionBlocks) = caption
37+ -- Only paragraphs are allowed in captions, all other blocks must be
38+ -- wrapped in @<p>@ elements.
39+ let needsWrapping = \case
40+ Plain{} -> False
41+ Para{} -> False
42+ _ -> True
37 tbl <- captionlessTable opts attr colspecs thead tbodies tfoot
43 tbl <- captionlessTable opts attr colspecs thead tbodies tfoot
38 captionDoc <- if null captionBlocks
44 captionDoc <- if null captionBlocks
39 then return empty
45 then return empty
40 else do
46 else do
41 blockToJATS <- asks jatsBlockWriter
47 blockToJATS <- asks jatsBlockWriter
42− inTagsIndented "caption" . vcat <$>
48+ inTagsIndented "caption" <$>
43− mapM (blockToJATS opts) captionBlocks
49+ blockToJATS needsWrapping opts captionBlocks
44 return $ inTags True "table-wrap" [] $ captionDoc $$ tbl
50 return $ inTags True "table-wrap" [] $ captionDoc $$ tbl
45
51
46captionlessTable :: PandocMonad m
52captionlessTable :: PandocMonad m
@@ -216,7 +222,7 @@ cellToJats opts celltype (Ann.Cell (colspec :| _) _colNum cell) =
216
222
217toAttribs :: Attr -> [Text] -> [(Text, Text)]
223toAttribs :: Attr -> [Text] -> [(Text, Text)]
218toAttribs (ident, _classes, kvs) knownAttribs =
224toAttribs (ident, _classes, kvs) knownAttribs =
219− (if T.null ident then id else (("id", ident) :)) $
225+ (if T.null ident then id else (("id", escapeNCName ident) :)) $
220 filter ((`elem` knownAttribs) . fst) kvs
226 filter ((`elem` knownAttribs) . fst) kvs
221
227
222tableCellToJats :: PandocMonad m
228tableCellToJats :: PandocMonad m
@@ -230,7 +236,7 @@ tableCellToJats opts ctype colAlign (Cell attr align rowspan colspan item) = do
230 inlinesToJats <- asks jatsInlinesWriter
236 inlinesToJats <- asks jatsInlinesWriter
231 let cellContents = \case
237 let cellContents = \case
232 [Plain inlines] -> inlinesToJats opts inlines
238 [Plain inlines] -> inlinesToJats opts inlines
233− blocks -> vcat <$> mapM (blockToJats opts) blocks
239+ blocks -> blockToJats needsWrapInCell opts blocks
234 let tag' = case ctype of
240 let tag' = case ctype of
235 BodyCell -> "td"
241 BodyCell -> "td"
236 HeaderCell -> "th"
242 HeaderCell -> "th"
@@ -246,3 +252,17 @@ tableCellToJats opts ctype colAlign (Cell attr align rowspan colspan item) = do
246 . maybeCons (colspanAttrib colspan)
252 . maybeCons (colspanAttrib colspan)
247 $ toAttribs attr validAttribs
253 $ toAttribs attr validAttribs
248 inTags False tag' attribs <$> cellContents item
254 inTags False tag' attribs <$> cellContents item
255+
256+-- | Whether the JATS produced from this block should be wrapped in a
257+-- @<p>@ element when put directly below a @<td>@ element.
258+needsWrapInCell :: Block -> Bool
259+needsWrapInCell = \case
260+ Plain{} -> False -- should be unwrapped anyway
261+ Para{} -> False
262+ BulletList{} -> False
263+ OrderedList{} -> False
264+ DefinitionList{} -> False
265+ HorizontalRule -> False
266+ CodeBlock{} -> False
267+ RawBlock{} -> False -- responsibility of the user
268+ _ -> True
src/Text/Pandoc/Writers/JATS/Types.hs+12−3
@@ -37,11 +37,20 @@ newtype JATSState = JATSState
37 { jatsNotes :: [(Int, Doc Text)]
37 { jatsNotes :: [(Int, Doc Text)]
38 }
38 }
39
39
40+-- | Environment containing all information relevant for rendering.
40data JATSEnv m = JATSEnv
41data JATSEnv m = JATSEnv
41− { jatsTagSet :: JATSTagSet
42+ { jatsTagSet :: JATSTagSet -- ^ The tag set that's being ouput
43+
44+ , jatsBlockWriter :: (Block -> Bool)
45+ -> WriterOptions -> [Block] -> JATS m (Doc Text)
46+ -- ^ Converts a block list to JATS, wrapping top-level blocks into a
47+ -- @<p>@ element if the property evaluates to @True@.
48+ -- See #7227.
49+
42 , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text)
50 , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text)
43− , jatsBlockWriter :: WriterOptions -> Block -> JATS m (Doc Text)
51+ -- ^ Converts an inline list to JATS.
52+
44− , jatsReferences :: [Reference Inlines]
53+ , jatsReferences :: [Reference Inlines] -- ^ List of references
45 }
54 }
46
55
47-- | JATS writer type
56-- | JATS writer type
src/Text/Pandoc/Writers/Jira.hs+41−18
@@ -39,11 +39,17 @@ writeJira :: PandocMonad m => WriterOptions -> Pandoc -> m Text
39writeJira opts = runDefaultConverter (writerWrapText opts) (pandocToJira opts)
39writeJira opts = runDefaultConverter (writerWrapText opts) (pandocToJira opts)
40
40
41-- | State to keep track of footnotes.
41-- | State to keep track of footnotes.
42−newtype ConverterState = ConverterState { stNotes :: [Text] }
42+data ConverterState = ConverterState
43+ { stNotes :: [Text] -- ^ Footnotes to be appended to the end of the text
44+ , stInPanel :: Bool -- ^ whether we are in a @{panel}@ block
45+ }
43
46
44-- | Initial converter state.
47-- | Initial converter state.
45startState :: ConverterState
48startState :: ConverterState
46−startState = ConverterState { stNotes = [] }
49+startState = ConverterState
50+ { stNotes = []
51+ , stInPanel = False
52+ }
47
53
48-- | Converter monad
54-- | Converter monad
49type JiraConverter m = ReaderT WrapOption (StateT ConverterState m)
55type JiraConverter m = ReaderT WrapOption (StateT ConverterState m)
@@ -126,13 +132,19 @@ toJiraCode :: PandocMonad m
126 -> Text
132 -> Text
127 -> JiraConverter m [Jira.Block]
133 -> JiraConverter m [Jira.Block]
128toJiraCode (ident, classes, _attribs) code = do
134toJiraCode (ident, classes, _attribs) code = do
135+ return . addAnchor ident . singleton $
129− let lang = case find (\c -> T.toLower c `elem` knownLanguages) classes of
136+ case find (\c -> T.toLower c `elem` knownLanguages) classes of
130− Nothing -> Jira.Language "java"
137+ Nothing -> Jira.NoFormat mempty code
131− Just l -> Jira.Language l
138+ Just l -> Jira.Code (Jira.Language l) mempty code
139+
140+-- | Prepends an anchor with the given identifier.
141+addAnchor :: Text -> [Jira.Block] -> [Jira.Block]
142+addAnchor ident =
132− let addAnchor b = if T.null ident
143+ if T.null ident
133− then b
144+ then id
145+ else \case
134− else [Jira.Para (singleton (Jira.Anchor ident))] <> b
146+ Jira.Para xs : bs -> (Jira.Para (Jira.Anchor ident : xs) : bs)
135− return . addAnchor . singleton $ Jira.Code lang mempty code
147+ bs -> (Jira.Para (singleton (Jira.Anchor ident)) : bs)
136
148
137-- | Creates a Jira definition list
149-- | Creates a Jira definition list
138toJiraDefinitionList :: PandocMonad m
150toJiraDefinitionList :: PandocMonad m
@@ -149,11 +161,16 @@ toJiraDefinitionList defItems = do
149toJiraPanel :: PandocMonad m
161toJiraPanel :: PandocMonad m
150 => Attr -> [Block]
162 => Attr -> [Block]
151 -> JiraConverter m [Jira.Block]
163 -> JiraConverter m [Jira.Block]
152−toJiraPanel attr blocks = do
164+toJiraPanel (ident, classes, attribs) blocks = do
153− jiraBlocks <- toJiraBlocks blocks
165+ inPanel <- gets stInPanel
154− return $ if attr == nullAttr
166+ if inPanel || ("panel" `notElem` classes && null attribs)
155− then jiraBlocks
167+ then addAnchor ident <$> toJiraBlocks blocks
168+ else do
169+ modify $ \st -> st{ stInPanel = True }
170+ jiraBlocks <- toJiraBlocks blocks
171+ modify $ \st -> st{ stInPanel = inPanel }
172+ let params = map (uncurry Jira.Parameter) attribs
156− else singleton (Jira.Panel [] jiraBlocks)
173+ return $ singleton (Jira.Panel params $ addAnchor ident jiraBlocks)
157
174
158-- | Creates a Jira header
175-- | Creates a Jira header
159toJiraHeader :: PandocMonad m
176toJiraHeader :: PandocMonad m
@@ -263,6 +280,8 @@ toJiraLink (_, classes, _) (url, _) alias = do
263 | Just email <- T.stripPrefix "mailto:" url' = (Jira.Email, email)
280 | Just email <- T.stripPrefix "mailto:" url' = (Jira.Email, email)
264 | "user-account" `elem` classes = (Jira.User, dropTilde url)
281 | "user-account" `elem` classes = (Jira.User, dropTilde url)
265 | "attachment" `elem` classes = (Jira.Attachment, url)
282 | "attachment" `elem` classes = (Jira.Attachment, url)
283+ | "smart-card" `elem` classes = (Jira.SmartCard, url)
284+ | "smart-link" `elem` classes = (Jira.SmartLink, url)
266 | otherwise = (Jira.External, url)
285 | otherwise = (Jira.External, url)
267 dropTilde txt = case T.uncons txt of
286 dropTilde txt = case T.uncons txt of
268 Just ('~', username) -> username
287 Just ('~', username) -> username
@@ -292,9 +311,13 @@ quotedToJira qtype xs = do
292spanToJira :: PandocMonad m
311spanToJira :: PandocMonad m
293 => Attr -> [Inline]
312 => Attr -> [Inline]
294 -> JiraConverter m [Jira.Inline]
313 -> JiraConverter m [Jira.Inline]
295−spanToJira (ident, _classes, _attribs) inls = case ident of
314+spanToJira (ident, _classes, attribs) inls =
315+ let wrap = case lookup "color" attribs of
316+ Nothing -> id
317+ Just color -> singleton . Jira.ColorInline (Jira.ColorName color)
318+ in wrap <$> case ident of
296− "" -> toJiraInlines inls
319+ "" -> toJiraInlines inls
297− _ -> (Jira.Anchor ident :) <$> toJiraInlines inls
320+ _ -> (Jira.Anchor ident :) <$> toJiraInlines inls
298
321
299registerNotes :: PandocMonad m => [Block] -> JiraConverter m [Jira.Inline]
322registerNotes :: PandocMonad m => [Block] -> JiraConverter m [Jira.Inline]
300registerNotes contents = do
323registerNotes contents = do
@@ -310,7 +333,7 @@ registerNotes contents = do
310knownLanguages :: [Text]
333knownLanguages :: [Text]
311knownLanguages =
334knownLanguages =
312 [ "actionscript", "ada", "applescript", "bash", "c", "c#", "c++"
335 [ "actionscript", "ada", "applescript", "bash", "c", "c#", "c++"
313− , "css", "erlang", "go", "groovy", "haskell", "html", "javascript"
336+ , "css", "erlang", "go", "groovy", "haskell", "html", "java", "javascript"
314 , "json", "lua", "nyan", "objc", "perl", "php", "python", "r", "ruby"
337 , "json", "lua", "nyan", "objc", "perl", "php", "python", "r", "ruby"
315 , "scala", "sql", "swift", "visualbasic", "xml", "yaml"
338 , "scala", "sql", "swift", "visualbasic", "xml", "yaml"
316 ]
339 ]
src/Text/Pandoc/Writers/LaTeX.hs+50−561
@@ -1,4 +1,5 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE TupleSections #-}
3{-# LANGUAGE TupleSections #-}
3{-# LANGUAGE PatternGuards #-}
4{-# LANGUAGE PatternGuards #-}
4{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
@@ -18,11 +19,9 @@ module Text.Pandoc.Writers.LaTeX (
18 writeLaTeX
19 writeLaTeX
19 , writeBeamer
20 , writeBeamer
20 ) where
21 ) where
21−import Control.Applicative ((<|>))
22import Control.Monad.State.Strict
22import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
23+import Data.Char (isDigit)
24− isPunctuation, ord)
25−import Data.List (foldl', intersperse, nubBy, (\\), uncons)
24+import Data.List (intersperse, nubBy, (\\))
26import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
25import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
27import qualified Data.Map as M
26import qualified Data.Map as M
28import Data.Text (Text)
27import Data.Text (Text)
@@ -30,11 +29,11 @@ import qualified Data.Text as T
30import Network.URI (unEscapeString)
29import Network.URI (unEscapeString)
31import Text.DocTemplates (FromContext(lookupContext), renderTemplate,
30import Text.DocTemplates (FromContext(lookupContext), renderTemplate,
32 Val(..), Context(..))
31 Val(..), Context(..))
33−import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
32+import Text.Collate.Lang (Lang (..), renderLang)
34import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
33import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
35import Text.Pandoc.Definition
34import Text.Pandoc.Definition
36import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
35import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
37− styleToLaTeX, toListingsLanguage)
36+ styleToLaTeX)
38import Text.Pandoc.ImageSize
37import Text.Pandoc.ImageSize
39import Text.Pandoc.Logging
38import Text.Pandoc.Logging
40import Text.Pandoc.Options
39import Text.Pandoc.Options
@@ -44,10 +43,15 @@ import Text.Pandoc.Slides
44import Text.Pandoc.Walk (query, walk, walkM)
43import Text.Pandoc.Walk (query, walk, walkM)
45import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
44import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
46import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
45import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
46+import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
47+ citationsToBiblatex)
47import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
48import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
49+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossia, toBabel)
50+import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
51+ toLabel, inCmd,
52+ wrapDiv, hypertarget, labelFor,
53+ getListingsLanguage, mbBraced)
48import Text.Pandoc.Writers.Shared
54import Text.Pandoc.Writers.Shared
49−import Text.Printf (printf)
50−import qualified Data.Text.Normalize as Normalize
51import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
55import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
52
56
53-- | Convert Pandoc to LaTeX.
57-- | Convert Pandoc to LaTeX.
@@ -142,6 +146,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do
142
146
143 let dirs = query (extract "dir") blocks
147 let dirs = query (extract "dir") blocks
144
148
149+ let nociteIds = query (\case
150+ Cite cs _ -> map citationId cs
151+ _ -> [])
152+ $ lookupMetaInlines "nocite" meta
153+
145 let context = defField "toc" (writerTableOfContents options) $
154 let context = defField "toc" (writerTableOfContents options) $
146 defField "toc-depth" (tshow
155 defField "toc-depth" (tshow
147 (writerTOCDepth options -
156 (writerTOCDepth options -
@@ -164,6 +173,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do
164 defField "has-chapters" (stHasChapters st) $
173 defField "has-chapters" (stHasChapters st) $
165 defField "has-frontmatter" (documentClass `elem` frontmatterClasses) $
174 defField "has-frontmatter" (documentClass `elem` frontmatterClasses) $
166 defField "listings" (writerListings options || stLHS st) $
175 defField "listings" (writerListings options || stLHS st) $
176+ defField "zero-width-non-joiner" (stZwnj st) $
167 defField "beamer" beamer $
177 defField "beamer" beamer $
168 (if stHighlighting st
178 (if stHighlighting st
169 then case writerHighlightStyle options of
179 then case writerHighlightStyle options of
@@ -174,9 +184,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do
174 else id) $
184 else id) $
175 (case writerCiteMethod options of
185 (case writerCiteMethod options of
176 Natbib -> defField "biblio-title" biblioTitle .
186 Natbib -> defField "biblio-title" biblioTitle .
177− defField "natbib" True
187+ defField "natbib" True .
188+ defField "nocite-ids" nociteIds
178 Biblatex -> defField "biblio-title" biblioTitle .
189 Biblatex -> defField "biblio-title" biblioTitle .
179− defField "biblatex" True
190+ defField "biblatex" True .
191+ defField "nocite-ids" nociteIds
180 _ -> id) $
192 _ -> id) $
181 defField "colorlinks" (any hasStringValue
193 defField "colorlinks" (any hasStringValue
182 ["citecolor", "urlcolor", "linkcolor", "toccolor",
194 ["citecolor", "urlcolor", "linkcolor", "toccolor",
@@ -241,152 +253,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
241 Nothing -> main
253 Nothing -> main
242 Just tpl -> renderTemplate tpl context'
254 Just tpl -> renderTemplate tpl context'
243
255
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−
390toSlides :: PandocMonad m => [Block] -> LW m [Block]
256toSlides :: PandocMonad m => [Block] -> LW m [Block]
391toSlides bs = do
257toSlides bs = do
392 opts <- gets stOptions
258 opts <- gets stOptions
@@ -425,7 +291,12 @@ blockToLaTeX :: PandocMonad m
425 => Block -- ^ Block to convert
291 => Block -- ^ Block to convert
426 -> LW m (Doc Text)
292 -> LW m (Doc Text)
427blockToLaTeX Null = return empty
293blockToLaTeX Null = return empty
428−blockToLaTeX (Div attr@(identifier,"block":_,_) (Header _ _ ils : bs)) = do
294+blockToLaTeX (Div attr@(identifier,"block":dclasses,_)
295+ (Header _ _ ils : bs)) = do
296+ let blockname
297+ | "example" `elem` dclasses = "exampleblock"
298+ | "alert" `elem` dclasses = "alertblock"
299+ | otherwise = "block"
429 ref <- toLabel identifier
300 ref <- toLabel identifier
430 let anchor = if T.null identifier
301 let anchor = if T.null identifier
431 then empty
302 then empty
@@ -433,8 +304,8 @@ blockToLaTeX (Div attr@(identifier,"block":_,_) (Header _ _ ils : bs)) = do
433 braces (literal ref) <> braces empty
304 braces (literal ref) <> braces empty
434 title' <- inlineListToLaTeX ils
305 title' <- inlineListToLaTeX ils
435 contents <- blockListToLaTeX bs
306 contents <- blockListToLaTeX bs
436− wrapDiv attr $ ("\\begin{block}" <> braces title' <> anchor) $$
307+ wrapDiv attr $ ("\\begin" <> braces blockname <> braces title' <> anchor) $$
437− contents $$ "\\end{block}"
308+ contents $$ "\\end" <> braces blockname
438blockToLaTeX (Div (identifier,"slide":dclasses,dkvs)
309blockToLaTeX (Div (identifier,"slide":dclasses,dkvs)
439 (Header _ (_,hclasses,hkvs) ils : bs)) = do
310 (Header _ (_,hclasses,hkvs) ils : bs)) = do
440 -- note: [fragile] is required or verbatim breaks
311 -- note: [fragile] is required or verbatim breaks
@@ -851,81 +722,6 @@ sectionHeader classes ident level lst = do
851 braces txtNoNotes
722 braces txtNoNotes
852 else empty
723 else empty
853
724
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−
929-- | Convert list of inline elements to LaTeX.
725-- | Convert list of inline elements to LaTeX.
930inlineListToLaTeX :: PandocMonad m
726inlineListToLaTeX :: PandocMonad m
931 => [Inline] -- ^ Inlines to convert
727 => [Inline] -- ^ Inlines to convert
@@ -950,10 +746,6 @@ inlineListToLaTeX lst = hcat <$>
950 fixInitialLineBreaks xs
746 fixInitialLineBreaks xs
951 fixInitialLineBreaks xs = xs
747 fixInitialLineBreaks xs = xs
952
748
953−isQuoted :: Inline -> Bool
954−isQuoted (Quoted _ _) = True
955−isQuoted _ = False
956−
957-- | Convert inline element to LaTeX
749-- | Convert inline element to LaTeX
958inlineToLaTeX :: PandocMonad m
750inlineToLaTeX :: PandocMonad m
959 => Inline -- ^ Inline to convert
751 => Inline -- ^ Inline to convert
@@ -1014,8 +806,8 @@ inlineToLaTeX (Cite cits lst) = do
1014 st <- get
806 st <- get
1015 let opts = stOptions st
807 let opts = stOptions st
1016 case writerCiteMethod opts of
808 case writerCiteMethod opts of
1017− Natbib -> citationsToNatbib cits
809+ Natbib -> citationsToNatbib inlineListToLaTeX cits
1018− Biblatex -> citationsToBiblatex cits
810+ Biblatex -> citationsToBiblatex inlineListToLaTeX cits
1019 _ -> inlineListToLaTeX lst
811 _ -> inlineListToLaTeX lst
1020
812
1021inlineToLaTeX (Code (_,classes,kvs) str) = do
813inlineToLaTeX (Code (_,classes,kvs) str) = do
@@ -1039,7 +831,19 @@ inlineToLaTeX (Code (_,classes,kvs) str) = do
1039 let chr = case "!\"'()*,-./:;?@" \\ T.unpack str of
831 let chr = case "!\"'()*,-./:;?@" \\ T.unpack str of
1040 (c:_) -> c
832 (c:_) -> c
1041 [] -> '!'
833 [] -> '!'
1042− let str' = escapeStringUsing (backslashEscapes "\\{}%~_&#^") str
834+ let isEscapable '\\' = True
835+ isEscapable '{' = True
836+ isEscapable '}' = True
837+ isEscapable '%' = True
838+ isEscapable '~' = True
839+ isEscapable '_' = True
840+ isEscapable '&' = True
841+ isEscapable '#' = True
842+ isEscapable '^' = True
843+ isEscapable _ = False
844+ let escChar c | isEscapable c = T.pack ['\\',c]
845+ | otherwise = T.singleton c
846+ let str' = T.concatMap escChar str
1043 -- we always put lstinline in a dummy 'passthrough' command
847 -- we always put lstinline in a dummy 'passthrough' command
1044 -- (defined in the default template) so that we don't have
848 -- (defined in the default template) so that we don't have
1045 -- to change the way we escape characters depending on whether
849 -- to change the way we escape characters depending on whether
@@ -1089,6 +893,10 @@ inlineToLaTeX (Quoted qt lst) = do
1089 if isEnabled Ext_smart opts
893 if isEnabled Ext_smart opts
1090 then char '`' <> inner <> char '\''
894 then char '`' <> inner <> char '\''
1091 else char '\x2018' <> inner <> char '\x2019'
895 else char '\x2018' <> inner <> char '\x2019'
896+ where
897+ isQuoted (Span _ (x:_)) = isQuoted x
898+ isQuoted (Quoted _ _) = True
899+ isQuoted _ = False
1092inlineToLaTeX (Str str) = do
900inlineToLaTeX (Str str) = do
1093 setEmptyLine False
901 setEmptyLine False
1094 liftM literal $ stringToLaTeX TextString str
902 liftM literal $ stringToLaTeX TextString str
@@ -1111,7 +919,7 @@ inlineToLaTeX il@(RawInline f str) = do
1111inlineToLaTeX LineBreak = do
919inlineToLaTeX LineBreak = do
1112 emptyLine <- gets stEmptyLine
920 emptyLine <- gets stEmptyLine
1113 setEmptyLine True
921 setEmptyLine True
1114− return $ (if emptyLine then "~" else "") <> "\\\\" <> cr
922+ return $ (if emptyLine then "\\strut " else "") <> "\\\\" <> cr
1115inlineToLaTeX SoftBreak = do
923inlineToLaTeX SoftBreak = do
1116 wrapText <- gets (writerWrapText . stOptions)
924 wrapText <- gets (writerWrapText . stOptions)
1117 case wrapText of
925 case wrapText of
@@ -1226,153 +1034,6 @@ protectCode x = [x]
1226setEmptyLine :: PandocMonad m => Bool -> LW m ()
1034setEmptyLine :: PandocMonad m => Bool -> LW m ()
1227setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
1035setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
1228
1036
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−
1376-- Extract a key from divs and spans
1037-- Extract a key from divs and spans
1377extract :: Text -> Block -> [Text]
1038extract :: Text -> Block -> [Text]
1378extract key (Div attr _) = lookKey key attr
1039extract key (Div attr _) = lookKey key attr
@@ -1389,175 +1050,3 @@ extractInline _ _ = []
1389-- Look up a key in an attribute and give a list of its values
1050-- Look up a key in an attribute and give a list of its values
1390lookKey :: Text -> Attr -> [Text]
1051lookKey :: Text -> Attr -> [Text]
1391lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs
1052lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs
1392−
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, "")
1438−
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/Caption.hs−1
@@ -1,4 +1,3 @@
1−{-# LANGUAGE OverloadedStrings #-}
2{- |
1{- |
3 Module : Text.Pandoc.Writers.LaTeX.Caption
2 Module : Text.Pandoc.Writers.LaTeX.Caption
4 Copyright : Copyright (C) 2006-2021 John MacFarlane
3 Copyright : Copyright (C) 2006-2021 John MacFarlane
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+192
@@ -0,0 +1,192 @@
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.Collate.Lang (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 BCP47 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" _ (Just "DZ") _ _ _) = ("arabic", "locale=algeria")
33+toPolyglossia (Lang "ar" _ (Just "IQ") _ _ _) = ("arabic", "locale=mashriq")
34+toPolyglossia (Lang "ar" _ (Just "JO") _ _ _) = ("arabic", "locale=mashriq")
35+toPolyglossia (Lang "ar" _ (Just "LB") _ _ _) = ("arabic", "locale=mashriq")
36+toPolyglossia (Lang "ar" _ (Just "LY") _ _ _) = ("arabic", "locale=libya")
37+toPolyglossia (Lang "ar" _ (Just "MA") _ _ _) = ("arabic", "locale=morocco")
38+toPolyglossia (Lang "ar" _ (Just "MR") _ _ _) = ("arabic", "locale=mauritania")
39+toPolyglossia (Lang "ar" _ (Just "PS") _ _ _) = ("arabic", "locale=mashriq")
40+toPolyglossia (Lang "ar" _ (Just "SY") _ _ _) = ("arabic", "locale=mashriq")
41+toPolyglossia (Lang "ar" _ (Just "TN") _ _ _) = ("arabic", "locale=tunisia")
42+toPolyglossia (Lang "de" _ _ vars _ _)
43+ | "1901" `elem` vars = ("german", "spelling=old")
44+toPolyglossia (Lang "de" _ (Just "AT") vars _ _)
45+ | "1901" `elem` vars = ("german", "variant=austrian, spelling=old")
46+toPolyglossia (Lang "de" _ (Just "AT") _ _ _) = ("german", "variant=austrian")
47+toPolyglossia (Lang "de" _ (Just "CH") vars _ _)
48+ | "1901" `elem` vars = ("german", "variant=swiss, spelling=old")
49+toPolyglossia (Lang "de" _ (Just "CH") _ _ _) = ("german", "variant=swiss")
50+toPolyglossia (Lang "de" _ _ _ _ _) = ("german", "")
51+toPolyglossia (Lang "dsb" _ _ _ _ _) = ("lsorbian", "")
52+toPolyglossia (Lang "el" _ _ vars _ _)
53+ | "polyton" `elem` vars = ("greek", "variant=poly")
54+toPolyglossia (Lang "en" _ (Just "AU") _ _ _) = ("english", "variant=australian")
55+toPolyglossia (Lang "en" _ (Just "CA") _ _ _) = ("english", "variant=canadian")
56+toPolyglossia (Lang "en" _ (Just "GB") _ _ _) = ("english", "variant=british")
57+toPolyglossia (Lang "en" _ (Just "NZ") _ _ _) = ("english", "variant=newzealand")
58+toPolyglossia (Lang "en" _ (Just "UK") _ _ _) = ("english", "variant=british")
59+toPolyglossia (Lang "en" _ (Just "US") _ _ _) = ("english", "variant=american")
60+toPolyglossia (Lang "grc" _ _ _ _ _) = ("greek", "variant=ancient")
61+toPolyglossia (Lang "hsb" _ _ _ _ _) = ("usorbian", "")
62+toPolyglossia (Lang "la" _ _ vars _ _)
63+ | "x-classic" `elem` vars = ("latin", "variant=classic")
64+toPolyglossia (Lang "pt" _ (Just "BR") _ _ _) = ("portuguese", "variant=brazilian")
65+toPolyglossia (Lang "sl" _ _ _ _ _) = ("slovenian", "")
66+toPolyglossia x = (commonFromBcp47 x, "")
67+
68+-- Takes a list of the constituents of a BCP47 language code and
69+-- converts it to a Babel language string.
70+-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
71+-- List of supported languages (slightly outdated):
72+-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
73+toBabel :: Lang -> Text
74+toBabel (Lang "de" _ (Just "AT") vars _ _)
75+ | "1901" `elem` vars = "austrian"
76+ | otherwise = "naustrian"
77+toBabel (Lang "de" _ (Just "CH") vars _ _)
78+ | "1901" `elem` vars = "swissgerman"
79+ | otherwise = "nswissgerman"
80+toBabel (Lang "de" _ _ vars _ _)
81+ | "1901" `elem` vars = "german"
82+ | otherwise = "ngerman"
83+toBabel (Lang "dsb" _ _ _ _ _) = "lowersorbian"
84+toBabel (Lang "el" _ _ vars _ _)
85+ | "polyton" `elem` vars = "polutonikogreek"
86+toBabel (Lang "en" _ (Just "AU") _ _ _) = "australian"
87+toBabel (Lang "en" _ (Just "CA") _ _ _) = "canadian"
88+toBabel (Lang "en" _ (Just "GB") _ _ _) = "british"
89+toBabel (Lang "en" _ (Just "NZ") _ _ _) = "newzealand"
90+toBabel (Lang "en" _ (Just "UK") _ _ _) = "british"
91+toBabel (Lang "en" _ (Just "US") _ _ _) = "american"
92+toBabel (Lang "fr" _ (Just "CA") _ _ _) = "canadien"
93+toBabel (Lang "fra" _ _ vars _ _)
94+ | "aca" `elem` vars = "acadian"
95+toBabel (Lang "grc" _ _ _ _ _) = "polutonikogreek"
96+toBabel (Lang "hsb" _ _ _ _ _) = "uppersorbian"
97+toBabel (Lang "la" _ _ vars _ _)
98+ | "x-classic" `elem` vars = "classiclatin"
99+toBabel (Lang "pt" _ (Just "BR") _ _ _) = "brazilian"
100+toBabel (Lang "sl" _ _ _ _ _) = "slovene"
101+toBabel x = commonFromBcp47 x
102+
103+-- Takes a list of the constituents of a BCP47 language code
104+-- and converts it to a string shared by Babel and Polyglossia.
105+-- https://tools.ietf.org/html/bcp47#section-2.1
106+commonFromBcp47 :: Lang -> Text
107+commonFromBcp47 (Lang "sr" (Just "Cyrl") _ _ _ _) = "serbianc"
108+commonFromBcp47 (Lang "zh" (Just "Latn") _ vars _ _)
109+ | "pinyin" `elem` vars = "pinyin"
110+commonFromBcp47 (Lang l _ _ _ _ _) = fromIso l
111+ where
112+ fromIso "af" = "afrikaans"
113+ fromIso "am" = "amharic"
114+ fromIso "ar" = "arabic"
115+ fromIso "as" = "assamese"
116+ fromIso "ast" = "asturian"
117+ fromIso "bg" = "bulgarian"
118+ fromIso "bn" = "bengali"
119+ fromIso "bo" = "tibetan"
120+ fromIso "br" = "breton"
121+ fromIso "ca" = "catalan"
122+ fromIso "cy" = "welsh"
123+ fromIso "cs" = "czech"
124+ fromIso "cop" = "coptic"
125+ fromIso "da" = "danish"
126+ fromIso "dv" = "divehi"
127+ fromIso "el" = "greek"
128+ fromIso "en" = "english"
129+ fromIso "eo" = "esperanto"
130+ fromIso "es" = "spanish"
131+ fromIso "et" = "estonian"
132+ fromIso "eu" = "basque"
133+ fromIso "fa" = "farsi"
134+ fromIso "fi" = "finnish"
135+ fromIso "fr" = "french"
136+ fromIso "fur" = "friulan"
137+ fromIso "ga" = "irish"
138+ fromIso "gd" = "scottish"
139+ fromIso "gez" = "ethiopic"
140+ fromIso "gl" = "galician"
141+ fromIso "he" = "hebrew"
142+ fromIso "hi" = "hindi"
143+ fromIso "hr" = "croatian"
144+ fromIso "hu" = "magyar"
145+ fromIso "hy" = "armenian"
146+ fromIso "ia" = "interlingua"
147+ fromIso "id" = "indonesian"
148+ fromIso "ie" = "interlingua"
149+ fromIso "is" = "icelandic"
150+ fromIso "it" = "italian"
151+ fromIso "ja" = "japanese"
152+ fromIso "km" = "khmer"
153+ fromIso "kmr" = "kurmanji"
154+ fromIso "kn" = "kannada"
155+ fromIso "ko" = "korean"
156+ fromIso "la" = "latin"
157+ fromIso "lo" = "lao"
158+ fromIso "lt" = "lithuanian"
159+ fromIso "lv" = "latvian"
160+ fromIso "ml" = "malayalam"
161+ fromIso "mn" = "mongolian"
162+ fromIso "mr" = "marathi"
163+ fromIso "nb" = "norsk"
164+ fromIso "nl" = "dutch"
165+ fromIso "nn" = "nynorsk"
166+ fromIso "no" = "norsk"
167+ fromIso "nqo" = "nko"
168+ fromIso "oc" = "occitan"
169+ fromIso "pa" = "panjabi"
170+ fromIso "pl" = "polish"
171+ fromIso "pms" = "piedmontese"
172+ fromIso "pt" = "portuguese"
173+ fromIso "rm" = "romansh"
174+ fromIso "ro" = "romanian"
175+ fromIso "ru" = "russian"
176+ fromIso "sa" = "sanskrit"
177+ fromIso "se" = "samin"
178+ fromIso "sk" = "slovak"
179+ fromIso "sq" = "albanian"
180+ fromIso "sr" = "serbian"
181+ fromIso "sv" = "swedish"
182+ fromIso "syr" = "syriac"
183+ fromIso "ta" = "tamil"
184+ fromIso "te" = "telugu"
185+ fromIso "th" = "thai"
186+ fromIso "ti" = "ethiopic"
187+ fromIso "tk" = "turkmen"
188+ fromIso "tr" = "turkish"
189+ fromIso "uk" = "ukrainian"
190+ fromIso "ur" = "urdu"
191+ fromIso "vi" = "vietnamese"
192+ fromIso _ = ""
src/Text/Pandoc/Writers/LaTeX/Table.hs+21−8
@@ -16,6 +16,7 @@ module Text.Pandoc.Writers.LaTeX.Table
16 ) where
16 ) where
17import Control.Monad.State.Strict
17import Control.Monad.State.Strict
18import Data.List (intersperse)
18import Data.List (intersperse)
19+import qualified Data.List.NonEmpty as NonEmpty
19import Data.List.NonEmpty (NonEmpty ((:|)))
20import Data.List.NonEmpty (NonEmpty ((:|)))
20import Data.Text (Text)
21import Data.Text (Text)
21import qualified Data.Text as T
22import qualified Data.Text as T
@@ -25,7 +26,8 @@ import Text.DocLayout
25 ( Doc, braces, cr, empty, hcat, hsep, isEmpty, literal, nest
26 ( Doc, braces, cr, empty, hcat, hsep, isEmpty, literal, nest
26 , text, vcat, ($$) )
27 , text, vcat, ($$) )
27import Text.Pandoc.Shared (blocksToInlines, splitBy, tshow)
28import Text.Pandoc.Shared (blocksToInlines, splitBy, tshow)
28−import Text.Pandoc.Walk (walk)
29+import Text.Pandoc.Walk (walk, query)
30+import Data.Monoid (Any(..))
29import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
31import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
30import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX)
32import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX)
31import Text.Pandoc.Writers.LaTeX.Types
33import Text.Pandoc.Writers.LaTeX.Types
@@ -219,9 +221,7 @@ footRows (Ann.TableFoot _attr rows) = map headerRowCells rows
219-- we need to go to some lengths to get line breaks working:
221-- we need to go to some lengths to get line breaks working:
220-- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}.
222-- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}.
221fixLineBreaks :: Block -> Block
223fixLineBreaks :: Block -> Block
222−fixLineBreaks (Para ils) = Para $ fixLineBreaks' ils
224+fixLineBreaks = walk fixLineBreaks'
223−fixLineBreaks (Plain ils) = Plain $ fixLineBreaks' ils
224−fixLineBreaks x = x
225
225
226fixLineBreaks' :: [Inline] -> [Inline]
226fixLineBreaks' :: [Inline] -> [Inline]
227fixLineBreaks' ils = case splitBy (== LineBreak) ils of
227fixLineBreaks' ils = case splitBy (== LineBreak) ils of
@@ -245,8 +245,13 @@ cellToLaTeX :: PandocMonad m
245 -> Ann.Cell
245 -> Ann.Cell
246 -> LW m (Doc Text)
246 -> LW m (Doc Text)
247cellToLaTeX blockListToLaTeX celltype annotatedCell = do
247cellToLaTeX blockListToLaTeX celltype annotatedCell = do
248− let (Ann.Cell _specs _colnum cell) = annotatedCell
248+ let (Ann.Cell specs _colnum cell) = annotatedCell
249+ let hasWidths = snd (NonEmpty.head specs) /= ColWidthDefault
250+ let specAlign = fst (NonEmpty.head specs)
249− let (Cell _attr align rowspan colspan blocks) = cell
251+ let (Cell _attr align' rowspan colspan blocks) = cell
252+ let align = case align' of
253+ AlignDefault -> specAlign
254+ _ -> align'
250 beamer <- gets stBeamer
255 beamer <- gets stBeamer
251 externalNotes <- gets stExternalNotes
256 externalNotes <- gets stExternalNotes
252 inMinipage <- gets stInMinipage
257 inMinipage <- gets stInMinipage
@@ -257,8 +262,13 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do
257 Para{} -> True
262 Para{} -> True
258 Plain{} -> True
263 Plain{} -> True
259 _ -> False
264 _ -> False
265+ let hasLineBreak LineBreak = Any True
266+ hasLineBreak _ = Any False
267+ let hasLineBreaks = getAny $ query hasLineBreak blocks
260 result <-
268 result <-
269+ if not hasWidths || (celltype /= HeaderCell
261− if all isPlainOrPara blocks
270+ && all isPlainOrPara blocks
271+ && not hasLineBreaks)
262 then
272 then
263 blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks
273 blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks
264 else do
274 else do
@@ -271,7 +281,9 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do
271 let halign = literal $ alignCommand align
281 let halign = literal $ alignCommand align
272 return $ "\\begin{minipage}" <> valign <>
282 return $ "\\begin{minipage}" <> valign <>
273 braces "\\linewidth" <> halign <> cr <>
283 braces "\\linewidth" <> halign <> cr <>
274− cellContents <> cr <>
284+ cellContents <>
285+ (if hasLineBreaks then "\\strut" else mempty)
286+ <> cr <>
275 "\\end{minipage}"
287 "\\end{minipage}"
276 modify $ \st -> st{ stExternalNotes = externalNotes }
288 modify $ \st -> st{ stExternalNotes = externalNotes }
277 when (rowspan /= RowSpan 1) $
289 when (rowspan /= RowSpan 1) $
@@ -292,3 +304,4 @@ cellToLaTeX blockListToLaTeX celltype annotatedCell = do
292data CellType
304data CellType
293 = HeaderCell
305 = HeaderCell
294 | BodyCell
306 | BodyCell
307+ deriving Eq
src/Text/Pandoc/Writers/LaTeX/Types.hs+2−1
@@ -40,7 +40,7 @@ data WriterState =
40 , stCsquotes :: Bool -- ^ true if document uses csquotes
40 , stCsquotes :: Bool -- ^ true if document uses csquotes
41 , stHighlighting :: Bool -- ^ true if document has highlighted code
41 , stHighlighting :: Bool -- ^ true if document has highlighted code
42 , stIncremental :: Bool -- ^ true if beamer lists should be
42 , stIncremental :: Bool -- ^ true if beamer lists should be
43− -- displayed bit by bit
43+ , stZwnj :: Bool -- ^ true if document has a ZWNJ character
44 , stInternalLinks :: [Text] -- ^ list of internal link targets
44 , stInternalLinks :: [Text] -- ^ list of internal link targets
45 , stBeamer :: Bool -- ^ produce beamer
45 , stBeamer :: Bool -- ^ produce beamer
46 , stEmptyLine :: Bool -- ^ true if no content on line
46 , stEmptyLine :: Bool -- ^ true if no content on line
@@ -74,6 +74,7 @@ startingState options =
74 , stCsquotes = False
74 , stCsquotes = False
75 , stHighlighting = False
75 , stHighlighting = False
76 , stIncremental = writerIncremental options
76 , stIncremental = writerIncremental options
77+ , stZwnj = False
77 , stInternalLinks = []
78 , stInternalLinks = []
78 , stBeamer = False
79 , stBeamer = False
79 , stEmptyLine = True
80 , stEmptyLine = True
src/Text/Pandoc/Writers/LaTeX/Util.hsnew file+275
@@ -0,0 +1,275 @@
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 Control.Monad (when)
26+import Text.Pandoc.Class (PandocMonad, toLang)
27+import Text.Pandoc.Options (WriterOptions(..), isEnabled)
28+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..))
29+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv)
30+import Text.Pandoc.Highlighting (toListingsLanguage)
31+import Text.DocLayout
32+import Text.Pandoc.Definition
33+import Text.Pandoc.ImageSize (showFl)
34+import Control.Monad.State.Strict (gets, modify)
35+import Data.Text (Text)
36+import qualified Data.Text as T
37+import Text.Pandoc.Extensions (Extension(Ext_smart))
38+import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum)
39+import Text.Printf (printf)
40+import Text.Pandoc.Shared (safeRead, elemText)
41+import qualified Data.Text.Normalize as Normalize
42+import Data.List (uncons)
43+
44+data StringContext = TextString
45+ | URLString
46+ | CodeString
47+ deriving (Eq)
48+
49+-- escape things as needed for LaTeX
50+stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
51+stringToLaTeX context zs = do
52+ opts <- gets stOptions
53+ when ('\x200c' `elemText` zs) $
54+ modify (\s -> s { stZwnj = True })
55+ return $ T.pack $
56+ foldr (go opts context) mempty $ T.unpack $
57+ if writerPreferAscii opts
58+ then Normalize.normalize Normalize.NFD zs
59+ else zs
60+ where
61+ go :: WriterOptions -> StringContext -> Char -> String -> String
62+ go opts ctx x xs =
63+ let ligatures = isEnabled Ext_smart opts && ctx == TextString
64+ isUrl = ctx == URLString
65+ mbAccentCmd =
66+ if writerPreferAscii opts && ctx == TextString
67+ then uncons xs >>= \(c,_) -> lookupAccent c
68+ else Nothing
69+ emits s =
70+ case mbAccentCmd of
71+ Just cmd ->
72+ cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
73+ Nothing -> s <> xs
74+ emitc c =
75+ case mbAccentCmd of
76+ Just cmd ->
77+ cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
78+ Nothing -> c : xs
79+ emitcseq cs =
80+ case xs of
81+ c:_ | isLetter c
82+ , ctx == TextString
83+ -> cs <> " " <> xs
84+ | isSpace c -> cs <> "{}" <> xs
85+ | ctx == TextString
86+ -> cs <> xs
87+ _ -> cs <> "{}" <> xs
88+ emitquote cs =
89+ case xs of
90+ '`':_ -> cs <> "\\," <> xs -- add thin space
91+ '\'':_ -> cs <> "\\," <> xs -- add thin space
92+ _ -> cs <> xs
93+ in case x of
94+ '?' | ligatures -> -- avoid ?` ligature
95+ case xs of
96+ '`':_ -> emits "?{}"
97+ _ -> emitc x
98+ '!' | ligatures -> -- avoid !` ligature
99+ case xs of
100+ '`':_ -> emits "!{}"
101+ _ -> emitc x
102+ '{' -> emits "\\{"
103+ '}' -> emits "\\}"
104+ '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
105+ '$' | not isUrl -> emits "\\$"
106+ '%' -> emits "\\%"
107+ '&' -> emits "\\&"
108+ '_' | not isUrl -> emits "\\_"
109+ '#' -> emits "\\#"
110+ '-' | not isUrl -> case xs of
111+ -- prevent adjacent hyphens from forming ligatures
112+ ('-':_) -> emits "-\\/"
113+ _ -> emitc '-'
114+ '~' | not isUrl -> emitcseq "\\textasciitilde"
115+ '^' -> emits "\\^{}"
116+ '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
117+ | otherwise -> emitcseq "\\textbackslash"
118+ '|' | not isUrl -> emitcseq "\\textbar"
119+ '<' -> emitcseq "\\textless"
120+ '>' -> emitcseq "\\textgreater"
121+ '[' -> emits "{[}" -- to avoid interpretation as
122+ ']' -> emits "{]}" -- optional arguments
123+ '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
124+ '\160' -> emits "~"
125+ '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
126+ '\x202F' -> emits "\\,"
127+ '\x2026' -> emitcseq "\\ldots"
128+ '\x2018' | ligatures -> emitquote "`"
129+ '\x2019' | ligatures -> emitquote "'"
130+ '\x201C' | ligatures -> emitquote "``"
131+ '\x201D' | ligatures -> emitquote "''"
132+ '\x2014' | ligatures -> emits "---"
133+ '\x2013' | ligatures -> emits "--"
134+ _ | writerPreferAscii opts
135+ -> case x of
136+ 'ı' -> emitcseq "\\i"
137+ 'ȷ' -> emitcseq "\\j"
138+ 'å' -> emitcseq "\\aa"
139+ 'Å' -> emitcseq "\\AA"
140+ 'ß' -> emitcseq "\\ss"
141+ 'ø' -> emitcseq "\\o"
142+ 'Ø' -> emitcseq "\\O"
143+ 'Ł' -> emitcseq "\\L"
144+ 'ł' -> emitcseq "\\l"
145+ 'æ' -> emitcseq "\\ae"
146+ 'Æ' -> emitcseq "\\AE"
147+ 'œ' -> emitcseq "\\oe"
148+ 'Œ' -> emitcseq "\\OE"
149+ '£' -> emitcseq "\\pounds"
150+ '€' -> emitcseq "\\euro"
151+ '©' -> emitcseq "\\copyright"
152+ _ -> emitc x
153+ | otherwise -> emitc x
154+
155+lookupAccent :: Char -> Maybe String
156+lookupAccent '\779' = Just "\\H"
157+lookupAccent '\768' = Just "\\`"
158+lookupAccent '\769' = Just "\\'"
159+lookupAccent '\770' = Just "\\^"
160+lookupAccent '\771' = Just "\\~"
161+lookupAccent '\776' = Just "\\\""
162+lookupAccent '\775' = Just "\\."
163+lookupAccent '\772' = Just "\\="
164+lookupAccent '\781' = Just "\\|"
165+lookupAccent '\817' = Just "\\b"
166+lookupAccent '\807' = Just "\\c"
167+lookupAccent '\783' = Just "\\G"
168+lookupAccent '\777' = Just "\\h"
169+lookupAccent '\803' = Just "\\d"
170+lookupAccent '\785' = Just "\\f"
171+lookupAccent '\778' = Just "\\r"
172+lookupAccent '\865' = Just "\\t"
173+lookupAccent '\782' = Just "\\U"
174+lookupAccent '\780' = Just "\\v"
175+lookupAccent '\774' = Just "\\u"
176+lookupAccent '\808' = Just "\\k"
177+lookupAccent '\8413' = Just "\\textcircled"
178+lookupAccent _ = Nothing
179+
180+toLabel :: PandocMonad m => Text -> LW m Text
181+toLabel z = go `fmap` stringToLaTeX URLString z
182+ where
183+ go = T.concatMap $ \x -> case x of
184+ _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
185+ | x `elemText` "_-+=:;." -> T.singleton x
186+ | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
187+
188+-- | Puts contents into LaTeX command.
189+inCmd :: Text -> Doc Text -> Doc Text
190+inCmd cmd contents = char '\\' <> literal cmd <> braces contents
191+
192+mapAlignment :: Text -> Text
193+mapAlignment a = case a of
194+ "top" -> "T"
195+ "top-baseline" -> "t"
196+ "bottom" -> "b"
197+ "center" -> "c"
198+ _ -> a
199+
200+wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
201+wrapDiv (_,classes,kvs) t = do
202+ beamer <- gets stBeamer
203+ let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
204+ lang <- toLang $ lookup "lang" kvs
205+ let wrapColumns = if beamer && "columns" `elem` classes
206+ then \contents ->
207+ let valign = maybe "T" mapAlignment (lookup "align" kvs)
208+ totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
209+ (lookup "totalwidth" kvs)
210+ onlytextwidth = filter ("onlytextwidth" ==) classes
211+ options = text $ T.unpack $ T.intercalate "," $
212+ valign : totalwidth ++ onlytextwidth
213+ in inCmd "begin" "columns" <> brackets options
214+ $$ contents
215+ $$ inCmd "end" "columns"
216+ else id
217+ wrapColumn = if beamer && "column" `elem` classes
218+ then \contents ->
219+ let valign =
220+ maybe ""
221+ (brackets . text . T.unpack . mapAlignment)
222+ (lookup "align" kvs)
223+ w = maybe "0.48" fromPct (lookup "width" kvs)
224+ in inCmd "begin" "column" <>
225+ valign <>
226+ braces (literal w <> "\\textwidth")
227+ $$ contents
228+ $$ inCmd "end" "column"
229+ else id
230+ fromPct xs =
231+ case T.unsnoc xs of
232+ Just (ds, '%') -> case safeRead ds of
233+ Just digits -> showFl (digits / 100 :: Double)
234+ Nothing -> xs
235+ _ -> xs
236+ wrapDir = case lookup "dir" kvs of
237+ Just "rtl" -> align "RTL"
238+ Just "ltr" -> align "LTR"
239+ _ -> id
240+ wrapLang txt = case lang of
241+ Just lng -> let (l, o) = toPolyglossiaEnv lng
242+ ops = if T.null o
243+ then ""
244+ else brackets $ literal o
245+ in inCmd "begin" (literal l) <> ops
246+ $$ blankline <> txt <> blankline
247+ $$ inCmd "end" (literal l)
248+ Nothing -> txt
249+ return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
250+
251+hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
252+hypertarget _ "" x = return x
253+hypertarget addnewline ident x = do
254+ ref <- literal `fmap` toLabel ident
255+ return $ text "\\hypertarget"
256+ <> braces ref
257+ <> braces ((if addnewline && not (isEmpty x)
258+ then "%" <> cr
259+ else empty) <> x)
260+
261+labelFor :: PandocMonad m => Text -> LW m (Doc Text)
262+labelFor "" = return empty
263+labelFor ident = do
264+ ref <- literal `fmap` toLabel ident
265+ return $ text "\\label" <> braces ref
266+
267+-- Determine listings language from list of class attributes.
268+getListingsLanguage :: [Text] -> Maybe Text
269+getListingsLanguage xs
270+ = foldr ((<|>) . toListingsLanguage) Nothing xs
271+
272+mbBraced :: Text -> Text
273+mbBraced x = if not (T.all isAlphaNum x)
274+ then "{" <> x <> "}"
275+ else x
src/Text/Pandoc/Writers/Man.hs+2−2
@@ -16,6 +16,7 @@ Conversion of 'Pandoc' documents to roff man page format.
16module Text.Pandoc.Writers.Man ( writeMan ) where
16module Text.Pandoc.Writers.Man ( writeMan ) where
17import Control.Monad.State.Strict
17import Control.Monad.State.Strict
18import Data.List (intersperse)
18import Data.List (intersperse)
19+import Data.List.NonEmpty (nonEmpty)
19import Data.Maybe (fromMaybe)
20import Data.Maybe (fromMaybe)
20import Data.Text (Text)
21import Data.Text (Text)
21import qualified Data.Text as T
22import qualified Data.Text as T
@@ -175,8 +176,7 @@ blockToMan opts (BulletList items) = do
175 return (vcat contents)
176 return (vcat contents)
176blockToMan opts (OrderedList attribs items) = do
177blockToMan opts (OrderedList attribs items) = do
177 let markers = take (length items) $ orderedListMarkers attribs
178 let markers = take (length items) $ orderedListMarkers attribs
178− let indent = 1 +
179− maximum (map T.length markers)
179+ let indent = 1 + maybe 0 maximum (nonEmpty (map T.length markers))
180 contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $
180 contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $
181 zip markers items
181 zip markers items
182 return (vcat contents)
182 return (vcat contents)
src/Text/Pandoc/Writers/Markdown.hs+59−646
@@ -22,15 +22,14 @@ module Text.Pandoc.Writers.Markdown (
22 writePlain) where
22 writePlain) where
23import Control.Monad.Reader
23import Control.Monad.Reader
24import Control.Monad.State.Strict
24import Control.Monad.State.Strict
25−import Data.Char (isAlphaNum, isDigit)
26import Data.Default
25import Data.Default
27−import Data.List (find, intersperse, sortOn, transpose)
26+import Data.List (intersperse, sortOn, transpose)
27+import Data.List.NonEmpty (nonEmpty, NonEmpty(..))
28import qualified Data.Map as M
28import qualified Data.Map as M
29−import Data.Maybe (fromMaybe, mapMaybe)
29+import Data.Maybe (fromMaybe, mapMaybe, isNothing)
30import qualified Data.Set as Set
30import qualified Data.Set as Set
31import Data.Text (Text)
31import Data.Text (Text)
32import qualified Data.Text as T
32import qualified Data.Text as T
33−import Network.HTTP (urlEncode)
34import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
33import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
35import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
34import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
36import Text.Pandoc.Definition
35import Text.Pandoc.Definition
@@ -44,59 +43,11 @@ import Text.Pandoc.Templates (renderTemplate)
44import Text.DocTemplates (Val(..), Context(..), FromContext(..))
43import Text.DocTemplates (Val(..), Context(..), FromContext(..))
45import Text.Pandoc.Walk
44import Text.Pandoc.Walk
46import Text.Pandoc.Writers.HTML (writeHtml5String)
45import Text.Pandoc.Writers.HTML (writeHtml5String)
47−import Text.Pandoc.Writers.Math (texMathToInlines)
46+import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown, linkAttributes, attrsToMarkdown)
48−import Text.Pandoc.XML (toHtml5Entities)
47+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
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
48+ WriterState(..),
58−evalMD md env st = evalStateT (runReaderT md env) st
49+ WriterEnv(..),
59−
50+ Ref, Refs, MD, evalMD)
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− }
100
51
101-- | Convert Pandoc to Markdown.
52-- | Convert Pandoc to Markdown.
102writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
53writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
@@ -116,7 +67,16 @@ writePlain opts document =
116-- | Convert Pandoc to Commonmark.
67-- | Convert Pandoc to Commonmark.
117writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text
68writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text
118writeCommonMark opts document =
69writeCommonMark opts document =
119− evalMD (pandocToMarkdown opts document) def{ envVariant = Commonmark } def
70+ evalMD (pandocToMarkdown opts' document) def{ envVariant = Commonmark } def
71+ where
72+ opts' = opts{ writerExtensions =
73+ -- These extensions can't be enabled or disabled
74+ -- for commonmark because they're part of the core;
75+ -- we set them here so that escapeText will behave
76+ -- properly.
77+ enableExtension Ext_all_symbols_escapable $
78+ enableExtension Ext_intraword_underscores $
79+ writerExtensions opts }
120
80
121pandocTitleBlock :: Doc Text -> [Doc Text] -> Doc Text -> Doc Text
81pandocTitleBlock :: Doc Text -> [Doc Text] -> Doc Text -> Doc Text
122pandocTitleBlock tit auths dat =
82pandocTitleBlock tit auths dat =
@@ -181,18 +141,17 @@ valToYaml (SimpleVal x)
181 | otherwise =
141 | otherwise =
182 if hasNewlines x
142 if hasNewlines x
183 then hang 0 ("|" <> cr) x
143 then hang 0 ("|" <> cr) x
184− else if fst $ foldr needsDoubleQuotes (False, True) x
144+ else if isNothing $ foldM needsDoubleQuotes True x
185 then "\"" <> fmap escapeInDoubleQuotes x <> "\""
145 then "\"" <> fmap escapeInDoubleQuotes x <> "\""
186 else x
146 else x
187 where
147 where
188− needsDoubleQuotes t (positive, isFirst)
148+ needsDoubleQuotes isFirst t
189 = if T.any isBadAnywhere t ||
149 = if T.any isBadAnywhere t ||
190 (isFirst && T.any isYamlPunct (T.take 1 t))
150 (isFirst && T.any isYamlPunct (T.take 1 t))
191− then (True, False)
151+ then Nothing
192− else (positive, False)
152+ else Just False
193 isBadAnywhere '#' = True
153 isBadAnywhere '#' = True
194 isBadAnywhere ':' = True
154 isBadAnywhere ':' = True
195− isBadAnywhere '`' = False
196 isBadAnywhere _ = False
155 isBadAnywhere _ = False
197 hasNewlines NewLine = True
156 hasNewlines NewLine = True
198 hasNewlines BlankLines{} = True
157 hasNewlines BlankLines{} = True
@@ -297,75 +256,11 @@ noteToMarkdown opts num blocks = do
297 then hang (writerTabStop opts) (marker <> spacer) contents
256 then hang (writerTabStop opts) (marker <> spacer) contents
298 else marker <> spacer <> contents
257 else marker <> spacer <> contents
299
258
300−-- | Escape special characters for Markdown.
259+-- | (Code) blocks with a single class and no attributes can just use it
301−escapeText :: WriterOptions -> Text -> Text
260+-- standalone, no need to bother with curly braces.
302−escapeText opts = T.pack . go . T.unpack
261+classOrAttrsToMarkdown :: Attr -> Doc Text
303− where
262+classOrAttrsToMarkdown ("",[cls],[]) = literal cls
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−
343−attrsToMarkdown :: Attr -> Doc Text
344−attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
345− where attribId = case attribs of
346− ("",_,_) -> empty
347− (i,_,_) -> "#" <> escAttr i
348− attribClasses = case attribs of
349− (_,[],_) -> empty
350− (_,cs,_) -> hsep $
351− map (escAttr . ("."<>))
352− cs
353− attribKeys = case attribs of
354− (_,_,[]) -> empty
355− (_,_,ks) -> hsep $
356− map (\(k,v) -> escAttr k
357− <> "=\"" <>
358− escAttr v <> "\"") ks
359− escAttr = mconcat . map escAttrChar . T.unpack
360− escAttrChar '"' = literal "\\\""
361− escAttrChar '\\' = literal "\\\\"
362− escAttrChar c = literal $ T.singleton c
363−
364−linkAttributes :: WriterOptions -> Attr -> Doc Text
365−linkAttributes opts attr =
366− if isEnabled Ext_link_attributes opts && attr /= nullAttr
367− then attrsToMarkdown attr
263+classOrAttrsToMarkdown attrs = attrsToMarkdown attrs
368− else empty
369
264
370-- | Ordered list start parser for use in Para below.
265-- | Ordered list start parser for use in Para below.
371olMarker :: Parser Text ParserState ()
266olMarker :: Parser Text ParserState ()
@@ -426,9 +321,12 @@ blockToMarkdown' opts (Div attrs ils) = do
426 case () of
321 case () of
427 _ | isEnabled Ext_fenced_divs opts &&
322 _ | isEnabled Ext_fenced_divs opts &&
428 attrs /= nullAttr ->
323 attrs /= nullAttr ->
324+ let attrsToMd = if variant == Commonmark
325+ then attrsToMarkdown
326+ else classOrAttrsToMarkdown
429− nowrap (literal ":::" <+> attrsToMarkdown attrs) $$
327+ in nowrap (literal ":::" <+> attrsToMd attrs) $$
430− chomp contents $$
328+ chomp contents $$
431− literal ":::" <> blankline
329+ literal ":::" <> blankline
432 | isEnabled Ext_native_divs opts ||
330 | isEnabled Ext_native_divs opts ||
433 (isEnabled Ext_raw_html opts &&
331 (isEnabled Ext_raw_html opts &&
434 (variant == Commonmark ||
332 (variant == Commonmark ||
@@ -470,7 +368,7 @@ blockToMarkdown' opts (Plain inlines) = do
470-- title beginning with fig: indicates figure
368-- title beginning with fig: indicates figure
471blockToMarkdown' opts (Para [Image attr alt (src,tgt@(T.stripPrefix "fig:" -> Just tit))])
369blockToMarkdown' opts (Para [Image attr alt (src,tgt@(T.stripPrefix "fig:" -> Just tit))])
472 | isEnabled Ext_raw_html opts &&
370 | isEnabled Ext_raw_html opts &&
473− not (isEnabled Ext_link_attributes opts) &&
371+ not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) &&
474 attr /= nullAttr = -- use raw HTML
372 attr /= nullAttr = -- use raw HTML
475 (<> blankline) . literal . T.strip <$>
373 (<> blankline) . literal . T.strip <$>
476 writeHtml5String opts{ writerTemplate = Nothing }
374 writeHtml5String opts{ writerTemplate = Nothing }
@@ -535,7 +433,8 @@ blockToMarkdown' opts (Header level attr inlines) = do
535 && id' == autoId -> empty
433 && id' == autoId -> empty
536 (id',_,_) | isEnabled Ext_mmd_header_identifiers opts ->
434 (id',_,_) | isEnabled Ext_mmd_header_identifiers opts ->
537 space <> brackets (literal id')
435 space <> brackets (literal id')
538− _ | isEnabled Ext_header_attributes opts ->
436+ _ | isEnabled Ext_header_attributes opts ||
437+ isEnabled Ext_attributes opts ->
539 space <> attrsToMarkdown attr
438 space <> attrsToMarkdown attr
540 | otherwise -> empty
439 | otherwise -> empty
541 contents <- inlineListToMarkdown opts $
440 contents <- inlineListToMarkdown opts $
@@ -585,19 +484,21 @@ blockToMarkdown' opts (CodeBlock attribs str) = do
585 | isEnabled Ext_fenced_code_blocks opts ->
484 | isEnabled Ext_fenced_code_blocks opts ->
586 tildes <> attrs <> cr <> literal str <> cr <> tildes <> blankline
485 tildes <> attrs <> cr <> literal str <> cr <> tildes <> blankline
587 _ -> nest (writerTabStop opts) (literal str) <> blankline
486 _ -> nest (writerTabStop opts) (literal str) <> blankline
487+ where
488+ endlineLen c = maybe 3 ((+1) . maximum) $ nonEmpty $
588− where endline c = literal $ case [T.length ln
489+ [T.length ln
589− | ln <- map trim (T.lines str)
490+ | ln <- map trim (T.lines str)
590− , T.pack [c,c,c] `T.isPrefixOf` ln
491+ , T.pack [c,c,c] `T.isPrefixOf` ln
591− , T.all (== c) ln] of
492+ , T.all (== c) ln]
592− [] -> T.replicate 3 $ T.singleton c
593− xs -> T.replicate (maximum xs + 1) $ T.singleton c
493+ endline c = literal $ T.replicate (endlineLen c) $ T.singleton c
594− backticks = endline '`'
494+ backticks = endline '`'
595− tildes = endline '~'
495+ tildes = endline '~'
596− attrs = if isEnabled Ext_fenced_code_attributes opts
496+ attrs = if isEnabled Ext_fenced_code_attributes opts ||
497+ isEnabled Ext_attributes opts
597− then nowrap $ " " <> attrsToMarkdown attribs
498+ then nowrap $ " " <> classOrAttrsToMarkdown attribs
598− else case attribs of
499+ else case attribs of
599− (_,cls:_,_) -> " " <> literal cls
500+ (_,cls:_,_) -> " " <> literal cls
600− _ -> empty
501+ _ -> empty
601blockToMarkdown' opts (BlockQuote blocks) = do
502blockToMarkdown' opts (BlockQuote blocks) = do
602 variant <- asks envVariant
503 variant <- asks envVariant
603 -- if we're writing literate haskell, put a space before the bird tracks
504 -- if we're writing literate haskell, put a space before the bird tracks
@@ -610,7 +511,7 @@ blockToMarkdown' opts (BlockQuote blocks) = do
610 return $ prefixed leader contents <> blankline
511 return $ prefixed leader contents <> blankline
611blockToMarkdown' opts t@(Table _ blkCapt specs thead tbody tfoot) = do
512blockToMarkdown' opts t@(Table _ blkCapt specs thead tbody tfoot) = do
612 let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
513 let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
613− let numcols = maximum (length aligns : length widths :
514+ let numcols = maximum (length aligns :| length widths :
614 map length (headers:rows))
515 map length (headers:rows))
615 caption' <- inlineListToMarkdown opts caption
516 caption' <- inlineListToMarkdown opts caption
616 let caption''
517 let caption''
@@ -712,7 +613,8 @@ pipeTable headless aligns rawHeaders rawRows = do
712 blockFor AlignCenter x y = cblock (x + 2) (sp <> y <> sp) <> lblock 0 empty
613 blockFor AlignCenter x y = cblock (x + 2) (sp <> y <> sp) <> lblock 0 empty
713 blockFor AlignRight x y = rblock (x + 2) (y <> sp) <> lblock 0 empty
614 blockFor AlignRight x y = rblock (x + 2) (y <> sp) <> lblock 0 empty
714 blockFor _ x y = lblock (x + 2) (sp <> y) <> lblock 0 empty
615 blockFor _ x y = lblock (x + 2) (sp <> y) <> lblock 0 empty
715− let widths = map (max 3 . maximum . map offset) $ transpose (rawHeaders : rawRows)
616+ let widths = map (max 3 . maybe 3 maximum . nonEmpty . map offset) $
617+ transpose (rawHeaders : rawRows)
716 let torow cs = nowrap $ literal "|" <>
618 let torow cs = nowrap $ literal "|" <>
717 hcat (intersperse (literal "|") $
619 hcat (intersperse (literal "|") $
718 zipWith3 blockFor aligns widths (map chomp cs))
620 zipWith3 blockFor aligns widths (map chomp cs))
@@ -746,11 +648,11 @@ pandocTable opts multiline headless aligns widths rawHeaders rawRows = do
746 -- Number of characters per column necessary to output every cell
648 -- Number of characters per column necessary to output every cell
747 -- without requiring a line break.
649 -- without requiring a line break.
748 -- The @+2@ is needed for specifying the alignment.
650 -- The @+2@ is needed for specifying the alignment.
749− let numChars = (+ 2) . maximum . map offset
651+ let numChars = (+ 2) . maybe 0 maximum . nonEmpty . map offset
750 -- Number of characters per column necessary to output every cell
652 -- Number of characters per column necessary to output every cell
751 -- without requiring a line break *inside a word*.
653 -- without requiring a line break *inside a word*.
752 -- The @+2@ is needed for specifying the alignment.
654 -- The @+2@ is needed for specifying the alignment.
753− let minNumChars = (+ 2) . maximum . map minOffset
655+ let minNumChars = (+ 2) . maybe 0 maximum . nonEmpty . map minOffset
754 let columns = transpose (rawHeaders : rawRows)
656 let columns = transpose (rawHeaders : rawRows)
755 -- minimal column width without wrapping a single word
657 -- minimal column width without wrapping a single word
756 let relWidth w col =
658 let relWidth w col =
@@ -889,6 +791,9 @@ blockListToMarkdown opts blocks = do
889 b1 : commentSep : fixBlocks (b2:bs)
791 b1 : commentSep : fixBlocks (b2:bs)
890 fixBlocks (Plain ils : bs@(RawBlock{}:_)) =
792 fixBlocks (Plain ils : bs@(RawBlock{}:_)) =
891 Plain ils : fixBlocks bs
793 Plain ils : fixBlocks bs
794+ fixBlocks (Plain ils : bs@(Div{}:_))
795+ | isEnabled Ext_fenced_divs opts =
796+ Para ils : fixBlocks bs
892 fixBlocks (Plain ils : bs) | inlist =
797 fixBlocks (Plain ils : bs) | inlist =
893 Plain ils : fixBlocks bs
798 Plain ils : fixBlocks bs
894 fixBlocks (Plain ils : bs) =
799 fixBlocks (Plain ils : bs) =
@@ -912,499 +817,7 @@ blockListToMarkdown opts blocks = do
912 | otherwise = RawBlock "markdown" "&nbsp;\n"
817 | otherwise = RawBlock "markdown" "&nbsp;\n"
913 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
818 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
914
819
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−
1391lineBreakToSpace :: Inline -> Inline
820lineBreakToSpace :: Inline -> Inline
1392lineBreakToSpace LineBreak = Space
821lineBreakToSpace LineBreak = Space
1393lineBreakToSpace SoftBreak = Space
822lineBreakToSpace SoftBreak = Space
1394lineBreakToSpace x = x
823lineBreakToSpace 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+616
@@ -0,0 +1,616 @@
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+ linkAttributes,
16+ attrsToMarkdown
17+ ) where
18+import Control.Monad.Reader
19+import Control.Monad.State.Strict
20+import Data.Char (isAlphaNum, isDigit)
21+import Data.List (find, intersperse)
22+import Data.List.NonEmpty (nonEmpty)
23+import qualified Data.Map as M
24+import Data.Maybe (fromMaybe)
25+import Data.Text (Text)
26+import qualified Data.Text as T
27+import Network.HTTP (urlEncode)
28+import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
29+import Text.Pandoc.Definition
30+import Text.Pandoc.Logging
31+import Text.Pandoc.Options
32+import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space)
33+import Text.DocLayout
34+import Text.Pandoc.Shared
35+import Text.Pandoc.Writers.Shared
36+import Text.Pandoc.Walk
37+import Text.Pandoc.Writers.HTML (writeHtml5String)
38+import Text.Pandoc.Writers.Math (texMathToInlines)
39+import Text.Pandoc.XML (toHtml5Entities)
40+import Data.Coerce (coerce)
41+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
42+ WriterState(..),
43+ WriterEnv(..), MD)
44+
45+-- | Escape special characters for Markdown.
46+escapeText :: WriterOptions -> Text -> Text
47+escapeText opts = T.pack . go . T.unpack
48+ where
49+ startsWithSpace (' ':_) = True
50+ startsWithSpace ('\t':_) = True
51+ startsWithSpace [] = True
52+ startsWithSpace _ = False
53+ go [] = []
54+ go (c:cs) =
55+ case c of
56+ '<' | isEnabled Ext_all_symbols_escapable opts ->
57+ '\\' : '<' : go cs
58+ | otherwise -> "&lt;" ++ go cs
59+ '>' | isEnabled Ext_all_symbols_escapable opts ->
60+ '\\' : '>' : go cs
61+ | otherwise -> "&gt;" ++ go cs
62+ '@' | isEnabled Ext_citations opts ->
63+ case cs of
64+ (d:_)
65+ | isAlphaNum d || d == '_' || d == '{'
66+ -> '\\':'@':go cs
67+ _ -> '@':go cs
68+ '#' | isEnabled Ext_space_in_atx_header opts
69+ , startsWithSpace cs
70+ -> '\\':'#':go cs
71+ _ | c `elem` ['\\','`','*','_','[',']'] ->
72+ '\\':c:go cs
73+ '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
74+ '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
75+ '~' | isEnabled Ext_subscript opts ||
76+ isEnabled Ext_strikeout opts -> '\\':'~':go cs
77+ '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
78+ '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
79+ '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
80+ '-' | isEnabled Ext_smart opts ->
81+ case cs of
82+ '-':_ -> '\\':'-':go cs
83+ _ -> '-':go cs
84+ '.' | isEnabled Ext_smart opts ->
85+ case cs of
86+ '.':'.':rest -> '\\':'.':'.':'.':go rest
87+ _ -> '.':go cs
88+ _ -> case cs of
89+ '_':x:xs
90+ | isEnabled Ext_intraword_underscores opts
91+ , isAlphaNum c
92+ , isAlphaNum x -> c : '_' : x : go xs
93+ '#':xs -> c : '#' : go xs
94+ '>':xs -> c : '>' : go xs
95+ _ -> c : go cs
96+
97+attrsToMarkdown :: Attr -> Doc Text
98+attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
99+ where attribId = case attribs of
100+ ("",_,_) -> empty
101+ (i,_,_) -> "#" <> escAttr i
102+ attribClasses = case attribs of
103+ (_,[],_) -> empty
104+ (_,cs,_) -> hsep $
105+ map (escAttr . ("."<>))
106+ cs
107+ attribKeys = case attribs of
108+ (_,_,[]) -> empty
109+ (_,_,ks) -> hsep $
110+ map (\(k,v) -> escAttr k
111+ <> "=\"" <>
112+ escAttr v <> "\"") ks
113+ escAttr = mconcat . map escAttrChar . T.unpack
114+ escAttrChar '"' = literal "\\\""
115+ escAttrChar '\\' = literal "\\\\"
116+ escAttrChar c = literal $ T.singleton c
117+
118+linkAttributes :: WriterOptions -> Attr -> Doc Text
119+linkAttributes opts attr =
120+ if (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr
121+ then attrsToMarkdown attr
122+ else empty
123+
124+getKey :: Doc Text -> Key
125+getKey = toKey . render Nothing
126+
127+findUsableIndex :: [Text] -> Int -> Int
128+findUsableIndex lbls i = if tshow i `elem` lbls
129+ then findUsableIndex lbls (i + 1)
130+ else i
131+
132+getNextIndex :: PandocMonad m => MD m Int
133+getNextIndex = do
134+ prevRefs <- gets stPrevRefs
135+ refs <- gets stRefs
136+ i <- (+ 1) <$> gets stLastIdx
137+ modify $ \s -> s{ stLastIdx = i }
138+ let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs
139+ return $ findUsableIndex refLbls i
140+
141+-- | Get reference for target; if none exists, create unique one and return.
142+-- Prefer label if possible; otherwise, generate a unique key.
143+getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text
144+getReference attr label target = do
145+ refs <- gets stRefs
146+ case find (\(_,t,a) -> t == target && a == attr) refs of
147+ Just (ref, _, _) -> return ref
148+ Nothing -> do
149+ keys <- gets stKeys
150+ let key = getKey label
151+ let rawkey = coerce key
152+ case M.lookup key keys of
153+ Nothing -> do -- no other refs with this label
154+ (lab', idx) <- if T.null rawkey ||
155+ T.length rawkey > 999 ||
156+ T.any (\c -> c == '[' || c == ']') rawkey
157+ then do
158+ i <- getNextIndex
159+ return (tshow i, i)
160+ else
161+ return (render Nothing label, 0)
162+ modify (\s -> s{
163+ stRefs = (lab', target, attr) : refs,
164+ stKeys = M.insert (getKey label)
165+ (M.insert (target, attr) idx mempty)
166+ (stKeys s) })
167+ return lab'
168+
169+ Just km -> -- we have refs with this label
170+ case M.lookup (target, attr) km of
171+ Just i -> do
172+ let lab' = render Nothing $
173+ label <> if i == 0
174+ then mempty
175+ else literal (tshow i)
176+ -- make sure it's in stRefs; it may be
177+ -- a duplicate that was printed in a previous
178+ -- block:
179+ when ((lab', target, attr) `notElem` refs) $
180+ modify (\s -> s{
181+ stRefs = (lab', target, attr) : refs })
182+ return lab'
183+ Nothing -> do -- but this one is to a new target
184+ i <- getNextIndex
185+ let lab' = tshow i
186+ modify (\s -> s{
187+ stRefs = (lab', target, attr) : refs,
188+ stKeys = M.insert key
189+ (M.insert (target, attr) i km)
190+ (stKeys s) })
191+ return lab'
192+
193+-- | Convert list of Pandoc inline elements to markdown.
194+inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text)
195+inlineListToMarkdown opts lst = do
196+ inlist <- asks envInList
197+ go (if inlist then avoidBadWrapsInList lst else lst)
198+ where go [] = return empty
199+ go (x@Math{}:y@(Str t):zs)
200+ | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058
201+ = liftM2 (<>) (inlineToMarkdown opts x)
202+ (go (RawInline (Format "html") "<!-- -->" : y : zs))
203+ go (i:is) = case i of
204+ Link {} -> case is of
205+ -- If a link is followed by another link, or '[', '(' or ':'
206+ -- then we don't shortcut
207+ Link {}:_ -> unshortcutable
208+ Space:Link {}:_ -> unshortcutable
209+ Space:(Str(thead -> Just '[')):_ -> unshortcutable
210+ Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
211+ Space:(Cite _ _):_ -> unshortcutable
212+ SoftBreak:Link {}:_ -> unshortcutable
213+ SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable
214+ SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
215+ SoftBreak:(Cite _ _):_ -> unshortcutable
216+ LineBreak:Link {}:_ -> unshortcutable
217+ LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable
218+ LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
219+ LineBreak:(Cite _ _):_ -> unshortcutable
220+ (Cite _ _):_ -> unshortcutable
221+ Str (thead -> Just '['):_ -> unshortcutable
222+ Str (thead -> Just '('):_ -> unshortcutable
223+ Str (thead -> Just ':'):_ -> unshortcutable
224+ (RawInline _ (thead -> Just '[')):_ -> unshortcutable
225+ (RawInline _ (thead -> Just '(')):_ -> unshortcutable
226+ (RawInline _ (thead -> Just ':')):_ -> unshortcutable
227+ (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable
228+ _ -> shortcutable
229+ _ -> shortcutable
230+ where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
231+ unshortcutable = do
232+ iMark <- local
233+ (\env -> env { envRefShortcutable = False })
234+ (inlineToMarkdown opts i)
235+ fmap (iMark <>) (go is)
236+ thead = fmap fst . T.uncons
237+
238+isSp :: Inline -> Bool
239+isSp Space = True
240+isSp SoftBreak = True
241+isSp _ = False
242+
243+avoidBadWrapsInList :: [Inline] -> [Inline]
244+avoidBadWrapsInList [] = []
245+avoidBadWrapsInList (s:Str (T.uncons -> Just ('>',cs)):xs) | isSp s =
246+ Str (" >" <> cs) : avoidBadWrapsInList xs
247+avoidBadWrapsInList [s, Str (T.uncons -> Just (c, cs))]
248+ | T.null cs && isSp s && c `elem` ['-','*','+'] = [Str $ T.pack [' ', c]]
249+avoidBadWrapsInList (s:Str (T.uncons -> Just (c, cs)):Space:xs)
250+ | T.null cs && isSp s && c `elem` ['-','*','+'] =
251+ Str (T.pack [' ', c]) : Space : avoidBadWrapsInList xs
252+avoidBadWrapsInList (s:Str cs:Space:xs)
253+ | isSp s && isOrderedListMarker cs =
254+ Str (" " <> cs) : Space : avoidBadWrapsInList xs
255+avoidBadWrapsInList [s, Str cs]
256+ | isSp s && isOrderedListMarker cs = [Str $ " " <> cs]
257+avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs
258+
259+isOrderedListMarker :: Text -> Bool
260+isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) &&
261+ isRight (runParser (anyOrderedListMarker >> eof)
262+ defaultParserState "" xs)
263+ where
264+ isRight (Right _) = True
265+ isRight (Left _) = False
266+
267+-- | Convert Pandoc inline element to markdown.
268+inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text)
269+inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) =
270+ case lookup "data-emoji" kvs of
271+ Just emojiname | isEnabled Ext_emoji opts ->
272+ return $ ":" <> literal emojiname <> ":"
273+ _ -> inlineToMarkdown opts (Str s)
274+inlineToMarkdown opts (Span attrs ils) = do
275+ variant <- asks envVariant
276+ contents <- inlineListToMarkdown opts ils
277+ return $ case attrs of
278+ (_,["csl-block"],_) -> (cr <>)
279+ (_,["csl-left-margin"],_) -> (cr <>)
280+ (_,["csl-indent"],_) -> (cr <>)
281+ _ -> id
282+ $ case variant of
283+ PlainText -> contents
284+ _ | attrs == nullAttr -> contents
285+ | isEnabled Ext_bracketed_spans opts ->
286+ let attrs' = if attrs /= nullAttr
287+ then attrsToMarkdown attrs
288+ else empty
289+ in "[" <> contents <> "]" <> attrs'
290+ | isEnabled Ext_raw_html opts ||
291+ isEnabled Ext_native_spans opts ->
292+ tagWithAttrs "span" attrs <> contents <> literal "</span>"
293+ | otherwise -> contents
294+inlineToMarkdown _ (Emph []) = return empty
295+inlineToMarkdown opts (Emph lst) = do
296+ variant <- asks envVariant
297+ contents <- inlineListToMarkdown opts lst
298+ return $ case variant of
299+ PlainText
300+ | isEnabled Ext_gutenberg opts -> "_" <> contents <> "_"
301+ | otherwise -> contents
302+ _ -> "*" <> contents <> "*"
303+inlineToMarkdown _ (Underline []) = return empty
304+inlineToMarkdown opts (Underline lst) = do
305+ variant <- asks envVariant
306+ contents <- inlineListToMarkdown opts lst
307+ case variant of
308+ PlainText -> return contents
309+ _ | isEnabled Ext_bracketed_spans opts ->
310+ return $ "[" <> contents <> "]" <> "{.ul}"
311+ | isEnabled Ext_native_spans opts ->
312+ return $ tagWithAttrs "span" ("", ["underline"], [])
313+ <> contents
314+ <> literal "</span>"
315+ | isEnabled Ext_raw_html opts ->
316+ return $ "<u>" <> contents <> "</u>"
317+ | otherwise -> inlineToMarkdown opts (Emph lst)
318+inlineToMarkdown _ (Strong []) = return empty
319+inlineToMarkdown opts (Strong lst) = do
320+ variant <- asks envVariant
321+ case variant of
322+ PlainText ->
323+ inlineListToMarkdown opts $
324+ if isEnabled Ext_gutenberg opts
325+ then capitalize lst
326+ else lst
327+ _ -> do
328+ contents <- inlineListToMarkdown opts lst
329+ return $ "**" <> contents <> "**"
330+inlineToMarkdown _ (Strikeout []) = return empty
331+inlineToMarkdown opts (Strikeout lst) = do
332+ contents <- inlineListToMarkdown opts lst
333+ return $ if isEnabled Ext_strikeout opts
334+ then "~~" <> contents <> "~~"
335+ else if isEnabled Ext_raw_html opts
336+ then "<s>" <> contents <> "</s>"
337+ else contents
338+inlineToMarkdown _ (Superscript []) = return empty
339+inlineToMarkdown opts (Superscript lst) =
340+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
341+ contents <- inlineListToMarkdown opts lst
342+ if isEnabled Ext_superscript opts
343+ then return $ "^" <> contents <> "^"
344+ else if isEnabled Ext_raw_html opts
345+ then return $ "<sup>" <> contents <> "</sup>"
346+ else
347+ case traverse toSuperscriptInline lst of
348+ Just xs' | not (writerPreferAscii opts)
349+ -> inlineListToMarkdown opts xs'
350+ _ -> do
351+ let rendered = render Nothing contents
352+ return $
353+ case mapM toSuperscript (T.unpack rendered) of
354+ Just r -> literal $ T.pack r
355+ Nothing -> literal $ "^(" <> rendered <> ")"
356+inlineToMarkdown _ (Subscript []) = return empty
357+inlineToMarkdown opts (Subscript lst) =
358+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
359+ contents <- inlineListToMarkdown opts lst
360+ if isEnabled Ext_subscript opts
361+ then return $ "~" <> contents <> "~"
362+ else if isEnabled Ext_raw_html opts
363+ then return $ "<sub>" <> contents <> "</sub>"
364+ else
365+ case traverse toSubscriptInline lst of
366+ Just xs' | not (writerPreferAscii opts)
367+ -> inlineListToMarkdown opts xs'
368+ _ -> do
369+ let rendered = render Nothing contents
370+ return $
371+ case mapM toSuperscript (T.unpack rendered) of
372+ Just r -> literal $ T.pack r
373+ Nothing -> literal $ "_(" <> rendered <> ")"
374+inlineToMarkdown opts (SmallCaps lst) = do
375+ variant <- asks envVariant
376+ if variant /= PlainText &&
377+ (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
378+ then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst)
379+ else inlineListToMarkdown opts $ capitalize lst
380+inlineToMarkdown opts (Quoted SingleQuote lst) = do
381+ contents <- inlineListToMarkdown opts lst
382+ return $ if isEnabled Ext_smart opts
383+ then "'" <> contents <> "'"
384+ else
385+ if writerPreferAscii opts
386+ then "&lsquo;" <> contents <> "&rsquo;"
387+ else "‘" <> contents <> "’"
388+inlineToMarkdown opts (Quoted DoubleQuote lst) = do
389+ contents <- inlineListToMarkdown opts lst
390+ return $ if isEnabled Ext_smart opts
391+ then "\"" <> contents <> "\""
392+ else
393+ if writerPreferAscii opts
394+ then "&ldquo;" <> contents <> "&rdquo;"
395+ else "“" <> contents <> "”"
396+inlineToMarkdown opts (Code attr str) = do
397+ let tickGroups = filter (T.any (== '`')) $ T.group str
398+ let longest = maybe 0 maximum $ nonEmpty $ map T.length tickGroups
399+ let marker = T.replicate (longest + 1) "`"
400+ let spacer = if longest == 0 then "" else " "
401+ let attrsEnabled = isEnabled Ext_inline_code_attributes opts ||
402+ isEnabled Ext_attributes opts
403+ let attrs = if attrsEnabled && attr /= nullAttr
404+ then attrsToMarkdown attr
405+ else empty
406+ variant <- asks envVariant
407+ case variant of
408+ PlainText -> return $ literal str
409+ _ -> return $ literal
410+ (marker <> spacer <> str <> spacer <> marker) <> attrs
411+inlineToMarkdown opts (Str str) = do
412+ variant <- asks envVariant
413+ let str' = (if writerPreferAscii opts
414+ then toHtml5Entities
415+ else id) .
416+ (if isEnabled Ext_smart opts
417+ then unsmartify opts
418+ else id) .
419+ (if variant == PlainText
420+ then id
421+ else escapeText opts) $ str
422+ return $ literal str'
423+inlineToMarkdown opts (Math InlineMath str) =
424+ case writerHTMLMathMethod opts of
425+ WebTeX url -> inlineToMarkdown opts
426+ (Image nullAttr [Str str] (url <> T.pack (urlEncode $ T.unpack str), str))
427+ _ | isEnabled Ext_tex_math_dollars opts ->
428+ return $ "$" <> literal str <> "$"
429+ | isEnabled Ext_tex_math_single_backslash opts ->
430+ return $ "\\(" <> literal str <> "\\)"
431+ | isEnabled Ext_tex_math_double_backslash opts ->
432+ return $ "\\\\(" <> literal str <> "\\\\)"
433+ | otherwise -> do
434+ variant <- asks envVariant
435+ texMathToInlines InlineMath str >>=
436+ inlineListToMarkdown opts .
437+ (if variant == PlainText then makeMathPlainer else id)
438+inlineToMarkdown opts (Math DisplayMath str) =
439+ case writerHTMLMathMethod opts of
440+ WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
441+ inlineToMarkdown opts (Image nullAttr [Str str]
442+ (url <> T.pack (urlEncode $ T.unpack str), str))
443+ _ | isEnabled Ext_tex_math_dollars opts ->
444+ return $ "$$" <> literal str <> "$$"
445+ | isEnabled Ext_tex_math_single_backslash opts ->
446+ return $ "\\[" <> literal str <> "\\]"
447+ | isEnabled Ext_tex_math_double_backslash opts ->
448+ return $ "\\\\[" <> literal str <> "\\\\]"
449+ | otherwise -> (\x -> cr <> x <> cr) `fmap`
450+ (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
451+inlineToMarkdown opts il@(RawInline f str) = do
452+ let tickGroups = filter (T.any (== '`')) $ T.group str
453+ let numticks = 1 + maybe 0 maximum (nonEmpty (map T.length tickGroups))
454+ variant <- asks envVariant
455+ let Format fmt = f
456+ let rawAttribInline = return $
457+ literal (T.replicate numticks "`") <> literal str <>
458+ literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}"
459+ let renderEmpty = mempty <$ report (InlineNotRendered il)
460+ case variant of
461+ PlainText -> renderEmpty
462+ Commonmark
463+ | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"]
464+ -> return $ literal str
465+ Markdown
466+ | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
467+ "markdown_mmd", "markdown_strict"]
468+ -> return $ literal str
469+ _ | isEnabled Ext_raw_attribute opts -> rawAttribInline
470+ | f `elem` ["html", "html5", "html4"]
471+ , isEnabled Ext_raw_html opts
472+ -> return $ literal str
473+ | f `elem` ["latex", "tex"]
474+ , isEnabled Ext_raw_tex opts
475+ -> return $ literal str
476+ _ -> renderEmpty
477+
478+
479+inlineToMarkdown opts LineBreak = do
480+ variant <- asks envVariant
481+ if variant == PlainText || isEnabled Ext_hard_line_breaks opts
482+ then return cr
483+ else return $
484+ if isEnabled Ext_escaped_line_breaks opts
485+ then "\\" <> cr
486+ else " " <> cr
487+inlineToMarkdown _ Space = do
488+ escapeSpaces <- asks envEscapeSpaces
489+ return $ if escapeSpaces then "\\ " else space
490+inlineToMarkdown opts SoftBreak = do
491+ escapeSpaces <- asks envEscapeSpaces
492+ let space' = if escapeSpaces then "\\ " else space
493+ return $ case writerWrapText opts of
494+ WrapNone -> space'
495+ WrapAuto -> space'
496+ WrapPreserve -> cr
497+inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst
498+inlineToMarkdown opts (Cite (c:cs) lst)
499+ | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst
500+ | otherwise =
501+ if citationMode c == AuthorInText
502+ then do
503+ suffs <- inlineListToMarkdown opts $ citationSuffix c
504+ rest <- mapM convertOne cs
505+ let inbr = suffs <+> joincits rest
506+ br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
507+ return $ literal ("@" <> maybeInBraces (citationId c)) <+> br
508+ else do
509+ cits <- mapM convertOne (c:cs)
510+ return $ literal "[" <> joincits cits <> literal "]"
511+ where
512+ maybeInBraces key =
513+ case readWith (citeKey False >> spaces >> eof)
514+ defaultParserState ("@" <> key) of
515+ Left _ -> "{" <> key <> "}"
516+ Right _ -> key
517+ joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
518+ convertOne Citation { citationId = k
519+ , citationPrefix = pinlines
520+ , citationSuffix = sinlines
521+ , citationMode = m }
522+ = do
523+ pdoc <- inlineListToMarkdown opts pinlines
524+ sdoc <- inlineListToMarkdown opts sinlines
525+ let k' = literal (modekey m <> "@" <> maybeInBraces k)
526+ r = case sinlines of
527+ Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
528+ _ -> k' <+> sdoc
529+ return $ pdoc <+> r
530+ modekey SuppressAuthor = "-"
531+ modekey _ = ""
532+inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do
533+ variant <- asks envVariant
534+ linktext <- inlineListToMarkdown opts txt
535+ let linktitle = if T.null tit
536+ then empty
537+ else literal $ " \"" <> tit <> "\""
538+ let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
539+ let useAuto = isURI src &&
540+ case txt of
541+ [Str s] | escapeURI s == srcSuffix -> True
542+ _ -> False
543+ let useRefLinks = writerReferenceLinks opts && not useAuto
544+ shortcutable <- asks envRefShortcutable
545+ let useShortcutRefLinks = shortcutable &&
546+ isEnabled Ext_shortcut_reference_links opts
547+ reftext <- if useRefLinks
548+ then literal <$> getReference attr linktext (src, tit)
549+ else return mempty
550+ case variant of
551+ PlainText
552+ | useAuto -> return $ literal srcSuffix
553+ | otherwise -> return linktext
554+ _ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
555+ | useRefLinks ->
556+ let first = "[" <> linktext <> "]"
557+ second = if getKey linktext == getKey reftext
558+ then if useShortcutRefLinks
559+ then ""
560+ else "[]"
561+ else "[" <> reftext <> "]"
562+ in return $ first <> second
563+ | isEnabled Ext_raw_html opts
564+ , not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts)
565+ , attr /= nullAttr -> -- use raw HTML to render attributes
566+ literal . T.strip <$>
567+ writeHtml5String opts{ writerTemplate = Nothing }
568+ (Pandoc nullMeta [Plain [lnk]])
569+ | otherwise -> return $
570+ "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <>
571+ linkAttributes opts attr
572+inlineToMarkdown opts img@(Image attr alternate (source, tit))
573+ | isEnabled Ext_raw_html opts &&
574+ not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) &&
575+ attr /= nullAttr = -- use raw HTML
576+ literal . T.strip <$>
577+ writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]])
578+ | otherwise = do
579+ variant <- asks envVariant
580+ let txt = if null alternate || alternate == [Str source]
581+ -- to prevent autolinks
582+ then [Str ""]
583+ else alternate
584+ linkPart <- inlineToMarkdown opts (Link attr txt (source, tit))
585+ return $ case variant of
586+ PlainText -> "[" <> linkPart <> "]"
587+ _ -> "!" <> linkPart
588+inlineToMarkdown opts (Note contents) = do
589+ modify (\st -> st{ stNotes = contents : stNotes st })
590+ st <- get
591+ let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1)
592+ if isEnabled Ext_footnotes opts
593+ then return $ "[^" <> ref <> "]"
594+ else return $ "[" <> ref <> "]"
595+
596+makeMathPlainer :: [Inline] -> [Inline]
597+makeMathPlainer = walk go
598+ where
599+ go (Emph xs) = Span nullAttr xs
600+ go x = x
601+
602+toSubscriptInline :: Inline -> Maybe Inline
603+toSubscriptInline Space = Just Space
604+toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
605+toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
606+toSubscriptInline LineBreak = Just LineBreak
607+toSubscriptInline SoftBreak = Just SoftBreak
608+toSubscriptInline _ = Nothing
609+
610+toSuperscriptInline :: Inline -> Maybe Inline
611+toSuperscriptInline Space = Just Space
612+toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
613+toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
614+toSuperscriptInline LineBreak = Just LineBreak
615+toSuperscriptInline SoftBreak = Just SoftBreak
616+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+
src/Text/Pandoc/Writers/Ms.hs+24−8
@@ -23,6 +23,7 @@ module Text.Pandoc.Writers.Ms ( writeMs ) where
23import Control.Monad.State.Strict
23import Control.Monad.State.Strict
24import Data.Char (isLower, isUpper, ord)
24import Data.Char (isLower, isUpper, ord)
25import Data.List (intercalate, intersperse)
25import Data.List (intercalate, intersperse)
26+import Data.List.NonEmpty (nonEmpty)
26import qualified Data.Map as Map
27import qualified Data.Map as Map
27import Data.Maybe (catMaybes)
28import Data.Maybe (catMaybes)
28import Data.Text (Text)
29import Data.Text (Text)
@@ -244,13 +245,17 @@ blockToMs opts (Table _ blkCapt specs thead tbody tfoot) =
244 aligncode AlignDefault = "l"
245 aligncode AlignDefault = "l"
245 in do
246 in do
246 caption' <- inlineListToMs' opts caption
247 caption' <- inlineListToMs' opts caption
247− let iwidths = if all (== 0) widths
248+ let isSimple = all (== 0) widths
248− then repeat ""
249+ let totalWidth = 70
249− else map (T.pack . printf "w(%0.1fn)" . (70 *)) widths
250 -- 78n default width - 8n indent = 70n
250 -- 78n default width - 8n indent = 70n
251 let coldescriptions = literal $ T.unwords
251 let coldescriptions = literal $ T.unwords
252− (zipWith (\align width -> aligncode align <> width)
252+ (zipWith (\align width -> aligncode align <>
253+ if width == 0
254+ then ""
255+ else T.pack $
256+ printf "w(%0.1fn)"
257+ (totalWidth * width))
253− alignments iwidths) <> "."
258+ alignments widths) <> "."
254 colheadings <- mapM (blockListToMs opts) headers
259 colheadings <- mapM (blockListToMs opts) headers
255 let makeRow cols = literal "T{" $$
260 let makeRow cols = literal "T{" $$
256 vcat (intersperse (literal "T}\tT{") cols) $$
261 vcat (intersperse (literal "T}\tT{") cols) $$
@@ -259,13 +264,25 @@ blockToMs opts (Table _ blkCapt specs thead tbody tfoot) =
259 then empty
264 then empty
260 else makeRow colheadings $$ char '_'
265 else makeRow colheadings $$ char '_'
261 body <- mapM (\row -> do
266 body <- mapM (\row -> do
267+ cols <- mapM (\(cell, w) ->
268+ (if isSimple
269+ then id
270+ else (literal (".nr LL " <>
271+ T.pack (printf "%0.1fn"
272+ (w * totalWidth))) $$)) <$>
262− cols <- mapM (blockListToMs opts) row
273+ blockListToMs opts cell) (zip row widths)
263 return $ makeRow cols) rows
274 return $ makeRow cols) rows
264 setFirstPara
275 setFirstPara
265 return $ literal ".PP" $$ caption' $$
276 return $ literal ".PP" $$ caption' $$
266 literal ".na" $$ -- we don't want justification in table cells
277 literal ".na" $$ -- we don't want justification in table cells
278+ (if isSimple
279+ then ""
280+ else ".nr LLold \\n[LL]") $$
267 literal ".TS" $$ literal "delim(@@) tab(\t);" $$ coldescriptions $$
281 literal ".TS" $$ literal "delim(@@) tab(\t);" $$ coldescriptions $$
268 colheadings' $$ vcat body $$ literal ".TE" $$
282 colheadings' $$ vcat body $$ literal ".TE" $$
283+ (if isSimple
284+ then ""
285+ else ".nr LL \\n[LLold]") $$
269 literal ".ad"
286 literal ".ad"
270
287
271blockToMs opts (BulletList items) = do
288blockToMs opts (BulletList items) = do
@@ -274,8 +291,7 @@ blockToMs opts (BulletList items) = do
274 return (vcat contents)
291 return (vcat contents)
275blockToMs opts (OrderedList attribs items) = do
292blockToMs opts (OrderedList attribs items) = do
276 let markers = take (length items) $ orderedListMarkers attribs
293 let markers = take (length items) $ orderedListMarkers attribs
277− let indent = 2 +
278− maximum (map T.length markers)
294+ let indent = 2 + maybe 0 maximum (nonEmpty (map T.length markers))
279 contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $
295 contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $
280 zip markers items
296 zip markers items
281 setFirstPara
297 setFirstPara
src/Text/Pandoc/Writers/Muse.hs+6−3
@@ -31,6 +31,7 @@ import Control.Monad.State.Strict
31import Data.Char (isAlphaNum, isAsciiLower, isAsciiUpper, isDigit, isSpace)
31import Data.Char (isAlphaNum, isAsciiLower, isAsciiUpper, isDigit, isSpace)
32import Data.Default
32import Data.Default
33import Data.List (intersperse, transpose)
33import Data.List (intersperse, transpose)
34+import Data.List.NonEmpty (nonEmpty, NonEmpty(..))
34import qualified Data.Set as Set
35import qualified Data.Set as Set
35import qualified Data.Text as T
36import qualified Data.Text as T
36import Data.Text (Text)
37import Data.Text (Text)
@@ -158,7 +159,8 @@ simpleTable caption headers rows = do
158 caption' <- inlineListToMuse caption
159 caption' <- inlineListToMuse caption
159 headers' <- mapM blockListToMuse headers
160 headers' <- mapM blockListToMuse headers
160 rows' <- mapM (mapM blockListToMuse) rows
161 rows' <- mapM (mapM blockListToMuse) rows
161− let widthsInChars = maximum . map offset <$> transpose (headers' : rows')
162+ let widthsInChars = maybe 0 maximum . nonEmpty . map offset <$>
163+ transpose (headers' : rows')
162 let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks
164 let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks
163 where sep' = lblock (T.length sep) $ literal sep
165 where sep' = lblock (T.length sep) $ literal sep
164 let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars
166 let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars
@@ -238,7 +240,7 @@ blockToMuse (DefinitionList items) = do
238 label' <- local (\env -> env { envOneLine = True, envAfterSpace = True }) $ inlineListToMuse' label
240 label' <- local (\env -> env { envOneLine = True, envAfterSpace = True }) $ inlineListToMuse' label
239 let ind = offset' label' -- using Text.DocLayout.offset results in round trip failures
241 let ind = offset' label' -- using Text.DocLayout.offset results in round trip failures
240 hang ind (nowrap label') . vcat <$> mapM descriptionToMuse defs
242 hang ind (nowrap label') . vcat <$> mapM descriptionToMuse defs
241− where offset' d = maximum (0: map T.length
243+ where offset' d = maximum (0 :| map T.length
242 (T.lines $ render Nothing d))
244 (T.lines $ render Nothing d))
243 descriptionToMuse :: PandocMonad m
245 descriptionToMuse :: PandocMonad m
244 => [Block]
246 => [Block]
@@ -269,7 +271,8 @@ blockToMuse (Table _ blkCapt specs thead tbody tfoot) =
269 (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
271 (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
270 blocksToDoc opts blocks =
272 blocksToDoc opts blocks =
271 local (\env -> env { envOptions = opts }) $ blockListToMuse blocks
273 local (\env -> env { envOptions = opts }) $ blockListToMuse blocks
274+ numcols = maximum
272− numcols = maximum (length aligns : length widths : map length (headers:rows))
275+ (length aligns :| length widths : map length (headers:rows))
273 isSimple = onlySimpleTableCells (headers : rows) && all (== 0) widths
276 isSimple = onlySimpleTableCells (headers : rows) && all (== 0) widths
274blockToMuse (Div _ bs) = flatBlockListToMuse bs
277blockToMuse (Div _ bs) = flatBlockListToMuse bs
275blockToMuse Null = return empty
278blockToMuse Null = return empty
src/Text/Pandoc/Writers/ODT.hs+4−3
@@ -16,6 +16,7 @@ import Codec.Archive.Zip
16import Control.Monad.Except (catchError, throwError)
16import Control.Monad.Except (catchError, throwError)
17import Control.Monad.State.Strict
17import Control.Monad.State.Strict
18import qualified Data.ByteString.Lazy as B
18import qualified Data.ByteString.Lazy as B
19+import Data.Maybe (fromMaybe)
19import Data.Generics (everywhere', mkT)
20import Data.Generics (everywhere', mkT)
20import Data.List (isPrefixOf)
21import Data.List (isPrefixOf)
21import qualified Data.Map as Map
22import qualified Data.Map as Map
@@ -23,7 +24,7 @@ import qualified Data.Text as T
23import qualified Data.Text.Lazy as TL
24import qualified Data.Text.Lazy as TL
24import Data.Time
25import Data.Time
25import System.FilePath (takeDirectory, takeExtension, (<.>))
26import System.FilePath (takeDirectory, takeExtension, (<.>))
26−import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
27+import Text.Collate.Lang (Lang (..), renderLang)
27import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
28import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
28import qualified Text.Pandoc.Class.PandocMonad as P
29import qualified Text.Pandoc.Class.PandocMonad as P
29import Text.Pandoc.Definition
30import Text.Pandoc.Definition
@@ -35,7 +36,7 @@ import Text.Pandoc.Options (WrapOption (..), WriterOptions (..))
35import Text.DocLayout
36import Text.DocLayout
36import Text.Pandoc.Shared (stringify, pandocVersion, tshow)
37import Text.Pandoc.Shared (stringify, pandocVersion, tshow)
37import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks,
38import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks,
38− fixDisplayMath)
39+ fixDisplayMath, getLang)
39import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy)
40import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy)
40import Text.Pandoc.Walk
41import Text.Pandoc.Walk
41import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
42import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
@@ -194,7 +195,7 @@ addLang lang = everywhere' (mkT updateLangAttr)
194 where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _)
195 where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _)
195 = Attr n (langLanguage lang)
196 = Attr n (langLanguage lang)
196 updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _)
197 updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _)
197− = Attr n (langRegion lang)
198+ = Attr n (fromMaybe "" $ langRegion lang)
198 updateLangAttr x = x
199 updateLangAttr x = x
199
200
200-- | transform both Image and Math elements
201-- | transform both Image and Math elements
src/Text/Pandoc/Writers/OpenDocument.hs+50−54
@@ -1,4 +1,5 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE PatternGuards #-}
4{-# LANGUAGE PatternGuards #-}
4{-# LANGUAGE ViewPatterns #-}
5{-# LANGUAGE ViewPatterns #-}
@@ -25,7 +26,7 @@ import Data.Ord (comparing)
25import qualified Data.Set as Set
26import qualified Data.Set as Set
26import Data.Text (Text)
27import Data.Text (Text)
27import qualified Data.Text as T
28import qualified Data.Text as T
28−import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
29+import Text.Collate.Lang (Lang (..), parseLang)
29import Text.Pandoc.Class.PandocMonad (PandocMonad, report, translateTerm,
30import Text.Pandoc.Class.PandocMonad (PandocMonad, report, translateTerm,
30 setTranslations, toLang)
31 setTranslations, toLang)
31import Text.Pandoc.Definition
32import Text.Pandoc.Definition
@@ -236,7 +237,7 @@ handleSpaces s = case T.uncons s of
236-- | Convert Pandoc document to string in OpenDocument format.
237-- | Convert Pandoc document to string in OpenDocument format.
237writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text
238writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text
238writeOpenDocument opts (Pandoc meta blocks) = do
239writeOpenDocument opts (Pandoc meta blocks) = do
239− let defLang = Lang "en" "US" "" []
240+ let defLang = Lang "en" (Just "US") Nothing [] [] []
240 lang <- case lookupMetaString "lang" meta of
241 lang <- case lookupMetaString "lang" meta of
241 "" -> pure defLang
242 "" -> pure defLang
242 s -> fromMaybe defLang <$> toLang (Just s)
243 s -> fromMaybe defLang <$> toLang (Just s)
@@ -372,36 +373,32 @@ blocksToOpenDocument o b = vcat <$> mapM (blockToOpenDocument o) b
372
373
373-- | Convert a Pandoc block element to OpenDocument.
374-- | Convert a Pandoc block element to OpenDocument.
374blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m (Doc Text)
375blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m (Doc Text)
375−blockToOpenDocument o bs
376+blockToOpenDocument o = \case
376− | Plain b <- bs = if null b
377+ Plain b -> if null b
377− then return empty
378+ then return empty
378− else inParagraphTags =<< inlinesToOpenDocument o b
379+ else inParagraphTags =<< inlinesToOpenDocument o b
379− | Para [Image attr c (s,T.stripPrefix "fig:" -> Just t)] <- bs
380+ Para [Image attr c (s,T.stripPrefix "fig:" -> Just t)] -> figure attr c s t
380− = figure attr c s t
381− | Para b <- bs = if null b &&
381+ Para b -> if null b &&
382− not (isEnabled Ext_empty_paragraphs o)
382+ not (isEnabled Ext_empty_paragraphs o)
383− then return empty
383+ then return empty
384− else inParagraphTags =<< inlinesToOpenDocument o b
384+ else inParagraphTags =<< inlinesToOpenDocument o b
385− | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b
385+ LineBlock b -> blockToOpenDocument o $ linesToPara b
386− | Div attr xs <- bs = mkDiv attr xs
386+ Div attr xs -> mkDiv attr xs
387− | Header i (ident,_,_) b
387+ Header i (ident,_,_) b -> do
388− <- bs = setFirstPara >> (inHeaderTags i ident
388+ setFirstPara
389− =<< inlinesToOpenDocument o b)
389+ inHeaderTags i ident =<< inlinesToOpenDocument o b
390− | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b
390+ BlockQuote b -> setFirstPara >> mkBlockQuote b
391− | DefinitionList b <- bs = setFirstPara >> defList b
391+ DefinitionList b -> setFirstPara >> defList b
392− | BulletList b <- bs = setFirstPara >> bulletListToOpenDocument o b
392+ BulletList b -> setFirstPara >> bulletListToOpenDocument o b
393− | OrderedList a b <- bs = setFirstPara >> orderedList a b
393+ OrderedList a b -> setFirstPara >> orderedList a b
394− | CodeBlock _ s <- bs = setFirstPara >> preformatted s
394+ CodeBlock _ s -> setFirstPara >> preformatted s
395− | Table a bc s th tb tf <- bs = setFirstPara >> table (Ann.toTable a bc s th tb tf)
395+ Table a bc s th tb tf -> setFirstPara >> table (Ann.toTable a bc s th tb tf)
396− | HorizontalRule <- bs = setFirstPara >> return (selfClosingTag "text:p"
396+ HorizontalRule -> setFirstPara >> return (selfClosingTag "text:p"
397− [ ("text:style-name", "Horizontal_20_Line") ])
397+ [ ("text:style-name", "Horizontal_20_Line") ])
398− | RawBlock f s <- bs = if f == Format "opendocument"
398+ b@(RawBlock f s) -> if f == Format "opendocument"
399− then return $ text $ T.unpack s
399+ then return $ text $ T.unpack s
400− else do
401− report $ BlockNotRendered bs
400+ else empty <$ report (BlockNotRendered b)
402− return empty
403− | Null <- bs = return empty
404− | otherwise = return empty
401+ Null -> return empty
405 where
402 where
406 defList b = do setInDefinitionList True
403 defList b = do setInDefinitionList True
407 r <- vcat <$> mapM (deflistItemToOpenDocument o) b
404 r <- vcat <$> mapM (deflistItemToOpenDocument o) b
@@ -874,34 +871,33 @@ data TextStyle = Italic
874textStyleAttr :: Map.Map Text Text
871textStyleAttr :: Map.Map Text Text
875 -> TextStyle
872 -> TextStyle
876 -> Map.Map Text Text
873 -> Map.Map Text Text
877−textStyleAttr m s
874+textStyleAttr m = \case
878− | Italic <- s = Map.insert "fo:font-style" "italic" .
875+ Italic -> Map.insert "fo:font-style" "italic" .
879− Map.insert "style:font-style-asian" "italic" .
876+ Map.insert "style:font-style-asian" "italic" .
880− Map.insert "style:font-style-complex" "italic" $ m
877+ Map.insert "style:font-style-complex" "italic" $ m
881− | Bold <- s = Map.insert "fo:font-weight" "bold" .
878+ Bold -> Map.insert "fo:font-weight" "bold" .
882− Map.insert "style:font-weight-asian" "bold" .
879+ Map.insert "style:font-weight-asian" "bold" .
883− Map.insert "style:font-weight-complex" "bold" $ m
880+ Map.insert "style:font-weight-complex" "bold" $ m
884− | Under <- s = Map.insert "style:text-underline-style" "solid" .
881+ Under -> Map.insert "style:text-underline-style" "solid" .
885− Map.insert "style:text-underline-width" "auto" .
882+ Map.insert "style:text-underline-width" "auto" .
886− Map.insert "style:text-underline-color" "font-color" $ m
883+ Map.insert "style:text-underline-color" "font-color" $ m
887− | Strike <- s = Map.insert "style:text-line-through-style" "solid" m
884+ Strike -> Map.insert "style:text-line-through-style" "solid" m
888− | Sub <- s = Map.insert "style:text-position" "sub 58%" m
885+ Sub -> Map.insert "style:text-position" "sub 58%" m
889− | Sup <- s = Map.insert "style:text-position" "super 58%" m
886+ Sup -> Map.insert "style:text-position" "super 58%" m
890− | SmallC <- s = Map.insert "fo:font-variant" "small-caps" m
887+ SmallC -> Map.insert "fo:font-variant" "small-caps" m
891− | Pre <- s = Map.insert "style:font-name" "Courier New" .
888+ Pre -> Map.insert "style:font-name" "Courier New" .
892− Map.insert "style:font-name-asian" "Courier New" .
889+ Map.insert "style:font-name-asian" "Courier New" .
893− Map.insert "style:font-name-complex" "Courier New" $ m
890+ Map.insert "style:font-name-complex" "Courier New" $ m
894− | Language lang <- s
891+ Language lang ->
895− = Map.insert "fo:language" (langLanguage lang) .
892+ Map.insert "fo:language" (langLanguage lang) .
896− Map.insert "fo:country" (langRegion lang) $ m
893+ maybe id (Map.insert "fo:country") (langRegion lang) $ m
897− | otherwise = m
898
894
899withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a
895withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a
900withLangFromAttr (_,_,kvs) action =
896withLangFromAttr (_,_,kvs) action =
901 case lookup "lang" kvs of
897 case lookup "lang" kvs of
902 Nothing -> action
898 Nothing -> action
903 Just l ->
899 Just l ->
904− case parseBCP47 l of
900+ case parseLang l of
905 Right lang -> withTextStyle (Language lang) action
901 Right lang -> withTextStyle (Language lang) action
906 Left _ -> do
902 Left _ -> do
907 report $ InvalidLang l
903 report $ InvalidLang l
src/Text/Pandoc/Writers/Org.hs+24−14
@@ -17,8 +17,9 @@ Org-Mode: <http://orgmode.org>
17-}
17-}
18module Text.Pandoc.Writers.Org (writeOrg) where
18module Text.Pandoc.Writers.Org (writeOrg) where
19import Control.Monad.State.Strict
19import Control.Monad.State.Strict
20−import Data.Char (isAlphaNum)
20+import Data.Char (isAlphaNum, isDigit)
21import Data.List (intersect, intersperse, partition, transpose)
21import Data.List (intersect, intersperse, partition, transpose)
22+import Data.List.NonEmpty (nonEmpty)
22import Data.Text (Text)
23import Data.Text (Text)
23import qualified Data.Text as T
24import qualified Data.Text as T
24import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
25import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
@@ -83,12 +84,15 @@ noteToOrg num note = do
83
84
84-- | Escape special characters for Org.
85-- | Escape special characters for Org.
85escapeString :: Text -> Text
86escapeString :: Text -> Text
86−escapeString = escapeStringUsing
87+escapeString t
88+ | T.all (\c -> c < '\x2013' || c > '\x2026') t = t
89+ | otherwise = T.concatMap escChar t
90+ where
91+ escChar '\x2013' = "--"
87− [ ('\x2014',"---")
92+ escChar '\x2014' = "---"
88− , ('\x2013',"--")
89− , ('\x2019',"'")
93+ escChar '\x2019' = "'"
90− , ('\x2026',"...")
94+ escChar '\x2026' = "..."
91− ]
95+ escChar c = T.singleton c
92
96
93isRawFormat :: Format -> Bool
97isRawFormat :: Format -> Bool
94isRawFormat f =
98isRawFormat f =
@@ -163,7 +167,7 @@ blockToOrg (Table _ blkCapt specs thead tbody tfoot) = do
163 else "#+caption: " <> caption''
167 else "#+caption: " <> caption''
164 headers' <- mapM blockListToOrg headers
168 headers' <- mapM blockListToOrg headers
165 rawRows <- mapM (mapM blockListToOrg) rows
169 rawRows <- mapM (mapM blockListToOrg) rows
166− let numChars = maximum . map offset
170+ let numChars = maybe 0 maximum . nonEmpty . map offset
167 -- FIXME: width is not being used.
171 -- FIXME: width is not being used.
168 let widthsInChars =
172 let widthsInChars =
169 map numChars $ transpose (headers' : rawRows)
173 map numChars $ transpose (headers' : rawRows)
@@ -198,7 +202,7 @@ blockToOrg (OrderedList (start, _, delim) items) = do
198 x -> x
202 x -> x
199 let markers = take (length items) $ orderedListMarkers
203 let markers = take (length items) $ orderedListMarkers
200 (start, Decimal, delim')
204 (start, Decimal, delim')
201− let maxMarkerLength = maximum $ map T.length markers
205+ let maxMarkerLength = maybe 0 maximum . nonEmpty $ map T.length markers
202 let markers' = map (\m -> let s = maxMarkerLength - T.length m
206 let markers' = map (\m -> let s = maxMarkerLength - T.length m
203 in m <> T.replicate s " ") markers
207 in m <> T.replicate s " ") markers
204 contents <- zipWithM orderedListItemToOrg markers' items
208 contents <- zipWithM orderedListItemToOrg markers' items
@@ -347,16 +351,20 @@ inlineListToOrg :: PandocMonad m
347 => [Inline]
351 => [Inline]
348 -> Org m (Doc Text)
352 -> Org m (Doc Text)
349inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixMarkers lst)
353inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixMarkers lst)
350− where fixMarkers [] = [] -- prevent note refs and list markers from wrapping, see #4171
354+ where -- Prevent note refs and list markers from wrapping, see #4171
355+ -- and #7132.
356+ fixMarkers [] = []
351 fixMarkers (Space : x : rest) | shouldFix x =
357 fixMarkers (Space : x : rest) | shouldFix x =
352 Str " " : x : fixMarkers rest
358 Str " " : x : fixMarkers rest
353 fixMarkers (SoftBreak : x : rest) | shouldFix x =
359 fixMarkers (SoftBreak : x : rest) | shouldFix x =
354 Str " " : x : fixMarkers rest
360 Str " " : x : fixMarkers rest
355 fixMarkers (x : rest) = x : fixMarkers rest
361 fixMarkers (x : rest) = x : fixMarkers rest
356
362
357− shouldFix Note{} = True -- Prevent footnotes
363+ shouldFix Note{} = True -- Prevent footnotes
358 shouldFix (Str "-") = True -- Prevent bullet list items
364 shouldFix (Str "-") = True -- Prevent bullet list items
359− -- TODO: prevent ordered list items
365+ shouldFix (Str x) -- Prevent ordered list items
366+ | Just (cs, c) <- T.unsnoc x = T.all isDigit cs &&
367+ (c == '.' || c == ')')
360 shouldFix _ = False
368 shouldFix _ = False
361
369
362-- | Convert Pandoc inline element to Org.
370-- | Convert Pandoc inline element to Org.
@@ -396,9 +404,11 @@ inlineToOrg (Str str) = return . literal $ escapeString str
396inlineToOrg (Math t str) = do
404inlineToOrg (Math t str) = do
397 modify $ \st -> st{ stHasMath = True }
405 modify $ \st -> st{ stHasMath = True }
398 return $ if t == InlineMath
406 return $ if t == InlineMath
399− then "$" <> literal str <> "$"
407+ then "\\(" <> literal str <> "\\)"
400− else "$$" <> literal str <> "$$"
408+ else "\\[" <> literal str <> "\\]"
401inlineToOrg il@(RawInline f str)
409inlineToOrg il@(RawInline f str)
410+ | elem f ["tex", "latex"] && T.isPrefixOf "\\begin" str =
411+ return $ cr <> literal str <> cr
402 | isRawFormat f = return $ literal str
412 | isRawFormat f = return $ literal str
403 | otherwise = do
413 | otherwise = do
404 report $ InlineNotRendered il
414 report $ InlineNotRendered il
src/Text/Pandoc/Writers/Powerpoint.hs+1−1
@@ -39,5 +39,5 @@ writePowerpoint opts (Pandoc meta blks) = do
39 let blks' = walk fixDisplayMath blks
39 let blks' = walk fixDisplayMath blks
40 let (pres, logMsgs) = documentToPresentation opts (Pandoc meta blks')
40 let (pres, logMsgs) = documentToPresentation opts (Pandoc meta blks')
41 mapM_ report logMsgs
41 mapM_ report logMsgs
42− archv <- presentationToArchive opts pres
42+ archv <- presentationToArchive opts meta pres
43 return $ fromArchive archv
43 return $ fromArchive archv
src/Text/Pandoc/Writers/Powerpoint/Output.hs+43−31
@@ -38,18 +38,21 @@ import qualified Text.Pandoc.Class.PandocMonad as P
38import Text.Pandoc.Options
38import Text.Pandoc.Options
39import Text.Pandoc.MIME
39import Text.Pandoc.MIME
40import qualified Data.ByteString.Lazy as BL
40import qualified Data.ByteString.Lazy as BL
41+import Text.Pandoc.Writers.Shared (metaToContext)
41import Text.Pandoc.Writers.OOXML
42import Text.Pandoc.Writers.OOXML
42import qualified Data.Map as M
43import qualified Data.Map as M
43import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isJust)
44import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isJust)
44import Text.Pandoc.ImageSize
45import Text.Pandoc.ImageSize
45import Control.Applicative ((<|>))
46import Control.Applicative ((<|>))
46import System.FilePath.Glob
47import System.FilePath.Glob
47−import Text.DocTemplates (FromContext(lookupContext))
48+import Text.DocTemplates (FromContext(lookupContext), Context)
49+import Text.DocLayout (literal)
48import Text.TeXMath
50import Text.TeXMath
49import Text.Pandoc.Writers.Math (convertMath)
51import Text.Pandoc.Writers.Math (convertMath)
50import Text.Pandoc.Writers.Powerpoint.Presentation
52import Text.Pandoc.Writers.Powerpoint.Presentation
51−import Text.Pandoc.Shared (tshow)
53+import Text.Pandoc.Shared (tshow, stringify)
52import Skylighting (fromColor)
54import Skylighting (fromColor)
55+import Data.List.NonEmpty (nonEmpty)
53
56
54-- |The 'EMU' type is used to specify sizes in English Metric Units.
57-- |The 'EMU' type is used to specify sizes in English Metric Units.
55type EMU = Integer
58type EMU = Integer
@@ -96,6 +99,7 @@ data WriterEnv = WriterEnv { envRefArchive :: Archive
96 , envDistArchive :: Archive
99 , envDistArchive :: Archive
97 , envUTCTime :: UTCTime
100 , envUTCTime :: UTCTime
98 , envOpts :: WriterOptions
101 , envOpts :: WriterOptions
102+ , envContext :: Context Text
99 , envPresentationSize :: (Integer, Integer)
103 , envPresentationSize :: (Integer, Integer)
100 , envSlideHasHeader :: Bool
104 , envSlideHasHeader :: Bool
101 , envInList :: Bool
105 , envInList :: Bool
@@ -121,6 +125,7 @@ instance Default WriterEnv where
121 , envDistArchive = emptyArchive
125 , envDistArchive = emptyArchive
122 , envUTCTime = posixSecondsToUTCTime 0
126 , envUTCTime = posixSecondsToUTCTime 0
123 , envOpts = def
127 , envOpts = def
128+ , envContext = mempty
124 , envPresentationSize = (720, 540)
129 , envPresentationSize = (720, 540)
125 , envSlideHasHeader = False
130 , envSlideHasHeader = False
126 , envInList = False
131 , envInList = False
@@ -167,7 +172,7 @@ runP env st p = evalStateT (runReaderT p env) st
167
172
168monospaceFont :: Monad m => P m T.Text
173monospaceFont :: Monad m => P m T.Text
169monospaceFont = do
174monospaceFont = do
170− vars <- writerVariables <$> asks envOpts
175+ vars <- asks envContext
171 case lookupContext "monofont" vars of
176 case lookupContext "monofont" vars of
172 Just s -> return s
177 Just s -> return s
173 Nothing -> return "Courier"
178 Nothing -> return "Courier"
@@ -303,8 +308,9 @@ makeSpeakerNotesMap (Presentation _ slides) =
303 then Nothing
308 then Nothing
304 else Just n
309 else Just n
305
310
306−presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive
311+presentationToArchive :: PandocMonad m
312+ => WriterOptions -> Meta -> Presentation -> m Archive
307−presentationToArchive opts pres = do
313+presentationToArchive opts meta pres = do
308 distArchive <- toArchive . BL.fromStrict <$>
314 distArchive <- toArchive . BL.fromStrict <$>
309 P.readDefaultDataFile "reference.pptx"
315 P.readDefaultDataFile "reference.pptx"
310 refArchive <- case writerReferenceDoc opts of
316 refArchive <- case writerReferenceDoc opts of
@@ -320,10 +326,18 @@ presentationToArchive opts pres = do
320 PandocSomeError
326 PandocSomeError
321 "Could not determine presentation size"
327 "Could not determine presentation size"
322
328
329+ -- note, we need writerTemplate to be Just _ or metaToContext does
330+ -- nothing
331+ context <- metaToContext opts{ writerTemplate =
332+ writerTemplate opts <|> Just mempty }
333+ (return . literal . stringify)
334+ (return . literal . stringify) meta
335+
323 let env = def { envRefArchive = refArchive
336 let env = def { envRefArchive = refArchive
324 , envDistArchive = distArchive
337 , envDistArchive = distArchive
325 , envUTCTime = utctime
338 , envUTCTime = utctime
326 , envOpts = opts
339 , envOpts = opts
340+ , envContext = context
327 , envPresentationSize = presSize
341 , envPresentationSize = presSize
328 , envSlideIdMap = makeSlideIdMap pres
342 , envSlideIdMap = makeSlideIdMap pres
329 , envSpeakerNotesIdMap = makeSpeakerNotesMap pres
343 , envSpeakerNotesIdMap = makeSpeakerNotesMap pres
@@ -474,15 +488,16 @@ registerLink link = do
474 linkReg <- gets stLinkIds
488 linkReg <- gets stLinkIds
475 mediaReg <- gets stMediaIds
489 mediaReg <- gets stMediaIds
476 hasSpeakerNotes <- curSlideHasSpeakerNotes
490 hasSpeakerNotes <- curSlideHasSpeakerNotes
477− let maxLinkId = case M.lookup curSlideId linkReg of
491+ let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of
478− Just mp -> case M.keys mp of
479− [] -> if hasSpeakerNotes then 2 else 1
480− ks -> maximum ks
492+ Just xs -> maximum xs
493+ Nothing
481− Nothing -> if hasSpeakerNotes then 2 else 1
494+ | hasSpeakerNotes -> 2
495+ | otherwise -> 1
482− maxMediaId = case M.lookup curSlideId mediaReg of
496+ maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of
483− Just [] -> if hasSpeakerNotes then 2 else 1
484− Just mInfos -> maximum $ map mInfoLocalId mInfos
497+ Just mInfos -> maximum $ fmap mInfoLocalId mInfos
498+ Nothing
485− Nothing -> if hasSpeakerNotes then 2 else 1
499+ | hasSpeakerNotes -> 2
500+ | otherwise -> 1
486 maxId = max maxLinkId maxMediaId
501 maxId = max maxLinkId maxMediaId
487 slideLinks = case M.lookup curSlideId linkReg of
502 slideLinks = case M.lookup curSlideId linkReg of
488 Just mp -> M.insert (maxId + 1) link mp
503 Just mp -> M.insert (maxId + 1) link mp
@@ -497,20 +512,19 @@ registerMedia fp caption = do
497 mediaReg <- gets stMediaIds
512 mediaReg <- gets stMediaIds
498 globalIds <- gets stMediaGlobalIds
513 globalIds <- gets stMediaGlobalIds
499 hasSpeakerNotes <- curSlideHasSpeakerNotes
514 hasSpeakerNotes <- curSlideHasSpeakerNotes
500− let maxLinkId = case M.lookup curSlideId linkReg of
515+ let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of
501− Just mp -> case M.keys mp of
502− [] -> if hasSpeakerNotes then 2 else 1
503− ks -> maximum ks
516+ Just ks -> maximum ks
517+ Nothing
504− Nothing -> if hasSpeakerNotes then 2 else 1
518+ | hasSpeakerNotes -> 2
519+ | otherwise -> 1
505− maxMediaId = case M.lookup curSlideId mediaReg of
520+ maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of
506− Just [] -> if hasSpeakerNotes then 2 else 1
507− Just mInfos -> maximum $ map mInfoLocalId mInfos
521+ Just mInfos -> maximum $ fmap mInfoLocalId mInfos
522+ Nothing
508− Nothing -> if hasSpeakerNotes then 2 else 1
523+ | hasSpeakerNotes -> 2
524+ | otherwise -> 1
509 maxLocalId = max maxLinkId maxMediaId
525 maxLocalId = max maxLinkId maxMediaId
510
526
511− maxGlobalId = case M.elems globalIds of
527+ maxGlobalId = maybe 0 maximum $ nonEmpty $ M.elems globalIds
512− [] -> 0
513− ids -> maximum ids
514
528
515 (imgBytes, mbMt) <- P.fetchItem $ T.pack fp
529 (imgBytes, mbMt) <- P.fetchItem $ T.pack fp
516 let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ "." <> x))
530 let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ "." <> x))
@@ -523,6 +537,7 @@ registerMedia fp caption = do
523 Just Eps -> Just ".eps"
537 Just Eps -> Just ".eps"
524 Just Svg -> Just ".svg"
538 Just Svg -> Just ".svg"
525 Just Emf -> Just ".emf"
539 Just Emf -> Just ".emf"
540+ Just Tiff -> Just ".tiff"
526 Nothing -> Nothing
541 Nothing -> Nothing
527
542
528 let newGlobalId = fromMaybe (maxGlobalId + 1) (M.lookup fp globalIds)
543 let newGlobalId = fromMaybe (maxGlobalId + 1) (M.lookup fp globalIds)
@@ -1427,11 +1442,8 @@ presentationToRels pres@(Presentation _ slides) = do
1427 -- all relWithoutSlide rels (unless they're 1)
1442 -- all relWithoutSlide rels (unless they're 1)
1428 -- 3. If we have a notesmaster slide, we make space for that as well.
1443 -- 3. If we have a notesmaster slide, we make space for that as well.
1429
1444
1445+ let minRelNotOne = maybe 0 minimum $ nonEmpty
1430− let minRelNotOne = case filter (1<) $ map relId relsWeKeep of
1446+ $ filter (1 <) $ map relId relsWeKeep
1431− [] -> 0 -- doesn't matter in this case, since
1432− -- there will be nothing to map the
1433− -- function over
1434− l -> minimum l
1435
1447
1436 modifyRelNum :: Int -> Int
1448 modifyRelNum :: Int -> Int
1437 modifyRelNum 1 = 1
1449 modifyRelNum 1 = 1
src/Text/Pandoc/Writers/Powerpoint/Presentation.hs+2−3
@@ -45,6 +45,7 @@ module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation
45import Control.Monad.Reader
45import Control.Monad.Reader
46import Control.Monad.State
46import Control.Monad.State
47import Data.List (intercalate)
47import Data.List (intercalate)
48+import Data.List.NonEmpty (nonEmpty)
48import Data.Default
49import Data.Default
49import Text.Pandoc.Definition
50import Text.Pandoc.Definition
50import Text.Pandoc.ImageSize
51import Text.Pandoc.ImageSize
@@ -363,9 +364,7 @@ inlineToParElems (Note blks) = do
363 then return []
364 then return []
364 else do
365 else do
365 notes <- gets stNoteIds
366 notes <- gets stNoteIds
366− let maxNoteId = case M.keys notes of
367+ let maxNoteId = maybe 0 maximum $ nonEmpty $ M.keys notes
367− [] -> 0
368− lst -> maximum lst
369 curNoteId = maxNoteId + 1
368 curNoteId = maxNoteId + 1
370 modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes }
369 modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes }
371 local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $
370 local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $
src/Text/Pandoc/Writers/RST.hs+14−10
@@ -16,7 +16,8 @@ reStructuredText: <http://docutils.sourceforge.net/rst.html>
16module Text.Pandoc.Writers.RST ( writeRST, flatten ) where
16module Text.Pandoc.Writers.RST ( writeRST, flatten ) where
17import Control.Monad.State.Strict
17import Control.Monad.State.Strict
18import Data.Char (isSpace)
18import Data.Char (isSpace)
19−import Data.List (transpose, intersperse)
19+import Data.List (transpose, intersperse, foldl')
20+import qualified Data.List.NonEmpty as NE
20import Data.Maybe (fromMaybe)
21import Data.Maybe (fromMaybe)
21import qualified Data.Text as T
22import qualified Data.Text as T
22import Data.Text (Text)
23import Data.Text (Text)
@@ -335,7 +336,7 @@ blockToRST (OrderedList (start, style', delim) items) = do
335 then replicate (length items) "#."
336 then replicate (length items) "#."
336 else take (length items) $ orderedListMarkers
337 else take (length items) $ orderedListMarkers
337 (start, style', delim)
338 (start, style', delim)
338− let maxMarkerLength = maximum $ map T.length markers
339+ let maxMarkerLength = maybe 0 maximum $ NE.nonEmpty $ map T.length markers
339 let markers' = map (\m -> let s = maxMarkerLength - T.length m
340 let markers' = map (\m -> let s = maxMarkerLength - T.length m
340 in m <> T.replicate s " ") markers
341 in m <> T.replicate s " ") markers
341 contents <- zipWithM orderedListItemToRST markers' items
342 contents <- zipWithM orderedListItemToRST markers' items
@@ -509,7 +510,7 @@ flatten outer
509 | null contents = [outer]
510 | null contents = [outer]
510 | otherwise = combineAll contents
511 | otherwise = combineAll contents
511 where contents = dropInlineParent outer
512 where contents = dropInlineParent outer
512− combineAll = foldl combine []
513+ combineAll = foldl' combine []
513
514
514 combine :: [Inline] -> Inline -> [Inline]
515 combine :: [Inline] -> Inline -> [Inline]
515 combine f i =
516 combine f i =
@@ -537,11 +538,15 @@ flatten outer
537 collapse f i = appendToLast f $ dropInlineParent i
538 collapse f i = appendToLast f $ dropInlineParent i
538
539
539 appendToLast :: [Inline] -> [Inline] -> [Inline]
540 appendToLast :: [Inline] -> [Inline] -> [Inline]
541+ appendToLast flattened toAppend =
542+ case NE.nonEmpty flattened of
540− appendToLast [] toAppend = [setInlineChildren outer toAppend]
543+ Nothing -> [setInlineChildren outer toAppend]
541− appendToLast flattened toAppend
544+ Just xs ->
545+ if isOuter lastFlat
542− | isOuter lastFlat = init flattened <> [appendTo lastFlat toAppend]
546+ then NE.init xs <> [appendTo lastFlat toAppend]
543− | otherwise = flattened <> [setInlineChildren outer toAppend]
547+ else flattened <> [setInlineChildren outer toAppend]
548+ where
544− where lastFlat = last flattened
549+ lastFlat = NE.last xs
545 appendTo o i = mapNested (<> i) o
550 appendTo o i = mapNested (<> i) o
546 isOuter i = emptyParent i == emptyParent outer
551 isOuter i = emptyParent i == emptyParent outer
547 emptyParent i = setInlineChildren i []
552 emptyParent i = setInlineChildren i []
@@ -761,8 +766,7 @@ simpleTable opts blocksToDoc headers rows = do
761 then return []
766 then return []
762 else fixEmpties <$> mapM (blocksToDoc opts) headers
767 else fixEmpties <$> mapM (blocksToDoc opts) headers
763 rowDocs <- mapM (fmap fixEmpties . mapM (blocksToDoc opts)) rows
768 rowDocs <- mapM (fmap fixEmpties . mapM (blocksToDoc opts)) rows
764− let numChars [] = 0
765− numChars xs = maximum . map offset $ xs
769+ let numChars = maybe 0 maximum . NE.nonEmpty . map offset
766 let colWidths = map numChars $ transpose (headerDocs : rowDocs)
770 let colWidths = map numChars $ transpose (headerDocs : rowDocs)
767 let toRow = mconcat . intersperse (lblock 1 " ") . zipWith lblock colWidths
771 let toRow = mconcat . intersperse (lblock 1 " ") . zipWith lblock colWidths
768 let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths)
772 let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths)
src/Text/Pandoc/Writers/RTF.hs+16−10
@@ -16,7 +16,7 @@ module Text.Pandoc.Writers.RTF ( writeRTF
16import Control.Monad.Except (catchError, throwError)
16import Control.Monad.Except (catchError, throwError)
17import Control.Monad
17import Control.Monad
18import qualified Data.ByteString as B
18import qualified Data.ByteString as B
19−import Data.Char (chr, isDigit, ord)
19+import Data.Char (chr, isDigit, ord, isAlphaNum)
20import qualified Data.Map as M
20import qualified Data.Map as M
21import Data.Text (Text)
21import Data.Text (Text)
22import qualified Data.Text as T
22import qualified Data.Text as T
@@ -137,15 +137,21 @@ handleUnicode = T.concatMap $ \c ->
137
137
138-- | Escape special characters.
138-- | Escape special characters.
139escapeSpecial :: Text -> Text
139escapeSpecial :: Text -> Text
140−escapeSpecial = escapeStringUsing $
140+escapeSpecial t
141+ | T.all isAlphaNum t = t
142+ | otherwise = T.concatMap escChar t
143+ where
141− [ ('\t',"\\tab ")
144+ escChar '\t' = "\\tab "
142− , ('\8216',"\\u8216'")
145+ escChar '\8216' = "\\u8216'"
143− , ('\8217',"\\u8217'")
146+ escChar '\8217' = "\\u8217'"
144− , ('\8220',"\\u8220\"")
147+ escChar '\8220' = "\\u8220\""
145− , ('\8221',"\\u8221\"")
148+ escChar '\8221' = "\\u8221\""
146− , ('\8211',"\\u8211-")
149+ escChar '\8211' = "\\u8211-"
147− , ('\8212',"\\u8212-")
150+ escChar '\8212' = "\\u8212-"
148− ] <> backslashEscapes "{\\}"
151+ escChar '{' = "\\{"
152+ escChar '}' = "\\}"
153+ escChar '\\' = "\\\\"
154+ escChar c = T.singleton c
149
155
150-- | Escape strings as needed for rich text format.
156-- | Escape strings as needed for rich text format.
151stringToRTF :: Text -> Text
157stringToRTF :: Text -> Text
src/Text/Pandoc/Writers/Shared.hs+18−3
@@ -20,6 +20,7 @@ module Text.Pandoc.Writers.Shared (
20 , setField
20 , setField
21 , resetField
21 , resetField
22 , defField
22 , defField
23+ , getLang
23 , tagWithAttrs
24 , tagWithAttrs
24 , isDisplayMath
25 , isDisplayMath
25 , fixDisplayMath
26 , fixDisplayMath
@@ -44,6 +45,7 @@ import Control.Monad (zipWithM)
44import Data.Aeson (ToJSON (..), encode)
45import Data.Aeson (ToJSON (..), encode)
45import Data.Char (chr, ord, isSpace)
46import Data.Char (chr, ord, isSpace)
46import Data.List (groupBy, intersperse, transpose, foldl')
47import Data.List (groupBy, intersperse, transpose, foldl')
48+import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
47import Data.Text.Conversions (FromText(..))
49import Data.Text.Conversions (FromText(..))
48import qualified Data.Map as M
50import qualified Data.Map as M
49import qualified Data.Text as T
51import qualified Data.Text as T
@@ -146,6 +148,19 @@ defField field val (Context m) =
146 where
148 where
147 f _newval oldval = oldval
149 f _newval oldval = oldval
148
150
151+-- | Get the contents of the `lang` metadata field or variable.
152+getLang :: WriterOptions -> Meta -> Maybe T.Text
153+getLang opts meta =
154+ case lookupContext "lang" (writerVariables opts) of
155+ Just s -> Just s
156+ _ ->
157+ case lookupMeta "lang" meta of
158+ Just (MetaBlocks [Para [Str s]]) -> Just s
159+ Just (MetaBlocks [Plain [Str s]]) -> Just s
160+ Just (MetaInlines [Str s]) -> Just s
161+ Just (MetaString s) -> Just s
162+ _ -> Nothing
163+
149-- | Produce an HTML tag with the given pandoc attributes.
164-- | Produce an HTML tag with the given pandoc attributes.
150tagWithAttrs :: HasChars a => T.Text -> Attr -> Doc a
165tagWithAttrs :: HasChars a => T.Text -> Attr -> Doc a
151tagWithAttrs tag (ident,classes,kvs) = hsep
166tagWithAttrs tag (ident,classes,kvs) = hsep
@@ -224,7 +239,7 @@ gridTable :: (Monad m, HasChars a)
224 -> m (Doc a)
239 -> m (Doc a)
225gridTable opts blocksToDoc headless aligns widths headers rows = do
240gridTable opts blocksToDoc headless aligns widths headers rows = do
226 -- the number of columns will be used in case of even widths
241 -- the number of columns will be used in case of even widths
227− let numcols = maximum (length aligns : length widths :
242+ let numcols = maximum (length aligns :| length widths :
228 map length (headers:rows))
243 map length (headers:rows))
229 let officialWidthsInChars widths' = map (
244 let officialWidthsInChars widths' = map (
230 (\x -> if x < 1 then 1 else x) .
245 (\x -> if x < 1 then 1 else x) .
@@ -253,8 +268,7 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do
253 let handleFullWidths widths' = do
268 let handleFullWidths widths' = do
254 rawHeaders' <- mapM (blocksToDoc opts) headers
269 rawHeaders' <- mapM (blocksToDoc opts) headers
255 rawRows' <- mapM (mapM (blocksToDoc opts)) rows
270 rawRows' <- mapM (mapM (blocksToDoc opts)) rows
256− let numChars [] = 0
257− numChars xs = maximum . map offset $ xs
271+ let numChars = maybe 0 maximum . nonEmpty . map offset
258 let minWidthsInChars =
272 let minWidthsInChars =
259 map numChars $ transpose (rawHeaders' : rawRows')
273 map numChars $ transpose (rawHeaders' : rawRows')
260 let widthsInChars' = zipWith max
274 let widthsInChars' = zipWith max
@@ -380,6 +394,7 @@ toSuperscript '2' = Just '\x00B2'
380toSuperscript '3' = Just '\x00B3'
394toSuperscript '3' = Just '\x00B3'
381toSuperscript '+' = Just '\x207A'
395toSuperscript '+' = Just '\x207A'
382toSuperscript '-' = Just '\x207B'
396toSuperscript '-' = Just '\x207B'
397+toSuperscript '\x2212' = Just '\x207B' -- unicode minus
383toSuperscript '=' = Just '\x207C'
398toSuperscript '=' = Just '\x207C'
384toSuperscript '(' = Just '\x207D'
399toSuperscript '(' = Just '\x207D'
385toSuperscript ')' = Just '\x207E'
400toSuperscript ')' = Just '\x207E'
src/Text/Pandoc/Writers/TEI.hs+4−3
@@ -146,16 +146,17 @@ blockToTEI opts (LineBlock lns) =
146 blockToTEI opts $ linesToPara lns
146 blockToTEI opts $ linesToPara lns
147blockToTEI opts (BlockQuote blocks) =
147blockToTEI opts (BlockQuote blocks) =
148 inTagsIndented "quote" <$> blocksToTEI opts blocks
148 inTagsIndented "quote" <$> blocksToTEI opts blocks
149−blockToTEI _ (CodeBlock (_,classes,_) str) =
149+blockToTEI opts (CodeBlock (_,classes,_) str) =
150 return $ literal ("<ab type='codeblock " <> lang <> "'>") <> cr <>
150 return $ literal ("<ab type='codeblock " <> lang <> "'>") <> cr <>
151 flush (literal (escapeStringForXML str) <> cr <> text "</ab>")
151 flush (literal (escapeStringForXML str) <> cr <> text "</ab>")
152 where lang = if null langs
152 where lang = if null langs
153 then ""
153 then ""
154 else escapeStringForXML (head langs)
154 else escapeStringForXML (head langs)
155+ syntaxMap = writerSyntaxMap opts
155− isLang l = T.toLower l `elem` map T.toLower languages
156+ isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap)
156 langsFrom s = if isLang s
157 langsFrom s = if isLang s
157 then [s]
158 then [s]
158− else languagesByExtension . T.toLower $ s
159+ else (languagesByExtension syntaxMap) . T.toLower $ s
159 langs = concatMap langsFrom classes
160 langs = concatMap langsFrom classes
160blockToTEI opts (BulletList lst) = do
161blockToTEI opts (BulletList lst) = do
161 let attribs = [("type", "unordered")]
162 let attribs = [("type", "unordered")]
src/Text/Pandoc/Writers/Texinfo.hs+22−15
@@ -14,8 +14,9 @@ Conversion of 'Pandoc' format into Texinfo.
14module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where
14module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where
15import Control.Monad.Except (throwError)
15import Control.Monad.Except (throwError)
16import Control.Monad.State.Strict
16import Control.Monad.State.Strict
17−import Data.Char (chr, ord)
17+import Data.Char (chr, ord, isAlphaNum)
18−import Data.List (maximumBy, transpose)
18+import Data.List (maximumBy, transpose, foldl')
19+import Data.List.NonEmpty (nonEmpty)
19import Data.Ord (comparing)
20import Data.Ord (comparing)
20import qualified Data.Set as Set
21import qualified Data.Set as Set
21import Data.Text (Text)
22import Data.Text (Text)
@@ -84,16 +85,18 @@ pandocToTexinfo options (Pandoc meta blocks) = do
84
85
85-- | Escape things as needed for Texinfo.
86-- | Escape things as needed for Texinfo.
86stringToTexinfo :: Text -> Text
87stringToTexinfo :: Text -> Text
87−stringToTexinfo = escapeStringUsing texinfoEscapes
88+stringToTexinfo t
88− where texinfoEscapes = [ ('{', "@{")
89+ | T.all isAlphaNum t = t
90+ | otherwise = T.concatMap escChar t
91+ where escChar '{' = "@{"
89− , ('}', "@}")
92+ escChar '}' = "@}"
90− , ('@', "@@")
93+ escChar '@' = "@@"
91− , ('\160', "@ ")
94+ escChar '\160' = "@ "
92− , ('\x2014', "---")
95+ escChar '\x2014' = "---"
93− , ('\x2013', "--")
96+ escChar '\x2013' = "--"
94− , ('\x2026', "@dots{}")
97+ escChar '\x2026' = "@dots{}"
95− , ('\x2019', "'")
98+ escChar '\x2019' = "'"
96− ]
99+ escChar c = T.singleton c
97
100
98escapeCommas :: PandocMonad m => TI m (Doc Text) -> TI m (Doc Text)
101escapeCommas :: PandocMonad m => TI m (Doc Text) -> TI m (Doc Text)
99escapeCommas parser = do
102escapeCommas parser = do
@@ -238,9 +241,13 @@ blockToTexinfo (Table _ blkCapt specs thead tbody tfoot) = do
238 colDescriptors <-
241 colDescriptors <-
239 if all (== 0) widths
242 if all (== 0) widths
240 then do -- use longest entry instead of column widths
243 then do -- use longest entry instead of column widths
241− cols <- mapM (mapM (liftM (T.unpack . render Nothing . hcat) . mapM blockToTexinfo)) $
244+ cols <- mapM (mapM (fmap (T.unpack . render Nothing . hcat) .
245+ mapM blockToTexinfo)) $
242 transpose $ heads : rows
246 transpose $ heads : rows
247+ return $ concatMap
248+ ((\x -> "{"++x++"} ") .
243− return $ concatMap ((\x -> "{"++x++"} ") . maximumBy (comparing length)) cols
249+ maybe "" (maximumBy (comparing length)) . nonEmpty)
250+ cols
244 else return $ "@columnfractions " ++ concatMap (printf "%.2f ") widths
251 else return $ "@columnfractions " ++ concatMap (printf "%.2f ") widths
245 let tableBody = text ("@multitable " ++ colDescriptors) $$
252 let tableBody = text ("@multitable " ++ colDescriptors) $$
246 headers $$
253 headers $$
@@ -271,7 +278,7 @@ tableAnyRowToTexinfo :: PandocMonad m
271 -> [[Block]]
278 -> [[Block]]
272 -> TI m (Doc Text)
279 -> TI m (Doc Text)
273tableAnyRowToTexinfo itemtype aligns cols =
280tableAnyRowToTexinfo itemtype aligns cols =
274− (literal itemtype $$) . foldl (\row item -> row $$
281+ (literal itemtype $$) . foldl' (\row item -> row $$
275 (if isEmpty row then empty else text " @tab ") <> item) empty <$> zipWithM alignedBlock aligns cols
282 (if isEmpty row then empty else text " @tab ") <> item) empty <$> zipWithM alignedBlock aligns cols
276
283
277alignedBlock :: PandocMonad m
284alignedBlock :: PandocMonad m
src/Text/Pandoc/Writers/ZimWiki.hs+4−2
@@ -20,6 +20,7 @@ import Control.Monad (zipWithM)
20import Control.Monad.State.Strict (StateT, evalStateT, gets, modify)
20import Control.Monad.State.Strict (StateT, evalStateT, gets, modify)
21import Data.Default (Default (..))
21import Data.Default (Default (..))
22import Data.List (transpose)
22import Data.List (transpose)
23+import Data.List.NonEmpty (nonEmpty)
23import qualified Data.Map as Map
24import qualified Data.Map as Map
24import Text.DocLayout (render, literal)
25import Text.DocLayout (render, literal)
25import Data.Maybe (fromMaybe)
26import Data.Maybe (fromMaybe)
@@ -115,7 +116,7 @@ blockToZimWiki opts b@(RawBlock f str)
115blockToZimWiki _ HorizontalRule = return "\n----\n"
116blockToZimWiki _ HorizontalRule = return "\n----\n"
116
117
117blockToZimWiki opts (Header level _ inlines) = do
118blockToZimWiki opts (Header level _ inlines) = do
118− contents <- inlineListToZimWiki opts $ removeFormatting inlines -- emphasis, links etc. not allowed in headers
119+ contents <- inlineListToZimWiki opts inlines
119 let eqs = T.replicate ( 7 - level ) "="
120 let eqs = T.replicate ( 7 - level ) "="
120 return $ eqs <> " " <> contents <> " " <> eqs <> "\n"
121 return $ eqs <> " " <> contents <> " " <> eqs <> "\n"
121
122
@@ -143,7 +144,8 @@ blockToZimWiki opts (Table _ blkCapt specs thead tbody tfoot) = do
143 then zipWithM (tableItemToZimWiki opts) aligns (head rows)
144 then zipWithM (tableItemToZimWiki opts) aligns (head rows)
144 else mapM (inlineListToZimWiki opts . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers
145 else mapM (inlineListToZimWiki opts . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers
145 rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows
146 rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows
146− let widths = map (maximum . map T.length) $ transpose (headers':rows')
147+ let widths = map (maybe 0 maximum . nonEmpty . map T.length) $
148+ transpose (headers':rows')
147 let padTo (width, al) s =
149 let padTo (width, al) s =
148 case width - T.length s of
150 case width - T.length s of
149 x | x > 0 ->
151 x | x > 0 ->
src/Text/Pandoc/XML.hs+28−2
@@ -13,6 +13,7 @@ Functions for escaping and formatting XML.
13-}
13-}
14module Text.Pandoc.XML ( escapeCharForXML,
14module Text.Pandoc.XML ( escapeCharForXML,
15 escapeStringForXML,
15 escapeStringForXML,
16+ escapeNCName,
16 inTags,
17 inTags,
17 selfClosingTag,
18 selfClosingTag,
18 inTagsSimple,
19 inTagsSimple,
@@ -24,7 +25,7 @@ module Text.Pandoc.XML ( escapeCharForXML,
24 html5Attributes,
25 html5Attributes,
25 rdfaAttributes ) where
26 rdfaAttributes ) where
26
27
27−import Data.Char (isAscii, isSpace, ord)
28+import Data.Char (isAscii, isSpace, ord, isLetter, isDigit)
28import Data.Text (Text)
29import Data.Text (Text)
29import qualified Data.Text as T
30import qualified Data.Text as T
30import Text.HTML.TagSoup.Entity (lookupEntity, htmlEntities)
31import Text.HTML.TagSoup.Entity (lookupEntity, htmlEntities)
@@ -119,8 +120,33 @@ html5EntityMap = foldr go mempty htmlEntities
119 where ent' = T.takeWhile (/=';') (T.pack ent)
120 where ent' = T.takeWhile (/=';') (T.pack ent)
120 _ -> entmap
121 _ -> entmap
121
122
123+-- | Converts a string into an NCName, i.e., an XML name without colons.
124+-- Disallowed characters are escaped using @ux%x@, where @%x@ is the
125+-- hexadecimal unicode identifier of the escaped character.
126+escapeNCName :: Text -> Text
127+escapeNCName t = case T.uncons t of
128+ Nothing -> T.empty
129+ Just (c, cs) -> escapeStartChar c <> T.concatMap escapeNCNameChar cs
130+ where
131+ escapeStartChar :: Char -> Text
132+ escapeStartChar c = if isLetter c || c == '_'
133+ then T.singleton c
134+ else escapeChar c
122
135
136+ escapeNCNameChar :: Char -> Text
137+ escapeNCNameChar c = if isNCNameChar c
138+ then T.singleton c
139+ else escapeChar c
140+
141+ isNCNameChar :: Char -> Bool
142+ isNCNameChar c = isLetter c || c `elem` ("_-.·" :: String) || isDigit c
143+ || '\x0300' <= c && c <= '\x036f'
144+ || '\x203f' <= c && c <= '\x2040'
145+
146+ escapeChar :: Char -> Text
147+ escapeChar = T.pack . printf "U%04X" . ord
148+
123−-- Unescapes XML entities
149+-- | Unescapes XML entities
124fromEntities :: Text -> Text
150fromEntities :: Text -> Text
125fromEntities t
151fromEntities t
126 = let (x, y) = T.break (== '&') t
152 = let (x, y) = T.break (== '&') t
src/Text/Pandoc/XML/Light/Proc.hs−1
@@ -1,5 +1,4 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE OverloadedStrings #-}
3{- |
2{- |
4 Module : Text.Pandoc.XML.Light.Proc
3 Module : Text.Pandoc.XML.Light.Proc
5 Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane
4 Copyright : Copyright (C) 2007 Galois, Inc., 2021 John MacFarlane
src/Text/Pandoc/XML/Light/Types.hs+4−4
@@ -53,7 +53,7 @@ type Line = Integer
53data Content = Elem Element
53data Content = Elem Element
54 | Text CData
54 | Text CData
55 | CRef Text
55 | CRef Text
56− deriving (Show, Typeable, Data)
56+ deriving (Show, Typeable, Data, Ord, Eq)
57
57
58-- | XML elements
58-- | XML elements
59data Element = Element {
59data Element = Element {
@@ -61,7 +61,7 @@ data Element = Element {
61 elAttribs :: [Attr],
61 elAttribs :: [Attr],
62 elContent :: [Content],
62 elContent :: [Content],
63 elLine :: Maybe Line
63 elLine :: Maybe Line
64− } deriving (Show, Typeable, Data)
64+ } deriving (Show, Typeable, Data, Ord, Eq)
65
65
66-- | XML attributes
66-- | XML attributes
67data Attr = Attr {
67data Attr = Attr {
@@ -74,13 +74,13 @@ data CData = CData {
74 cdVerbatim :: CDataKind,
74 cdVerbatim :: CDataKind,
75 cdData :: Text,
75 cdData :: Text,
76 cdLine :: Maybe Line
76 cdLine :: Maybe Line
77− } deriving (Show, Typeable, Data)
77+ } deriving (Show, Typeable, Data, Ord, Eq)
78
78
79data CDataKind
79data CDataKind
80 = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc.
80 = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc.
81 | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in <![CDATA[..
81 | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in <![CDATA[..
82 | CDataRaw -- ^ As-is character data; pretty printer passes it along without any escaping or CDATA wrap-up.
82 | CDataRaw -- ^ As-is character data; pretty printer passes it along without any escaping or CDATA wrap-up.
83− deriving ( Eq, Show, Typeable, Data )
83+ deriving ( Eq, Ord, Show, Typeable, Data )
84
84
85-- | XML qualified names
85-- | XML qualified names
86data QName = QName {
86data QName = QName {
stack.yaml+11−15
@@ -2,25 +2,21 @@ flags:
2 pandoc:
2 pandoc:
3 trypandoc: false
3 trypandoc: false
4 embed_data_files: true
4 embed_data_files: true
5+ QuickCheck:
6+ old-random: false
5packages:
7packages:
6- '.'
8- '.'
7extra-deps:
9extra-deps:
8−- hslua-1.3.0
9−- hslua-module-path-0.1.0
10−- jira-wiki-markup-1.3.3
11−- commonmark-0.1.1.4
10+- commonmark-0.2.1
12−- commonmark-extensions-0.2.0.4
13−- commonmark-pandoc-0.2.0.1
11+- commonmark-pandoc-0.2.1.1
14−- tasty-bench-0.2.1
15−- citeproc-0.3.0.7
16−- skylighting-0.10.4
17−- skylighting-core-0.10.4
12+- skylighting-core-0.10.5.2
18−- xml-conduit-1.9.1.0
13+- skylighting-0.10.5.2
19−#- citeproc:
14+- git: https://github.com/jgm/doctemplates
15+ commit: 428c26d5303cf7a2b1051fe1ffd9aafe9ba71c81
20−# git: https://github.com/jgm/citeproc.git
16+- git: https://github.com/jgm/citeproc
21−# commit: feb3b7580c6738eec3b23921f7c1739cfba611aa
17+ commit: 4ce0501cd6f9c86eee087afcf878c256e49b9615
22ghc-options:
18ghc-options:
23 "$locals": -fhide-source-paths -Wno-missing-home-modules
19 "$locals": -fhide-source-paths -Wno-missing-home-modules
24−resolver: lts-17.2
20+resolver: lts-18.0
25nix:
21nix:
26 packages: [zlib]
22 packages: [zlib]
test/Tests/Command.hs+7−14
@@ -1,4 +1,3 @@
1−{-# LANGUAGE TupleSections #-}
2{- |
1{- |
3 Module : Tests.Command
2 Module : Tests.Command
4 Copyright : © 2006-2021 John MacFarlane
3 Copyright : © 2006-2021 John MacFarlane
@@ -14,14 +13,13 @@ module Tests.Command (runTest, tests)
14where
13where
15
14
16import Data.Algorithm.Diff
15import Data.Algorithm.Diff
17−import System.Environment.Executable (getExecutablePath)
16+import System.Environment (getExecutablePath)
18import qualified Data.ByteString as BS
17import qualified Data.ByteString as BS
19import qualified Data.Text as T
18import qualified Data.Text as T
20import Data.List (isSuffixOf)
19import Data.List (isSuffixOf)
21import System.Directory
20import System.Directory
22−import qualified System.Environment as Env
23import System.Exit
21import System.Exit
24−import System.FilePath (takeDirectory, (</>))
22+import System.FilePath ((</>))
25import System.IO (hPutStr, stderr)
23import System.IO (hPutStr, stderr)
26import System.IO.Unsafe (unsafePerformIO)
24import System.IO.Unsafe (unsafePerformIO)
27import System.Process
25import System.Process
@@ -38,15 +36,7 @@ execTest :: String -- ^ Path to test executable
38 -> String -- ^ Input text
36 -> String -- ^ Input text
39 -> IO (ExitCode, String) -- ^ Exit code and actual output
37 -> IO (ExitCode, String) -- ^ Exit code and actual output
40execTest testExePath cmd inp = do
38execTest testExePath cmd inp = do
41− mldpath <- Env.lookupEnv "LD_LIBRARY_PATH"
42− mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH"
43− let env' = ("PATH",takeDirectory testExePath) :
39+ env' <- setupEnvironment testExePath
44− ("TMP",".") :
45− ("LANG","en_US.UTF-8") :
46− ("HOME", "./") :
47− ("pandoc_datadir", "..") :
48− maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++
49− maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath
50 let pr = (shell (pandocToEmulate True cmd)){ env = Just env' }
40 let pr = (shell (pandocToEmulate True cmd)){ env = Just env' }
51 (ec, out', err') <- readCreateProcessWithExitCode pr inp
41 (ec, out', err') <- readCreateProcessWithExitCode pr inp
52 -- filter \r so the tests will work on Windows machines
42 -- filter \r so the tests will work on Windows machines
@@ -140,4 +130,7 @@ extractCommandTest testExePath fp = unsafePerformIO $ do
140 def{ readerExtensions = pandocExtensions } contents)
130 def{ readerExtensions = pandocExtensions } contents)
141 let codeblocks = map extractCode $ filter isCodeBlock blocks
131 let codeblocks = map extractCode $ filter isCodeBlock blocks
142 let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks
132 let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks
143− return $ testGroup fp cases
133+ return $ testGroup fp
134+ $ if null cases
135+ then [testCase "!!" $ assertFailure "No command tests defined"]
136+ else cases
test/Tests/Helpers.hs+23
@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TupleSections #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
3{- |
4{- |
4 Module : Tests.Helpers
5 Module : Tests.Helpers
@@ -13,6 +14,7 @@ Utility functions for the test suite.
13-}
14-}
14module Tests.Helpers ( test
15module Tests.Helpers ( test
15 , TestResult(..)
16 , TestResult(..)
17+ , setupEnvironment
16 , showDiff
18 , showDiff
17 , (=?>)
19 , (=?>)
18 , purely
20 , purely
@@ -25,6 +27,8 @@ import Data.Algorithm.Diff
25import qualified Data.Map as M
27import qualified Data.Map as M
26import Data.Text (Text, unpack)
28import Data.Text (Text, unpack)
27import System.Exit
29import System.Exit
30+import System.FilePath (takeDirectory)
31+import qualified System.Environment as Env
28import Test.Tasty
32import Test.Tasty
29import Test.Tasty.HUnit
33import Test.Tasty.HUnit
30import Text.Pandoc.Builder (Blocks, Inlines, doc, plain)
34import Text.Pandoc.Builder (Blocks, Inlines, doc, plain)
@@ -57,6 +61,25 @@ test fn name (input, expected) =
57 dashes "" = replicate 72 '-'
61 dashes "" = replicate 72 '-'
58 dashes x = replicate (72 - length x - 5) '-' ++ " " ++ x ++ " ---"
62 dashes x = replicate (72 - length x - 5) '-' ++ " " ++ x ++ " ---"
59
63
64+-- | Set up environment for pandoc command tests.
65+setupEnvironment :: FilePath -> IO [(String, String)]
66+setupEnvironment testExePath = do
67+ mldpath <- Env.lookupEnv "LD_LIBRARY_PATH"
68+ mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH"
69+ mpdd <- Env.lookupEnv "pandoc_datadir"
70+ -- Note that Cabal sets the pandoc_datadir environment variable
71+ -- to point to the source directory, since otherwise getDataFilename
72+ -- will look in the data directory into which pandoc will be installed
73+ -- (but has not yet been). So when we spawn a new process with
74+ -- pandoc, we need to make sure this environment variable is set.
75+ return $ ("PATH",takeDirectory testExePath) :
76+ ("TMP",".") :
77+ ("LANG","en_US.UTF-8") :
78+ ("HOME", "./") :
79+ maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++
80+ maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++
81+ maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath
82+
60data TestResult = TestPassed
83data TestResult = TestPassed
61 | TestError ExitCode
84 | TestError ExitCode
62 | TestFailed String FilePath [Diff String]
85 | TestFailed String FilePath [Diff String]
test/Tests/Old.hs+3−12
@@ -1,4 +1,3 @@
1−{-# LANGUAGE TupleSections #-}
2{- |
1{- |
3 Module : Tests.Old
2 Module : Tests.Old
4 Copyright : © 2006-2021 John MacFarlane
3 Copyright : © 2006-2021 John MacFarlane
@@ -15,8 +14,7 @@ module Tests.Old (tests) where
15import Data.Algorithm.Diff
14import Data.Algorithm.Diff
16import System.Exit
15import System.Exit
17import System.FilePath ((<.>), (</>))
16import System.FilePath ((<.>), (</>))
18−import qualified System.Environment as Env
19−import System.Environment.Executable (getExecutablePath)
17+import System.Environment (getExecutablePath)
20import Text.Pandoc.Process (pipeProcess)
18import Text.Pandoc.Process (pipeProcess)
21import Test.Tasty (TestTree, testGroup)
19import Test.Tasty (TestTree, testGroup)
22import Test.Tasty.Golden.Advanced (goldenTest)
20import Test.Tasty.Golden.Advanced (goldenTest)
@@ -319,14 +317,7 @@ testWithNormalize normalizer pandocPath testname opts inp norm =
319 (compareValues norm options) updateGolden
317 (compareValues norm options) updateGolden
320 where getExpected = normalizer <$> readFile' norm
318 where getExpected = normalizer <$> readFile' norm
321 getActual = do
319 getActual = do
322− mldpath <- Env.lookupEnv "LD_LIBRARY_PATH"
320+ env <- setupEnvironment pandocPath
323− mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH"
324− let env = ("TMP",".") :
325− ("LANG","en_US.UTF-8") :
326− ("HOME", "./") :
327− maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++
328− maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath
329−
330 (ec, out) <- pipeProcess (Just env) pandocPath
321 (ec, out) <- pipeProcess (Just env) pandocPath
331 ("--emulate":options) mempty
322 ("--emulate":options) mempty
332 if ec == ExitSuccess
323 if ec == ExitSuccess
@@ -335,7 +326,7 @@ testWithNormalize normalizer pandocPath testname opts inp norm =
335 -- filter \r so the tests will work on Windows machines
326 -- filter \r so the tests will work on Windows machines
336 else fail $ "Pandoc failed with error code " ++ show ec
327 else fail $ "Pandoc failed with error code " ++ show ec
337 updateGolden = UTF8.writeFile norm . T.pack
328 updateGolden = UTF8.writeFile norm . T.pack
338− options = ["--data-dir=../data","--quiet"] ++ [inp] ++ opts
329+ options = ["--quiet"] ++ [inp] ++ opts
339
330
340compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String)
331compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String)
341compareValues norm options expected actual = do
332compareValues norm options expected actual = do
test/Tests/Readers/Docx.hs+25−5
@@ -24,7 +24,7 @@ import Test.Tasty.HUnit
24import Tests.Helpers
24import Tests.Helpers
25import Text.Pandoc
25import Text.Pandoc
26import qualified Text.Pandoc.Class as P
26import qualified Text.Pandoc.Class as P
27−import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory)
27+import qualified Text.Pandoc.MediaBag as MB
28import Text.Pandoc.UTF8 as UTF8
28import Text.Pandoc.UTF8 as UTF8
29
29
30-- We define a wrapper around pandoc that doesn't normalize in the
30-- We define a wrapper around pandoc that doesn't normalize in the
@@ -91,11 +91,11 @@ getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString)
91getMedia archivePath mediaPath = fmap fromEntry . findEntryByPath
91getMedia archivePath mediaPath = fmap fromEntry . findEntryByPath
92 ("word/" ++ mediaPath) . toArchive <$> B.readFile archivePath
92 ("word/" ++ mediaPath) . toArchive <$> B.readFile archivePath
93
93
94−compareMediaPathIO :: FilePath -> MediaBag -> FilePath -> IO Bool
94+compareMediaPathIO :: FilePath -> MB.MediaBag -> FilePath -> IO Bool
95compareMediaPathIO mediaPath mediaBag docxPath = do
95compareMediaPathIO mediaPath mediaBag docxPath = do
96 docxMedia <- getMedia docxPath mediaPath
96 docxMedia <- getMedia docxPath mediaPath
97− let mbBS = case lookupMedia mediaPath mediaBag of
97+ let mbBS = case MB.lookupMedia mediaPath mediaBag of
98− Just (_, bs) -> bs
98+ Just item -> MB.mediaContents item
99 Nothing -> error ("couldn't find " ++
99 Nothing -> error ("couldn't find " ++
100 mediaPath ++
100 mediaPath ++
101 " in media bag")
101 " in media bag")
@@ -110,7 +110,7 @@ compareMediaBagIO docxFile = do
110 mb <- runIOorExplode $ readDocx defopts df >> P.getMediaBag
110 mb <- runIOorExplode $ readDocx defopts df >> P.getMediaBag
111 bools <- mapM
111 bools <- mapM
112 (\(fp, _, _) -> compareMediaPathIO fp mb docxFile)
112 (\(fp, _, _) -> compareMediaPathIO fp mb docxFile)
113− (mediaDirectory mb)
113+ (MB.mediaDirectory mb)
114 return $ and bools
114 return $ and bools
115
115
116testMediaBagIO :: String -> FilePath -> IO TestTree
116testMediaBagIO :: String -> FilePath -> IO TestTree
@@ -155,6 +155,10 @@ tests = [ testGroup "document"
155 "VML image"
155 "VML image"
156 "docx/image_vml.docx"
156 "docx/image_vml.docx"
157 "docx/image_vml.native"
157 "docx/image_vml.native"
158+ , testCompare
159+ "VML image as object"
160+ "docx/image_vml_as_object.docx"
161+ "docx/image_vml_as_object.native"
158 , testCompare
162 , testCompare
159 "inline image in links"
163 "inline image in links"
160 "docx/inline_images.docx"
164 "docx/inline_images.docx"
@@ -313,14 +317,30 @@ tests = [ testGroup "document"
313 "tables with lists in cells"
317 "tables with lists in cells"
314 "docx/table_with_list_cell.docx"
318 "docx/table_with_list_cell.docx"
315 "docx/table_with_list_cell.native"
319 "docx/table_with_list_cell.native"
320+ , testCompare
321+ "a table with a header which contains rowspans greater than 1"
322+ "docx/table_header_rowspan.docx"
323+ "docx/table_header_rowspan.native"
316 , testCompare
324 , testCompare
317 "tables with one row"
325 "tables with one row"
318 "docx/table_one_row.docx"
326 "docx/table_one_row.docx"
319 "docx/table_one_row.native"
327 "docx/table_one_row.native"
328+ , testCompare
329+ "tables with just one row, which is a header"
330+ "docx/table_one_header_row.docx"
331+ "docx/table_one_header_row.native"
320 , testCompare
332 , testCompare
321 "tables with variable width"
333 "tables with variable width"
322 "docx/table_variable_width.docx"
334 "docx/table_variable_width.docx"
323 "docx/table_variable_width.native"
335 "docx/table_variable_width.native"
336+ , testCompare
337+ "tables with captions which contain a Table field"
338+ "docx/table_captions_with_field.docx"
339+ "docx/table_captions_with_field.native"
340+ , testCompare
341+ "tables with captions which don't contain a Table field"
342+ "docx/table_captions_no_field.docx"
343+ "docx/table_captions_no_field.native"
324 , testCompare
344 , testCompare
325 "code block"
345 "code block"
326 "docx/codeblock.docx"
346 "docx/codeblock.docx"
test/Tests/Readers/EPUB.hs+1−1
@@ -47,7 +47,7 @@ featuresBag = [("img/check.gif","image/gif",1340)
47
47
48-- with additional meta tag for cover in EPUB2 format
48-- with additional meta tag for cover in EPUB2 format
49epub3CoverBag :: [(String, String, Int)]
49epub3CoverBag :: [(String, String, Int)]
50−epub3CoverBag = [("wasteland-cover.jpg","image/jpeg",103477)]
50+epub3CoverBag = [("wasteland-cover.jpg","image/jpeg", 16586)]
51
51
52epub3NoCoverBag :: [(String, String, Int)]
52epub3NoCoverBag :: [(String, String, Int)]
53epub3NoCoverBag = [("img/check.gif","image/gif",1340)
53epub3NoCoverBag = [("img/check.gif","image/gif",1340)
test/Tests/Readers/HTML.hs+20−5
@@ -99,11 +99,26 @@ tests = [ testGroup "base tag"
99 plain (codeWith ("",["sample"],[]) "Answer is 42")
99 plain (codeWith ("",["sample"],[]) "Answer is 42")
100 ]
100 ]
101 , testGroup "var"
101 , testGroup "var"
102− [
103− test html "inline var block" $
102+ [ test html "inline var block" $
104− "<var>result</var>" =?>
103+ "<var>result</var>" =?>
105− plain (codeWith ("",["variable"],[]) "result")
104+ plain (codeWith ("",["variable"],[]) "result")
105+ ]
106+ , testGroup "header"
107+ [ test htmlNativeDivs "<header> is parsed as a div" $
108+ "<header id=\"title\">Title</header>" =?>
109+ divWith ("title", mempty, mempty) (plain "Title")
110+ ]
111+ , testGroup "code block"
112+ [ test html "attributes in pre > code element" $
113+ "<pre><code id=\"a\" class=\"python\">\nprint('hi')\n</code></pre>"
114+ =?>
115+ codeBlockWith ("a", ["python"], []) "print('hi')"
116+
117+ , test html "attributes in pre take precendence" $
118+ "<pre id=\"c\"><code id=\"d\">\nprint('hi mom!')\n</code></pre>"
119+ =?>
120+ codeBlockWith ("c", [], []) "print('hi mom!')"
106− ]
121+ ]
107 , askOption $ \(QuickCheckTests numtests) ->
122 , askOption $ \(QuickCheckTests numtests) ->
108 testProperty "Round trip" $
123 testProperty "Round trip" $
109 withMaxSuccess (if QuickCheckTests numtests == defaultValue
124 withMaxSuccess (if QuickCheckTests numtests == defaultValue
test/Tests/Readers/Jira.hs+14
@@ -96,6 +96,12 @@ tests =
96 simpleTable [para "Name"] [[para "Test"]]
96 simpleTable [para "Name"] [[para "Test"]]
97 ]
97 ]
98
98
99+ , testGroup "panel"
100+ [ "simple panel" =:
101+ "{panel}\nInterviewer: Jane Doe{panel}\n" =?>
102+ divWith ("", ["panel"], []) (para "Interviewer: Jane Doe")
103+ ]
104+
99 , testGroup "inlines"
105 , testGroup "inlines"
100 [ "emphasis" =:
106 [ "emphasis" =:
101 "*quid pro quo*" =?>
107 "*quid pro quo*" =?>
@@ -161,6 +167,14 @@ tests =
161 , "user with description" =:
167 , "user with description" =:
162 "[John Doe|~johndoe]" =?>
168 "[John Doe|~johndoe]" =?>
163 para (linkWith ("", ["user-account"], []) "~johndoe" "" "John Doe")
169 para (linkWith ("", ["user-account"], []) "~johndoe" "" "John Doe")
170+
171+ , "'smart' link" =:
172+ "[x|http://example.com|smart-link]" =?>
173+ para (linkWith ("", ["smart-link"], []) "http://example.com" "" "x")
174+
175+ , "'smart' card" =:
176+ "[x|http://example.com|smart-card]" =?>
177+ para (linkWith ("", ["smart-card"], []) "http://example.com" "" "x")
164 ]
178 ]
165
179
166 , "image" =:
180 , "image" =:
test/Tests/Readers/Markdown.hs+3−3
@@ -358,7 +358,7 @@ tests = [ testGroup "inline code"
358 para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.")
358 para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.")
359 , test markdownSmart "unclosed double quote"
359 , test markdownSmart "unclosed double quote"
360 ("**this should \"be bold**"
360 ("**this should \"be bold**"
361− =?> para (strong "this should \"be bold"))
361+ =?> para (strong "this should \8220be bold"))
362 ]
362 ]
363 , testGroup "footnotes"
363 , testGroup "footnotes"
364 [ "indent followed by newline and flush-left text" =:
364 [ "indent followed by newline and flush-left text" =:
@@ -374,8 +374,8 @@ tests = [ testGroup "inline code"
374 , testGroup "lhs"
374 , testGroup "lhs"
375 [ test (purely $ readMarkdown def{ readerExtensions = enableExtension
375 [ test (purely $ readMarkdown def{ readerExtensions = enableExtension
376 Ext_literate_haskell pandocExtensions })
376 Ext_literate_haskell pandocExtensions })
377− "inverse bird tracks and html" $
377+ "inverse bird tracks and html"
378− "> a\n\n< b\n\n<div>\n"
378+ $ ("> a\n\n< b\n\n<div>\n" :: Text)
379 =?> codeBlockWith ("",["haskell","literate"],[]) "a"
379 =?> codeBlockWith ("",["haskell","literate"],[]) "a"
380 <>
380 <>
381 codeBlockWith ("",["haskell"],[]) "b"
381 codeBlockWith ("",["haskell"],[]) "b"
test/Tests/Readers/Odt.hs+1
@@ -176,6 +176,7 @@ namesOfTestsComparingToNative = [ "blockquote"
176 , "referenceToText"
176 , "referenceToText"
177 , "simpleTable"
177 , "simpleTable"
178 , "simpleTableWithCaption"
178 , "simpleTableWithCaption"
179+ , "tab"
179-- , "table"
180-- , "table"
180 , "textMixedStyles"
181 , "textMixedStyles"
181 , "tableWithContents"
182 , "tableWithContents"
test/Tests/Writers/ConTeXt.hs+122−113

This diff is too big to show here.

test/Tests/Writers/Docbook.hs+43−6

This diff is too big to show here.

test/Tests/Writers/Docx.hs+5

This diff is too big to show here.

test/Tests/Writers/HTML.hs+57−52

This diff is too big to show here.

test/Tests/Writers/JATS.hs+135−91

This diff is too big to show here.

test/Tests/Writers/Jira.hs+52

This diff is too big to show here.

test/Tests/Writers/OOXML.hs+3−1

This diff is too big to show here.

test/command/1279.md+1−1

This diff is too big to show here.

test/command/2103.mdnew file+8

This diff is too big to show here.

test/command/2874.md+2−2

This diff is too big to show here.

test/command/3752.mdnew file+60

This diff is too big to show here.

test/command/3792.md+1−1

This diff is too big to show here.

test/command/4063.mdnew file+29

This diff is too big to show here.

test/command/4164.md+1−1

This diff is too big to show here.

test/command/4442.md+1−1

This diff is too big to show here.

test/command/4465.mdnew file+15

This diff is too big to show here.

test/command/5304.md+1−1

This diff is too big to show here.

test/command/5367.md+6−2

This diff is too big to show here.

test/command/5519.md+1−1

This diff is too big to show here.

test/command/6026.mdnew file+19

This diff is too big to show here.

test/command/6541.mdnew file+27

This diff is too big to show here.

test/command/6620.md+3

This diff is too big to show here.

test/command/6658.mdnew file+75

This diff is too big to show here.

test/command/6709.mdnew file+11

This diff is too big to show here.

test/command/6890.md+2−2

This diff is too big to show here.

test/command/6925.md+2−2

This diff is too big to show here.

test/command/6958.mdnew file+10

This diff is too big to show here.

test/command/7041.mdnew file+23

This diff is too big to show here.

test/command/7099.md+1−1

This diff is too big to show here.

test/command/7129.mdnew file+27

This diff is too big to show here.

test/command/7132.mdnew file+10

This diff is too big to show here.

test/command/7134.mdnew file+16

This diff is too big to show here.

test/command/7145.mdnew file+12

This diff is too big to show here.

test/command/7155.mdnew file+15

This diff is too big to show here.

test/command/7173.mdnew file+137

This diff is too big to show here.

test/command/7208.mdnew file+6

This diff is too big to show here.

test/command/7214.mdnew file+28

This diff is too big to show here.

test/command/7216.mdnew file+19

This diff is too big to show here.

test/command/7266.mdnew file+10

This diff is too big to show here.

test/command/7272.mdnew file+27

This diff is too big to show here.

test/command/7278.mdnew file+38

This diff is too big to show here.

test/command/7282.mdnew file+7

This diff is too big to show here.

test/command/7288.mdnew file+40

This diff is too big to show here.

test/command/7299.mdnew file+23

This diff is too big to show here.

test/command/7321.mdnew file+24

This diff is too big to show here.

test/command/7323.mdnew file+29

This diff is too big to show here.

test/command/7324.mdnew file+25

This diff is too big to show here.

test/command/7339.mdnew file+11

This diff is too big to show here.

test/command/7340.mdnew file+6

This diff is too big to show here.

test/command/7376.mdnew file+16

This diff is too big to show here.

test/command/7394.mdnew file+85

This diff is too big to show here.

test/command/7397.mdnew file+14

This diff is too big to show here.

test/command/7400.mdnew file+9

This diff is too big to show here.

test/command/7416.mdnew file+19

This diff is too big to show here.

test/command/averroes.bibnew file+95

This diff is too big to show here.

test/command/biblatex-basic.md+5−3

This diff is too big to show here.

test/command/biblatex-examples.bibdeleted−1674

This diff is too big to show here.

test/command/biblatex-inproceedings.md+1−1

This diff is too big to show here.

test/command/biblatex-jaffe.md+2−2

This diff is too big to show here.

test/command/biblatex-moraux.md+1−1

This diff is too big to show here.

test/command/biblatex-nussbaum.mddeleted−63

This diff is too big to show here.

test/command/biblatex-quotes.md+1−1

This diff is too big to show here.

test/command/biblatex-sigfridsson.md+1−1

This diff is too big to show here.

test/command/biblatex-spiegelberg.md+2−2

This diff is too big to show here.

test/command/biblatex-test-case-conversion.md+2−2

This diff is too big to show here.

test/command/biblatex-textnormal.md+1−1

This diff is too big to show here.

test/command/chap1/spider.pngnew file

Binary file not shown.

test/command/chap1/text.mdnew file+17

This diff is too big to show here.

test/command/chap2/spider.pngnew file

Binary file not shown.

test/command/chap2/text.mdnew file+5

This diff is too big to show here.

test/command/defaults-inheritance-2.md+1−1

This diff is too big to show here.

test/command/duplicate_attributes.mdnew file+7

This diff is too big to show here.

test/command/mmd-metadata.mdnew file+20

This diff is too big to show here.

test/command/newif.mdnew file+55

This diff is too big to show here.

test/command/pandoc-citeproc-119.md+1−1

This diff is too big to show here.

test/command/pandoc-citeproc-320a.md+3−3

This diff is too big to show here.

test/command/pandoc-citeproc-322.md+1−1

This diff is too big to show here.

test/command/pandoc-citeproc-356.md+2−7

This diff is too big to show here.

test/command/pandoc-citeproc-65.md+1−1

This diff is too big to show here.

test/command/pandoc-citeproc-70.md+2−2

This diff is too big to show here.

test/command/pandoc-citeproc-chicago-author-date.md+7−7

This diff is too big to show here.

test/command/pandoc-citeproc-chicago-fullnote-bibliography.md+6−7

This diff is too big to show here.

test/command/svg.md+1−1

This diff is too big to show here.

test/command/toc.md+2−2

This diff is too big to show here.

test/command/unicode-collation.mdnew file+130

This diff is too big to show here.

test/docbook-reader.docbook+3−1

This diff is too big to show here.

test/docx/0_level_headers.native+2−2

This diff is too big to show here.

test/docx/golden/block_quotes.docx

Binary file not shown.

test/docx/golden/codeblock.docx

Binary file not shown.

test/docx/golden/comments.docx

Binary file not shown.

test/docx/golden/custom_style_no_reference.docx

Binary file not shown.

test/docx/golden/custom_style_preserve.docx

Binary file not shown.

test/docx/golden/custom_style_reference.docx

Binary file not shown.

test/docx/golden/definition_list.docx

Binary file not shown.

test/docx/golden/document-properties-short-desc.docx

Binary file not shown.

test/docx/golden/document-properties.docx

Binary file not shown.

test/docx/golden/headers.docx

Binary file not shown.

test/docx/golden/image.docx

Binary file not shown.

test/docx/golden/inline_code.docx

Binary file not shown.

test/docx/golden/inline_formatting.docx

Binary file not shown.

test/docx/golden/inline_images.docx

Binary file not shown.

test/docx/golden/link_in_notes.docx

Binary file not shown.

test/docx/golden/links.docx

Binary file not shown.

test/docx/golden/lists.docx

Binary file not shown.

test/docx/golden/lists_continuing.docx

Binary file not shown.

test/docx/golden/lists_multiple_initial.docx

Binary file not shown.

test/docx/golden/lists_restarting.docx

Binary file not shown.

test/docx/golden/nested_anchors_in_header.docx

Binary file not shown.

test/docx/golden/notes.docx

Binary file not shown.

test/docx/golden/raw-blocks.docx

Binary file not shown.

test/docx/golden/raw-bookmarks.docx

Binary file not shown.

test/docx/golden/table_one_row.docx

Binary file not shown.

test/docx/golden/table_with_list_cell.docx

Binary file not shown.

test/docx/golden/tables-default-widths.docxnew file

Binary file not shown.

test/docx/golden/tables.docx

Binary file not shown.

test/docx/golden/track_changes_deletion.docx

Binary file not shown.

test/docx/golden/track_changes_insertion.docx

Binary file not shown.

test/docx/golden/track_changes_move.docx

Binary file not shown.

test/docx/golden/track_changes_scrubbed_metadata.docx

Binary file not shown.

test/docx/golden/unicode.docx

Binary file not shown.

test/docx/golden/verbatim_subsuper.docx

Binary file not shown.

test/docx/image_vml_as_object.docxnew file

Binary file not shown.

test/docx/image_vml_as_object.nativenew file+2

This diff is too big to show here.

test/docx/sdt_elements.native+9−10

This diff is too big to show here.

test/docx/table_captions_no_field.docxnew file

Binary file not shown.

test/docx/table_captions_no_field.nativenew file+34

This diff is too big to show here.

test/docx/table_captions_with_field.docxnew file

Binary file not shown.

test/docx/table_captions_with_field.nativenew file+54

This diff is too big to show here.

test/docx/table_header_rowspan.docxnew file

Binary file not shown.

test/docx/table_header_rowspan.nativenew file+189

This diff is too big to show here.

test/docx/table_one_header_row.docxnew file

Binary file not shown.

test/docx/table_one_header_row.nativenew file+18

This diff is too big to show here.

test/docx/table_one_row.docx

Binary file not shown.

test/docx/table_one_row.native+4−4

This diff is too big to show here.

test/docx/table_variable_width.native+12−17

This diff is too big to show here.

test/docx/table_with_list_cell.native+3−3

This diff is too big to show here.

test/docx/tables-default-widths.nativenew file+92

This diff is too big to show here.

test/docx/tables.native+9−9

This diff is too big to show here.

test/epub/features.native+2−2

This diff is too big to show here.

test/epub/wasteland.epub

Binary file not shown.

test/fb2/images.fb2+1−1

This diff is too big to show here.

test/fb2/test.jpg

Binary file not shown.

test/html-reader.native+2−1

This diff is too big to show here.

test/jira-reader.native+1−1

This diff is too big to show here.

test/lhs-test.html+2−1

This diff is too big to show here.

test/lhs-test.html+lhs+2−1

This diff is too big to show here.

test/odt/native/tab.nativenew file+1

This diff is too big to show here.

test/odt/odt/tab.odtnew file

Binary file not shown.

test/pipe-tables.native+2−10

This diff is too big to show here.

test/pptx/code-custom_templated.pptx

Binary file not shown.

test/pptx/code_templated.pptx

Binary file not shown.

test/pptx/document-properties-short-desc_templated.pptx

Binary file not shown.

test/pptx/document-properties_templated.pptx

Binary file not shown.

test/pptx/endnotes_templated.pptx

Binary file not shown.

test/pptx/endnotes_toc_templated.pptx

Binary file not shown.

test/pptx/images_templated.pptx

Binary file not shown.

test/pptx/inline_formatting_templated.pptx

Binary file not shown.

test/pptx/lists_templated.pptx

Binary file not shown.

test/pptx/raw_ooxml_templated.pptx

Binary file not shown.

test/pptx/reference_depth.pptx

Binary file not shown.

test/pptx/remove_empty_slides_templated.pptx

Binary file not shown.

test/pptx/slide_breaks_slide_level_1_templated.pptx

Binary file not shown.

test/pptx/slide_breaks_templated.pptx

Binary file not shown.

test/pptx/slide_breaks_toc_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_after_metadata_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_afterheader_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_afterseps_templated.pptx

Binary file not shown.

test/pptx/speaker_notes_templated.pptx

Binary file not shown.

test/pptx/start_numbering_at_templated.pptx

Binary file not shown.

test/pptx/tables_templated.pptx

Binary file not shown.

test/pptx/two_column_templated.pptx

Binary file not shown.

test/tables.latex+27−3

This diff is too big to show here.

test/tables.ms+30

This diff is too big to show here.

test/tables/nordics.latex+22−4

This diff is too big to show here.

test/tables/planets.latex+4−4

This diff is too big to show here.

test/tables/students.latex+10−2

This diff is too big to show here.

test/test-pandoc.hs+1−2

This diff is too big to show here.

test/writer.context+42−38

This diff is too big to show here.

test/writer.html4+2−1

This diff is too big to show here.

test/writer.html5+2−1

This diff is too big to show here.

test/writer.jats_archiving+37−47

This diff is too big to show here.

test/writer.jats_articleauthoring+41−49

This diff is too big to show here.

test/writer.jats_publishing+37−47

This diff is too big to show here.

test/writer.jira+23−35

This diff is too big to show here.

test/writer.ms+1−1

This diff is too big to show here.

test/writer.opml+1−1

This diff is too big to show here.

test/writer.org+7−7

This diff is too big to show here.

test/writer.zimwiki+3−3

This diff is too big to show here.

test/writers-lang-and-dir.context+4

This diff is too big to show here.

tools/build-arm.shnew file+110

This diff is too big to show here.

trypandoc/trypandoc.hs−4

This diff is too big to show here.