git.iko.soy/typeable/pandoc
on 022d89b help sign in
.circleci/config.yml+2−2
since splitsnapshots
unifiedsplit
@@ -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: |
cabal.project+1−1
@@ -3,7 +3,7 @@ packages: pandoc.cabal
3tests: True
3tests: True
4
4
5package pandoc
5package pandoc
6− flags: +embed_data_files -trypandoc
6+ flags: +embed_data_files
7 ghc-options: -j +RTS -A256m -RTS
7 ghc-options: -j +RTS -A256m -RTS
8
8
9-- source-repository-package
9-- source-repository-package
changelog.md+182−98
@@ -2,6 +2,11 @@
2
2
3## pandoc 2.12 (UNRELEASED -- PROVISIONAL)
3## pandoc 2.12 (UNRELEASED -- PROVISIONAL)
4
4
5+ * When downloading content from URL arguments, be sensitive to
6+ the character encoding (#5600). We can properly handle UTF-8 and latin1
7+ (ISO-8859-1); for others we raise an error. Fall back to latin1 if
8+ no charset is given in the mime type and UTF-8 decoding fails.
9+
5 * Add new unexported module Text.Pandoc.XML.Light, as well
10 * Add new unexported module Text.Pandoc.XML.Light, as well
6 as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc,
11 as Text.Pandoc.XML.Light.Types, Text.Pantoc.XML.Light.Proc,
7 Text.Pandoc.XML.Light.Output. (Closes #6001, #6565, #7091).
12 Text.Pandoc.XML.Light.Output. (Closes #6001, #6565, #7091).
@@ -34,75 +39,6 @@
34 these have been added. And the docx golden tests have been updated,
39 these have been added. And the docx golden tests have been updated,
35 because the new parser does not preserve the order of attributes.
40 because the new parser does not preserve the order of attributes.
36
41
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:
42 * DocBook reader:
107
43
108 + Avoid expensive tree normalization step, as it is not necessary
44 + Avoid expensive tree normalization step, as it is not necessary
@@ -137,6 +73,11 @@
137
73
138 * LaTeX reader:
74 * LaTeX reader:
139
75
76+ + Don't export `tokenize`, `untokenize` [API change]. These are internal
77+ implementation details, which were only exported for testing.
78+ They don't belong in the public API.
79+ + Improved efficiency of the parser. With these changes the reader
80+ is almost twice as fast as in the last release in our benchmarks.
140 + Code cleanup, removing some unnecessary things.
81 + Code cleanup, removing some unnecessary things.
141 + Rewrite `withRaw` so it doesn't rely on fragile assumptions
82 + Rewrite `withRaw` so it doesn't rely on fragile assumptions
142 about token positions (which break when macros are expanded)
83 about token positions (which break when macros are expanded)
@@ -156,11 +97,19 @@
156 it doesn't change anything.
97 it doesn't change anything.
157 + Improve `braced'`. Remove the parameter, have it parse the
98 + Improve `braced'`. Remove the parameter, have it parse the
158 opening brace, and make it more efficient.
99 opening brace, and make it more efficient.
100+ + Factor out pieces of the LaTeX reader to make the module smaller.
101+ This reduces memory demands when compiling. Created
102+ Text.Pandoc.Readers.{LaTeX,Math,Citation,Table,Macro,Accent}.
103+ Changed Text.Pandoc.Readers.LaTeX.SIunitx to export a command map
104+ instead of individual commands.
105+
106+ * Make Text.Pandoc.Readers.LaTeX.Types an unexported module [API change].
159
107
160 * Markdown reader:
108 * Markdown reader:
161
109
162 + Improved handling of mmd link attributes in references (#7080).
110 + Improved handling of mmd link attributes in references (#7080).
163 Previously they only worked for links that had titles.
111 Previously they only worked for links that had titles.
112+ + Improved efficiency of the parser (benchmarks show a 15% speedup).
164
113
165 * OPML reader:
114 * OPML reader:
166
115
@@ -218,7 +167,7 @@
218 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
167 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
219 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds.
168 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds.
220
169
221− * Text.Pandoc.Writers.EPUB
170+ * EPUB writer:
222
171
223 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
172 + Use `getTimestamp` instead of `getCurrentTime` for timestamp.
224 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093).
173 Setting `SOURCE_DATE_EPOCH` will allow reproducible builds (#7093).
@@ -233,6 +182,10 @@
233 contains an ampersand or another character with a special meaning
182 contains an ampersand or another character with a special meaning
234 in XML.
183 in XML.
235
184
185+ * Jira writer:
186+
187+ + Use Span identifiers as anchors (Albert Krewinkel,
188+
236 * LaTeX writer:
189 * LaTeX writer:
237
190
238 + Adjust hypertargets to beginnings of paragraphs (#7078).
191 + Adjust hypertargets to beginnings of paragraphs (#7078).
@@ -241,6 +194,8 @@
241 This makes a particular difference for links to citations using
194 This makes a particular difference for links to citations using
242 `--citeproc` and `link-citations: true`.
195 `--citeproc` and `link-citations: true`.
243 + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047).
196 + Change BCP47 lang tag from `jp` to `ja` (Mauro Bieg, #7047).
197+ + Use function instead of map for accent lookup (should be
198+ more efficient).
244
199
245 * Markdown writer:
200 * Markdown writer:
246
201
@@ -279,40 +234,151 @@
279 `--include-in-header`, `--include-before-body`, `--include-after-body`)
234 `--include-in-header`, `--include-before-body`, `--include-after-body`)
280 may be used.
235 may be used.
281
236
237+ * LaTeX template:
238+
282− * LaTeX template: Update to iftex package (#7073) (Andrew Dunning)
239+ + Update to iftex package (#7073) (Andrew Dunning)
240+ + Wrap url colours in braces (#7121) (Loïc Grobol).
283
241
284 * revealjs template: Add 'center' option for vertical slide centering.
242 * revealjs template: Add 'center' option for vertical slide centering.
285 (maurerle, #7104).
243 (maurerle, #7104).
286
244
287 * Text.Pandoc.XML: Improve efficiency of `fromEntities`.
245 * Text.Pandoc.XML: Improve efficiency of `fromEntities`.
288
246
247+ * Text.Pandoc.MIME
248+
249+ + Add exported function `getCharset` [API change].
250+
251+ * Text.Pandoc.UTF8: change IO functions to return Text, not String
252+ [API change]. This affects `readFile`, `getContents`, `writeFileWith`,
253+ `writeFile`, `putStrWith`, `putStr`, `putStrLnWith`, `putStrLn`.
254+ `hPutStrWith`, `hPutStr`, `hPutStrLnWith`, `hPutStrLn`, `hGetContents`.
255+ This avoids the need to uselessly create a linked list of characters
256+ when emiting output.
257+
258+ * Text.Pandoc.App
259+
260+ + Add `parseOptionsFromArgs` [API change, new exported function].
261+
262+ * Text.Pandoc.Citeproc.BibTeX
263+
264+ + `Text.Pandoc.Citeproc.writeBibTeXString` now returns
265+ `Doc Text` instead of `Text` (#7068).
266+ + Correctly handle `pages` (= `page` in CSL) (#7067).
267+ + Correctly handle BibLaTeX `langid` (= `language` in CSL, #7067).
268+ + In BibTeX output, protect foreign titles since there's no language
269+ field (#7067).
270+ + Clean up BibTeX parsing (#7049). Previously there was a messy code
271+ path that gave strange results in some cases, not passing through raw
272+ tex but trying to extract a string content. This was an artefact of
273+ trying to handle some special bibtex-specific commands in the BibTeX
274+ reader. Now we just handle these in the LaTeX reader and simplify
275+ parsing in the BibTeX reader. This does mean that more raw tex will
276+ be passed through (and currently this is not sensitive to the
277+ `raw_tex` extension; this should be fixed).
278+
279+ * Text.Pandoc.Citeproc.MetaValue
280+
281+ + Correctly parse "raw" date value in markdown references metadata.
282+ (See jgm/citeproc#53.)
283+
284+ * Text.Pandoc.Citeproc
285+
286+ + Use https URLs for links (Salim B, #7122).
287+
288+ * Text.Pandoc.Class
289+
290+ + Add `getTimestamp` [API change]. This attempts to read the
291+ `SOURCE_DATE_EPOCH` environment variable and parse a UTC time
292+ from it (treating it as a unix date stamp, see
293+ https://reproducible-builds.org/specs/source-date-epoch/). If the
294+ variable is not set or can't be parsed as a unix date stamp, then the
295+ function returns the current date.
296+
297+ * Text.Pandoc.Error
298+
299+ + Add `PandocUnsupportedCharsetError` constructor for
300+ `PandocError` [API change].
301+ + Export `renderError` [API change].
302+ + Refactor `handleError` to use `renderError`. This allows us render
303+ error messages without exiting.
304+ + Remove unused variables (Albert Krewinkel)
305+
306+ * Text.Pandoc.Extensions
307+
308+ + `Ext_task_lists` is now supported by org (and turned
309+ on by default) (Albert Krewinkel, #6336).
310+ + Remove `Ext_fenced_code_attributes` from allowed commonmark attributes
311+ (#7097). This attribute was listed as allowed, but it didn't actually
312+ do anything. Use `attributes` for code attributes and more.
313+
314+ * Lua subsystem:
315+
316+ + Always load built-in Lua scripts from default data-dir (Albert
317+ Krewinkel). The Lua modules `pandoc` and `pandoc.List` are now always
318+ loaded from the system's default data directory. Loading from a
319+ different directory by overriding the default path, e.g. via
320+ `--data-dir`, is no longer supported to avoid unexpected behavior
321+ and to address security concerns.
322+ + Add module "pandoc.path" (Albert Krewinkel, #6001, #6565).
323+ The module allows to work with file paths in a convenient and
324+ platform-independent manner.
325+ + Use strict evaluation when retrieving AST value from the stack
326+ (Albert Krewinkel, #6674).
327+
328+ * Text.Pandoc.PDF
329+
330+ + Disable `smart` extension when building PDF via LaTeX.
331+ This is to prevent accidental creation of ligatures like
332+ `` ?` `` and `` !` `` (especially in languages with quotations like
333+ German), and similar ligature issues. (See jgm/citeproc#54.)
334+
335+ * Text.Pandoc.CSV:
336+
337+ + Fix parsing of unquoted values (#7112). Previously we didn't allow
338+ unescaped quotes in unquoted values, but they are allowed
339+ in CSV.
340+
341+ * Test suite:
342+
289− * Test suite: a more robust way of testing the executable.
343+ + Use a more robust method for testing the executable. Many
290− Many of our tests require running the pandoc executable. This is
344+ of our tests require running the pandoc executable. This
291− problematic for a few different reasons. First, cabal-install will
345+ is problematic for a few different reasons. First,
292− sometimes run the test suite after building the library but before
346+ cabal-install will sometimes run the test suite after
347+ building the library but before building the executable,
293− building the executable, which means the executable isn't in place for
348+ 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
349+ One can work around that by first building, then building
295− running the tests, but that's fragile. Second, we have to find the
350+ and running the tests, but that's fragile. Second, we
296− executable. So far, we've done that using a function `findPandoc` that
351+ have to find the executable. So far, we've done that using
352+ a function `findPandoc` that attempts to locate it
297− attempts to locate it relative to the test executable (which can be
353+ relative to the test executable (which can be located
298− located using findExecutablePath). But the logic here is delicate and
354+ using findExecutablePath). But the logic here is delicate
299− work with every combination of options. To solve both problems, we add
355+ and work with every combination of options. To solve both
356+ problems, we add an `--emulate` option to the
300− an `--emulate` option to the `test-pandoc` executable. When `--emulate`
357+ `test-pandoc` executable. When `--emulate` occurs as the
301− occurs as the first argument passed to `test-pandoc`, the program simply
358+ first argument passed to `test-pandoc`, the program simply
302− emulates the regular pandoc executable, using the rest of the arguments
359+ emulates the regular pandoc executable, using the rest of
303− (after `--emulate`). Thus, `test-pandoc --emulate -f markdown -t latex`
360+ the arguments (after `--emulate`). Thus, `test-pandoc
304− is just like `pandoc -f markdown -t latex`.
361+ --emulate -f markdown -t latex` is just like `pandoc -f
305− Since all the work is done by library functions, implementing this
362+ markdown -t latex`. Since all the work is done by library
363+ functions, implementing this emulation just takes a couple
306− emulation just takes a couple lines of code and should be entirely
364+ lines of code and should be entirely reliable. With this
307− reliable. With this change, we can test the pandoc executable by running
365+ change, we can test the pandoc executable by running the
308− the test program itself (locatable using `findExecutablePath`) with the
366+ test program itself (locatable using `findExecutablePath`)
309− `--emulate` option. This removes the need for the fragile `findPandoc`
367+ with the `--emulate` option. This removes the need for the
368+ 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
369+ integration tests even when we're just building the
311− building the library, not the executable. [Note: part of this change
370+ library, not the executable. [Note: part of this change
312− involved simplifying some complex handling to set environment variables
371+ involved simplifying some complex handling to set
313− for dynamic library paths. I have tested a build with
372+ environment variables for dynamic library paths. I have
373+ tested a build with `--enable-dynamic-executable`, and it
314− `--enable-dynamic-executable`, and it works, but further testing may be
374+ works, but further testing may be needed.]
315− needed.]
375+ + Print accurate location if a test fails (Albert
376+ Krewinkel). Ensures that tasty-hunit reports the location
377+ of the failing test instead of the location of the helper
378+ `test` function.
379+
380+ * Documentation: Update URLs and use `https` where possible (#7122,
381+ Salim B).
316
382
317 * MANUAL.txt
383 * MANUAL.txt
318
384
@@ -328,6 +394,9 @@
328 * Makefile: in `make bench`, create CSV files for comparison and compare
394 * Makefile: in `make bench`, create CSV files for comparison and compare
329 against previous benchmark run. Add timestamp to CSV filenames.
395 against previous benchmark run. Add timestamp to CSV filenames.
330
396
397+ * cabal.project: don't explicitly set -trypandoc.
398+ If we do, this can't be overridden on the cabal command line.
399+
331 * doc/lua-filters.md: improve documentation for
400 * doc/lua-filters.md: improve documentation for
332 `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`,
401 `pandoc.mediabag.insert`, `pandoc.mediabag.fetch`,
333 `directory`, `normalize` (Albert Krewinkel).
402 `directory`, `normalize` (Albert Krewinkel).
@@ -339,6 +408,14 @@
339 * Require citeproc 0.3.0.7, which correctly titlecases when titles
408 * Require citeproc 0.3.0.7, which correctly titlecases when titles
340 contain non-ASCII characters.
409 contain non-ASCII characters.
341
410
411+ * Use skylighting 0.10.4. This version of skylighting uses xml-conduit
412+ rather than hxt. This speeds up parsing of XML syntax definitions
413+ fourfold, and removes four packages from pandoc's dependency graph:
414+ hxt-charproperties, hxt-unicode, hxt-regex-xmlschema, hxt.
415+
416+ * Add script `tools/parseTimings.pl` to help pin down which
417+ modules take the most time and memory to compile.
418+
342 * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert
419 * Avoid unnecessary use of NoImplicitPrelude pragma (#7089) (Albert
343 Krewinkel)
420 Krewinkel)
344
421
@@ -353,6 +430,13 @@
353 + Clean up benchmark code.
430 + Clean up benchmark code.
354 + Allow specifying patterns using `-p blah'.
431 + Allow specifying patterns using `-p blah'.
355
432
433+ * trypandoc: add 2 second timeout.
434+
435+ * Use `-split-sections` in creating linux release binary.
436+ This reduces executable size significantly (by about 30%).
437+
438+ * Remove `weigh-pandoc`. It's not really useful any more, now that our
439+ regular benchmarks include data on allocation.
356
440
357
441
358## pandoc 2.11.4 (2021-01-22)
442## pandoc 2.11.4 (2021-01-22)
doc/libraries.mdnew file+59
@@ -0,0 +1,59 @@
1+---
2+title: The pandoc family of libraries
3+author: John MacFarlane
4+---
5+
6+The following Haskell libraries have been developed to support
7+pandoc:
8+
9+[skylighting-core] and [skylighting]
10+: Syntax highlighting engine supporting over 140 languages.
11+
12+[citeproc]
13+: Citation processing using CSL stylesheets.
14+
15+[texmath]
16+: Conversion of math between tex, Word equation, MathML, and GNU eqn.
17+
18+[doclayout]
19+: Combinators for laying out a textual document, with support
20+ for line wrapping, tabular layout, and more.
21+
22+[doctemplates]
23+: Supports pandoc's templates.
24+
25+[commonmark], [commonmark-extensions], and [commonmark-pandoc]
26+: Efficient, standards-compliant parser for commonmark and extensions.
27+
28+[ipynb]
29+: Representation of Jupyter notebooks and conversion to and
30+ from JSON.
31+
32+[zip-archive]
33+: A pure zip file creator and extractor, used by pandoc for
34+ docx, ODT, and EPUB.
35+
36+[rfc5051]
37+: Simple unicode collation (used for citation sorting).
38+
39+[emojis]
40+: Conversion between emoji characters and aliases.
41+
42+[jira-wiki-markup]
43+: Support for parsing Jira wiki syntax.
44+
45+[skylighting]: https://hackage.haskell.org/package/skylighting
46+[skylighting-core]: https://hackage.haskell.org/package/skylighting-core
47+[citeproc]: https://hackage.haskell.org/package/citeproc
48+[texmath]: https://hackage.haskell.org/package/texmath
49+[doclayout]: https://hackage.haskell.org/package/doclayout
50+[doctemplates]: https://hackage.haskell.org/package/doctemplates
51+[commonmark]: https://hackage.haskell.org/package/commonmark
52+[commonmark-extensions]: https://hackage.haskell.org/package/commonmark-extensions
53+[commonmark-pandoc]: https://hackage.haskell.org/package/commonmark-pandoc
54+[ipynb]: https://hackage.haskell.org/package/ipynb
55+[zip-archive]: https://hackage.haskell.org/package/zip-archive
56+[rfc5051]: https://hackage.haskell.org/package/rfc5051
57+[emojis]: https://hackage.haskell.org/package/emojis
58+[jira-wiki-markup]: https://hackage.haskell.org/package/jira-wiki-markup
59+
pandoc.cabal+12−6
@@ -626,15 +626,16 @@ library
626 Text.Pandoc.Readers.HTML.Table,
626 Text.Pandoc.Readers.HTML.Table,
627 Text.Pandoc.Readers.HTML.TagCategories,
627 Text.Pandoc.Readers.HTML.TagCategories,
628 Text.Pandoc.Readers.HTML.Types,
628 Text.Pandoc.Readers.HTML.Types,
629− Text.Pandoc.Readers.LaTeX.Types,
629+ Text.Pandoc.Readers.LaTeX.Include,
630− Text.Pandoc.Readers.LaTeX.Parsing,
630+ Text.Pandoc.Readers.LaTeX.Inline,
631− Text.Pandoc.Readers.LaTeX.Lang,
632− Text.Pandoc.Readers.LaTeX.SIunitx,
633− Text.Pandoc.Readers.LaTeX.Accent,
634 Text.Pandoc.Readers.LaTeX.Citation,
631 Text.Pandoc.Readers.LaTeX.Citation,
632+ Text.Pandoc.Readers.LaTeX.Lang,
633+ Text.Pandoc.Readers.LaTeX.Macro,
635 Text.Pandoc.Readers.LaTeX.Math,
634 Text.Pandoc.Readers.LaTeX.Math,
635+ Text.Pandoc.Readers.LaTeX.Parsing,
636+ Text.Pandoc.Readers.LaTeX.SIunitx,
636 Text.Pandoc.Readers.LaTeX.Table,
637 Text.Pandoc.Readers.LaTeX.Table,
637− Text.Pandoc.Readers.LaTeX.Macro,
638+ Text.Pandoc.Readers.LaTeX.Types,
638 Text.Pandoc.Readers.Odt.Base,
639 Text.Pandoc.Readers.Odt.Base,
639 Text.Pandoc.Readers.Odt.Namespaces,
640 Text.Pandoc.Readers.Odt.Namespaces,
640 Text.Pandoc.Readers.Odt.StyleReader,
641 Text.Pandoc.Readers.Odt.StyleReader,
@@ -664,7 +665,12 @@ library
664 Text.Pandoc.Writers.LaTeX.Caption,
665 Text.Pandoc.Writers.LaTeX.Caption,
665 Text.Pandoc.Writers.LaTeX.Notes,
666 Text.Pandoc.Writers.LaTeX.Notes,
666 Text.Pandoc.Writers.LaTeX.Table,
667 Text.Pandoc.Writers.LaTeX.Table,
668+ Text.Pandoc.Writers.LaTeX.Lang,
667 Text.Pandoc.Writers.LaTeX.Types,
669 Text.Pandoc.Writers.LaTeX.Types,
670+ Text.Pandoc.Writers.LaTeX.Citation,
671+ Text.Pandoc.Writers.LaTeX.Util,
672+ Text.Pandoc.Writers.Markdown.Types,
673+ Text.Pandoc.Writers.Markdown.Inline,
668 Text.Pandoc.Writers.Roff,
674 Text.Pandoc.Writers.Roff,
669 Text.Pandoc.Writers.Powerpoint.Presentation,
675 Text.Pandoc.Writers.Powerpoint.Presentation,
670 Text.Pandoc.Writers.Powerpoint.Output,
676 Text.Pandoc.Writers.Powerpoint.Output,
src/Text/Pandoc/Readers/LaTeX.hs+109−440
@@ -34,14 +34,13 @@ import qualified Data.Set as Set
34import Data.Text (Text)
34import Data.Text (Text)
35import qualified Data.Text as T
35import qualified Data.Text as T
36import System.FilePath (addExtension, replaceExtension, takeExtension)
36import System.FilePath (addExtension, replaceExtension, takeExtension)
37−import Text.Pandoc.BCP47 (Lang (..), renderLang)
37+import Text.Pandoc.BCP47 (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+ report, setResourcePath)
42− translateTerm)
43−import Text.Pandoc.Error (PandocError (PandocParseError, PandocParsecError))
42+import Text.Pandoc.Error (PandocError (PandocParsecError))
44−import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
43+import Text.Pandoc.Highlighting (languagesByExtension)
45import Text.Pandoc.ImageSize (numUnit, showFl)
44import Text.Pandoc.ImageSize (numUnit, showFl)
46import Text.Pandoc.Logging
45import Text.Pandoc.Logging
47import Text.Pandoc.Options
46import Text.Pandoc.Options
@@ -49,7 +48,6 @@ import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,
49 optional, space, spaces, withRaw, (<|>))
48 optional, space, spaces, withRaw, (<|>))
50import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
49import Text.Pandoc.Readers.LaTeX.Types (Tok (..), TokType (..))
51import Text.Pandoc.Readers.LaTeX.Parsing
50import Text.Pandoc.Readers.LaTeX.Parsing
52−import Text.Pandoc.Readers.LaTeX.Accent (accentCommands)
53import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites)
51import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites)
54import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
52import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
55 inlineEnvironment,
53 inlineEnvironment,
@@ -58,13 +56,20 @@ import Text.Pandoc.Readers.LaTeX.Math (dollarsMath, inlineEnvironments,
58 theoremEnvironment)
56 theoremEnvironment)
59import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments)
57import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments)
60import Text.Pandoc.Readers.LaTeX.Macro (macroDef)
58import Text.Pandoc.Readers.LaTeX.Macro (macroDef)
61−import Text.Pandoc.Readers.LaTeX.Lang (polyglossiaLangToBCP47,
59+import Text.Pandoc.Readers.LaTeX.Lang (inlineLanguageCommands,
60+ enquoteCommands,
62 babelLangToBCP47, setDefaultLanguage)
61 babelLangToBCP47, setDefaultLanguage)
63import Text.Pandoc.Readers.LaTeX.SIunitx (siunitxCommands)
62import Text.Pandoc.Readers.LaTeX.SIunitx (siunitxCommands)
63+import Text.Pandoc.Readers.LaTeX.Include (insertIncluded,
64+ readFileFromTexinputs)
65+import Text.Pandoc.Readers.LaTeX.Inline (acronymCommands, refCommands,
66+ nameCommands, charCommands,
67+ accentCommands,
68+ biblatexInlineCommands,
69+ verbCommands, rawInlineOr,
70+ listingsLanguage)
64import Text.Pandoc.Shared
71import Text.Pandoc.Shared
65−import qualified Text.Pandoc.Translations as Translations
66import Text.Pandoc.Walk
72import Text.Pandoc.Walk
67−import qualified Text.Pandoc.Builder as B
68import Safe
73import Safe
69
74
70-- for debugging:
75-- for debugging:
@@ -231,19 +236,10 @@ mkImage options (T.unpack -> src) = do
231 _ -> return src
236 _ -> return src
232 return $ imageWith attr (T.pack src') "" alt
237 return $ imageWith attr (T.pack src') "" alt
233
238
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
239
244removeDoubleQuotes :: Text -> Text
240removeDoubleQuotes :: Text -> Text
245removeDoubleQuotes t =
241removeDoubleQuotes t =
246− Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
242+ fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
247
243
248doubleQuote :: PandocMonad m => LP m Inlines
244doubleQuote :: PandocMonad m => LP m Inlines
249doubleQuote =
245doubleQuote =
@@ -290,18 +286,6 @@ quoted' f starter ender = do
290lit :: Text -> LP m Inlines
286lit :: Text -> LP m Inlines
291lit = pure . str
287lit = pure . str
292
288
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
289blockquote :: PandocMonad m => Bool -> Maybe Text -> LP m Blocks
306blockquote cvariant mblang = do
290blockquote cvariant mblang = do
307 citepar <- if cvariant
291 citepar <- if cvariant
@@ -317,76 +301,6 @@ blockquote cvariant mblang = do
317 optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored
301 optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored
318 return $ blockQuote . langdiv $ (bs <> citepar)
302 return $ blockQuote . langdiv $ (bs <> citepar)
319
303
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
304inlineCommand' :: PandocMonad m => LP m Inlines
391inlineCommand' = try $ do
305inlineCommand' = try $ do
392 Tok _ (CtrlSeq name) cmd <- anyControlSeq
306 Tok _ (CtrlSeq name) cmd <- anyControlSeq
@@ -405,9 +319,6 @@ inlineCommand' = try $ do
405tok :: PandocMonad m => LP m Inlines
319tok :: PandocMonad m => LP m Inlines
406tok = tokWith inline
320tok = tokWith inline
407
321
408−inBrackets :: Inlines -> Inlines
409−inBrackets x = str "[" <> x <> str "]"
410−
411unescapeURL :: Text -> Text
322unescapeURL :: Text -> Text
412unescapeURL = T.concat . go . T.splitOn "\\"
323unescapeURL = T.concat . go . T.splitOn "\\"
413 where
324 where
@@ -420,234 +331,99 @@ unescapeURL = T.concat . go . T.splitOn "\\"
420 | otherwise = "\\" <> t
331 | otherwise = "\\" <> t
421
332
422inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
333inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
423−inlineCommands =
334+inlineCommands = M.unions
424− M.union inlineLanguageCommands $
425− M.union (accentCommands tok) $
335+ [ accentCommands tok
426− M.union (citationCommands inline) $
336+ , citationCommands inline
427− M.union (siunitxCommands tok) $
337+ , siunitxCommands tok
338+ , acronymCommands
339+ , refCommands
340+ , nameCommands
341+ , verbCommands
342+ , charCommands
343+ , enquoteCommands tok
344+ , inlineLanguageCommands tok
345+ , biblatexInlineCommands tok
346+ , rest ]
347+ where
428− M.fromList
348+ rest = M.fromList
429− [ ("emph", extractSpaces emph <$> tok)
349+ [ ("emph", extractSpaces emph <$> tok)
430− , ("textit", extractSpaces emph <$> tok)
350+ , ("textit", extractSpaces emph <$> tok)
431− , ("textsl", extractSpaces emph <$> tok)
351+ , ("textsl", extractSpaces emph <$> tok)
432− , ("textsc", extractSpaces smallcaps <$> tok)
352+ , ("textsc", extractSpaces smallcaps <$> tok)
433− , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
353+ , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
434− , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
354+ , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
435− , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
355+ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
436− , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
356+ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
437− , ("texttt", ttfamily)
357+ , ("texttt", ttfamily)
438− , ("sout", extractSpaces strikeout <$> tok)
358+ , ("sout", extractSpaces strikeout <$> tok)
439− , ("alert", skipopts >> spanWith ("",["alert"],[]) <$> tok) -- beamer
359+ , ("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)
360+ , ("textsuperscript", extractSpaces superscript <$> tok)
447− , ("textsubscript", extractSpaces subscript <$> tok)
361+ , ("textsubscript", extractSpaces subscript <$> tok)
448− , ("textbackslash", lit "\\")
449− , ("backslash", lit "\\")
450− , ("slash", lit "/")
451− , ("textbf", extractSpaces strong <$> tok)
362+ , ("textbf", extractSpaces strong <$> tok)
452− , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
363+ , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
453− , ("underline", underline <$> tok)
364+ , ("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)
365+ , ("mbox", rawInlineOr "mbox" $ processHBox <$> tok)
466− , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
366+ , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok)
467− , ("lettrine", rawInlineOr "lettrine" lettrine)
367+ , ("lettrine", rawInlineOr "lettrine" lettrine)
468− , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
368+ , ("(", mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))
469− , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
369+ , ("[", mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))
470− , ("ensuremath", mathInline . untokenize <$> braced)
370+ , ("ensuremath", mathInline . untokenize <$> braced)
471− , ("texorpdfstring", const <$> tok <*> tok)
371+ , ("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
372+ -- old TeX commands
483− , ("em", extractSpaces emph <$> inlines)
373+ , ("em", extractSpaces emph <$> inlines)
484− , ("it", extractSpaces emph <$> inlines)
374+ , ("it", extractSpaces emph <$> inlines)
485− , ("sl", extractSpaces emph <$> inlines)
375+ , ("sl", extractSpaces emph <$> inlines)
486− , ("bf", extractSpaces strong <$> inlines)
376+ , ("bf", extractSpaces strong <$> inlines)
487− , ("tt", code . stringify . toList <$> inlines)
377+ , ("tt", code . stringify . toList <$> inlines)
488− , ("rm", inlines)
378+ , ("rm", inlines)
489− , ("itshape", extractSpaces emph <$> inlines)
379+ , ("itshape", extractSpaces emph <$> inlines)
490− , ("slshape", extractSpaces emph <$> inlines)
380+ , ("slshape", extractSpaces emph <$> inlines)
491− , ("scshape", extractSpaces smallcaps <$> inlines)
381+ , ("scshape", extractSpaces smallcaps <$> inlines)
492− , ("bfseries", extractSpaces strong <$> inlines)
382+ , ("bfseries", extractSpaces strong <$> inlines)
493− , ("MakeUppercase", makeUppercase <$> tok)
383+ , ("MakeUppercase", makeUppercase <$> tok)
494− , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
384+ , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase
495− , ("uppercase", makeUppercase <$> tok)
385+ , ("uppercase", makeUppercase <$> tok)
496− , ("MakeLowercase", makeLowercase <$> tok)
386+ , ("MakeLowercase", makeLowercase <$> tok)
497− , ("MakeTextLowercase", makeLowercase <$> tok)
387+ , ("MakeTextLowercase", makeLowercase <$> tok)
498− , ("lowercase", makeLowercase <$> tok)
388+ , ("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)
389+ , ("thanks", skipopts >> note <$> grouped block)
514− , ("footnote", skipopts >> note <$> grouped block)
390+ , ("footnote", skipopts >> note <$> grouped block)
515− , ("passthrough", tok) -- \passthrough macro used by latex writer
391+ , ("passthrough", tok) -- \passthrough macro used by latex writer
516− -- for listings
392+ -- for listings
517− , ("verb", doverb)
518− , ("lstinline", dolstinline)
519− , ("mintinline", domintinline)
520− , ("Verb", doverb)
521− , ("url", (\url -> link url "" (str url)) . unescapeURL . untokenize <$>
393+ , ("url", (\url -> link url "" (str url)) . unescapeURL . untokenize <$>
522− bracedUrl)
394+ bracedUrl)
523− , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl)
395+ , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl)
524− , ("href", do url <- bracedUrl
396+ , ("href", do url <- bracedUrl
525− sp
397+ sp
526− link (unescapeURL $ untokenize url) "" <$> tok)
398+ link (unescapeURL $ untokenize url) "" <$> tok)
527− , ("includegraphics", do options <- option [] keyvals
399+ , ("includegraphics", do options <- option [] keyvals
528− src <- braced
400+ src <- braced
529− mkImage options . unescapeURL . removeDoubleQuotes $
401+ mkImage options . unescapeURL .
530− untokenize src)
402+ removeDoubleQuotes $ untokenize src)
531− , ("enquote*", enquote True Nothing)
532− , ("enquote", enquote False Nothing)
533− -- foreignquote is supposed to use native quote marks
534− , ("foreignquote*", braced >>= enquote True . Just . untokenize)
535− , ("foreignquote", braced >>= enquote False . Just . untokenize)
536− -- hypehnquote uses regular quotes
537− , ("hyphenquote*", braced >>= enquote True . Just . untokenize)
538− , ("hyphenquote", braced >>= enquote False . Just . untokenize)
539− , ("figurename", doTerm Translations.Figure)
540− , ("prefacename", doTerm Translations.Preface)
541− , ("refname", doTerm Translations.References)
542− , ("bibname", doTerm Translations.Bibliography)
543− , ("chaptername", doTerm Translations.Chapter)
544− , ("partname", doTerm Translations.Part)
545− , ("contentsname", doTerm Translations.Contents)
546− , ("listfigurename", doTerm Translations.ListOfFigures)
547− , ("listtablename", doTerm Translations.ListOfTables)
548− , ("indexname", doTerm Translations.Index)
549− , ("abstractname", doTerm Translations.Abstract)
550− , ("tablename", doTerm Translations.Table)
551− , ("enclname", doTerm Translations.Encl)
552− , ("ccname", doTerm Translations.Cc)
553− , ("headtoname", doTerm Translations.To)
554− , ("pagename", doTerm Translations.Page)
555− , ("seename", doTerm Translations.See)
556− , ("seealsoname", doTerm Translations.SeeAlso)
557− , ("proofname", doTerm Translations.Proof)
558− , ("glossaryname", doTerm Translations.Glossary)
559− , ("lstlistingname", doTerm Translations.Listing)
560− , ("hyperlink", hyperlink)
403+ , ("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,26 +447,6 @@ 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
@@ -711,31 +467,6 @@ hypertargetInline = try $ do
711 ils <- grouped inline
467 ils <- grouped inline
712 return $ spanWith (ref, [], []) ils
468 return $ spanWith (ref, [], []) ils
713
469
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
470newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a
740newToggle name = do
471newToggle name = do
741 updateState $ \st ->
472 updateState $ \st ->
@@ -766,9 +497,6 @@ ifToggle = do
766 report $ UndefinedToggle name' pos
497 report $ UndefinedToggle name' pos
767 return ()
498 return ()
768
499
769−doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
770−doTerm term = str <$> translateTerm term
771−
772ifstrequal :: (PandocMonad m, Monoid a) => LP m a
500ifstrequal :: (PandocMonad m, Monoid a) => LP m a
773ifstrequal = do
501ifstrequal = do
774 str1 <- tok
502 str1 <- tok
@@ -789,13 +517,6 @@ coloredInline stylename = do
789ttfamily :: PandocMonad m => LP m Inlines
517ttfamily :: PandocMonad m => LP m Inlines
790ttfamily = code . stringify . toList <$> tok
518ttfamily = code . stringify . toList <$> tok
791
519
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
520processHBox :: Inlines -> Inlines
800processHBox = walk convert
521processHBox = walk convert
801 where
522 where
@@ -846,25 +567,6 @@ treatAsInline = Set.fromList
846 , "pagebreak"
567 , "pagebreak"
847 ]
568 ]
848
569
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
570lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
869lookupListDefault d = (fromMaybe d .) . lookupList
571lookupListDefault d = (fromMaybe d .) . lookupList
870 where lookupList l m = msum $ map (`M.lookup` m) l
572 where lookupList l m = msum $ map (`M.lookup` m) l
@@ -986,39 +688,6 @@ include name = do
986 mapM_ (insertIncluded defaultExt) fs
688 mapM_ (insertIncluded defaultExt) fs
987 return mempty
689 return mempty
988
690
989−readFileFromTexinputs :: PandocMonad m => FilePath -> LP m (Maybe Text)
990−readFileFromTexinputs fp = do
991− fileContentsMap <- sFileContents <$> getState
992− case M.lookup (T.pack fp) fileContentsMap of
993− Just t -> return (Just t)
994− Nothing -> do
995− dirs <- map T.unpack . splitTextBy (==':') . fromMaybe "."
996− <$> lookupEnv "TEXINPUTS"
997− readFileFromDirs dirs fp
998−
999−insertIncluded :: PandocMonad m
1000− => FilePath
1001− -> FilePath
1002− -> LP m ()
1003−insertIncluded defaultExtension f' = do
1004− let f = case takeExtension f' of
1005− ".tex" -> f'
1006− ".sty" -> f'
1007− _ -> addExtension f' defaultExtension
1008− pos <- getPosition
1009− containers <- getIncludeFiles <$> getState
1010− when (T.pack f `elem` containers) $
1011− throwError $ PandocParseError $ T.pack $ "Include file loop at " ++ show pos
1012− updateState $ addIncludeFile $ T.pack f
1013− mbcontents <- readFileFromTexinputs f
1014− contents <- case mbcontents of
1015− Just s -> return s
1016− Nothing -> do
1017− report $ CouldNotLoadIncludeFile (T.pack f) pos
1018− return ""
1019− getInput >>= setInput . (tokenize f contents ++)
1020− updateState dropLatestIncludeFile
1021−
1022authors :: PandocMonad m => LP m ()
691authors :: PandocMonad m => LP m ()
1023authors = try $ do
692authors = try $ do
1024 bgroup
693 bgroup
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/Include.hsnew file+66
@@ -0,0 +1,66 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Readers.LaTeX.Include
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.Readers.LaTeX.Include
12+ ( readFileFromTexinputs
13+ , insertIncluded
14+ )
15+where
16+
17+import qualified Data.Map as M
18+import Data.Text (Text)
19+import qualified Data.Text as T
20+import Text.Pandoc.Shared (splitTextBy)
21+import System.FilePath (takeExtension, addExtension)
22+import Control.Monad (when)
23+import Control.Monad.Except (throwError)
24+import Text.Pandoc.Error (PandocError(PandocParseError))
25+import Text.Pandoc.Logging (LogMessage(CouldNotLoadIncludeFile))
26+import Text.Pandoc.Class (PandocMonad (..), readFileFromDirs, report)
27+import Text.Pandoc.Readers.LaTeX.Parsing
28+import Text.Pandoc.Parsing (updateState, getState, getInput, setInput,
29+ getPosition, addIncludeFile, getIncludeFiles,
30+ dropLatestIncludeFile)
31+import Data.Maybe (fromMaybe)
32+
33+readFileFromTexinputs :: PandocMonad m => FilePath -> LP m (Maybe Text)
34+readFileFromTexinputs fp = do
35+ fileContentsMap <- sFileContents <$> getState
36+ case M.lookup (T.pack fp) fileContentsMap of
37+ Just t -> return (Just t)
38+ Nothing -> do
39+ dirs <- map T.unpack . splitTextBy (==':') . fromMaybe "."
40+ <$> lookupEnv "TEXINPUTS"
41+ readFileFromDirs dirs fp
42+
43+insertIncluded :: PandocMonad m
44+ => FilePath
45+ -> FilePath
46+ -> LP m ()
47+insertIncluded defaultExtension f' = do
48+ let f = case takeExtension f' of
49+ ".tex" -> f'
50+ ".sty" -> f'
51+ _ -> addExtension f' defaultExtension
52+ pos <- getPosition
53+ containers <- getIncludeFiles <$> getState
54+ when (T.pack f `elem` containers) $
55+ throwError $ PandocParseError $ T.pack $ "Include file loop at " ++ show pos
56+ updateState $ addIncludeFile $ T.pack f
57+ mbcontents <- readFileFromTexinputs f
58+ contents <- case mbcontents of
59+ Just s -> return s
60+ Nothing -> do
61+ report $ CouldNotLoadIncludeFile (T.pack f) pos
62+ return ""
63+ getInput >>= setInput . (tokenize f contents ++)
64+ updateState dropLatestIncludeFile
65+
66+
src/Text/Pandoc/Readers/LaTeX/Inline.hsnew file+408
@@ -0,0 +1,408 @@
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, lookAhead)
39+import Data.Char (isDigit, isLetter)
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+doxspace :: PandocMonad m => LP m Inlines
54+doxspace =
55+ (space <$ lookAhead (satisfyTok startsWithLetter)) <|> return mempty
56+ where startsWithLetter (Tok _ Word t) =
57+ case T.uncons t of
58+ Just (c, _) | isLetter c -> True
59+ _ -> False
60+ startsWithLetter _ = False
61+
62+dolabel :: PandocMonad m => LP m Inlines
63+dolabel = do
64+ v <- braced
65+ let refstr = untokenize v
66+ updateState $ \st ->
67+ st{ sLastLabel = Just refstr }
68+ return $ spanWith (refstr,[],[("label", refstr)])
69+ $ inBrackets $ str $ untokenize v
70+
71+doref :: PandocMonad m => Text -> LP m Inlines
72+doref cls = do
73+ v <- braced
74+ let refstr = untokenize v
75+ return $ linkWith ("",[],[ ("reference-type", cls)
76+ , ("reference", refstr)])
77+ ("#" <> refstr)
78+ ""
79+ (inBrackets $ str refstr)
80+
81+inBrackets :: Inlines -> Inlines
82+inBrackets x = str "[" <> x <> str "]"
83+
84+doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
85+doTerm term = str <$> translateTerm term
86+
87+lit :: Text -> LP m Inlines
88+lit = pure . str
89+
90+doverb :: PandocMonad m => LP m Inlines
91+doverb = do
92+ Tok _ Symbol t <- anySymbol
93+ marker <- case T.uncons t of
94+ Just (c, ts) | T.null ts -> return c
95+ _ -> mzero
96+ withVerbatimMode $
97+ code . untokenize <$>
98+ manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker)
99+
100+verbTok :: PandocMonad m => Char -> LP m Tok
101+verbTok stopchar = do
102+ t@(Tok pos toktype txt) <- anyTok
103+ case T.findIndex (== stopchar) txt of
104+ Nothing -> return t
105+ Just i -> do
106+ let (t1, t2) = T.splitAt i txt
107+ inp <- getInput
108+ setInput $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar)
109+ : totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp
110+ return $ Tok pos toktype t1
111+
112+listingsLanguage :: [(Text, Text)] -> Maybe Text
113+listingsLanguage opts =
114+ case lookup "language" opts of
115+ Nothing -> Nothing
116+ Just l -> fromListingsLanguage l `mplus` Just l
117+
118+dolstinline :: PandocMonad m => LP m Inlines
119+dolstinline = do
120+ options <- option [] keyvals
121+ let classes = maybeToList $ listingsLanguage options
122+ doinlinecode classes
123+
124+domintinline :: PandocMonad m => LP m Inlines
125+domintinline = do
126+ skipopts
127+ cls <- untokenize <$> braced
128+ doinlinecode [cls]
129+
130+doinlinecode :: PandocMonad m => [Text] -> LP m Inlines
131+doinlinecode classes = do
132+ Tok _ Symbol t <- anySymbol
133+ marker <- case T.uncons t of
134+ Just (c, ts) | T.null ts -> return c
135+ _ -> mzero
136+ let stopchar = if marker == '{' then '}' else marker
137+ withVerbatimMode $
138+ codeWith ("",classes,[]) . T.map nlToSpace . untokenize <$>
139+ manyTill (verbTok stopchar) (symbol stopchar)
140+
141+nlToSpace :: Char -> Char
142+nlToSpace '\n' = ' '
143+nlToSpace x = x
144+
145+romanNumeralUpper :: (PandocMonad m) => LP m Inlines
146+romanNumeralUpper =
147+ str . toRomanNumeral <$> romanNumeralArg
148+
149+romanNumeralLower :: (PandocMonad m) => LP m Inlines
150+romanNumeralLower =
151+ str . T.toLower . toRomanNumeral <$> romanNumeralArg
152+
153+romanNumeralArg :: (PandocMonad m) => LP m Int
154+romanNumeralArg = spaces *> (parser <|> inBraces)
155+ where
156+ inBraces = do
157+ symbol '{'
158+ spaces
159+ res <- parser
160+ spaces
161+ symbol '}'
162+ return res
163+ parser = do
164+ s <- untokenize <$> many1 (satisfyTok isWordTok)
165+ let (digits, rest) = T.span isDigit s
166+ unless (T.null rest) $
167+ Prelude.fail "Non-digits in argument to \\Rn or \\RN"
168+ safeRead digits
169+
170+accentWith :: PandocMonad m
171+ => LP m Inlines -> Char -> Maybe Char -> LP m Inlines
172+accentWith tok combiningAccent fallBack = try $ do
173+ ils <- tok
174+ case toList ils of
175+ (Str (T.uncons -> Just (x, xs)) : ys) -> return $ fromList $
176+ -- try to normalize to the combined character:
177+ Str (Normalize.normalize Normalize.NFC
178+ (T.pack [x, combiningAccent]) <> xs) : ys
179+ [Space] -> return $ str $ T.singleton
180+ $ fromMaybe combiningAccent fallBack
181+ [] -> return $ str $ T.singleton
182+ $ fromMaybe combiningAccent fallBack
183+ _ -> return ils
184+
185+
186+verbCommands :: PandocMonad m => M.Map Text (LP m Inlines)
187+verbCommands = M.fromList
188+ [ ("verb", doverb)
189+ , ("lstinline", dolstinline)
190+ , ("mintinline", domintinline)
191+ , ("Verb", doverb)
192+ ]
193+
194+accentCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines)
195+accentCommands tok =
196+ let accent = accentWith tok
197+ in M.fromList
198+ [ ("aa", lit "å")
199+ , ("AA", lit "Å")
200+ , ("ss", lit "ß")
201+ , ("o", lit "ø")
202+ , ("O", lit "Ø")
203+ , ("L", lit "Ł")
204+ , ("l", lit "ł")
205+ , ("ae", lit "æ")
206+ , ("AE", lit "Æ")
207+ , ("oe", lit "œ")
208+ , ("OE", lit "Œ")
209+ , ("pounds", lit "£")
210+ , ("euro", lit "€")
211+ , ("copyright", lit "©")
212+ , ("textasciicircum", lit "^")
213+ , ("textasciitilde", lit "~")
214+ , ("H", accent '\779' Nothing) -- hungarumlaut
215+ , ("`", accent '\768' (Just '`')) -- grave
216+ , ("'", accent '\769' (Just '\'')) -- acute
217+ , ("^", accent '\770' (Just '^')) -- circ
218+ , ("~", accent '\771' (Just '~')) -- tilde
219+ , ("\"", accent '\776' Nothing) -- umlaut
220+ , (".", accent '\775' Nothing) -- dot
221+ , ("=", accent '\772' Nothing) -- macron
222+ , ("|", accent '\781' Nothing) -- vertical line above
223+ , ("b", accent '\817' Nothing) -- macron below
224+ , ("c", accent '\807' Nothing) -- cedilla
225+ , ("G", accent '\783' Nothing) -- doublegrave
226+ , ("h", accent '\777' Nothing) -- hookabove
227+ , ("d", accent '\803' Nothing) -- dotbelow
228+ , ("f", accent '\785' Nothing) -- inverted breve
229+ , ("r", accent '\778' Nothing) -- ringabove
230+ , ("t", accent '\865' Nothing) -- double inverted breve
231+ , ("U", accent '\782' Nothing) -- double vertical line above
232+ , ("v", accent '\780' Nothing) -- hacek
233+ , ("u", accent '\774' Nothing) -- breve
234+ , ("k", accent '\808' Nothing) -- ogonek
235+ , ("textogonekcentered", accent '\808' Nothing) -- ogonek
236+ , ("i", lit "ı") -- dotless i
237+ , ("j", lit "ȷ") -- dotless j
238+ , ("newtie", accent '\785' Nothing) -- inverted breve
239+ , ("textcircled", accent '\8413' Nothing) -- combining circle
240+ ]
241+
242+charCommands :: PandocMonad m => M.Map Text (LP m Inlines)
243+charCommands = M.fromList
244+ [ ("ldots", lit "…")
245+ , ("vdots", lit "\8942")
246+ , ("dots", lit "…")
247+ , ("mdots", lit "…")
248+ , ("sim", lit "~")
249+ , ("sep", lit ",")
250+ , ("P", lit "¶")
251+ , ("S", lit "§")
252+ , ("$", lit "$")
253+ , ("%", lit "%")
254+ , ("&", lit "&")
255+ , ("#", lit "#")
256+ , ("_", lit "_")
257+ , ("{", lit "{")
258+ , ("}", lit "}")
259+ , ("qed", lit "\a0\x25FB")
260+ , ("lq", return (str "‘"))
261+ , ("rq", return (str "’"))
262+ , ("textquoteleft", return (str "‘"))
263+ , ("textquoteright", return (str "’"))
264+ , ("textquotedblleft", return (str "“"))
265+ , ("textquotedblright", return (str "”"))
266+ , ("/", pure mempty) -- italic correction
267+ , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
268+ guard $ not inTableCell
269+ optional rawopt
270+ spaces))
271+ , (",", lit "\8198")
272+ , ("@", pure mempty)
273+ , (" ", lit "\160")
274+ , ("ps", pure $ str "PS." <> space)
275+ , ("TeX", lit "TeX")
276+ , ("LaTeX", lit "LaTeX")
277+ , ("bar", lit "|")
278+ , ("textless", lit "<")
279+ , ("textgreater", lit ">")
280+ , ("textbackslash", lit "\\")
281+ , ("backslash", lit "\\")
282+ , ("slash", lit "/")
283+ -- fontawesome
284+ , ("faCheck", lit "\10003")
285+ , ("faClose", lit "\10007")
286+ -- hyphenat
287+ , ("bshyp", lit "\\\173")
288+ , ("fshyp", lit "/\173")
289+ , ("dothyp", lit ".\173")
290+ , ("colonhyp", lit ":\173")
291+ , ("hyp", lit "-")
292+ -- xspace
293+ , ("xspace", doxspace)
294+ ]
295+
296+biblatexInlineCommands :: PandocMonad m
297+ => LP m Inlines -> M.Map Text (LP m Inlines)
298+biblatexInlineCommands tok = M.fromList
299+ -- biblatex misc
300+ [ ("RN", romanNumeralUpper)
301+ , ("Rn", romanNumeralLower)
302+ , ("mkbibquote", spanWith nullAttr . doubleQuoted <$> tok)
303+ , ("mkbibemph", spanWith nullAttr . emph <$> tok)
304+ , ("mkbibitalic", spanWith nullAttr . emph <$> tok)
305+ , ("mkbibbold", spanWith nullAttr . strong <$> tok)
306+ , ("mkbibparens",
307+ spanWith nullAttr . (\x -> str "(" <> x <> str ")") <$> tok)
308+ , ("mkbibbrackets",
309+ spanWith nullAttr . (\x -> str "[" <> x <> str "]") <$> tok)
310+ , ("autocap", spanWith nullAttr <$> tok)
311+ , ("textnormal", spanWith ("",["nodecor"],[]) <$> tok)
312+ , ("bibstring",
313+ (\x -> spanWith ("",[],[("bibstring",x)]) (str x)) . untokenize
314+ <$> braced)
315+ , ("adddot", pure (str "."))
316+ , ("adddotspace", pure (spanWith nullAttr (str "." <> space)))
317+ , ("addabbrvspace", pure space)
318+ , ("hyphen", pure (str "-"))
319+ ]
320+
321+nameCommands :: PandocMonad m => M.Map Text (LP m Inlines)
322+nameCommands = M.fromList
323+ [ ("figurename", doTerm Translations.Figure)
324+ , ("prefacename", doTerm Translations.Preface)
325+ , ("refname", doTerm Translations.References)
326+ , ("bibname", doTerm Translations.Bibliography)
327+ , ("chaptername", doTerm Translations.Chapter)
328+ , ("partname", doTerm Translations.Part)
329+ , ("contentsname", doTerm Translations.Contents)
330+ , ("listfigurename", doTerm Translations.ListOfFigures)
331+ , ("listtablename", doTerm Translations.ListOfTables)
332+ , ("indexname", doTerm Translations.Index)
333+ , ("abstractname", doTerm Translations.Abstract)
334+ , ("tablename", doTerm Translations.Table)
335+ , ("enclname", doTerm Translations.Encl)
336+ , ("ccname", doTerm Translations.Cc)
337+ , ("headtoname", doTerm Translations.To)
338+ , ("pagename", doTerm Translations.Page)
339+ , ("seename", doTerm Translations.See)
340+ , ("seealsoname", doTerm Translations.SeeAlso)
341+ , ("proofname", doTerm Translations.Proof)
342+ , ("glossaryname", doTerm Translations.Glossary)
343+ , ("lstlistingname", doTerm Translations.Listing)
344+ ]
345+
346+refCommands :: PandocMonad m => M.Map Text (LP m Inlines)
347+refCommands = M.fromList
348+ [ ("label", rawInlineOr "label" dolabel)
349+ , ("ref", rawInlineOr "ref" $ doref "ref")
350+ , ("cref", rawInlineOr "cref" $ doref "ref") -- from cleveref.sty
351+ , ("vref", rawInlineOr "vref" $ doref "ref+page") -- from varioref.sty
352+ , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty
353+ ]
354+
355+acronymCommands :: PandocMonad m => M.Map Text (LP m Inlines)
356+acronymCommands = M.fromList
357+ -- glossaries package
358+ [ ("gls", doAcronym "short")
359+ , ("Gls", doAcronym "short")
360+ , ("glsdesc", doAcronym "long")
361+ , ("Glsdesc", doAcronym "long")
362+ , ("GLSdesc", doAcronym "long")
363+ , ("acrlong", doAcronym "long")
364+ , ("Acrlong", doAcronym "long")
365+ , ("acrfull", doAcronym "full")
366+ , ("Acrfull", doAcronym "full")
367+ , ("acrshort", doAcronym "abbrv")
368+ , ("Acrshort", doAcronym "abbrv")
369+ , ("glspl", doAcronymPlural "short")
370+ , ("Glspl", doAcronymPlural "short")
371+ , ("glsdescplural", doAcronymPlural "long")
372+ , ("Glsdescplural", doAcronymPlural "long")
373+ , ("GLSdescplural", doAcronymPlural "long")
374+ -- acronyms package
375+ , ("ac", doAcronym "short")
376+ , ("acf", doAcronym "full")
377+ , ("acs", doAcronym "abbrv")
378+ , ("acl", doAcronym "long")
379+ , ("acp", doAcronymPlural "short")
380+ , ("acfp", doAcronymPlural "full")
381+ , ("acsp", doAcronymPlural "abbrv")
382+ , ("aclp", doAcronymPlural "long")
383+ , ("Ac", doAcronym "short")
384+ , ("Acf", doAcronym "full")
385+ , ("Acs", doAcronym "abbrv")
386+ , ("Acl", doAcronym "long")
387+ , ("Acp", doAcronymPlural "short")
388+ , ("Acfp", doAcronymPlural "full")
389+ , ("Acsp", doAcronymPlural "abbrv")
390+ , ("Aclp", doAcronymPlural "long")
391+ ]
392+
393+doAcronym :: PandocMonad m => Text -> LP m Inlines
394+doAcronym form = do
395+ acro <- braced
396+ return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
397+ ("acronym-form", "singular+" <> form)])
398+ $ str $ untokenize acro]
399+
400+doAcronymPlural :: PandocMonad m => Text -> LP m Inlines
401+doAcronymPlural form = do
402+ acro <- braced
403+ let plural = str "s"
404+ return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro),
405+ ("acronym-form", "plural+" <> form)]) $
406+ mconcat [str $ untokenize acro, plural]]
407+
408+
src/Text/Pandoc/Readers/LaTeX/Lang.hs+60−2
@@ -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
25+import Text.Pandoc.Shared (extractSpaces)
22import Text.Pandoc.BCP47 (Lang(..), renderLang)
26import Text.Pandoc.BCP47 (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
src/Text/Pandoc/Writers/LaTeX.hs+15−551
@@ -18,11 +18,9 @@ module Text.Pandoc.Writers.LaTeX (
18 writeLaTeX
18 writeLaTeX
19 , writeBeamer
19 , writeBeamer
20 ) where
20 ) where
21−import Control.Applicative ((<|>))
22import Control.Monad.State.Strict
21import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
22+import Data.Char (isDigit)
24− isPunctuation, ord)
25−import Data.List (foldl', intersperse, nubBy, (\\), uncons)
23+import Data.List (intersperse, nubBy, (\\))
26import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
24import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
27import qualified Data.Map as M
25import qualified Data.Map as M
28import Data.Text (Text)
26import Data.Text (Text)
@@ -34,7 +32,7 @@ import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
34import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
32import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
35import Text.Pandoc.Definition
33import Text.Pandoc.Definition
36import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
34import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
37− styleToLaTeX, toListingsLanguage)
35+ styleToLaTeX)
38import Text.Pandoc.ImageSize
36import Text.Pandoc.ImageSize
39import Text.Pandoc.Logging
37import Text.Pandoc.Logging
40import Text.Pandoc.Options
38import Text.Pandoc.Options
@@ -44,10 +42,15 @@ import Text.Pandoc.Slides
44import Text.Pandoc.Walk (query, walk, walkM)
42import Text.Pandoc.Walk (query, walk, walkM)
45import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
43import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
46import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
44import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
45+import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
46+ citationsToBiblatex)
47import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
47import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
48+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossia, toBabel)
49+import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
50+ toLabel, inCmd,
51+ wrapDiv, hypertarget, labelFor,
52+ getListingsLanguage, mbBraced)
48import Text.Pandoc.Writers.Shared
53import 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
54import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
52
55
53-- | Convert Pandoc to LaTeX.
56-- | Convert Pandoc to LaTeX.
@@ -241,152 +244,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
241 Nothing -> main
244 Nothing -> main
242 Just tpl -> renderTemplate tpl context'
245 Just tpl -> renderTemplate tpl context'
243
246
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]
247toSlides :: PandocMonad m => [Block] -> LW m [Block]
391toSlides bs = do
248toSlides bs = do
392 opts <- gets stOptions
249 opts <- gets stOptions
@@ -851,81 +708,6 @@ sectionHeader classes ident level lst = do
851 braces txtNoNotes
708 braces txtNoNotes
852 else empty
709 else empty
853
710
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.
711-- | Convert list of inline elements to LaTeX.
930inlineListToLaTeX :: PandocMonad m
712inlineListToLaTeX :: PandocMonad m
931 => [Inline] -- ^ Inlines to convert
713 => [Inline] -- ^ Inlines to convert
@@ -950,10 +732,6 @@ inlineListToLaTeX lst = hcat <$>
950 fixInitialLineBreaks xs
732 fixInitialLineBreaks xs
951 fixInitialLineBreaks xs = xs
733 fixInitialLineBreaks xs = xs
952
734
953−isQuoted :: Inline -> Bool
954−isQuoted (Quoted _ _) = True
955−isQuoted _ = False
956−
957-- | Convert inline element to LaTeX
735-- | Convert inline element to LaTeX
958inlineToLaTeX :: PandocMonad m
736inlineToLaTeX :: PandocMonad m
959 => Inline -- ^ Inline to convert
737 => Inline -- ^ Inline to convert
@@ -1014,8 +792,8 @@ inlineToLaTeX (Cite cits lst) = do
1014 st <- get
792 st <- get
1015 let opts = stOptions st
793 let opts = stOptions st
1016 case writerCiteMethod opts of
794 case writerCiteMethod opts of
1017− Natbib -> citationsToNatbib cits
795+ Natbib -> citationsToNatbib inlineListToLaTeX cits
1018− Biblatex -> citationsToBiblatex cits
796+ Biblatex -> citationsToBiblatex inlineListToLaTeX cits
1019 _ -> inlineListToLaTeX lst
797 _ -> inlineListToLaTeX lst
1020
798
1021inlineToLaTeX (Code (_,classes,kvs) str) = do
799inlineToLaTeX (Code (_,classes,kvs) str) = do
@@ -1089,6 +867,9 @@ inlineToLaTeX (Quoted qt lst) = do
1089 if isEnabled Ext_smart opts
867 if isEnabled Ext_smart opts
1090 then char '`' <> inner <> char '\''
868 then char '`' <> inner <> char '\''
1091 else char '\x2018' <> inner <> char '\x2019'
869 else char '\x2018' <> inner <> char '\x2019'
870+ where
871+ isQuoted (Quoted _ _) = True
872+ isQuoted _ = False
1092inlineToLaTeX (Str str) = do
873inlineToLaTeX (Str str) = do
1093 setEmptyLine False
874 setEmptyLine False
1094 liftM literal $ stringToLaTeX TextString str
875 liftM literal $ stringToLaTeX TextString str
@@ -1226,153 +1007,6 @@ protectCode x = [x]
1226setEmptyLine :: PandocMonad m => Bool -> LW m ()
1007setEmptyLine :: PandocMonad m => Bool -> LW m ()
1227setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
1008setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
1228
1009
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
1010-- Extract a key from divs and spans
1377extract :: Text -> Block -> [Text]
1011extract :: Text -> Block -> [Text]
1378extract key (Div attr _) = lookKey key attr
1012extract key (Div attr _) = lookKey key attr
@@ -1390,174 +1024,4 @@ extractInline _ _ = []
1390lookKey :: Text -> Attr -> [Text]
1024lookKey :: Text -> Attr -> [Text]
1391lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs
1025lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs
1392
1026
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
1027
1439−-- Takes a list of the constituents of a BCP 47 language code and
1440−-- converts it to a Babel language string.
1441−-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
1442−-- List of supported languages (slightly outdated):
1443−-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
1444−toBabel :: Lang -> Text
1445−toBabel (Lang "de" _ "AT" vars)
1446− | "1901" `elem` vars = "austrian"
1447− | otherwise = "naustrian"
1448−toBabel (Lang "de" _ "CH" vars)
1449− | "1901" `elem` vars = "swissgerman"
1450− | otherwise = "nswissgerman"
1451−toBabel (Lang "de" _ _ vars)
1452− | "1901" `elem` vars = "german"
1453− | otherwise = "ngerman"
1454−toBabel (Lang "dsb" _ _ _) = "lowersorbian"
1455−toBabel (Lang "el" _ _ vars)
1456− | "polyton" `elem` vars = "polutonikogreek"
1457−toBabel (Lang "en" _ "AU" _) = "australian"
1458−toBabel (Lang "en" _ "CA" _) = "canadian"
1459−toBabel (Lang "en" _ "GB" _) = "british"
1460−toBabel (Lang "en" _ "NZ" _) = "newzealand"
1461−toBabel (Lang "en" _ "UK" _) = "british"
1462−toBabel (Lang "en" _ "US" _) = "american"
1463−toBabel (Lang "fr" _ "CA" _) = "canadien"
1464−toBabel (Lang "fra" _ _ vars)
1465− | "aca" `elem` vars = "acadian"
1466−toBabel (Lang "grc" _ _ _) = "polutonikogreek"
1467−toBabel (Lang "hsb" _ _ _) = "uppersorbian"
1468−toBabel (Lang "la" _ _ vars)
1469− | "x-classic" `elem` vars = "classiclatin"
1470−toBabel (Lang "pt" _ "BR" _) = "brazilian"
1471−toBabel (Lang "sl" _ _ _) = "slovene"
1472−toBabel x = commonFromBcp47 x
1473−
1474−-- Takes a list of the constituents of a BCP 47 language code
1475−-- and converts it to a string shared by Babel and Polyglossia.
1476−-- https://tools.ietf.org/html/bcp47#section-2.1
1477−commonFromBcp47 :: Lang -> Text
1478−commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc"
1479−commonFromBcp47 (Lang "zh" "Latn" _ vars)
1480− | "pinyin" `elem` vars = "pinyin"
1481−commonFromBcp47 (Lang l _ _ _) = fromIso l
1482− where
1483− fromIso "af" = "afrikaans"
1484− fromIso "am" = "amharic"
1485− fromIso "ar" = "arabic"
1486− fromIso "as" = "assamese"
1487− fromIso "ast" = "asturian"
1488− fromIso "bg" = "bulgarian"
1489− fromIso "bn" = "bengali"
1490− fromIso "bo" = "tibetan"
1491− fromIso "br" = "breton"
1492− fromIso "ca" = "catalan"
1493− fromIso "cy" = "welsh"
1494− fromIso "cs" = "czech"
1495− fromIso "cop" = "coptic"
1496− fromIso "da" = "danish"
1497− fromIso "dv" = "divehi"
1498− fromIso "el" = "greek"
1499− fromIso "en" = "english"
1500− fromIso "eo" = "esperanto"
1501− fromIso "es" = "spanish"
1502− fromIso "et" = "estonian"
1503− fromIso "eu" = "basque"
1504− fromIso "fa" = "farsi"
1505− fromIso "fi" = "finnish"
1506− fromIso "fr" = "french"
1507− fromIso "fur" = "friulan"
1508− fromIso "ga" = "irish"
1509− fromIso "gd" = "scottish"
1510− fromIso "gez" = "ethiopic"
1511− fromIso "gl" = "galician"
1512− fromIso "he" = "hebrew"
1513− fromIso "hi" = "hindi"
1514− fromIso "hr" = "croatian"
1515− fromIso "hu" = "magyar"
1516− fromIso "hy" = "armenian"
1517− fromIso "ia" = "interlingua"
1518− fromIso "id" = "indonesian"
1519− fromIso "ie" = "interlingua"
1520− fromIso "is" = "icelandic"
1521− fromIso "it" = "italian"
1522− fromIso "ja" = "japanese"
1523− fromIso "km" = "khmer"
1524− fromIso "kmr" = "kurmanji"
1525− fromIso "kn" = "kannada"
1526− fromIso "ko" = "korean"
1527− fromIso "la" = "latin"
1528− fromIso "lo" = "lao"
1529− fromIso "lt" = "lithuanian"
1530− fromIso "lv" = "latvian"
1531− fromIso "ml" = "malayalam"
1532− fromIso "mn" = "mongolian"
1533− fromIso "mr" = "marathi"
1534− fromIso "nb" = "norsk"
1535− fromIso "nl" = "dutch"
1536− fromIso "nn" = "nynorsk"
1537− fromIso "no" = "norsk"
1538− fromIso "nqo" = "nko"
1539− fromIso "oc" = "occitan"
1540− fromIso "pa" = "panjabi"
1541− fromIso "pl" = "polish"
1542− fromIso "pms" = "piedmontese"
1543− fromIso "pt" = "portuguese"
1544− fromIso "rm" = "romansh"
1545− fromIso "ro" = "romanian"
1546− fromIso "ru" = "russian"
1547− fromIso "sa" = "sanskrit"
1548− fromIso "se" = "samin"
1549− fromIso "sk" = "slovak"
1550− fromIso "sq" = "albanian"
1551− fromIso "sr" = "serbian"
1552− fromIso "sv" = "swedish"
1553− fromIso "syr" = "syriac"
1554− fromIso "ta" = "tamil"
1555− fromIso "te" = "telugu"
1556− fromIso "th" = "thai"
1557− fromIso "ti" = "ethiopic"
1558− fromIso "tk" = "turkmen"
1559− fromIso "tr" = "turkish"
1560− fromIso "uk" = "ukrainian"
1561− fromIso "ur" = "urdu"
1562− fromIso "vi" = "vietnamese"
1563− fromIso _ = ""
src/Text/Pandoc/Writers/LaTeX/Citation.hsnew file+181
@@ -0,0 +1,181 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Citation
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Citation
12+ ( citationsToNatbib,
13+ citationsToBiblatex
14+ ) where
15+
16+import Data.Text (Text)
17+import Data.Char (isPunctuation)
18+import qualified Data.Text as T
19+import Text.Pandoc.Class.PandocMonad (PandocMonad)
20+import Text.Pandoc.Definition
21+import Data.List (foldl')
22+import Text.DocLayout (Doc, brackets, empty, (<+>), text, isEmpty, literal,
23+ braces)
24+import Text.Pandoc.Walk
25+import Text.Pandoc.Writers.LaTeX.Types ( LW )
26+
27+citationsToNatbib :: PandocMonad m
28+ => ([Inline] -> LW m (Doc Text))
29+ -> [Citation]
30+ -> LW m (Doc Text)
31+citationsToNatbib inlineListToLaTeX [one]
32+ = citeCommand inlineListToLaTeX c p s k
33+ where
34+ Citation { citationId = k
35+ , citationPrefix = p
36+ , citationSuffix = s
37+ , citationMode = m
38+ }
39+ = one
40+ c = case m of
41+ AuthorInText -> "citet"
42+ SuppressAuthor -> "citeyearpar"
43+ NormalCitation -> "citep"
44+
45+citationsToNatbib inlineListToLaTeX cits
46+ | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
47+ = citeCommand inlineListToLaTeX "citep" p s ks
48+ where
49+ noPrefix = all (null . citationPrefix)
50+ noSuffix = all (null . citationSuffix)
51+ ismode m = all ((==) m . citationMode)
52+ p = citationPrefix $
53+ head cits
54+ s = citationSuffix $
55+ last cits
56+ ks = T.intercalate ", " $ map citationId cits
57+
58+citationsToNatbib inlineListToLaTeX (c:cs)
59+ | citationMode c == AuthorInText = do
60+ author <- citeCommand inlineListToLaTeX "citeauthor" [] [] (citationId c)
61+ cits <- citationsToNatbib inlineListToLaTeX
62+ (c { citationMode = SuppressAuthor } : cs)
63+ return $ author <+> cits
64+
65+citationsToNatbib inlineListToLaTeX cits = do
66+ cits' <- mapM convertOne cits
67+ return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
68+ where
69+ citeCommand' = citeCommand inlineListToLaTeX
70+ combineTwo a b | isEmpty a = b
71+ | otherwise = a <> text "; " <> b
72+ convertOne Citation { citationId = k
73+ , citationPrefix = p
74+ , citationSuffix = s
75+ , citationMode = m
76+ }
77+ = case m of
78+ AuthorInText -> citeCommand' "citealt" p s k
79+ SuppressAuthor -> citeCommand' "citeyear" p s k
80+ NormalCitation -> citeCommand' "citealp" p s k
81+
82+citeCommand :: PandocMonad m
83+ => ([Inline] -> LW m (Doc Text))
84+ -> Text
85+ -> [Inline]
86+ -> [Inline]
87+ -> Text
88+ -> LW m (Doc Text)
89+citeCommand inlineListToLaTeX c p s k = do
90+ args <- citeArguments inlineListToLaTeX p s k
91+ return $ literal ("\\" <> c) <> args
92+
93+type Prefix = [Inline]
94+type Suffix = [Inline]
95+type CiteId = Text
96+data CiteGroup = CiteGroup Prefix Suffix [CiteId]
97+
98+citeArgumentsList :: PandocMonad m
99+ => ([Inline] -> LW m (Doc Text))
100+ -> CiteGroup
101+ -> LW m (Doc Text)
102+citeArgumentsList _inlineListToLaTeX (CiteGroup _ _ []) = return empty
103+citeArgumentsList inlineListToLaTeX (CiteGroup pfxs sfxs ids) = do
104+ pdoc <- inlineListToLaTeX pfxs
105+ sdoc <- inlineListToLaTeX sfxs'
106+ return $ optargs pdoc sdoc <>
107+ braces (literal (T.intercalate "," (reverse ids)))
108+ where sfxs' = stripLocatorBraces $ case sfxs of
109+ (Str t : r) -> case T.uncons t of
110+ Just (x, xs)
111+ | T.null xs
112+ , isPunctuation x -> dropWhile (== Space) r
113+ | isPunctuation x -> Str xs : r
114+ _ -> sfxs
115+ _ -> sfxs
116+ optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of
117+ (True, True ) -> empty
118+ (True, False) -> brackets sdoc
119+ (_ , _ ) -> brackets pdoc <> brackets sdoc
120+
121+citeArguments :: PandocMonad m
122+ => ([Inline] -> LW m (Doc Text))
123+ -> [Inline]
124+ -> [Inline]
125+ -> Text
126+ -> LW m (Doc Text)
127+citeArguments inlineListToLaTeX p s k =
128+ citeArgumentsList inlineListToLaTeX (CiteGroup p s [k])
129+
130+-- strip off {} used to define locator in pandoc-citeproc; see #5722
131+stripLocatorBraces :: [Inline] -> [Inline]
132+stripLocatorBraces = walk go
133+ where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs
134+ go x = x
135+
136+citationsToBiblatex :: PandocMonad m
137+ => ([Inline] -> LW m (Doc Text))
138+ -> [Citation] -> LW m (Doc Text)
139+citationsToBiblatex inlineListToLaTeX [one]
140+ = citeCommand inlineListToLaTeX cmd p s k
141+ where
142+ Citation { citationId = k
143+ , citationPrefix = p
144+ , citationSuffix = s
145+ , citationMode = m
146+ } = one
147+ cmd = case m of
148+ SuppressAuthor -> "autocite*"
149+ AuthorInText -> "textcite"
150+ NormalCitation -> "autocite"
151+
152+citationsToBiblatex inlineListToLaTeX (c:cs)
153+ | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs)
154+ = do
155+ let cmd = case citationMode c of
156+ SuppressAuthor -> "\\autocite*"
157+ AuthorInText -> "\\textcite"
158+ NormalCitation -> "\\autocite"
159+ return $ text cmd <>
160+ braces (literal (T.intercalate "," (map citationId (c:cs))))
161+ | otherwise
162+ = do
163+ let cmd = case citationMode c of
164+ SuppressAuthor -> "\\autocites*"
165+ AuthorInText -> "\\textcites"
166+ NormalCitation -> "\\autocites"
167+
168+ groups <- mapM (citeArgumentsList inlineListToLaTeX)
169+ (reverse (foldl' grouper [] (c:cs)))
170+
171+ return $ text cmd <> mconcat groups
172+
173+ where grouper prev cit = case prev of
174+ ((CiteGroup oPfx oSfx ids):rest)
175+ | null oSfx && null pfx -> CiteGroup oPfx sfx (cid:ids) : rest
176+ _ -> CiteGroup pfx sfx [cid] : prev
177+ where pfx = citationPrefix cit
178+ sfx = citationSuffix cit
179+ cid = citationId cit
180+
181+citationsToBiblatex _ _ = return empty
src/Text/Pandoc/Writers/LaTeX/Lang.hsnew file+191
@@ -0,0 +1,191 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{- |
4+ Module : Text.Pandoc.Writers.LaTeX.Lang
5+ Copyright : Copyright (C) 2006-2021 John MacFarlane
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : John MacFarlane <jgm@berkeley.edu>
9+ Stability : alpha
10+ Portability : portable
11+-}
12+module Text.Pandoc.Writers.LaTeX.Lang
13+ ( toPolyglossiaEnv,
14+ toPolyglossia,
15+ toBabel
16+ ) where
17+import Data.Text (Text)
18+import Text.Pandoc.BCP47 (Lang (..))
19+
20+
21+-- In environments \Arabic instead of \arabic is used
22+toPolyglossiaEnv :: Lang -> (Text, Text)
23+toPolyglossiaEnv l =
24+ case toPolyglossia l of
25+ ("arabic", o) -> ("Arabic", o)
26+ x -> x
27+
28+-- Takes a list of the constituents of a BCP 47 language code and
29+-- converts it to a Polyglossia (language, options) tuple
30+-- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf
31+toPolyglossia :: Lang -> (Text, Text)
32+toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria")
33+toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq")
34+toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq")
35+toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq")
36+toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya")
37+toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco")
38+toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania")
39+toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq")
40+toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq")
41+toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia")
42+toPolyglossia (Lang "de" _ _ vars)
43+ | "1901" `elem` vars = ("german", "spelling=old")
44+toPolyglossia (Lang "de" _ "AT" vars)
45+ | "1901" `elem` vars = ("german", "variant=austrian, spelling=old")
46+toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian")
47+toPolyglossia (Lang "de" _ "CH" vars)
48+ | "1901" `elem` vars = ("german", "variant=swiss, spelling=old")
49+toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss")
50+toPolyglossia (Lang "de" _ _ _) = ("german", "")
51+toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "")
52+toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly")
53+toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian")
54+toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian")
55+toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british")
56+toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand")
57+toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british")
58+toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american")
59+toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient")
60+toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "")
61+toPolyglossia (Lang "la" _ _ vars)
62+ | "x-classic" `elem` vars = ("latin", "variant=classic")
63+toPolyglossia (Lang "pt" _ "BR" _) = ("portuguese", "variant=brazilian")
64+toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "")
65+toPolyglossia x = (commonFromBcp47 x, "")
66+
67+-- Takes a list of the constituents of a BCP 47 language code and
68+-- converts it to a Babel language string.
69+-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
70+-- List of supported languages (slightly outdated):
71+-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
72+toBabel :: Lang -> Text
73+toBabel (Lang "de" _ "AT" vars)
74+ | "1901" `elem` vars = "austrian"
75+ | otherwise = "naustrian"
76+toBabel (Lang "de" _ "CH" vars)
77+ | "1901" `elem` vars = "swissgerman"
78+ | otherwise = "nswissgerman"
79+toBabel (Lang "de" _ _ vars)
80+ | "1901" `elem` vars = "german"
81+ | otherwise = "ngerman"
82+toBabel (Lang "dsb" _ _ _) = "lowersorbian"
83+toBabel (Lang "el" _ _ vars)
84+ | "polyton" `elem` vars = "polutonikogreek"
85+toBabel (Lang "en" _ "AU" _) = "australian"
86+toBabel (Lang "en" _ "CA" _) = "canadian"
87+toBabel (Lang "en" _ "GB" _) = "british"
88+toBabel (Lang "en" _ "NZ" _) = "newzealand"
89+toBabel (Lang "en" _ "UK" _) = "british"
90+toBabel (Lang "en" _ "US" _) = "american"
91+toBabel (Lang "fr" _ "CA" _) = "canadien"
92+toBabel (Lang "fra" _ _ vars)
93+ | "aca" `elem` vars = "acadian"
94+toBabel (Lang "grc" _ _ _) = "polutonikogreek"
95+toBabel (Lang "hsb" _ _ _) = "uppersorbian"
96+toBabel (Lang "la" _ _ vars)
97+ | "x-classic" `elem` vars = "classiclatin"
98+toBabel (Lang "pt" _ "BR" _) = "brazilian"
99+toBabel (Lang "sl" _ _ _) = "slovene"
100+toBabel x = commonFromBcp47 x
101+
102+-- Takes a list of the constituents of a BCP 47 language code
103+-- and converts it to a string shared by Babel and Polyglossia.
104+-- https://tools.ietf.org/html/bcp47#section-2.1
105+commonFromBcp47 :: Lang -> Text
106+commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc"
107+commonFromBcp47 (Lang "zh" "Latn" _ vars)
108+ | "pinyin" `elem` vars = "pinyin"
109+commonFromBcp47 (Lang l _ _ _) = fromIso l
110+ where
111+ fromIso "af" = "afrikaans"
112+ fromIso "am" = "amharic"
113+ fromIso "ar" = "arabic"
114+ fromIso "as" = "assamese"
115+ fromIso "ast" = "asturian"
116+ fromIso "bg" = "bulgarian"
117+ fromIso "bn" = "bengali"
118+ fromIso "bo" = "tibetan"
119+ fromIso "br" = "breton"
120+ fromIso "ca" = "catalan"
121+ fromIso "cy" = "welsh"
122+ fromIso "cs" = "czech"
123+ fromIso "cop" = "coptic"
124+ fromIso "da" = "danish"
125+ fromIso "dv" = "divehi"
126+ fromIso "el" = "greek"
127+ fromIso "en" = "english"
128+ fromIso "eo" = "esperanto"
129+ fromIso "es" = "spanish"
130+ fromIso "et" = "estonian"
131+ fromIso "eu" = "basque"
132+ fromIso "fa" = "farsi"
133+ fromIso "fi" = "finnish"
134+ fromIso "fr" = "french"
135+ fromIso "fur" = "friulan"
136+ fromIso "ga" = "irish"
137+ fromIso "gd" = "scottish"
138+ fromIso "gez" = "ethiopic"
139+ fromIso "gl" = "galician"
140+ fromIso "he" = "hebrew"
141+ fromIso "hi" = "hindi"
142+ fromIso "hr" = "croatian"
143+ fromIso "hu" = "magyar"
144+ fromIso "hy" = "armenian"
145+ fromIso "ia" = "interlingua"
146+ fromIso "id" = "indonesian"
147+ fromIso "ie" = "interlingua"
148+ fromIso "is" = "icelandic"
149+ fromIso "it" = "italian"
150+ fromIso "ja" = "japanese"
151+ fromIso "km" = "khmer"
152+ fromIso "kmr" = "kurmanji"
153+ fromIso "kn" = "kannada"
154+ fromIso "ko" = "korean"
155+ fromIso "la" = "latin"
156+ fromIso "lo" = "lao"
157+ fromIso "lt" = "lithuanian"
158+ fromIso "lv" = "latvian"
159+ fromIso "ml" = "malayalam"
160+ fromIso "mn" = "mongolian"
161+ fromIso "mr" = "marathi"
162+ fromIso "nb" = "norsk"
163+ fromIso "nl" = "dutch"
164+ fromIso "nn" = "nynorsk"
165+ fromIso "no" = "norsk"
166+ fromIso "nqo" = "nko"
167+ fromIso "oc" = "occitan"
168+ fromIso "pa" = "panjabi"
169+ fromIso "pl" = "polish"
170+ fromIso "pms" = "piedmontese"
171+ fromIso "pt" = "portuguese"
172+ fromIso "rm" = "romansh"
173+ fromIso "ro" = "romanian"
174+ fromIso "ru" = "russian"
175+ fromIso "sa" = "sanskrit"
176+ fromIso "se" = "samin"
177+ fromIso "sk" = "slovak"
178+ fromIso "sq" = "albanian"
179+ fromIso "sr" = "serbian"
180+ fromIso "sv" = "swedish"
181+ fromIso "syr" = "syriac"
182+ fromIso "ta" = "tamil"
183+ fromIso "te" = "telugu"
184+ fromIso "th" = "thai"
185+ fromIso "ti" = "ethiopic"
186+ fromIso "tk" = "turkmen"
187+ fromIso "tr" = "turkish"
188+ fromIso "uk" = "ukrainian"
189+ fromIso "ur" = "urdu"
190+ fromIso "vi" = "vietnamese"
191+ fromIso _ = ""
src/Text/Pandoc/Writers/LaTeX/Util.hsnew file+274
@@ -0,0 +1,274 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Util
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Util (
12+ stringToLaTeX
13+ , StringContext(..)
14+ , toLabel
15+ , inCmd
16+ , wrapDiv
17+ , hypertarget
18+ , labelFor
19+ , getListingsLanguage
20+ , mbBraced
21+ )
22+where
23+
24+import Control.Applicative ((<|>))
25+import Text.Pandoc.Class (PandocMonad, toLang)
26+import Text.Pandoc.Options (WriterOptions(..), isEnabled)
27+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..))
28+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv)
29+import Text.Pandoc.Highlighting (toListingsLanguage)
30+import Text.DocLayout
31+import Text.Pandoc.Definition
32+import Text.Pandoc.ImageSize (showFl)
33+import Control.Monad.State.Strict (gets)
34+import Data.Text (Text)
35+import qualified Data.Text as T
36+import Text.Pandoc.Extensions (Extension(Ext_smart))
37+import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum)
38+import Text.Printf (printf)
39+import Text.Pandoc.Shared (safeRead, elemText)
40+import qualified Data.Text.Normalize as Normalize
41+import Data.List (uncons)
42+
43+data StringContext = TextString
44+ | URLString
45+ | CodeString
46+ deriving (Eq)
47+
48+-- escape things as needed for LaTeX
49+stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
50+stringToLaTeX context zs = do
51+ opts <- gets stOptions
52+ return $ T.pack $
53+ foldr (go opts context) mempty $ T.unpack $
54+ if writerPreferAscii opts
55+ then Normalize.normalize Normalize.NFD zs
56+ else zs
57+ where
58+ go :: WriterOptions -> StringContext -> Char -> String -> String
59+ go opts ctx x xs =
60+ let ligatures = isEnabled Ext_smart opts && ctx == TextString
61+ isUrl = ctx == URLString
62+ mbAccentCmd =
63+ if writerPreferAscii opts && ctx == TextString
64+ then uncons xs >>= \(c,_) -> lookupAccent c
65+ else Nothing
66+ emits s =
67+ case mbAccentCmd of
68+ Just cmd ->
69+ cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
70+ Nothing -> s <> xs
71+ emitc c =
72+ case mbAccentCmd of
73+ Just cmd ->
74+ cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
75+ Nothing -> c : xs
76+ emitcseq cs =
77+ case xs of
78+ c:_ | isLetter c
79+ , ctx == TextString
80+ -> cs <> " " <> xs
81+ | isSpace c -> cs <> "{}" <> xs
82+ | ctx == TextString
83+ -> cs <> xs
84+ _ -> cs <> "{}" <> xs
85+ emitquote cs =
86+ case xs of
87+ '`':_ -> cs <> "\\," <> xs -- add thin space
88+ '\'':_ -> cs <> "\\," <> xs -- add thin space
89+ _ -> cs <> xs
90+ in case x of
91+ '?' | ligatures -> -- avoid ?` ligature
92+ case xs of
93+ '`':_ -> emits "?{}"
94+ _ -> emitc x
95+ '!' | ligatures -> -- avoid !` ligature
96+ case xs of
97+ '`':_ -> emits "!{}"
98+ _ -> emitc x
99+ '{' -> emits "\\{"
100+ '}' -> emits "\\}"
101+ '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
102+ '$' | not isUrl -> emits "\\$"
103+ '%' -> emits "\\%"
104+ '&' -> emits "\\&"
105+ '_' | not isUrl -> emits "\\_"
106+ '#' -> emits "\\#"
107+ '-' | not isUrl -> case xs of
108+ -- prevent adjacent hyphens from forming ligatures
109+ ('-':_) -> emits "-\\/"
110+ _ -> emitc '-'
111+ '~' | not isUrl -> emitcseq "\\textasciitilde"
112+ '^' -> emits "\\^{}"
113+ '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
114+ | otherwise -> emitcseq "\\textbackslash"
115+ '|' | not isUrl -> emitcseq "\\textbar"
116+ '<' -> emitcseq "\\textless"
117+ '>' -> emitcseq "\\textgreater"
118+ '[' -> emits "{[}" -- to avoid interpretation as
119+ ']' -> emits "{]}" -- optional arguments
120+ '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
121+ '\160' -> emits "~"
122+ '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
123+ '\x202F' -> emits "\\,"
124+ '\x2026' -> emitcseq "\\ldots"
125+ '\x2018' | ligatures -> emitquote "`"
126+ '\x2019' | ligatures -> emitquote "'"
127+ '\x201C' | ligatures -> emitquote "``"
128+ '\x201D' | ligatures -> emitquote "''"
129+ '\x2014' | ligatures -> emits "---"
130+ '\x2013' | ligatures -> emits "--"
131+ _ | writerPreferAscii opts
132+ -> case x of
133+ 'ı' -> emitcseq "\\i"
134+ 'ȷ' -> emitcseq "\\j"
135+ 'å' -> emitcseq "\\aa"
136+ 'Å' -> emitcseq "\\AA"
137+ 'ß' -> emitcseq "\\ss"
138+ 'ø' -> emitcseq "\\o"
139+ 'Ø' -> emitcseq "\\O"
140+ 'Ł' -> emitcseq "\\L"
141+ 'ł' -> emitcseq "\\l"
142+ 'æ' -> emitcseq "\\ae"
143+ 'Æ' -> emitcseq "\\AE"
144+ 'œ' -> emitcseq "\\oe"
145+ 'Œ' -> emitcseq "\\OE"
146+ '£' -> emitcseq "\\pounds"
147+ '€' -> emitcseq "\\euro"
148+ '©' -> emitcseq "\\copyright"
149+ _ -> emitc x
150+ | otherwise -> emitc x
151+
152+lookupAccent :: Char -> Maybe String
153+lookupAccent '\779' = Just "\\H"
154+lookupAccent '\768' = Just "\\`"
155+lookupAccent '\769' = Just "\\'"
156+lookupAccent '\770' = Just "\\^"
157+lookupAccent '\771' = Just "\\~"
158+lookupAccent '\776' = Just "\\\""
159+lookupAccent '\775' = Just "\\."
160+lookupAccent '\772' = Just "\\="
161+lookupAccent '\781' = Just "\\|"
162+lookupAccent '\817' = Just "\\b"
163+lookupAccent '\807' = Just "\\c"
164+lookupAccent '\783' = Just "\\G"
165+lookupAccent '\777' = Just "\\h"
166+lookupAccent '\803' = Just "\\d"
167+lookupAccent '\785' = Just "\\f"
168+lookupAccent '\778' = Just "\\r"
169+lookupAccent '\865' = Just "\\t"
170+lookupAccent '\782' = Just "\\U"
171+lookupAccent '\780' = Just "\\v"
172+lookupAccent '\774' = Just "\\u"
173+lookupAccent '\808' = Just "\\k"
174+lookupAccent '\8413' = Just "\\textcircled"
175+lookupAccent _ = Nothing
176+
177+toLabel :: PandocMonad m => Text -> LW m Text
178+toLabel z = go `fmap` stringToLaTeX URLString z
179+ where
180+ go = T.concatMap $ \x -> case x of
181+ _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
182+ | x `elemText` "_-+=:;." -> T.singleton x
183+ | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
184+
185+-- | Puts contents into LaTeX command.
186+inCmd :: Text -> Doc Text -> Doc Text
187+inCmd cmd contents = char '\\' <> literal cmd <> braces contents
188+
189+mapAlignment :: Text -> Text
190+mapAlignment a = case a of
191+ "top" -> "T"
192+ "top-baseline" -> "t"
193+ "bottom" -> "b"
194+ "center" -> "c"
195+ _ -> a
196+
197+wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
198+wrapDiv (_,classes,kvs) t = do
199+ beamer <- gets stBeamer
200+ let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
201+ lang <- toLang $ lookup "lang" kvs
202+ let wrapColumns = if beamer && "columns" `elem` classes
203+ then \contents ->
204+ let valign = maybe "T" mapAlignment (lookup "align" kvs)
205+ totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
206+ (lookup "totalwidth" kvs)
207+ onlytextwidth = filter ("onlytextwidth" ==) classes
208+ options = text $ T.unpack $ T.intercalate "," $
209+ valign : totalwidth ++ onlytextwidth
210+ in inCmd "begin" "columns" <> brackets options
211+ $$ contents
212+ $$ inCmd "end" "columns"
213+ else id
214+ wrapColumn = if beamer && "column" `elem` classes
215+ then \contents ->
216+ let valign =
217+ maybe ""
218+ (brackets . text . T.unpack . mapAlignment)
219+ (lookup "align" kvs)
220+ w = maybe "0.48" fromPct (lookup "width" kvs)
221+ in inCmd "begin" "column" <>
222+ valign <>
223+ braces (literal w <> "\\textwidth")
224+ $$ contents
225+ $$ inCmd "end" "column"
226+ else id
227+ fromPct xs =
228+ case T.unsnoc xs of
229+ Just (ds, '%') -> case safeRead ds of
230+ Just digits -> showFl (digits / 100 :: Double)
231+ Nothing -> xs
232+ _ -> xs
233+ wrapDir = case lookup "dir" kvs of
234+ Just "rtl" -> align "RTL"
235+ Just "ltr" -> align "LTR"
236+ _ -> id
237+ wrapLang txt = case lang of
238+ Just lng -> let (l, o) = toPolyglossiaEnv lng
239+ ops = if T.null o
240+ then ""
241+ else brackets $ literal o
242+ in inCmd "begin" (literal l) <> ops
243+ $$ blankline <> txt <> blankline
244+ $$ inCmd "end" (literal l)
245+ Nothing -> txt
246+ return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
247+
248+hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
249+hypertarget _ "" x = return x
250+hypertarget addnewline ident x = do
251+ ref <- literal `fmap` toLabel ident
252+ return $ text "\\hypertarget"
253+ <> braces ref
254+ <> braces ((if addnewline && not (isEmpty x)
255+ then "%" <> cr
256+ else empty) <> x)
257+
258+labelFor :: PandocMonad m => Text -> LW m (Doc Text)
259+labelFor "" = return empty
260+labelFor ident = do
261+ ref <- literal `fmap` toLabel ident
262+ return $ text "\\label" <> braces ref
263+
264+-- Determine listings language from list of class attributes.
265+getListingsLanguage :: [Text] -> Maybe Text
266+getListingsLanguage xs
267+ = foldr ((<|>) . toListingsLanguage) Nothing xs
268+
269+mbBraced :: Text -> Text
270+mbBraced x = if not (T.all isAlphaNum x)
271+ then "{" <> x <> "}"
272+ else x
273+
274+
src/Text/Pandoc/Writers/Markdown.hs+6−591
@@ -22,15 +22,13 @@ module Text.Pandoc.Writers.Markdown (
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)
28import qualified Data.Map as M
27import qualified Data.Map as M
29import Data.Maybe (fromMaybe, mapMaybe)
28import Data.Maybe (fromMaybe, mapMaybe)
30import qualified Data.Set as Set
29import qualified Data.Set as Set
31import Data.Text (Text)
30import Data.Text (Text)
32import qualified Data.Text as T
31import qualified Data.Text as T
33−import Network.HTTP (urlEncode)
34import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
32import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
35import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
33import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
36import Text.Pandoc.Definition
34import Text.Pandoc.Definition
@@ -44,59 +42,11 @@ import Text.Pandoc.Templates (renderTemplate)
44import Text.DocTemplates (Val(..), Context(..), FromContext(..))
42import Text.DocTemplates (Val(..), Context(..), FromContext(..))
45import Text.Pandoc.Walk
43import Text.Pandoc.Walk
46import Text.Pandoc.Writers.HTML (writeHtml5String)
44import Text.Pandoc.Writers.HTML (writeHtml5String)
47−import Text.Pandoc.Writers.Math (texMathToInlines)
45+import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown)
48−import Text.Pandoc.XML (toHtml5Entities)
46+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
47+ WriterState(..),
58−evalMD md env st = evalStateT (runReaderT md env) st
59−
60−data WriterEnv = WriterEnv { envInList :: Bool
48+ WriterEnv(..),
61− , envVariant :: MarkdownVariant
49+ Ref, Refs, MD, evalMD)
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
50
101-- | Convert Pandoc to Markdown.
51-- | Convert Pandoc to Markdown.
102writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
52writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
@@ -297,49 +247,6 @@ noteToMarkdown opts num blocks = do
297 then hang (writerTabStop opts) (marker <> spacer) contents
247 then hang (writerTabStop opts) (marker <> spacer) contents
298 else marker <> spacer <> contents
248 else marker <> spacer <> contents
299
249
300−-- | Escape special characters for Markdown.
301−escapeText :: WriterOptions -> Text -> Text
302−escapeText opts = T.pack . go . T.unpack
303− where
304− go [] = []
305− go (c:cs) =
306− case c of
307− '<' | isEnabled Ext_all_symbols_escapable opts ->
308− '\\' : '<' : go cs
309− | otherwise -> "&lt;" ++ go cs
310− '>' | isEnabled Ext_all_symbols_escapable opts ->
311− '\\' : '>' : go cs
312− | otherwise -> "&gt;" ++ go cs
313− '@' | isEnabled Ext_citations opts ->
314− case cs of
315− (d:_)
316− | isAlphaNum d || d == '_'
317− -> '\\':'@':go cs
318− _ -> '@':go cs
319− _ | c `elem` ['\\','`','*','_','[',']','#'] ->
320− '\\':c:go cs
321− '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
322− '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
323− '~' | isEnabled Ext_subscript opts ||
324− isEnabled Ext_strikeout opts -> '\\':'~':go cs
325− '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
326− '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
327− '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
328− '-' | isEnabled Ext_smart opts ->
329− case cs of
330− '-':_ -> '\\':'-':go cs
331− _ -> '-':go cs
332− '.' | isEnabled Ext_smart opts ->
333− case cs of
334− '.':'.':rest -> '\\':'.':'.':'.':go rest
335− _ -> '.':go cs
336− _ -> case cs of
337− '_':x:xs
338− | isEnabled Ext_intraword_underscores opts
339− , isAlphaNum c
340− , isAlphaNum x -> c : '_' : x : go xs
341− _ -> c : go cs
342−
343attrsToMarkdown :: Attr -> Doc Text
250attrsToMarkdown :: Attr -> Doc Text
344attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
251attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
345 where attribId = case attribs of
252 where attribId = case attribs of
@@ -912,499 +819,7 @@ blockListToMarkdown opts blocks = do
912 | otherwise = RawBlock "markdown" "&nbsp;\n"
819 | otherwise = RawBlock "markdown" "&nbsp;\n"
913 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
820 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
914
821
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
822lineBreakToSpace :: Inline -> Inline
1392lineBreakToSpace LineBreak = Space
823lineBreakToSpace LineBreak = Space
1393lineBreakToSpace SoftBreak = Space
824lineBreakToSpace SoftBreak = Space
1394lineBreakToSpace x = x
825lineBreakToSpace x = x
1395−
1396−toSubscriptInline :: Inline -> Maybe Inline
1397−toSubscriptInline Space = Just Space
1398−toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
1399−toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
1400−toSubscriptInline LineBreak = Just LineBreak
1401−toSubscriptInline SoftBreak = Just SoftBreak
1402−toSubscriptInline _ = Nothing
1403−
1404−toSuperscriptInline :: Inline -> Maybe Inline
1405−toSuperscriptInline Space = Just Space
1406−toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
1407−toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
1408−toSuperscriptInline LineBreak = Just LineBreak
1409−toSuperscriptInline SoftBreak = Just SoftBreak
1410−toSuperscriptInline _ = Nothing
src/Text/Pandoc/Writers/Markdown/Inline.hsnew file+601
@@ -0,0 +1,601 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE ViewPatterns #-}
4+{- |
5+ Module : Text.Pandoc.Writers.Markdown.Inline
6+ Copyright : Copyright (C) 2006-2021 John MacFarlane
7+ License : GNU GPL, version 2 or above
8+
9+ Maintainer : John MacFarlane <jgm@berkeley.edu>
10+ Stability : alpha
11+ Portability : portable
12+-}
13+module Text.Pandoc.Writers.Markdown.Inline (
14+ inlineListToMarkdown
15+ ) where
16+import Control.Monad.Reader
17+import Control.Monad.State.Strict
18+import Data.Char (isAlphaNum, isDigit)
19+import Data.List (find, intersperse)
20+import qualified Data.Map as M
21+import Data.Maybe (fromMaybe)
22+import Data.Text (Text)
23+import qualified Data.Text as T
24+import Network.HTTP (urlEncode)
25+import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
26+import Text.Pandoc.Definition
27+import Text.Pandoc.Logging
28+import Text.Pandoc.Options
29+import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space)
30+import Text.DocLayout
31+import Text.Pandoc.Shared
32+import Text.Pandoc.Writers.Shared
33+import Text.Pandoc.Walk
34+import Text.Pandoc.Writers.HTML (writeHtml5String)
35+import Text.Pandoc.Writers.Math (texMathToInlines)
36+import Text.Pandoc.XML (toHtml5Entities)
37+import Data.Coerce (coerce)
38+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
39+ WriterState(..),
40+ WriterEnv(..), MD)
41+
42+-- | Escape special characters for Markdown.
43+escapeText :: WriterOptions -> Text -> Text
44+escapeText opts = T.pack . go . T.unpack
45+ where
46+ go [] = []
47+ go (c:cs) =
48+ case c of
49+ '<' | isEnabled Ext_all_symbols_escapable opts ->
50+ '\\' : '<' : go cs
51+ | otherwise -> "&lt;" ++ go cs
52+ '>' | isEnabled Ext_all_symbols_escapable opts ->
53+ '\\' : '>' : go cs
54+ | otherwise -> "&gt;" ++ go cs
55+ '@' | isEnabled Ext_citations opts ->
56+ case cs of
57+ (d:_)
58+ | isAlphaNum d || d == '_'
59+ -> '\\':'@':go cs
60+ _ -> '@':go cs
61+ _ | c `elem` ['\\','`','*','_','[',']','#'] ->
62+ '\\':c:go cs
63+ '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
64+ '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
65+ '~' | isEnabled Ext_subscript opts ||
66+ isEnabled Ext_strikeout opts -> '\\':'~':go cs
67+ '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
68+ '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
69+ '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
70+ '-' | isEnabled Ext_smart opts ->
71+ case cs of
72+ '-':_ -> '\\':'-':go cs
73+ _ -> '-':go cs
74+ '.' | isEnabled Ext_smart opts ->
75+ case cs of
76+ '.':'.':rest -> '\\':'.':'.':'.':go rest
77+ _ -> '.':go cs
78+ _ -> case cs of
79+ '_':x:xs
80+ | isEnabled Ext_intraword_underscores opts
81+ , isAlphaNum c
82+ , isAlphaNum x -> c : '_' : x : go xs
83+ _ -> c : go cs
84+
85+attrsToMarkdown :: Attr -> Doc Text
86+attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
87+ where attribId = case attribs of
88+ ("",_,_) -> empty
89+ (i,_,_) -> "#" <> escAttr i
90+ attribClasses = case attribs of
91+ (_,[],_) -> empty
92+ (_,cs,_) -> hsep $
93+ map (escAttr . ("."<>))
94+ cs
95+ attribKeys = case attribs of
96+ (_,_,[]) -> empty
97+ (_,_,ks) -> hsep $
98+ map (\(k,v) -> escAttr k
99+ <> "=\"" <>
100+ escAttr v <> "\"") ks
101+ escAttr = mconcat . map escAttrChar . T.unpack
102+ escAttrChar '"' = literal "\\\""
103+ escAttrChar '\\' = literal "\\\\"
104+ escAttrChar c = literal $ T.singleton c
105+
106+linkAttributes :: WriterOptions -> Attr -> Doc Text
107+linkAttributes opts attr =
108+ if isEnabled Ext_link_attributes opts && attr /= nullAttr
109+ then attrsToMarkdown attr
110+ else empty
111+
112+getKey :: Doc Text -> Key
113+getKey = toKey . render Nothing
114+
115+findUsableIndex :: [Text] -> Int -> Int
116+findUsableIndex lbls i = if tshow i `elem` lbls
117+ then findUsableIndex lbls (i + 1)
118+ else i
119+
120+getNextIndex :: PandocMonad m => MD m Int
121+getNextIndex = do
122+ prevRefs <- gets stPrevRefs
123+ refs <- gets stRefs
124+ i <- (+ 1) <$> gets stLastIdx
125+ modify $ \s -> s{ stLastIdx = i }
126+ let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs
127+ return $ findUsableIndex refLbls i
128+
129+-- | Get reference for target; if none exists, create unique one and return.
130+-- Prefer label if possible; otherwise, generate a unique key.
131+getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text
132+getReference attr label target = do
133+ refs <- gets stRefs
134+ case find (\(_,t,a) -> t == target && a == attr) refs of
135+ Just (ref, _, _) -> return ref
136+ Nothing -> do
137+ keys <- gets stKeys
138+ let key = getKey label
139+ let rawkey = coerce key
140+ case M.lookup key keys of
141+ Nothing -> do -- no other refs with this label
142+ (lab', idx) <- if T.null rawkey ||
143+ T.length rawkey > 999 ||
144+ T.any (\c -> c == '[' || c == ']') rawkey
145+ then do
146+ i <- getNextIndex
147+ return (tshow i, i)
148+ else
149+ return (render Nothing label, 0)
150+ modify (\s -> s{
151+ stRefs = (lab', target, attr) : refs,
152+ stKeys = M.insert (getKey label)
153+ (M.insert (target, attr) idx mempty)
154+ (stKeys s) })
155+ return lab'
156+
157+ Just km -> -- we have refs with this label
158+ case M.lookup (target, attr) km of
159+ Just i -> do
160+ let lab' = render Nothing $
161+ label <> if i == 0
162+ then mempty
163+ else literal (tshow i)
164+ -- make sure it's in stRefs; it may be
165+ -- a duplicate that was printed in a previous
166+ -- block:
167+ when ((lab', target, attr) `notElem` refs) $
168+ modify (\s -> s{
169+ stRefs = (lab', target, attr) : refs })
170+ return lab'
171+ Nothing -> do -- but this one is to a new target
172+ i <- getNextIndex
173+ let lab' = tshow i
174+ modify (\s -> s{
175+ stRefs = (lab', target, attr) : refs,
176+ stKeys = M.insert key
177+ (M.insert (target, attr) i km)
178+ (stKeys s) })
179+ return lab'
180+
181+-- | Convert list of Pandoc inline elements to markdown.
182+inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text)
183+inlineListToMarkdown opts lst = do
184+ inlist <- asks envInList
185+ go (if inlist then avoidBadWrapsInList lst else lst)
186+ where go [] = return empty
187+ go (x@Math{}:y@(Str t):zs)
188+ | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058
189+ = liftM2 (<>) (inlineToMarkdown opts x)
190+ (go (RawInline (Format "html") "<!-- -->" : y : zs))
191+ go (i:is) = case i of
192+ Link {} -> case is of
193+ -- If a link is followed by another link, or '[', '(' or ':'
194+ -- then we don't shortcut
195+ Link {}:_ -> unshortcutable
196+ Space:Link {}:_ -> unshortcutable
197+ Space:(Str(thead -> Just '[')):_ -> unshortcutable
198+ Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
199+ Space:(Cite _ _):_ -> unshortcutable
200+ SoftBreak:Link {}:_ -> unshortcutable
201+ SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable
202+ SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
203+ SoftBreak:(Cite _ _):_ -> unshortcutable
204+ LineBreak:Link {}:_ -> unshortcutable
205+ LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable
206+ LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
207+ LineBreak:(Cite _ _):_ -> unshortcutable
208+ (Cite _ _):_ -> unshortcutable
209+ Str (thead -> Just '['):_ -> unshortcutable
210+ Str (thead -> Just '('):_ -> unshortcutable
211+ Str (thead -> Just ':'):_ -> unshortcutable
212+ (RawInline _ (thead -> Just '[')):_ -> unshortcutable
213+ (RawInline _ (thead -> Just '(')):_ -> unshortcutable
214+ (RawInline _ (thead -> Just ':')):_ -> unshortcutable
215+ (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable
216+ _ -> shortcutable
217+ _ -> shortcutable
218+ where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
219+ unshortcutable = do
220+ iMark <- local
221+ (\env -> env { envRefShortcutable = False })
222+ (inlineToMarkdown opts i)
223+ fmap (iMark <>) (go is)
224+ thead = fmap fst . T.uncons
225+
226+isSp :: Inline -> Bool
227+isSp Space = True
228+isSp SoftBreak = True
229+isSp _ = False
230+
231+avoidBadWrapsInList :: [Inline] -> [Inline]
232+avoidBadWrapsInList [] = []
233+avoidBadWrapsInList (s:Str (T.uncons -> Just ('>',cs)):xs) | isSp s =
234+ Str (" >" <> cs) : avoidBadWrapsInList xs
235+avoidBadWrapsInList [s, Str (T.uncons -> Just (c, cs))]
236+ | T.null cs && isSp s && c `elem` ['-','*','+'] = [Str $ T.pack [' ', c]]
237+avoidBadWrapsInList (s:Str (T.uncons -> Just (c, cs)):Space:xs)
238+ | T.null cs && isSp s && c `elem` ['-','*','+'] =
239+ Str (T.pack [' ', c]) : Space : avoidBadWrapsInList xs
240+avoidBadWrapsInList (s:Str cs:Space:xs)
241+ | isSp s && isOrderedListMarker cs =
242+ Str (" " <> cs) : Space : avoidBadWrapsInList xs
243+avoidBadWrapsInList [s, Str cs]
244+ | isSp s && isOrderedListMarker cs = [Str $ " " <> cs]
245+avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs
246+
247+isOrderedListMarker :: Text -> Bool
248+isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) &&
249+ isRight (runParser (anyOrderedListMarker >> eof)
250+ defaultParserState "" xs)
251+ where
252+ isRight (Right _) = True
253+ isRight (Left _) = False
254+
255+-- | Convert Pandoc inline element to markdown.
256+inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text)
257+inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) =
258+ case lookup "data-emoji" kvs of
259+ Just emojiname | isEnabled Ext_emoji opts ->
260+ return $ ":" <> literal emojiname <> ":"
261+ _ -> inlineToMarkdown opts (Str s)
262+inlineToMarkdown opts (Span attrs ils) = do
263+ variant <- asks envVariant
264+ contents <- inlineListToMarkdown opts ils
265+ return $ case attrs of
266+ (_,["csl-block"],_) -> (cr <>)
267+ (_,["csl-left-margin"],_) -> (cr <>)
268+ (_,["csl-indent"],_) -> (cr <>)
269+ _ -> id
270+ $ case variant of
271+ PlainText -> contents
272+ _ | attrs == nullAttr -> contents
273+ | isEnabled Ext_bracketed_spans opts ->
274+ let attrs' = if attrs /= nullAttr
275+ then attrsToMarkdown attrs
276+ else empty
277+ in "[" <> contents <> "]" <> attrs'
278+ | isEnabled Ext_raw_html opts ||
279+ isEnabled Ext_native_spans opts ->
280+ tagWithAttrs "span" attrs <> contents <> literal "</span>"
281+ | otherwise -> contents
282+inlineToMarkdown _ (Emph []) = return empty
283+inlineToMarkdown opts (Emph lst) = do
284+ variant <- asks envVariant
285+ contents <- inlineListToMarkdown opts lst
286+ return $ case variant of
287+ PlainText
288+ | isEnabled Ext_gutenberg opts -> "_" <> contents <> "_"
289+ | otherwise -> contents
290+ _ -> "*" <> contents <> "*"
291+inlineToMarkdown _ (Underline []) = return empty
292+inlineToMarkdown opts (Underline lst) = do
293+ variant <- asks envVariant
294+ contents <- inlineListToMarkdown opts lst
295+ case variant of
296+ PlainText -> return contents
297+ _ | isEnabled Ext_bracketed_spans opts ->
298+ return $ "[" <> contents <> "]" <> "{.ul}"
299+ | isEnabled Ext_native_spans opts ->
300+ return $ tagWithAttrs "span" ("", ["underline"], [])
301+ <> contents
302+ <> literal "</span>"
303+ | isEnabled Ext_raw_html opts ->
304+ return $ "<u>" <> contents <> "</u>"
305+ | otherwise -> inlineToMarkdown opts (Emph lst)
306+inlineToMarkdown _ (Strong []) = return empty
307+inlineToMarkdown opts (Strong lst) = do
308+ variant <- asks envVariant
309+ case variant of
310+ PlainText ->
311+ inlineListToMarkdown opts $
312+ if isEnabled Ext_gutenberg opts
313+ then capitalize lst
314+ else lst
315+ _ -> do
316+ contents <- inlineListToMarkdown opts lst
317+ return $ "**" <> contents <> "**"
318+inlineToMarkdown _ (Strikeout []) = return empty
319+inlineToMarkdown opts (Strikeout lst) = do
320+ contents <- inlineListToMarkdown opts lst
321+ return $ if isEnabled Ext_strikeout opts
322+ then "~~" <> contents <> "~~"
323+ else if isEnabled Ext_raw_html opts
324+ then "<s>" <> contents <> "</s>"
325+ else contents
326+inlineToMarkdown _ (Superscript []) = return empty
327+inlineToMarkdown opts (Superscript lst) =
328+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
329+ contents <- inlineListToMarkdown opts lst
330+ if isEnabled Ext_superscript opts
331+ then return $ "^" <> contents <> "^"
332+ else if isEnabled Ext_raw_html opts
333+ then return $ "<sup>" <> contents <> "</sup>"
334+ else
335+ case traverse toSuperscriptInline lst of
336+ Just xs' | not (writerPreferAscii opts)
337+ -> inlineListToMarkdown opts xs'
338+ _ -> do
339+ let rendered = render Nothing contents
340+ return $
341+ case mapM toSuperscript (T.unpack rendered) of
342+ Just r -> literal $ T.pack r
343+ Nothing -> literal $ "^(" <> rendered <> ")"
344+inlineToMarkdown _ (Subscript []) = return empty
345+inlineToMarkdown opts (Subscript lst) =
346+ local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
347+ contents <- inlineListToMarkdown opts lst
348+ if isEnabled Ext_subscript opts
349+ then return $ "~" <> contents <> "~"
350+ else if isEnabled Ext_raw_html opts
351+ then return $ "<sub>" <> contents <> "</sub>"
352+ else
353+ case traverse toSubscriptInline lst of
354+ Just xs' | not (writerPreferAscii opts)
355+ -> inlineListToMarkdown opts xs'
356+ _ -> do
357+ let rendered = render Nothing contents
358+ return $
359+ case mapM toSuperscript (T.unpack rendered) of
360+ Just r -> literal $ T.pack r
361+ Nothing -> literal $ "_(" <> rendered <> ")"
362+inlineToMarkdown opts (SmallCaps lst) = do
363+ variant <- asks envVariant
364+ if variant /= PlainText &&
365+ (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
366+ then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst)
367+ else inlineListToMarkdown opts $ capitalize lst
368+inlineToMarkdown opts (Quoted SingleQuote lst) = do
369+ contents <- inlineListToMarkdown opts lst
370+ return $ if isEnabled Ext_smart opts
371+ then "'" <> contents <> "'"
372+ else
373+ if writerPreferAscii opts
374+ then "&lsquo;" <> contents <> "&rsquo;"
375+ else "‘" <> contents <> "’"
376+inlineToMarkdown opts (Quoted DoubleQuote lst) = do
377+ contents <- inlineListToMarkdown opts lst
378+ return $ if isEnabled Ext_smart opts
379+ then "\"" <> contents <> "\""
380+ else
381+ if writerPreferAscii opts
382+ then "&ldquo;" <> contents <> "&rdquo;"
383+ else "“" <> contents <> "”"
384+inlineToMarkdown opts (Code attr str) = do
385+ let tickGroups = filter (T.any (== '`')) $ T.group str
386+ let longest = if null tickGroups
387+ then 0
388+ else maximum $ map T.length tickGroups
389+ let marker = T.replicate (longest + 1) "`"
390+ let spacer = if longest == 0 then "" else " "
391+ let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
392+ then attrsToMarkdown attr
393+ else empty
394+ variant <- asks envVariant
395+ case variant of
396+ PlainText -> return $ literal str
397+ _ -> return $ literal
398+ (marker <> spacer <> str <> spacer <> marker) <> attrs
399+inlineToMarkdown opts (Str str) = do
400+ variant <- asks envVariant
401+ let str' = (if writerPreferAscii opts
402+ then toHtml5Entities
403+ else id) .
404+ (if isEnabled Ext_smart opts
405+ then unsmartify opts
406+ else id) .
407+ (if variant == PlainText
408+ then id
409+ else escapeText opts) $ str
410+ return $ literal str'
411+inlineToMarkdown opts (Math InlineMath str) =
412+ case writerHTMLMathMethod opts of
413+ WebTeX url -> inlineToMarkdown opts
414+ (Image nullAttr [Str str] (url <> T.pack (urlEncode $ T.unpack str), str))
415+ _ | isEnabled Ext_tex_math_dollars opts ->
416+ return $ "$" <> literal str <> "$"
417+ | isEnabled Ext_tex_math_single_backslash opts ->
418+ return $ "\\(" <> literal str <> "\\)"
419+ | isEnabled Ext_tex_math_double_backslash opts ->
420+ return $ "\\\\(" <> literal str <> "\\\\)"
421+ | otherwise -> do
422+ variant <- asks envVariant
423+ texMathToInlines InlineMath str >>=
424+ inlineListToMarkdown opts .
425+ (if variant == PlainText then makeMathPlainer else id)
426+inlineToMarkdown opts (Math DisplayMath str) =
427+ case writerHTMLMathMethod opts of
428+ WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
429+ inlineToMarkdown opts (Image nullAttr [Str str]
430+ (url <> T.pack (urlEncode $ T.unpack str), str))
431+ _ | isEnabled Ext_tex_math_dollars opts ->
432+ return $ "$$" <> literal str <> "$$"
433+ | isEnabled Ext_tex_math_single_backslash opts ->
434+ return $ "\\[" <> literal str <> "\\]"
435+ | isEnabled Ext_tex_math_double_backslash opts ->
436+ return $ "\\\\[" <> literal str <> "\\\\]"
437+ | otherwise -> (\x -> cr <> x <> cr) `fmap`
438+ (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
439+inlineToMarkdown opts il@(RawInline f str) = do
440+ let tickGroups = filter (T.any (== '`')) $ T.group str
441+ let numticks = if null tickGroups
442+ then 1
443+ else 1 + maximum (map T.length tickGroups)
444+ variant <- asks envVariant
445+ let Format fmt = f
446+ let rawAttribInline = return $
447+ literal (T.replicate numticks "`") <> literal str <>
448+ literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}"
449+ let renderEmpty = mempty <$ report (InlineNotRendered il)
450+ case variant of
451+ PlainText -> renderEmpty
452+ Commonmark
453+ | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"]
454+ -> return $ literal str
455+ Markdown
456+ | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
457+ "markdown_mmd", "markdown_strict"]
458+ -> return $ literal str
459+ _ | isEnabled Ext_raw_attribute opts -> rawAttribInline
460+ | f `elem` ["html", "html5", "html4"]
461+ , isEnabled Ext_raw_html opts
462+ -> return $ literal str
463+ | f `elem` ["latex", "tex"]
464+ , isEnabled Ext_raw_tex opts
465+ -> return $ literal str
466+ _ -> renderEmpty
467+
468+
469+inlineToMarkdown opts LineBreak = do
470+ variant <- asks envVariant
471+ if variant == PlainText || isEnabled Ext_hard_line_breaks opts
472+ then return cr
473+ else return $
474+ if isEnabled Ext_escaped_line_breaks opts
475+ then "\\" <> cr
476+ else " " <> cr
477+inlineToMarkdown _ Space = do
478+ escapeSpaces <- asks envEscapeSpaces
479+ return $ if escapeSpaces then "\\ " else space
480+inlineToMarkdown opts SoftBreak = do
481+ escapeSpaces <- asks envEscapeSpaces
482+ let space' = if escapeSpaces then "\\ " else space
483+ return $ case writerWrapText opts of
484+ WrapNone -> space'
485+ WrapAuto -> space'
486+ WrapPreserve -> cr
487+inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst
488+inlineToMarkdown opts (Cite (c:cs) lst)
489+ | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst
490+ | otherwise =
491+ if citationMode c == AuthorInText
492+ then do
493+ suffs <- inlineListToMarkdown opts $ citationSuffix c
494+ rest <- mapM convertOne cs
495+ let inbr = suffs <+> joincits rest
496+ br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
497+ return $ literal ("@" <> citationId c) <+> br
498+ else do
499+ cits <- mapM convertOne (c:cs)
500+ return $ literal "[" <> joincits cits <> literal "]"
501+ where
502+ joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
503+ convertOne Citation { citationId = k
504+ , citationPrefix = pinlines
505+ , citationSuffix = sinlines
506+ , citationMode = m }
507+ = do
508+ pdoc <- inlineListToMarkdown opts pinlines
509+ sdoc <- inlineListToMarkdown opts sinlines
510+ let k' = literal (modekey m <> "@" <> k)
511+ r = case sinlines of
512+ Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
513+ _ -> k' <+> sdoc
514+ return $ pdoc <+> r
515+ modekey SuppressAuthor = "-"
516+ modekey _ = ""
517+inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do
518+ variant <- asks envVariant
519+ linktext <- inlineListToMarkdown opts txt
520+ let linktitle = if T.null tit
521+ then empty
522+ else literal $ " \"" <> tit <> "\""
523+ let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
524+ let useAuto = isURI src &&
525+ case txt of
526+ [Str s] | escapeURI s == srcSuffix -> True
527+ _ -> False
528+ let useRefLinks = writerReferenceLinks opts && not useAuto
529+ shortcutable <- asks envRefShortcutable
530+ let useShortcutRefLinks = shortcutable &&
531+ isEnabled Ext_shortcut_reference_links opts
532+ reftext <- if useRefLinks
533+ then literal <$> getReference attr linktext (src, tit)
534+ else return mempty
535+ case variant of
536+ PlainText
537+ | useAuto -> return $ literal srcSuffix
538+ | otherwise -> return linktext
539+ _ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
540+ | useRefLinks ->
541+ let first = "[" <> linktext <> "]"
542+ second = if getKey linktext == getKey reftext
543+ then if useShortcutRefLinks
544+ then ""
545+ else "[]"
546+ else "[" <> reftext <> "]"
547+ in return $ first <> second
548+ | isEnabled Ext_raw_html opts
549+ , not (isEnabled Ext_link_attributes opts)
550+ , attr /= nullAttr -> -- use raw HTML to render attributes
551+ literal . T.strip <$>
552+ writeHtml5String opts{ writerTemplate = Nothing }
553+ (Pandoc nullMeta [Plain [lnk]])
554+ | otherwise -> return $
555+ "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <>
556+ linkAttributes opts attr
557+inlineToMarkdown opts img@(Image attr alternate (source, tit))
558+ | isEnabled Ext_raw_html opts &&
559+ not (isEnabled Ext_link_attributes opts) &&
560+ attr /= nullAttr = -- use raw HTML
561+ literal . T.strip <$>
562+ writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]])
563+ | otherwise = do
564+ variant <- asks envVariant
565+ let txt = if null alternate || alternate == [Str source]
566+ -- to prevent autolinks
567+ then [Str ""]
568+ else alternate
569+ linkPart <- inlineToMarkdown opts (Link attr txt (source, tit))
570+ return $ case variant of
571+ PlainText -> "[" <> linkPart <> "]"
572+ _ -> "!" <> linkPart
573+inlineToMarkdown opts (Note contents) = do
574+ modify (\st -> st{ stNotes = contents : stNotes st })
575+ st <- get
576+ let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1)
577+ if isEnabled Ext_footnotes opts
578+ then return $ "[^" <> ref <> "]"
579+ else return $ "[" <> ref <> "]"
580+
581+makeMathPlainer :: [Inline] -> [Inline]
582+makeMathPlainer = walk go
583+ where
584+ go (Emph xs) = Span nullAttr xs
585+ go x = x
586+
587+toSubscriptInline :: Inline -> Maybe Inline
588+toSubscriptInline Space = Just Space
589+toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
590+toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
591+toSubscriptInline LineBreak = Just LineBreak
592+toSubscriptInline SoftBreak = Just SoftBreak
593+toSubscriptInline _ = Nothing
594+
595+toSuperscriptInline :: Inline -> Maybe Inline
596+toSuperscriptInline Space = Just Space
597+toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
598+toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
599+toSuperscriptInline LineBreak = Just LineBreak
600+toSuperscriptInline SoftBreak = Just SoftBreak
601+toSuperscriptInline _ = Nothing
src/Text/Pandoc/Writers/Markdown/Types.hsnew file+81
@@ -0,0 +1,81 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.Markdown.Types
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.Markdown.Types (
12+ MarkdownVariant(..),
13+ WriterState(..),
14+ WriterEnv(..),
15+ Notes,
16+ Ref,
17+ Refs,
18+ MD,
19+ evalMD
20+ ) where
21+import Control.Monad.Reader
22+import Control.Monad.State.Strict
23+import Data.Default
24+import qualified Data.Map as M
25+import qualified Data.Set as Set
26+import Data.Text (Text)
27+import Text.Pandoc.Parsing (Key)
28+import Text.Pandoc.Class.PandocMonad (PandocMonad)
29+import Text.Pandoc.Definition
30+
31+type Notes = [[Block]]
32+type Ref = (Text, Target, Attr)
33+type Refs = [Ref]
34+
35+type MD m = ReaderT WriterEnv (StateT WriterState m)
36+
37+evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a
38+evalMD md env st = evalStateT (runReaderT md env) st
39+
40+data WriterEnv = WriterEnv { envInList :: Bool
41+ , envVariant :: MarkdownVariant
42+ , envRefShortcutable :: Bool
43+ , envBlockLevel :: Int
44+ , envEscapeSpaces :: Bool
45+ }
46+
47+data MarkdownVariant =
48+ PlainText
49+ | Commonmark
50+ | Markdown
51+ deriving (Show, Eq)
52+
53+instance Default WriterEnv
54+ where def = WriterEnv { envInList = False
55+ , envVariant = Markdown
56+ , envRefShortcutable = True
57+ , envBlockLevel = 0
58+ , envEscapeSpaces = False
59+ }
60+
61+data WriterState = WriterState { stNotes :: Notes
62+ , stPrevRefs :: Refs
63+ , stRefs :: Refs
64+ , stKeys :: M.Map Key
65+ (M.Map (Target, Attr) Int)
66+ , stLastIdx :: Int
67+ , stIds :: Set.Set Text
68+ , stNoteNum :: Int
69+ }
70+
71+instance Default WriterState
72+ where def = WriterState{ stNotes = []
73+ , stPrevRefs = []
74+ , stRefs = []
75+ , stKeys = M.empty
76+ , stLastIdx = 0
77+ , stIds = Set.empty
78+ , stNoteNum = 1
79+ }
80+
81+
stack.yaml+1−1
@@ -12,7 +12,7 @@ extra-deps:
12- commonmark-extensions-0.2.0.4
12- commonmark-extensions-0.2.0.4
13- commonmark-pandoc-0.2.0.1
13- commonmark-pandoc-0.2.0.1
14- tasty-bench-0.2.1
14- tasty-bench-0.2.1
15−- citeproc-0.3.0.7
15+- citeproc-0.3.0.8
16- skylighting-0.10.4
16- skylighting-0.10.4
17- skylighting-core-0.10.4
17- skylighting-core-0.10.4
18- xml-conduit-1.9.1.0
18- xml-conduit-1.9.1.0
trypandoc/trypandoc.hs−4
@@ -14,16 +14,12 @@ module Main where
14import Network.Wai.Handler.CGI
14import Network.Wai.Handler.CGI
15import Network.Wai.Middleware.Timeout (timeout)
15import Network.Wai.Middleware.Timeout (timeout)
16import Network.Wai
16import Network.Wai
17−import Control.Applicative ((<$>))
18import Data.Maybe (fromMaybe)
17import Data.Maybe (fromMaybe)
19import Network.HTTP.Types.Status (status200)
18import Network.HTTP.Types.Status (status200)
20import Network.HTTP.Types.Header (hContentType)
19import Network.HTTP.Types.Header (hContentType)
21import Network.HTTP.Types.URI (queryToQueryText)
20import Network.HTTP.Types.URI (queryToQueryText)
22import Text.Pandoc
21import Text.Pandoc
23−import Text.Pandoc.Writers.Math (defaultMathJaxURL)
24import Text.Pandoc.Highlighting (pygments)
22import Text.Pandoc.Highlighting (pygments)
25−import Text.Pandoc.Readers (getReader, Reader(..))
26−import Text.Pandoc.Writers (getWriter, Writer(..))
27import Text.Pandoc.Shared (tabFilter)
23import Text.Pandoc.Shared (tabFilter)
28import Data.Aeson
24import Data.Aeson
29import qualified Data.Text as T
25import qualified Data.Text as T