Integrate server into main pandoc.
- Remove server flag. - Remove pandoc-server executable. - Add Text.Pandoc.Server as exposed module. [API change] - Re-use Opt (and our existing FromJSON instance) for Params. - Document.
This commit is contained in:
parent
90d52b7129
commit
8ddc2fc79a
13 changed files with 604 additions and 570 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -56,7 +56,7 @@ jobs:
|
||||||
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
|
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
|
||||||
- ghc: '8.10.7'
|
- ghc: '8.10.7'
|
||||||
cabal: '3.2'
|
cabal: '3.2'
|
||||||
cabalopts: '-fserver'
|
cabalopts: ''
|
||||||
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
|
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
|
||||||
- ghc: '9.0.2'
|
- ghc: '9.0.2'
|
||||||
cabal: '3.4'
|
cabal: '3.4'
|
||||||
|
|
9
.github/workflows/release-candidate.yml
vendored
9
.github/workflows/release-candidate.yml
vendored
|
@ -96,7 +96,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
stack --no-terminal setup
|
stack --no-terminal setup
|
||||||
stack --no-terminal update
|
stack --no-terminal update
|
||||||
stack --no-terminal install
|
stack --no-terminal install --ghc-options='-j4 +RTS -A256m -RTS -split-sections'
|
||||||
|
|
||||||
- name: Create artifacts
|
- name: Create artifacts
|
||||||
run: |
|
run: |
|
||||||
|
@ -112,8 +112,13 @@ jobs:
|
||||||
mkdir -p ${DEST}/bin
|
mkdir -p ${DEST}/bin
|
||||||
mkdir -p ${DEST}/share/man/man1
|
mkdir -p ${DEST}/share/man/man1
|
||||||
cp ~/.local/bin/pandoc ${DEST}/bin/
|
cp ~/.local/bin/pandoc ${DEST}/bin/
|
||||||
strip ${DEST}/bin/pandoc
|
SRCDIR=$(pwd)
|
||||||
|
cd ${DEST}/bin
|
||||||
|
strip pandoc
|
||||||
|
ln -s pandoc pandoc-server
|
||||||
|
cd ${SRCDIR}
|
||||||
cp man/pandoc.1 ${DEST}/share/man/man1/pandoc.1
|
cp man/pandoc.1 ${DEST}/share/man/man1/pandoc.1
|
||||||
|
cp man/pandoc-server.1 ${DEST}/share/man/man1/pandoc-server.1
|
||||||
~/.local/bin/pandoc -s COPYING.md -Vpagetitle=License -o ${RESOURCES}/license.html
|
~/.local/bin/pandoc -s COPYING.md -Vpagetitle=License -o ${RESOURCES}/license.html
|
||||||
chown -R $ME:staff ${ROOT}
|
chown -R $ME:staff ${ROOT}
|
||||||
sed -e "s/PANDOCVERSION/${VERSION}/" macos/distribution.xml.in > ${ARTIFACTS}/distribution.xml
|
sed -e "s/PANDOCVERSION/${VERSION}/" macos/distribution.xml.in > ${ARTIFACTS}/distribution.xml
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -102,7 +102,7 @@ man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after
|
||||||
--variable footer="pandoc $(version)" \
|
--variable footer="pandoc $(version)" \
|
||||||
-o $@
|
-o $@
|
||||||
|
|
||||||
man/pandoc-server.1: server/pandoc-server.md
|
man/pandoc-server.1: doc/pandoc-server.md
|
||||||
pandoc $< -f markdown -t man -s \
|
pandoc $< -f markdown -t man -s \
|
||||||
--lua-filter man/manfilter.lua \
|
--lua-filter man/manfilter.lua \
|
||||||
--variable footer="pandoc-server $(version)" \
|
--variable footer="pandoc-server $(version)" \
|
||||||
|
|
|
@ -14,7 +14,18 @@ module Main where
|
||||||
import qualified Control.Exception as E
|
import qualified Control.Exception as E
|
||||||
import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions)
|
import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions)
|
||||||
import Text.Pandoc.Error (handleError)
|
import Text.Pandoc.Error (handleError)
|
||||||
|
import Text.Pandoc.Server (ServerOpts(..), parseServerOpts, app)
|
||||||
|
import System.Environment (getProgName)
|
||||||
|
import qualified Network.Wai.Handler.CGI as CGI
|
||||||
|
import qualified Network.Wai.Handler.Warp as Warp
|
||||||
|
import Network.Wai.Middleware.Timeout (timeout)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = E.catch (parseOptions options defaultOpts >>= convertWithOpts)
|
main = E.handle (handleError . Left) $ do
|
||||||
(handleError . Left)
|
prg <- getProgName
|
||||||
|
case prg of
|
||||||
|
"pandoc-server.cgi" -> CGI.run (timeout 2 app)
|
||||||
|
"pandoc-server" -> do
|
||||||
|
sopts <- parseServerOpts
|
||||||
|
Warp.run (serverPort sopts) (timeout (serverTimeout sopts) app)
|
||||||
|
_ -> parseOptions options defaultOpts >>= convertWithOpts
|
||||||
|
|
|
@ -12,16 +12,17 @@ date: August 15, 2022
|
||||||
|
|
||||||
`pandoc-server` is a web server that can perform pandoc
|
`pandoc-server` is a web server that can perform pandoc
|
||||||
conversions. It can be used either as a running server
|
conversions. It can be used either as a running server
|
||||||
or as a CGI program. To use `pandoc-server` as a CGI
|
or as a CGI program.
|
||||||
program, rename it (or symlink it) as `pandoc-server.cgi`.
|
|
||||||
(Note: if you symlink it, you may need to adjust your
|
To use `pandoc-server` as a CGI program, rename it (or symlink
|
||||||
webserver's configuration in order to allow it to follow
|
it) as `pandoc-server.cgi`. (Note: if you symlink it, you may
|
||||||
symlinks for the CGI script.)
|
need to adjust your webserver's configuration in order to allow
|
||||||
|
it to follow symlinks for the CGI script.)
|
||||||
|
|
||||||
All pandoc functions are run in the PandocPure monad, which
|
All pandoc functions are run in the PandocPure monad, which
|
||||||
ensures that they can do no I/O operations on the server.
|
ensures that they can do no I/O operations on the server.
|
||||||
This should provide a high degree of security. It does,
|
This should provide a high degree of security. This security
|
||||||
however, impose certain limitations:
|
does, however, impose certain limitations:
|
||||||
|
|
||||||
- PDFs cannot be produced.
|
- PDFs cannot be produced.
|
||||||
|
|
||||||
|
@ -85,17 +86,38 @@ the first one given is the default.
|
||||||
: The output format, possibly with extensions, just as it is
|
: The output format, possibly with extensions, just as it is
|
||||||
specified on the pandoc command line.
|
specified on the pandoc command line.
|
||||||
|
|
||||||
`wrapText` (`"auto"|"preserve"|"none"`)
|
`shift-heading-level-by` (integer, default 0)
|
||||||
|
|
||||||
: Text wrapping option: either `"auto"` (automatic
|
: Increase or decrease the level of all headings.
|
||||||
hard-wrapping to fit within a column width), `"preserve"`
|
|
||||||
(insert newlines where they are present in the source),
|
|
||||||
or `"none"` (don't insert any unnecessary newlines at all).
|
|
||||||
|
|
||||||
`columns` (integer, default 72)
|
`indented-code-classes` (array of strings)
|
||||||
|
|
||||||
: Column width (affects text wrapping and calculation of
|
: List of classes to be applied to indented Markdown code blocks.
|
||||||
table column widths in plain text formats)
|
|
||||||
|
`default-image-extension` (string)
|
||||||
|
|
||||||
|
: Extension to be applied to image sources that lack extensions
|
||||||
|
(e.g. `".jpg"`).
|
||||||
|
|
||||||
|
`metadata` (JSON map)
|
||||||
|
|
||||||
|
: String-valued metadata.
|
||||||
|
|
||||||
|
`tab-stop` (integer, default 4)
|
||||||
|
|
||||||
|
: Tab stop (spaces per tab).
|
||||||
|
|
||||||
|
`track-changes` (`"accept"|"reject"|"all"`)
|
||||||
|
|
||||||
|
: Specifies what to do with insertions, deletions, and
|
||||||
|
comments produced by the MS Word "Track Changes" feature. Only
|
||||||
|
affects docx input.
|
||||||
|
|
||||||
|
`abbreviations` (file path)
|
||||||
|
|
||||||
|
: List of strings to be regarded as abbreviations when
|
||||||
|
parsing Markdown. See `--abbreviations` in `pandoc(1)` for
|
||||||
|
details.
|
||||||
|
|
||||||
`standalone` (boolean, default false)
|
`standalone` (boolean, default false)
|
||||||
|
|
||||||
|
@ -108,119 +130,7 @@ the first one given is the default.
|
||||||
: String contents of a document template (see Templates in
|
: String contents of a document template (see Templates in
|
||||||
`pandoc(1)` for the format).
|
`pandoc(1)` for the format).
|
||||||
|
|
||||||
`tabStop` (integer, default 4)
|
`variables` (JSON map)
|
||||||
|
|
||||||
: Tab stop (spaces per tab).
|
|
||||||
|
|
||||||
`indentedCodeClasses` (array of strings)
|
|
||||||
|
|
||||||
: List of classes to be applied to indented Markdown code blocks.
|
|
||||||
|
|
||||||
`abbreviations` (array of strings)
|
|
||||||
|
|
||||||
: List of strings to be regarded as abbreviations when
|
|
||||||
parsing Markdown. See `--abbreviations` in `pandoc(1)` for
|
|
||||||
details.
|
|
||||||
|
|
||||||
`defaultImageExtension` (string)
|
|
||||||
|
|
||||||
: Extension to be applied to image sources that lack extensions
|
|
||||||
(e.g. `".jpg"`).
|
|
||||||
|
|
||||||
`trackChanges` (`"accept"|"reject"|"all"`)
|
|
||||||
|
|
||||||
: Specifies what to do with insertions, deletions, and
|
|
||||||
comments produced by the MS Word "Track Changes" feature. Only
|
|
||||||
affects docx input.
|
|
||||||
|
|
||||||
`stripComments` (boolean, default false)
|
|
||||||
|
|
||||||
: Causes HTML comments to be stripped in Markdown or Textile
|
|
||||||
source, instead of being passed through to the output format.
|
|
||||||
|
|
||||||
`citeproc` (boolean, default false)
|
|
||||||
|
|
||||||
: Causes citations to be processed using citeproc. See
|
|
||||||
Citations in `pandoc(1)` for details.
|
|
||||||
|
|
||||||
`citeMethod` (`"citeproc"|"natbib"|"biblatex"`)
|
|
||||||
|
|
||||||
: Determines how citations are formatted in LaTeX output.
|
|
||||||
|
|
||||||
`tableOfContents` (boolean, default false)
|
|
||||||
|
|
||||||
: Include a table of contents (in supported formats).
|
|
||||||
|
|
||||||
`tocDepth` (integer, default 3)
|
|
||||||
|
|
||||||
: Depth of sections to include in the table of contents.
|
|
||||||
|
|
||||||
`numberSections` (boolean, default false)
|
|
||||||
|
|
||||||
: Automatically number sections (in supported formats).
|
|
||||||
|
|
||||||
`numberOffset` (array of integers)
|
|
||||||
|
|
||||||
: Offsets to be added to each component of the section number.
|
|
||||||
For example, `[1]` will cause the first section to be
|
|
||||||
numbered "2" and the first subsection "2.1"; `[0,1]` will
|
|
||||||
cause the first section to be numbered "1" and the first
|
|
||||||
subsection "1.2."
|
|
||||||
|
|
||||||
`identifierPrefix` (string)
|
|
||||||
|
|
||||||
: Prefix to be added to all automatically-generated identifiers.
|
|
||||||
|
|
||||||
`sectionDivs` (boolean, default false)
|
|
||||||
|
|
||||||
: Arrange the document into a hierarchy of nested sections
|
|
||||||
based on the headings.
|
|
||||||
|
|
||||||
`htmlQTags` (boolean, default false)
|
|
||||||
|
|
||||||
: Use `<q>` elements in HTML instead of literal quotation marks.
|
|
||||||
|
|
||||||
`listings` (boolean, default false)
|
|
||||||
|
|
||||||
: Use the `listings` package to format code in LaTeX output.
|
|
||||||
|
|
||||||
`referenceLinks` (boolean, default false)
|
|
||||||
|
|
||||||
: Create reference links rather than inline links in Markdown output.
|
|
||||||
|
|
||||||
`setextHeaders` (boolean, default false)
|
|
||||||
|
|
||||||
: Use Setext (underlined) headings instead of ATX (`#`-prefixed)
|
|
||||||
in Markdown output.
|
|
||||||
|
|
||||||
`preferAscii` (boolean, default false)
|
|
||||||
|
|
||||||
: Use entities and escapes when possible to avoid non-ASCII
|
|
||||||
characters in the output.
|
|
||||||
|
|
||||||
`referenceLocation` (`"document"|"section"|"block"`)
|
|
||||||
|
|
||||||
: Determines whether link references and footnotes are placed
|
|
||||||
at the end of the document, the end of the section, or the
|
|
||||||
end of the block (e.g. paragraph), in
|
|
||||||
certain formats. (See `pandoc(1)` under `--reference-location`.)
|
|
||||||
|
|
||||||
|
|
||||||
`topLevelDivision` (`"default"|"part"|"chapter"|"section"`)
|
|
||||||
|
|
||||||
: Determines how top-level headings are interpreted in
|
|
||||||
LaTeX, ConTeXt, DocBook, and TEI. The `"default"` value
|
|
||||||
tries to choose the best interpretation based on heuristics.
|
|
||||||
|
|
||||||
`emailObfuscation` (`"none"|"references"|"javascript"`)
|
|
||||||
|
|
||||||
: Determines how email addresses are obfuscated in HTML.
|
|
||||||
|
|
||||||
`htmlMathMethod` (`"plain"|"webtex"|"gladtex"|"mathml"|"mathjax"|"katex"`)
|
|
||||||
|
|
||||||
: Determines how math is represented in HTML.
|
|
||||||
|
|
||||||
`variables` (JSON mapping)
|
|
||||||
|
|
||||||
: Variables to be interpolated in the template. (See Templates
|
: Variables to be interpolated in the template. (See Templates
|
||||||
in `pandoc(1)`.)
|
in `pandoc(1)`.)
|
||||||
|
@ -230,17 +140,32 @@ the first one given is the default.
|
||||||
: Dots-per-inch to use for conversions between pixels and
|
: Dots-per-inch to use for conversions between pixels and
|
||||||
other measurements (for image sizes).
|
other measurements (for image sizes).
|
||||||
|
|
||||||
`incremental` (boolean, default false)
|
`wrap` (`"auto"|"preserve"|"none"`)
|
||||||
|
|
||||||
: If true, lists appear incrementally by default in slide shows.
|
: Text wrapping option: either `"auto"` (automatic
|
||||||
|
hard-wrapping to fit within a column width), `"preserve"`
|
||||||
|
(insert newlines where they are present in the source),
|
||||||
|
or `"none"` (don't insert any unnecessary newlines at all).
|
||||||
|
|
||||||
`slideLevel` (integer)
|
`columns` (integer, default 72)
|
||||||
|
|
||||||
: Heading level that deterimes slide divisions in slide shows.
|
: Column width (affects text wrapping and calculation of
|
||||||
The default is to pick the highest heading level under which
|
table column widths in plain text formats)
|
||||||
there is body text.
|
|
||||||
|
|
||||||
`highlightStyle` (string, default `"pygments"`)
|
`table-of-contents` (boolean, default false)
|
||||||
|
|
||||||
|
: Include a table of contents (in supported formats).
|
||||||
|
|
||||||
|
`toc-depth` (integer, default 3)
|
||||||
|
|
||||||
|
: Depth of sections to include in the table of contents.
|
||||||
|
|
||||||
|
`strip-comments` (boolean, default false)
|
||||||
|
|
||||||
|
: Causes HTML comments to be stripped in Markdown or Textile
|
||||||
|
source, instead of being passed through to the output format.
|
||||||
|
|
||||||
|
`highlight-style` (string, default `"pygments"`)
|
||||||
|
|
||||||
: Specify the style to use for syntax highlighting of code.
|
: Specify the style to use for syntax highlighting of code.
|
||||||
Standard styles are `"pygments"` (the default), `"kate"`,
|
Standard styles are `"pygments"` (the default), `"kate"`,
|
||||||
|
@ -250,27 +175,149 @@ the first one given is the default.
|
||||||
case, the relevant file contents must also be included
|
case, the relevant file contents must also be included
|
||||||
in `files`, see below).
|
in `files`, see below).
|
||||||
|
|
||||||
`epubMetadata` (string)
|
`embed-resources`
|
||||||
|
|
||||||
: Dublin core XML elements to be used for EPUB metadata.
|
: Embed images, scripts, styles and other resources in an HTML
|
||||||
|
document using `data` URIs. Note that this will not work
|
||||||
|
unless the contents of all external resources are included
|
||||||
|
under `files`.
|
||||||
|
|
||||||
`epubChapterLevel` (integer, default 1)
|
`html-q-tags` (boolean, default false)
|
||||||
|
|
||||||
|
: Use `<q>` elements in HTML instead of literal quotation marks.
|
||||||
|
|
||||||
|
`ascii` (boolean, default false)
|
||||||
|
|
||||||
|
: Use entities and escapes when possible to avoid non-ASCII
|
||||||
|
characters in the output.
|
||||||
|
|
||||||
|
`reference-links` (boolean, default false)
|
||||||
|
|
||||||
|
: Create reference links rather than inline links in Markdown output.
|
||||||
|
|
||||||
|
`referenceLocation` (`"document"|"section"|"block"`)
|
||||||
|
|
||||||
|
: Determines whether link references and footnotes are placed
|
||||||
|
at the end of the document, the end of the section, or the
|
||||||
|
end of the block (e.g. paragraph), in
|
||||||
|
certain formats. (See `pandoc(1)` under `--reference-location`.)
|
||||||
|
|
||||||
|
`setext-headers` (boolean, default false)
|
||||||
|
|
||||||
|
: Use Setext (underlined) headings instead of ATX (`#`-prefixed)
|
||||||
|
in Markdown output.
|
||||||
|
|
||||||
|
`top-level-division` (`"default"|"part"|"chapter"|"section"`)
|
||||||
|
|
||||||
|
: Determines how top-level headings are interpreted in
|
||||||
|
LaTeX, ConTeXt, DocBook, and TEI. The `"default"` value
|
||||||
|
tries to choose the best interpretation based on heuristics.
|
||||||
|
|
||||||
|
`number-sections` (boolean, default false)
|
||||||
|
|
||||||
|
: Automatically number sections (in supported formats).
|
||||||
|
|
||||||
|
|
||||||
|
`number-offset` (array of integers)
|
||||||
|
|
||||||
|
: Offsets to be added to each component of the section number.
|
||||||
|
For example, `[1]` will cause the first section to be
|
||||||
|
numbered "2" and the first subsection "2.1"; `[0,1]` will
|
||||||
|
cause the first section to be numbered "1" and the first
|
||||||
|
subsection "1.2."
|
||||||
|
|
||||||
|
`html-math-method` (`"plain"|"webtex"|"gladtex"|"mathml"|"mathjax"|"katex"`)
|
||||||
|
|
||||||
|
: Determines how math is represented in HTML.
|
||||||
|
|
||||||
|
`listings` (boolean, default false)
|
||||||
|
|
||||||
|
: Use the `listings` package to format code in LaTeX output.
|
||||||
|
|
||||||
|
`incremental` (boolean, default false)
|
||||||
|
|
||||||
|
: If true, lists appear incrementally by default in slide shows.
|
||||||
|
|
||||||
|
`slide-level` (integer)
|
||||||
|
|
||||||
|
: Heading level that deterimes slide divisions in slide shows.
|
||||||
|
The default is to pick the highest heading level under which
|
||||||
|
there is body text.
|
||||||
|
|
||||||
|
`section-divs` (boolean, default false)
|
||||||
|
|
||||||
|
: Arrange the document into a hierarchy of nested sections
|
||||||
|
based on the headings.
|
||||||
|
|
||||||
|
`email-obfuscation` (`"none"|"references"|"javascript"`)
|
||||||
|
|
||||||
|
: Determines how email addresses are obfuscated in HTML.
|
||||||
|
|
||||||
|
`identifier-prefix` (string)
|
||||||
|
|
||||||
|
: Prefix to be added to all automatically-generated identifiers.
|
||||||
|
|
||||||
|
`title-prefix` (string)
|
||||||
|
|
||||||
|
: Prefix to be added to the title in the HTML header.
|
||||||
|
|
||||||
|
`reference-doc` (file path)
|
||||||
|
|
||||||
|
: Reference doc to use in creating `docx` or `odt` or `pptx`.
|
||||||
|
See `pandoc(1)` under `--reference-doc` for details.
|
||||||
|
The contents of the file must be included under `files`.
|
||||||
|
|
||||||
|
`epub-cover-image` (file path)
|
||||||
|
|
||||||
|
: Cover image for EPUB.
|
||||||
|
The contents of the file must be included under `files`.
|
||||||
|
|
||||||
|
`epub-metadata` (file path)
|
||||||
|
|
||||||
|
: Path of file containing Dublin core XML elements to be used for
|
||||||
|
EPUB metadata. The contents of the file must be included
|
||||||
|
under `files`.
|
||||||
|
|
||||||
|
`epub-chapter-level` (integer, default 1)
|
||||||
|
|
||||||
: Heading level at which chapter splitting occurs in EPUBs.
|
: Heading level at which chapter splitting occurs in EPUBs.
|
||||||
|
|
||||||
`epubSubdirectory` (string, default "EPUB")
|
`epub-subdirectory` (string, default "EPUB")
|
||||||
|
|
||||||
: Name of content subdirectory in the EPUB container.
|
: Name of content subdirectory in the EPUB container.
|
||||||
|
|
||||||
`epubFonts` (array of file paths)
|
`epub-fonts` (array of file paths)
|
||||||
|
|
||||||
: Fonts to include in the EPUB. The fonts themselves must be
|
: Fonts to include in the EPUB. The fonts themselves must be
|
||||||
included in `files` (see below).
|
included in `files` (see below).
|
||||||
|
|
||||||
`referenceDoc` (file path)
|
`ipynb-output` (`"best"|"all"|"none"`)
|
||||||
|
|
||||||
: Reference doc to use in creating `docx` or `odt` or `pptx`.
|
: Determines how ipynb output cells are treated. `all` means
|
||||||
See `pandoc(1)` under `--reference-doc` for details.
|
that all of the data formats included in the original are
|
||||||
|
preserved. `none` means that the contents of data cells
|
||||||
|
are omitted. `best` causes pandoc to try to pick the
|
||||||
|
richest data block in each output cell that is compatible
|
||||||
|
with the output format.
|
||||||
|
|
||||||
|
`citeproc` (boolean, default false)
|
||||||
|
|
||||||
|
: Causes citations to be processed using citeproc. See
|
||||||
|
Citations in `pandoc(1)` for details.
|
||||||
|
|
||||||
|
`bibliography` (array of file paths)
|
||||||
|
|
||||||
|
: Files containing bibliographic data. The contents of the
|
||||||
|
files must be included in `files`.
|
||||||
|
|
||||||
|
`csl` (file path)
|
||||||
|
|
||||||
|
: CSL style file. The contents of the file must be included
|
||||||
|
in `files`.
|
||||||
|
|
||||||
|
`cite-method` (`"citeproc"|"natbib"|"biblatex"`)
|
||||||
|
|
||||||
|
: Determines how citations are formatted in LaTeX output.
|
||||||
|
|
||||||
`files` (JSON mapping of file paths to base64-encoded strings)
|
`files` (JSON mapping of file paths to base64-encoded strings)
|
||||||
|
|
||||||
|
@ -280,7 +327,6 @@ the first one given is the default.
|
||||||
left as it is, unless it is *also* valid base 64 data,
|
left as it is, unless it is *also* valid base 64 data,
|
||||||
in which case it will be interpreted that way.
|
in which case it will be interpreted that way.
|
||||||
|
|
||||||
|
|
||||||
## `/batch` endpoint
|
## `/batch` endpoint
|
||||||
|
|
||||||
The `/batch` endpoint behaves like the root endpoint,
|
The `/batch` endpoint behaves like the root endpoint,
|
|
@ -27,42 +27,44 @@ ghc --version
|
||||||
|
|
||||||
cabal update
|
cabal update
|
||||||
cabal clean
|
cabal clean
|
||||||
cabal configure -fserver -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc pandoc-server
|
cabal configure -f-export-dynamic -fembed_data_files --enable-executable-static --ghc-options '-j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread' pandoc
|
||||||
cabal build -j4
|
cabal build -j4
|
||||||
for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f $ARTIFACTS/; done
|
for f in $(find dist-newstyle -name 'pandoc' -type f -perm /400); do cp $f $ARTIFACTS/; done
|
||||||
for f in $(find dist-newstyle -name 'pandoc-server' -type f -perm /400); do cp $f /$ARTIFACTS/; done
|
|
||||||
|
|
||||||
# Confirm that we have static builds
|
# Confirm that we have static builds
|
||||||
file $ARTIFACTS/pandoc | grep "statically linked"
|
file $ARTIFACTS/pandoc | grep "statically linked"
|
||||||
file $ARTIFACTS/pandoc-server | grep "statically linked"
|
|
||||||
|
|
||||||
# make deb for EXE
|
|
||||||
make_deb() {
|
make_deb() {
|
||||||
VERSION=`$ARTIFACTS/$EXE --version | awk '{print $2; exit;}'`
|
VERSION=`$ARTIFACTS/pandoc --version | awk '{print $2; exit;}'`
|
||||||
REVISION=${REVISION:-1}
|
REVISION=${REVISION:-1}
|
||||||
DEBVER=$VERSION-$REVISION
|
DEBVER=$VERSION-$REVISION
|
||||||
BASE=$EXE-$DEBVER-$ARCHITECTURE
|
BASE=pandoc-$DEBVER-$ARCHITECTURE
|
||||||
DIST=/mnt/$BASE
|
DIST=/mnt/$BASE
|
||||||
DEST=$DIST/usr
|
DEST=$DIST/usr
|
||||||
COPYRIGHT=$DEST/share/doc/$EXE/copyright
|
COPYRIGHT=$DEST/share/doc/pandoc/copyright
|
||||||
|
|
||||||
cd /mnt
|
cd /mnt
|
||||||
mkdir -p $DEST/bin
|
mkdir -p $DEST/bin
|
||||||
mkdir -p $DEST/share/man/man1
|
mkdir -p $DEST/share/man/man1
|
||||||
mkdir -p $DEST/share/doc/$EXE
|
mkdir -p $DEST/share/doc/pandoc
|
||||||
|
|
||||||
find $DIST -type d | xargs chmod 755
|
find $DIST -type d | xargs chmod 755
|
||||||
cp $ARTIFACTS/$EXE $DEST/bin/
|
cp $ARTIFACTS/pandoc $DEST/bin/
|
||||||
strip $DEST/bin/$EXE
|
cd $DEST/bin
|
||||||
cp /mnt/man/$EXE.1 $DEST/share/man/man1/$EXE.1
|
strip pandoc
|
||||||
gzip -9 $DEST/share/man/man1/$EXE.1
|
ln -s pandoc pandoc-server
|
||||||
|
cd /mnt
|
||||||
|
cp /mnt/man/pandoc.1 $DEST/share/man/man1/pandoc.1
|
||||||
|
gzip -9 $DEST/share/man/man1/pandoc.1
|
||||||
|
cp /mnt/man/pandoc-server.1 $DEST/share/man/man1/pandoc-server.1
|
||||||
|
gzip -9 $DEST/share/man/man1/pandoc-server.1
|
||||||
|
|
||||||
cp /mnt/COPYRIGHT $COPYRIGHT
|
cp /mnt/COPYRIGHT $COPYRIGHT
|
||||||
echo "" >> $COPYRIGHT
|
echo "" >> $COPYRIGHT
|
||||||
|
|
||||||
INSTALLED_SIZE=$(du -k -s $DEST | awk '{print $1}')
|
INSTALLED_SIZE=$(du -k -s $DEST | awk '{print $1}')
|
||||||
mkdir $DIST/DEBIAN
|
mkdir $DIST/DEBIAN
|
||||||
perl -pe "s/VERSION/$DEBVER/" /mnt/linux/$EXE.control.in | \
|
perl -pe "s/VERSION/$DEBVER/" /mnt/linux/control.in | \
|
||||||
perl -pe "s/ARCHITECTURE/$ARCHITECTURE/" | \
|
perl -pe "s/ARCHITECTURE/$ARCHITECTURE/" | \
|
||||||
perl -pe "s/INSTALLED_SIZE/$INSTALLED_SIZE/" \
|
perl -pe "s/INSTALLED_SIZE/$INSTALLED_SIZE/" \
|
||||||
> $DIST/DEBIAN/control
|
> $DIST/DEBIAN/control
|
||||||
|
@ -73,26 +75,28 @@ make_deb() {
|
||||||
cp $BASE.deb $ARTIFACTS/
|
cp $BASE.deb $ARTIFACTS/
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make tarball for EXE
|
# Make tarball for pandoc
|
||||||
make_tarball() {
|
make_tarball() {
|
||||||
TARGET=$EXE-$VERSION
|
TARGET=pandoc-$VERSION
|
||||||
cd $ARTIFACTS
|
cd $ARTIFACTS
|
||||||
rm -rf $TARGET
|
rm -rf $TARGET
|
||||||
mkdir $TARGET
|
mkdir $TARGET
|
||||||
mkdir $TARGET/bin $TARGET/share $TARGET/share/man $TARGET/share/man/man1
|
mkdir $TARGET/bin $TARGET/share $TARGET/share/man $TARGET/share/man/man1
|
||||||
cp /mnt/man/$EXE.1 $TARGET/share/man/man1
|
cp /mnt/man/pandoc.1 $TARGET/share/man/man1
|
||||||
mv $EXE $TARGET/bin
|
cp /mnt/man/pandoc-server.1 $TARGET/share/man/man1
|
||||||
strip $TARGET/bin/$EXE
|
mv pandoc $TARGET/bin
|
||||||
gzip -9 $TARGET/share/man/man1/$EXE.1
|
cd $TARGET/bin
|
||||||
|
strip pandoc
|
||||||
|
ln -s pandoc pandoc-server
|
||||||
|
cd $ARTIFACTS
|
||||||
|
gzip -9 $TARGET/share/man/man1/pandoc.1
|
||||||
|
gzip -9 $TARGET/share/man/man1/pandoc-server.1
|
||||||
|
|
||||||
tar cvzf $TARGET-linux-$ARCHITECTURE.tar.gz $TARGET
|
tar cvzf $TARGET-linux-$ARCHITECTURE.tar.gz $TARGET
|
||||||
rm -r $TARGET
|
rm -r $TARGET
|
||||||
}
|
}
|
||||||
|
|
||||||
for EXE in pandoc pandoc-server
|
make_deb
|
||||||
do
|
make_tarball
|
||||||
make_deb
|
|
||||||
make_tarball
|
|
||||||
done
|
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
Package: pandoc-server
|
|
||||||
Version: VERSION
|
|
||||||
Section: text
|
|
||||||
Priority: optional
|
|
||||||
Architecture: ARCHITECTURE
|
|
||||||
Installed-Size: INSTALLED_SIZE
|
|
||||||
Depends: libc6 (>= 2.13), libgmp10, zlib1g (>= 1:1.1.4)
|
|
||||||
Maintainer: John MacFarlane <jgm@berkeley.edu>
|
|
||||||
Description: HTTP server for pandoc document format converter
|
|
38
pandoc.cabal
38
pandoc.cabal
|
@ -429,10 +429,6 @@ flag lua53
|
||||||
Description: Embed Lua 5.3 instead of 5.4.
|
Description: Embed Lua 5.3 instead of 5.4.
|
||||||
Default: False
|
Default: False
|
||||||
|
|
||||||
flag server
|
|
||||||
Description: Build pandoc-server executable.
|
|
||||||
Default: False
|
|
||||||
|
|
||||||
flag nightly
|
flag nightly
|
||||||
Description: Add '-nightly-COMPILEDATE' to the output of '--version'.
|
Description: Add '-nightly-COMPILEDATE' to the output of '--version'.
|
||||||
Default: False
|
Default: False
|
||||||
|
@ -538,7 +534,10 @@ library
|
||||||
xml-types >= 0.3 && < 0.4,
|
xml-types >= 0.3 && < 0.4,
|
||||||
yaml >= 0.11 && < 0.12,
|
yaml >= 0.11 && < 0.12,
|
||||||
zip-archive >= 0.2.3.4 && < 0.5,
|
zip-archive >= 0.2.3.4 && < 0.5,
|
||||||
zlib >= 0.5 && < 0.7
|
zlib >= 0.5 && < 0.7,
|
||||||
|
servant-server,
|
||||||
|
wai >= 0.3
|
||||||
|
|
||||||
if !os(windows)
|
if !os(windows)
|
||||||
build-depends: unix >= 2.4 && < 2.8
|
build-depends: unix >= 2.4 && < 2.8
|
||||||
if flag(nightly)
|
if flag(nightly)
|
||||||
|
@ -564,6 +563,7 @@ library
|
||||||
Text.Pandoc.MediaBag,
|
Text.Pandoc.MediaBag,
|
||||||
Text.Pandoc.Error,
|
Text.Pandoc.Error,
|
||||||
Text.Pandoc.Filter,
|
Text.Pandoc.Filter,
|
||||||
|
Text.Pandoc.Server,
|
||||||
Text.Pandoc.Readers,
|
Text.Pandoc.Readers,
|
||||||
Text.Pandoc.Readers.HTML,
|
Text.Pandoc.Readers.HTML,
|
||||||
Text.Pandoc.Readers.LaTeX,
|
Text.Pandoc.Readers.LaTeX,
|
||||||
|
@ -789,32 +789,8 @@ executable pandoc
|
||||||
main-is: pandoc.hs
|
main-is: pandoc.hs
|
||||||
buildable: True
|
buildable: True
|
||||||
other-modules: Paths_pandoc
|
other-modules: Paths_pandoc
|
||||||
|
build-depends: wai-extra >= 3.0.24,
|
||||||
executable pandoc-server
|
warp
|
||||||
import: common-executable
|
|
||||||
main-is: Main.hs
|
|
||||||
other-modules: PandocServer
|
|
||||||
hs-source-dirs: server
|
|
||||||
if flag(server)
|
|
||||||
build-depends: base,
|
|
||||||
pandoc,
|
|
||||||
aeson,
|
|
||||||
text,
|
|
||||||
containers,
|
|
||||||
data-default,
|
|
||||||
bytestring,
|
|
||||||
skylighting,
|
|
||||||
base64 >= 0.4,
|
|
||||||
doctemplates,
|
|
||||||
servant-server,
|
|
||||||
wai >= 0.3,
|
|
||||||
wai-extra >= 3.0.24,
|
|
||||||
warp,
|
|
||||||
optparse-applicative
|
|
||||||
|
|
||||||
buildable: True
|
|
||||||
else
|
|
||||||
buildable: False
|
|
||||||
|
|
||||||
test-suite test-pandoc
|
test-suite test-pandoc
|
||||||
import: common-executable
|
import: common-executable
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
module Main where
|
|
||||||
|
|
||||||
import PandocServer (app)
|
|
||||||
import Text.Pandoc (pandocVersion)
|
|
||||||
import Control.Monad (when)
|
|
||||||
import qualified Network.Wai.Handler.CGI as CGI
|
|
||||||
import qualified Network.Wai.Handler.Warp as Warp
|
|
||||||
import Network.Wai.Middleware.Timeout (timeout)
|
|
||||||
import System.Environment (getProgName)
|
|
||||||
import Options.Applicative
|
|
||||||
import System.Exit (exitWith, ExitCode(ExitSuccess))
|
|
||||||
import Data.Text as T
|
|
||||||
|
|
||||||
data Opts = Opts
|
|
||||||
{ optPort :: Warp.Port,
|
|
||||||
optTimeout :: Int, -- seconds
|
|
||||||
optVersion :: Bool }
|
|
||||||
|
|
||||||
options :: Parser Opts
|
|
||||||
options = Opts
|
|
||||||
<$> option auto
|
|
||||||
( long "port"
|
|
||||||
<> value 3030
|
|
||||||
<> metavar "PORT"
|
|
||||||
<> help "Port to serve on" )
|
|
||||||
<*> option auto
|
|
||||||
( long "timeout"
|
|
||||||
<> value 2
|
|
||||||
<> metavar "SECONDS"
|
|
||||||
<> help "Seconds timeout" )
|
|
||||||
<*> flag False True
|
|
||||||
( long "version"
|
|
||||||
<> help "Print version" )
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
progname <- getProgName
|
|
||||||
let optspec = info (options <**> helper)
|
|
||||||
( fullDesc
|
|
||||||
<> progDesc "Run a pandoc server"
|
|
||||||
<> header "pandoc-server - text conversion server" )
|
|
||||||
opts <- execParser optspec
|
|
||||||
|
|
||||||
when (optVersion opts) $ do
|
|
||||||
putStrLn $ progname <> " " <> T.unpack pandocVersion
|
|
||||||
exitWith ExitSuccess
|
|
||||||
|
|
||||||
let port = optPort opts
|
|
||||||
let app' = timeout (optTimeout opts) app
|
|
||||||
if progname == "pandoc-server.cgi"
|
|
||||||
then -- operate as a CGI script
|
|
||||||
CGI.run app'
|
|
||||||
else -- operate as a persistent server
|
|
||||||
Warp.run port app'
|
|
|
@ -1,301 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
module PandocServer
|
|
||||||
( app
|
|
||||||
, Params(..)
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Data.Aeson
|
|
||||||
import Data.Aeson.TH
|
|
||||||
import Network.Wai
|
|
||||||
import Servant
|
|
||||||
import Text.DocTemplates as DocTemplates
|
|
||||||
import Text.Pandoc
|
|
||||||
import Text.Pandoc.Citeproc (processCitations)
|
|
||||||
import Text.Pandoc.Highlighting (lookupHighlightingStyle)
|
|
||||||
import qualified Text.Pandoc.UTF8 as UTF8
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified Data.Text.Lazy as TL
|
|
||||||
import qualified Data.Text.Lazy.Encoding as TLE
|
|
||||||
import Data.Maybe (fromMaybe)
|
|
||||||
import Data.Char (isAlphaNum)
|
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import qualified Data.ByteString.Lazy as BL
|
|
||||||
import Data.ByteString.Base64 (decodeBase64, encodeBase64)
|
|
||||||
import Data.Default
|
|
||||||
import Data.Map (Map)
|
|
||||||
import Data.Set (Set)
|
|
||||||
import Skylighting (defaultSyntaxMap)
|
|
||||||
|
|
||||||
newtype Blob = Blob BL.ByteString
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Blob where
|
|
||||||
toJSON (Blob bs) = toJSON (encodeBase64 $ BL.toStrict bs)
|
|
||||||
|
|
||||||
instance FromJSON Blob where
|
|
||||||
parseJSON = withText "Blob" $ \t -> do
|
|
||||||
let inp = UTF8.fromText t
|
|
||||||
case decodeBase64 inp of
|
|
||||||
Right bs -> return $ Blob $ BL.fromStrict bs
|
|
||||||
Left _ -> -- treat as regular text
|
|
||||||
return $ Blob $ BL.fromStrict inp
|
|
||||||
|
|
||||||
-- This is the data to be supplied by the JSON payload
|
|
||||||
-- of requests. Maybe values may be omitted and will be
|
|
||||||
-- given default values.
|
|
||||||
data Params = Params
|
|
||||||
{ text :: Text
|
|
||||||
, from :: Maybe Text
|
|
||||||
, to :: Maybe Text
|
|
||||||
, wrapText :: Maybe WrapOption
|
|
||||||
, columns :: Maybe Int
|
|
||||||
, standalone :: Maybe Bool
|
|
||||||
, template :: Maybe Text
|
|
||||||
, tabStop :: Maybe Int
|
|
||||||
, indentedCodeClasses :: Maybe [Text]
|
|
||||||
, abbreviations :: Maybe (Set Text)
|
|
||||||
, defaultImageExtension :: Maybe Text
|
|
||||||
, trackChanges :: Maybe TrackChanges
|
|
||||||
, stripComments :: Maybe Bool
|
|
||||||
, citeproc :: Maybe Bool
|
|
||||||
, variables :: Maybe (DocTemplates.Context Text)
|
|
||||||
, tableOfContents :: Maybe Bool
|
|
||||||
, incremental :: Maybe Bool
|
|
||||||
, htmlMathMethod :: Maybe HTMLMathMethod
|
|
||||||
, numberSections :: Maybe Bool
|
|
||||||
, numberOffset :: Maybe [Int]
|
|
||||||
, sectionDivs :: Maybe Bool
|
|
||||||
, referenceLinks :: Maybe Bool
|
|
||||||
, dpi :: Maybe Int
|
|
||||||
, emailObfuscation :: Maybe ObfuscationMethod
|
|
||||||
, identifierPrefix :: Maybe Text
|
|
||||||
, citeMethod :: Maybe CiteMethod
|
|
||||||
, htmlQTags :: Maybe Bool
|
|
||||||
, slideLevel :: Maybe Int
|
|
||||||
, topLevelDivision :: Maybe TopLevelDivision
|
|
||||||
, listings :: Maybe Bool
|
|
||||||
, highlightStyle :: Maybe Text
|
|
||||||
, setextHeaders :: Maybe Bool
|
|
||||||
, epubSubdirectory :: Maybe Text
|
|
||||||
, epubFonts :: Maybe [FilePath]
|
|
||||||
, epubMetadata :: Maybe Text
|
|
||||||
, epubChapterLevel :: Maybe Int
|
|
||||||
, tocDepth :: Maybe Int
|
|
||||||
, referenceDoc :: Maybe FilePath
|
|
||||||
, referenceLocation :: Maybe ReferenceLocation
|
|
||||||
, preferAscii :: Maybe Bool
|
|
||||||
, files :: Maybe (Map FilePath Blob)
|
|
||||||
} deriving (Show)
|
|
||||||
|
|
||||||
instance Default Params where
|
|
||||||
def = Params
|
|
||||||
{ text = ""
|
|
||||||
, from = Nothing
|
|
||||||
, to = Nothing
|
|
||||||
, wrapText = Nothing
|
|
||||||
, columns = Nothing
|
|
||||||
, standalone = Nothing
|
|
||||||
, template = Nothing
|
|
||||||
, tabStop = Nothing
|
|
||||||
, indentedCodeClasses = Nothing
|
|
||||||
, abbreviations = Nothing
|
|
||||||
, defaultImageExtension = Nothing
|
|
||||||
, trackChanges = Nothing
|
|
||||||
, stripComments = Nothing
|
|
||||||
, citeproc = Nothing
|
|
||||||
, variables = Nothing
|
|
||||||
, tableOfContents = Nothing
|
|
||||||
, incremental = Nothing
|
|
||||||
, htmlMathMethod = Nothing
|
|
||||||
, numberSections = Nothing
|
|
||||||
, numberOffset = Nothing
|
|
||||||
, sectionDivs = Nothing
|
|
||||||
, referenceLinks = Nothing
|
|
||||||
, dpi = Nothing
|
|
||||||
, emailObfuscation = Nothing
|
|
||||||
, identifierPrefix = Nothing
|
|
||||||
, citeMethod = Nothing
|
|
||||||
, htmlQTags = Nothing
|
|
||||||
, slideLevel = Nothing
|
|
||||||
, topLevelDivision = Nothing
|
|
||||||
, listings = Nothing
|
|
||||||
, highlightStyle = Nothing
|
|
||||||
, setextHeaders = Nothing
|
|
||||||
, epubSubdirectory = Nothing
|
|
||||||
, epubMetadata = Nothing
|
|
||||||
, epubChapterLevel = Nothing
|
|
||||||
, epubFonts = Nothing
|
|
||||||
, tocDepth = Nothing
|
|
||||||
, referenceDoc = Nothing
|
|
||||||
, referenceLocation = Nothing
|
|
||||||
, preferAscii = Nothing
|
|
||||||
, files = Nothing
|
|
||||||
}
|
|
||||||
-- TODO:
|
|
||||||
-- shiftHeadingLevelBy
|
|
||||||
-- metadata
|
|
||||||
-- selfContained
|
|
||||||
-- embedResources
|
|
||||||
-- epubCoverImage
|
|
||||||
-- stripEmptyParagraphs
|
|
||||||
-- titlePrefix
|
|
||||||
-- ipynbOutput
|
|
||||||
-- eol
|
|
||||||
-- csl
|
|
||||||
-- bibliography
|
|
||||||
-- citationAbbreviations
|
|
||||||
|
|
||||||
-- Automatically derive code to convert to/from JSON.
|
|
||||||
$(deriveJSON defaultOptions ''Params)
|
|
||||||
|
|
||||||
-- This is the API. The "/convert" endpoint takes a request body
|
|
||||||
-- consisting of a JSON-encoded Params structure and responds to
|
|
||||||
-- Get requests with either plain text or JSON, depending on the
|
|
||||||
-- Accept header.
|
|
||||||
type API =
|
|
||||||
ReqBody '[JSON] Params :> Post '[PlainText, JSON] Text
|
|
||||||
:<|>
|
|
||||||
ReqBody '[JSON] Params :> Post '[OctetStream] BS.ByteString
|
|
||||||
:<|>
|
|
||||||
"batch" :> ReqBody '[JSON] [Params] :> Post '[JSON] [Text]
|
|
||||||
:<|>
|
|
||||||
"babelmark" :> QueryParam' '[Required] "text" Text :> QueryParam "from" Text :> QueryParam "to" Text :> QueryFlag "standalone" :> Get '[JSON] Value
|
|
||||||
:<|>
|
|
||||||
"version" :> Get '[PlainText, JSON] Text
|
|
||||||
|
|
||||||
app :: Application
|
|
||||||
app = serve api server
|
|
||||||
|
|
||||||
api :: Proxy API
|
|
||||||
api = Proxy
|
|
||||||
|
|
||||||
server :: Server API
|
|
||||||
server = convert
|
|
||||||
:<|> convertBytes
|
|
||||||
:<|> mapM convert
|
|
||||||
:<|> babelmark -- for babelmark which expects {"html": "", "version": ""}
|
|
||||||
:<|> pure pandocVersion
|
|
||||||
where
|
|
||||||
babelmark text' from' to' standalone' = do
|
|
||||||
res <- convert def{ text = text',
|
|
||||||
from = from', to = to',
|
|
||||||
standalone = Just standalone' }
|
|
||||||
return $ toJSON $ object [ "html" .= res, "version" .= pandocVersion ]
|
|
||||||
|
|
||||||
-- We use runPure for the pandoc conversions, which ensures that
|
|
||||||
-- they will do no IO. This makes the server safe to use. However,
|
|
||||||
-- it will mean that features requiring IO, like RST includes, will not work.
|
|
||||||
-- Changing this to
|
|
||||||
-- handleErr =<< liftIO (runIO (convert' params))
|
|
||||||
-- will allow the IO operations.
|
|
||||||
convert params = handleErr $
|
|
||||||
runPure (convert' id (encodeBase64 . BL.toStrict) params)
|
|
||||||
|
|
||||||
convertBytes params = handleErr $
|
|
||||||
runPure (convert' UTF8.fromText BL.toStrict params)
|
|
||||||
|
|
||||||
convert' :: PandocMonad m
|
|
||||||
=> (Text -> a) -> (BL.ByteString -> a) -> Params -> m a
|
|
||||||
convert' textHandler bsHandler params = do
|
|
||||||
let readerFormat = fromMaybe "markdown" $ from params
|
|
||||||
let writerFormat = fromMaybe "html" $ to params
|
|
||||||
(readerSpec, readerExts) <- getReader readerFormat
|
|
||||||
(writerSpec, writerExts) <- getWriter writerFormat
|
|
||||||
let binaryOutput = case writerSpec of
|
|
||||||
ByteStringWriter{} -> True
|
|
||||||
_ -> False
|
|
||||||
let isStandalone = fromMaybe binaryOutput (standalone params)
|
|
||||||
let toformat = T.toLower $ T.takeWhile isAlphaNum $ writerFormat
|
|
||||||
hlStyle <- traverse (lookupHighlightingStyle . T.unpack)
|
|
||||||
$ highlightStyle params
|
|
||||||
mbTemplate <- if isStandalone
|
|
||||||
then case template params of
|
|
||||||
Nothing -> Just <$>
|
|
||||||
compileDefaultTemplate toformat
|
|
||||||
Just t -> Just <$>
|
|
||||||
compileCustomTemplate toformat t
|
|
||||||
else return Nothing
|
|
||||||
let readeropts = def{ readerExtensions = readerExts
|
|
||||||
, readerStandalone = isStandalone
|
|
||||||
, readerTabStop = fromMaybe 4 (tabStop params)
|
|
||||||
, readerIndentedCodeClasses = fromMaybe []
|
|
||||||
(indentedCodeClasses params)
|
|
||||||
, readerAbbreviations =
|
|
||||||
fromMaybe mempty (abbreviations params)
|
|
||||||
, readerDefaultImageExtension =
|
|
||||||
fromMaybe mempty (defaultImageExtension params)
|
|
||||||
, readerTrackChanges =
|
|
||||||
fromMaybe AcceptChanges (trackChanges params)
|
|
||||||
, readerStripComments =
|
|
||||||
fromMaybe False (stripComments params)
|
|
||||||
}
|
|
||||||
let writeropts =
|
|
||||||
def{ writerExtensions = writerExts
|
|
||||||
, writerTabStop = fromMaybe 4 (tabStop params)
|
|
||||||
, writerWrapText = fromMaybe WrapAuto (wrapText params)
|
|
||||||
, writerColumns = fromMaybe 72 (columns params)
|
|
||||||
, writerTemplate = mbTemplate
|
|
||||||
, writerSyntaxMap = defaultSyntaxMap
|
|
||||||
, writerVariables = fromMaybe mempty (variables params)
|
|
||||||
, writerTableOfContents = fromMaybe False (tableOfContents params)
|
|
||||||
, writerIncremental = fromMaybe False (incremental params)
|
|
||||||
, writerHTMLMathMethod =
|
|
||||||
fromMaybe PlainMath (htmlMathMethod params)
|
|
||||||
, writerNumberSections = fromMaybe False (numberSections params)
|
|
||||||
, writerNumberOffset = fromMaybe [] (numberOffset params)
|
|
||||||
, writerSectionDivs = fromMaybe False (sectionDivs params)
|
|
||||||
, writerReferenceLinks = fromMaybe False (referenceLinks params)
|
|
||||||
, writerDpi = fromMaybe 96 (dpi params)
|
|
||||||
, writerEmailObfuscation =
|
|
||||||
fromMaybe NoObfuscation (emailObfuscation params)
|
|
||||||
, writerIdentifierPrefix =
|
|
||||||
fromMaybe mempty (identifierPrefix params)
|
|
||||||
, writerCiteMethod = fromMaybe Citeproc (citeMethod params)
|
|
||||||
, writerHtmlQTags = fromMaybe False (htmlQTags params)
|
|
||||||
, writerSlideLevel = slideLevel params
|
|
||||||
, writerTopLevelDivision =
|
|
||||||
fromMaybe TopLevelDefault (topLevelDivision params)
|
|
||||||
, writerListings = fromMaybe False (listings params)
|
|
||||||
, writerHighlightStyle = hlStyle
|
|
||||||
, writerSetextHeaders = fromMaybe False (setextHeaders params)
|
|
||||||
, writerEpubSubdirectory =
|
|
||||||
fromMaybe "EPUB" (epubSubdirectory params)
|
|
||||||
, writerEpubMetadata = epubMetadata params
|
|
||||||
, writerEpubFonts = fromMaybe [] (epubFonts params)
|
|
||||||
, writerEpubChapterLevel = fromMaybe 1 (epubChapterLevel params)
|
|
||||||
, writerTOCDepth = fromMaybe 3 (tocDepth params)
|
|
||||||
, writerReferenceDoc = referenceDoc params
|
|
||||||
, writerReferenceLocation =
|
|
||||||
fromMaybe EndOfDocument (referenceLocation params)
|
|
||||||
, writerPreferAscii = fromMaybe False (preferAscii params)
|
|
||||||
}
|
|
||||||
let reader = case readerSpec of
|
|
||||||
TextReader r -> r readeropts
|
|
||||||
ByteStringReader r -> \t -> do
|
|
||||||
let eitherbs = decodeBase64 $ UTF8.fromText t
|
|
||||||
case eitherbs of
|
|
||||||
Left errt -> throwError $ PandocSomeError errt
|
|
||||||
Right bs -> r readeropts $ BL.fromStrict bs
|
|
||||||
let writer = case writerSpec of
|
|
||||||
TextWriter w -> fmap textHandler . w writeropts
|
|
||||||
ByteStringWriter w -> fmap bsHandler . w writeropts
|
|
||||||
reader (text params) >>=
|
|
||||||
(if citeproc params == Just True
|
|
||||||
then processCitations
|
|
||||||
else return) >>=
|
|
||||||
writer
|
|
||||||
|
|
||||||
handleErr (Right t) = return t
|
|
||||||
handleErr (Left err) = throwError $
|
|
||||||
err500 { errBody = TLE.encodeUtf8 $ TL.fromStrict $ renderError err }
|
|
||||||
|
|
||||||
compileCustomTemplate toformat t = do
|
|
||||||
res <- runWithPartials $ compileTemplate ("custom." <> T.unpack toformat) t
|
|
||||||
case res of
|
|
||||||
Left e -> throwError $ PandocTemplateError (T.pack e)
|
|
||||||
Right tpl -> return tpl
|
|
357
src/Text/Pandoc/Server.hs
Normal file
357
src/Text/Pandoc/Server.hs
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Text.Pandoc.Server
|
||||||
|
( app
|
||||||
|
, ServerOpts(..)
|
||||||
|
, Params(..)
|
||||||
|
, Blob(..)
|
||||||
|
, parseServerOpts
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import Network.Wai
|
||||||
|
import Servant
|
||||||
|
import Text.DocTemplates as DocTemplates
|
||||||
|
import Text.Pandoc
|
||||||
|
import Text.Pandoc.Citeproc (processCitations)
|
||||||
|
import Text.Pandoc.Highlighting (lookupHighlightingStyle)
|
||||||
|
import qualified Text.Pandoc.UTF8 as UTF8
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Lazy as TL
|
||||||
|
import qualified Data.Text.Lazy.Encoding as TLE
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Char (isAlphaNum)
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import Data.ByteString.Base64 (decodeBase64, encodeBase64)
|
||||||
|
import Data.Default
|
||||||
|
import Control.Monad (when, foldM)
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
import Skylighting (defaultSyntaxMap)
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import System.Console.GetOpt
|
||||||
|
import System.Environment (getArgs, getProgName)
|
||||||
|
import qualified Control.Exception as E
|
||||||
|
import Text.Pandoc.Shared (safeStrRead, headerShift, filterIpynbOutput,
|
||||||
|
eastAsianLineBreakFilter, stripEmptyParagraphs)
|
||||||
|
import Text.Pandoc.App.Opt ( IpynbOutput (..), Opt(..), defaultOpts )
|
||||||
|
import Text.Pandoc.Filter (Filter(..))
|
||||||
|
import Text.Pandoc.Builder (setMeta)
|
||||||
|
import Text.Pandoc.SelfContained (makeSelfContained)
|
||||||
|
import System.Exit
|
||||||
|
|
||||||
|
data ServerOpts =
|
||||||
|
ServerOpts
|
||||||
|
{ serverPort :: Int
|
||||||
|
, serverTimeout :: Int }
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
defaultServerOpts :: ServerOpts
|
||||||
|
defaultServerOpts = ServerOpts { serverPort = 3030, serverTimeout = 2 }
|
||||||
|
|
||||||
|
cliOptions :: [OptDescr (ServerOpts -> IO ServerOpts)]
|
||||||
|
cliOptions =
|
||||||
|
[ Option ['p'] ["port"]
|
||||||
|
(ReqArg (\s opts -> case safeStrRead s of
|
||||||
|
Just i -> return opts{ serverPort = i }
|
||||||
|
Nothing ->
|
||||||
|
E.throwIO $ PandocOptionError $ T.pack
|
||||||
|
s <> " is not a number") "NUMBER")
|
||||||
|
"port number"
|
||||||
|
, Option ['t'] ["timeout"]
|
||||||
|
(ReqArg (\s opts -> case safeStrRead s of
|
||||||
|
Just i -> return opts{ serverTimeout = i }
|
||||||
|
Nothing ->
|
||||||
|
E.throwIO $ PandocOptionError $ T.pack
|
||||||
|
s <> " is not a number") "NUMBER")
|
||||||
|
"timeout (seconds)"
|
||||||
|
|
||||||
|
, Option ['h'] ["help"]
|
||||||
|
(NoArg (\_ -> do
|
||||||
|
prg <- getProgName
|
||||||
|
let header = "Usage: " <> prg <> " [OPTION...]"
|
||||||
|
putStrLn $ usageInfo header cliOptions
|
||||||
|
exitWith ExitSuccess))
|
||||||
|
"help message"
|
||||||
|
|
||||||
|
, Option ['v'] ["version"]
|
||||||
|
(NoArg (\_ -> do
|
||||||
|
prg <- getProgName
|
||||||
|
putStrLn $ prg <> " " <> T.unpack pandocVersion
|
||||||
|
exitWith ExitSuccess))
|
||||||
|
"version info"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
parseServerOpts :: IO ServerOpts
|
||||||
|
parseServerOpts = do
|
||||||
|
args <- getArgs
|
||||||
|
let handleUnknownOpt x = "Unknown option: " <> x
|
||||||
|
case getOpt' Permute cliOptions args of
|
||||||
|
(os, ns, unrecognizedOpts, es) -> do
|
||||||
|
when (not (null es) || not (null unrecognizedOpts)) $
|
||||||
|
E.throwIO $ PandocOptionError $ T.pack $
|
||||||
|
concat es ++ unlines (map handleUnknownOpt unrecognizedOpts) ++
|
||||||
|
("Try --help for more information.")
|
||||||
|
when (not (null ns)) $
|
||||||
|
E.throwIO $ PandocOptionError $ T.pack $
|
||||||
|
"Unknown arguments: " <> unwords ns
|
||||||
|
foldM (flip ($)) defaultServerOpts os
|
||||||
|
|
||||||
|
newtype Blob = Blob BL.ByteString
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
instance ToJSON Blob where
|
||||||
|
toJSON (Blob bs) = toJSON (encodeBase64 $ BL.toStrict bs)
|
||||||
|
|
||||||
|
instance FromJSON Blob where
|
||||||
|
parseJSON = withText "Blob" $ \t -> do
|
||||||
|
let inp = UTF8.fromText t
|
||||||
|
case decodeBase64 inp of
|
||||||
|
Right bs -> return $ Blob $ BL.fromStrict bs
|
||||||
|
Left _ -> -- treat as regular text
|
||||||
|
return $ Blob $ BL.fromStrict inp
|
||||||
|
|
||||||
|
-- This is the data to be supplied by the JSON payload
|
||||||
|
-- of requests. Maybe values may be omitted and will be
|
||||||
|
-- given default values.
|
||||||
|
data Params = Params
|
||||||
|
{ options :: Opt
|
||||||
|
, text :: Text
|
||||||
|
, files :: Maybe (M.Map FilePath Blob)
|
||||||
|
} deriving (Show)
|
||||||
|
|
||||||
|
instance Default Params where
|
||||||
|
def = Params
|
||||||
|
{ options = defaultOpts
|
||||||
|
, text = mempty
|
||||||
|
, files = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Automatically derive code to convert to/from JSON.
|
||||||
|
instance FromJSON Params where
|
||||||
|
parseJSON = withObject "Params" $ \o ->
|
||||||
|
Params
|
||||||
|
<$> parseJSON (Object o)
|
||||||
|
<*> o .: "text"
|
||||||
|
<*> o .:? "files"
|
||||||
|
|
||||||
|
|
||||||
|
-- This is the API. The "/convert" endpoint takes a request body
|
||||||
|
-- consisting of a JSON-encoded Params structure and responds to
|
||||||
|
-- Get requests with either plain text or JSON, depending on the
|
||||||
|
-- Accept header.
|
||||||
|
type API =
|
||||||
|
ReqBody '[JSON] Params :> Post '[PlainText, JSON] Text
|
||||||
|
:<|>
|
||||||
|
ReqBody '[JSON] Params :> Post '[OctetStream] BS.ByteString
|
||||||
|
:<|>
|
||||||
|
"batch" :> ReqBody '[JSON] [Params] :> Post '[JSON] [Text]
|
||||||
|
:<|>
|
||||||
|
"babelmark" :> QueryParam' '[Required] "text" Text :> QueryParam "from" Text :> QueryParam "to" Text :> QueryFlag "standalone" :> Get '[JSON] Value
|
||||||
|
:<|>
|
||||||
|
"version" :> Get '[PlainText, JSON] Text
|
||||||
|
|
||||||
|
app :: Application
|
||||||
|
app = serve api server
|
||||||
|
|
||||||
|
api :: Proxy API
|
||||||
|
api = Proxy
|
||||||
|
|
||||||
|
server :: Server API
|
||||||
|
server = convert
|
||||||
|
:<|> convertBytes
|
||||||
|
:<|> mapM convert
|
||||||
|
:<|> babelmark -- for babelmark which expects {"html": "", "version": ""}
|
||||||
|
:<|> pure pandocVersion
|
||||||
|
where
|
||||||
|
babelmark text' from' to' standalone' = do
|
||||||
|
res <- convert def{ text = text',
|
||||||
|
options = defaultOpts{
|
||||||
|
optFrom = from',
|
||||||
|
optTo = to',
|
||||||
|
optStandalone = standalone' }
|
||||||
|
}
|
||||||
|
return $ toJSON $ object [ "html" .= res, "version" .= pandocVersion ]
|
||||||
|
|
||||||
|
-- We use runPure for the pandoc conversions, which ensures that
|
||||||
|
-- they will do no IO. This makes the server safe to use. However,
|
||||||
|
-- it will mean that features requiring IO, like RST includes, will not work.
|
||||||
|
-- Changing this to
|
||||||
|
-- handleErr =<< liftIO (runIO (convert' params))
|
||||||
|
-- will allow the IO operations.
|
||||||
|
convert params = handleErr $
|
||||||
|
runPure (convert' id (encodeBase64 . BL.toStrict) params)
|
||||||
|
|
||||||
|
convertBytes params = handleErr $
|
||||||
|
runPure (convert' UTF8.fromText BL.toStrict params)
|
||||||
|
|
||||||
|
convert' :: (Text -> a) -> (BL.ByteString -> a) -> Params -> PandocPure a
|
||||||
|
convert' textHandler bsHandler params = do
|
||||||
|
curtime <- getCurrentTime
|
||||||
|
-- put files params in ersatz file system
|
||||||
|
let addFile :: FilePath -> Blob -> FileTree -> FileTree
|
||||||
|
addFile fp (Blob lbs) =
|
||||||
|
insertInFileTree fp FileInfo{ infoFileMTime = curtime
|
||||||
|
, infoFileContents = BL.toStrict lbs }
|
||||||
|
case files params of
|
||||||
|
Nothing -> return ()
|
||||||
|
Just fs -> do
|
||||||
|
let filetree = M.foldrWithKey addFile mempty fs
|
||||||
|
modifyPureState $ \st -> st{ stFiles = filetree }
|
||||||
|
|
||||||
|
let opts = options params
|
||||||
|
let readerFormat = fromMaybe "markdown" $ optFrom opts
|
||||||
|
let writerFormat = fromMaybe "html" $ optTo opts
|
||||||
|
(readerSpec, readerExts) <- getReader readerFormat
|
||||||
|
(writerSpec, writerExts) <- getWriter writerFormat
|
||||||
|
|
||||||
|
let isStandalone = optStandalone opts
|
||||||
|
let toformat = T.toLower $ T.takeWhile isAlphaNum $ writerFormat
|
||||||
|
hlStyle <- traverse (lookupHighlightingStyle . T.unpack)
|
||||||
|
$ optHighlightStyle opts
|
||||||
|
|
||||||
|
mbTemplate <- if isStandalone
|
||||||
|
then case optTemplate opts of
|
||||||
|
Nothing -> Just <$>
|
||||||
|
compileDefaultTemplate toformat
|
||||||
|
Just t -> Just <$>
|
||||||
|
compileCustomTemplate toformat t
|
||||||
|
else return Nothing
|
||||||
|
|
||||||
|
abbrevs <- Set.fromList . filter (not . T.null) . T.lines . UTF8.toText <$>
|
||||||
|
case optAbbreviations opts of
|
||||||
|
Nothing -> readDataFile "abbreviations"
|
||||||
|
Just f -> readFileStrict f
|
||||||
|
|
||||||
|
let readeropts = def{ readerExtensions = readerExts
|
||||||
|
, readerStandalone = isStandalone
|
||||||
|
, readerTabStop = optTabStop opts
|
||||||
|
, readerIndentedCodeClasses =
|
||||||
|
optIndentedCodeClasses opts
|
||||||
|
, readerAbbreviations = abbrevs
|
||||||
|
, readerDefaultImageExtension =
|
||||||
|
optDefaultImageExtension opts
|
||||||
|
, readerTrackChanges = optTrackChanges opts
|
||||||
|
, readerStripComments = optStripComments opts
|
||||||
|
}
|
||||||
|
let writeropts =
|
||||||
|
def{ writerExtensions = writerExts
|
||||||
|
, writerTabStop = optTabStop opts
|
||||||
|
, writerWrapText = optWrap opts
|
||||||
|
, writerColumns = optColumns opts
|
||||||
|
, writerTemplate = mbTemplate
|
||||||
|
, writerSyntaxMap = defaultSyntaxMap
|
||||||
|
, writerVariables = optVariables opts
|
||||||
|
, writerTableOfContents = optTableOfContents opts
|
||||||
|
, writerIncremental = optIncremental opts
|
||||||
|
, writerHTMLMathMethod = optHTMLMathMethod opts
|
||||||
|
, writerNumberSections = optNumberSections opts
|
||||||
|
, writerNumberOffset = optNumberOffset opts
|
||||||
|
, writerSectionDivs = optSectionDivs opts
|
||||||
|
, writerReferenceLinks = optReferenceLinks opts
|
||||||
|
, writerDpi = optDpi opts
|
||||||
|
, writerEmailObfuscation = optEmailObfuscation opts
|
||||||
|
, writerIdentifierPrefix = optIdentifierPrefix opts
|
||||||
|
, writerCiteMethod = optCiteMethod opts
|
||||||
|
, writerHtmlQTags = optHtmlQTags opts
|
||||||
|
, writerSlideLevel = optSlideLevel opts
|
||||||
|
, writerTopLevelDivision = optTopLevelDivision opts
|
||||||
|
, writerListings = optListings opts
|
||||||
|
, writerHighlightStyle = hlStyle
|
||||||
|
, writerSetextHeaders = optSetextHeaders opts
|
||||||
|
, writerEpubSubdirectory = T.pack $ optEpubSubdirectory opts
|
||||||
|
, writerEpubMetadata = T.pack <$> optEpubMetadata opts
|
||||||
|
, writerEpubFonts = optEpubFonts opts
|
||||||
|
, writerEpubChapterLevel = optEpubChapterLevel opts
|
||||||
|
, writerTOCDepth = optTOCDepth opts
|
||||||
|
, writerReferenceDoc = optReferenceDoc opts
|
||||||
|
, writerReferenceLocation = optReferenceLocation opts
|
||||||
|
, writerPreferAscii = optAscii opts
|
||||||
|
}
|
||||||
|
let reader = case readerSpec of
|
||||||
|
TextReader r -> r readeropts
|
||||||
|
ByteStringReader r -> \t -> do
|
||||||
|
let eitherbs = decodeBase64 $ UTF8.fromText t
|
||||||
|
case eitherbs of
|
||||||
|
Left errt -> throwError $ PandocSomeError errt
|
||||||
|
Right bs -> r readeropts $ BL.fromStrict bs
|
||||||
|
let writer = case writerSpec of
|
||||||
|
TextWriter w ->
|
||||||
|
fmap textHandler .
|
||||||
|
(\d -> w writeropts d >>=
|
||||||
|
if optEmbedResources opts && htmlFormat (optTo opts)
|
||||||
|
then makeSelfContained
|
||||||
|
else return)
|
||||||
|
ByteStringWriter w -> fmap bsHandler . w writeropts
|
||||||
|
|
||||||
|
let transforms :: Pandoc -> Pandoc
|
||||||
|
transforms = (case optShiftHeadingLevelBy opts of
|
||||||
|
0 -> id
|
||||||
|
x -> headerShift x) .
|
||||||
|
(case optStripEmptyParagraphs opts of
|
||||||
|
True -> stripEmptyParagraphs
|
||||||
|
False -> id) .
|
||||||
|
(if extensionEnabled Ext_east_asian_line_breaks
|
||||||
|
readerExts &&
|
||||||
|
not (extensionEnabled Ext_east_asian_line_breaks
|
||||||
|
writerExts &&
|
||||||
|
optWrap opts == WrapPreserve)
|
||||||
|
then eastAsianLineBreakFilter
|
||||||
|
else id) .
|
||||||
|
(case optIpynbOutput opts of
|
||||||
|
IpynbOutputAll -> id
|
||||||
|
IpynbOutputNone -> filterIpynbOutput Nothing
|
||||||
|
IpynbOutputBest -> filterIpynbOutput (Just $
|
||||||
|
case optTo opts of
|
||||||
|
Just "latex" -> Format "latex"
|
||||||
|
Just "beamer" -> Format "latex"
|
||||||
|
Nothing -> Format "html"
|
||||||
|
Just f
|
||||||
|
| htmlFormat (optTo opts) -> Format "html"
|
||||||
|
| otherwise -> Format f))
|
||||||
|
|
||||||
|
let meta = (case optBibliography opts of
|
||||||
|
[] -> id
|
||||||
|
fs -> setMeta "bibliography" (MetaList
|
||||||
|
(map (MetaString . T.pack) fs))) .
|
||||||
|
maybe id (setMeta "csl" . MetaString . T.pack)
|
||||||
|
(optCSL opts) .
|
||||||
|
maybe id (setMeta "citation-abbreviations" . MetaString .
|
||||||
|
T.pack)
|
||||||
|
(optCitationAbbreviations opts) $
|
||||||
|
optMetadata opts
|
||||||
|
|
||||||
|
let addMetadata m' (Pandoc m bs) = Pandoc (m <> m') bs
|
||||||
|
|
||||||
|
let hasCiteprocFilter [] = False
|
||||||
|
hasCiteprocFilter (CiteprocFilter:_) = True
|
||||||
|
hasCiteprocFilter (_:xs) = hasCiteprocFilter xs
|
||||||
|
|
||||||
|
reader (text params) >>=
|
||||||
|
return . transforms . addMetadata meta >>=
|
||||||
|
(if hasCiteprocFilter (optFilters opts)
|
||||||
|
then processCitations
|
||||||
|
else return) >>=
|
||||||
|
writer
|
||||||
|
|
||||||
|
htmlFormat :: Maybe Text -> Bool
|
||||||
|
htmlFormat Nothing = True
|
||||||
|
htmlFormat (Just f) =
|
||||||
|
any (`T.isPrefixOf` f)
|
||||||
|
["html","html4","html5","s5","slidy", "slideous","dzslides","revealjs"]
|
||||||
|
|
||||||
|
handleErr (Right t) = return t
|
||||||
|
handleErr (Left err) = throwError $
|
||||||
|
err500 { errBody = TLE.encodeUtf8 $ TL.fromStrict $ renderError err }
|
||||||
|
|
||||||
|
compileCustomTemplate toformat t = do
|
||||||
|
res <- runWithPartials $ compileTemplate ("custom." <> T.unpack toformat)
|
||||||
|
(T.pack t)
|
||||||
|
case res of
|
||||||
|
Left e -> throwError $ PandocTemplateError (T.pack e)
|
||||||
|
Right tpl -> return tpl
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
flags:
|
flags:
|
||||||
pandoc:
|
pandoc:
|
||||||
server: false
|
|
||||||
embed_data_files: true
|
embed_data_files: true
|
||||||
QuickCheck:
|
QuickCheck:
|
||||||
old-random: false
|
old-random: false
|
||||||
|
|
Loading…
Add table
Reference in a new issue