summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn MacFarlane <jgm@berkeley.edu>2022-08-16 16:27:31 -0700
committerJohn MacFarlane <jgm@berkeley.edu>2022-08-17 12:28:14 -0700
commit8ddc2fc79a45283e7b90f59e9a7763e877d4c044 (patch)
tree3e9e8f4fdc7370137c46344ba1829aac6c43c6cd
parent90d52b7129440d7d91bcdf3210513f380063be0a (diff)
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.
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/release-candidate.yml9
-rw-r--r--Makefile2
-rw-r--r--app/pandoc.hs15
-rw-r--r--doc/pandoc-server.md (renamed from server/pandoc-server.md)248
-rw-r--r--linux/control.in (renamed from linux/pandoc.control.in)0
-rw-r--r--linux/make_artifacts.sh52
-rw-r--r--linux/pandoc-server.control.in9
-rw-r--r--pandoc.cabal38
-rw-r--r--server/Main.hs54
-rw-r--r--server/PandocServer.hs301
-rw-r--r--src/Text/Pandoc/Server.hs357
-rw-r--r--stack.yaml1
13 files changed, 561 insertions, 527 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cdf4345d5..fe65baa6e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -56,7 +56,7 @@ jobs:
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
- ghc: '8.10.7'
cabal: '3.2'
- cabalopts: '-fserver'
+ cabalopts: ''
testopts: '--test-option=--hide-successes --test-option=--ansi-tricks=false'
- ghc: '9.0.2'
cabal: '3.4'
diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml
index 1c5d764e1..c536e5a74 100644
--- a/.github/workflows/release-candidate.yml
+++ b/.github/workflows/release-candidate.yml
@@ -96,7 +96,7 @@ jobs:
run: |
stack --no-terminal setup
stack --no-terminal update
- stack --no-terminal install
+ stack --no-terminal install --ghc-options='-j4 +RTS -A256m -RTS -split-sections'
- name: Create artifacts
run: |
@@ -112,8 +112,13 @@ jobs:
mkdir -p ${DEST}/bin
mkdir -p ${DEST}/share/man/man1
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-server.1 ${DEST}/share/man/man1/pandoc-server.1
~/.local/bin/pandoc -s COPYING.md -Vpagetitle=License -o ${RESOURCES}/license.html
chown -R $ME:staff ${ROOT}
sed -e "s/PANDOCVERSION/${VERSION}/" macos/distribution.xml.in > ${ARTIFACTS}/distribution.xml
diff --git a/Makefile b/Makefile
index df2d9cc44..fbd3c0eb6 100644
--- a/Makefile
+++ b/Makefile
@@ -102,7 +102,7 @@ man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after
--variable footer="pandoc $(version)" \
-o $@
-man/pandoc-server.1: server/pandoc-server.md
+man/pandoc-server.1: doc/pandoc-server.md
pandoc $< -f markdown -t man -s \
--lua-filter man/manfilter.lua \
--variable footer="pandoc-server $(version)" \
diff --git a/app/pandoc.hs b/app/pandoc.hs
index 24c7c5adc..753bea399 100644
--- a/app/pandoc.hs
+++ b/app/pandoc.hs
@@ -14,7 +14,18 @@ module Main where
import qualified Control.Exception as E
import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions)
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 = E.catch (parseOptions options defaultOpts >>= convertWithOpts)
- (handleError . Left)
+main = E.handle (handleError . Left) $ do
+ 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
diff --git a/server/pandoc-server.md b/doc/pandoc-server.md
index e22063fa8..b5c68d564 100644
--- a/server/pandoc-server.md
+++ b/doc/pandoc-server.md
@@ -12,16 +12,17 @@ date: August 15, 2022
`pandoc-server` is a web server that can perform pandoc
conversions. It can be used either as a running server
-or as a CGI program. To use `pandoc-server` as a CGI
-program, rename it (or symlink it) as `pandoc-server.cgi`.
-(Note: if you symlink it, you may need to adjust your
-webserver's configuration in order to allow it to follow
-symlinks for the CGI script.)
+or as a CGI program.
+
+To use `pandoc-server` as a CGI program, rename it (or symlink
+it) as `pandoc-server.cgi`. (Note: if you symlink it, you may
+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
ensures that they can do no I/O operations on the server.
-This should provide a high degree of security. It does,
-however, impose certain limitations:
+This should provide a high degree of security. This security
+does, however, impose certain limitations:
- PDFs cannot be produced.
@@ -85,119 +86,115 @@ the first one given is the default.
: The output format, possibly with extensions, just as it is
specified on the pandoc command line.
-`wrapText` (`"auto"|"preserve"|"none"`)
+`shift-heading-level-by` (integer, default 0)
-: 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).
+: Increase or decrease the level of all headings.
-`columns` (integer, default 72)
+`indented-code-classes` (array of strings)
-: Column width (affects text wrapping and calculation of
- table column widths in plain text formats)
+: List of classes to be applied to indented Markdown code blocks.
-`standalone` (boolean, default false)
+`default-image-extension` (string)
-: If true, causes a standalone document to be produced, using
- the default template or the custom template specified using
- `template`. If false, a fragment will be produced.
+: Extension to be applied to image sources that lack extensions
+ (e.g. `".jpg"`).
-`template` (string)
+`metadata` (JSON map)
-: String contents of a document template (see Templates in
- `pandoc(1)` for the format).
+: String-valued metadata.
-`tabStop` (integer, default 4)
+`tab-stop` (integer, default 4)
: Tab stop (spaces per tab).
-`indentedCodeClasses` (array of strings)
+`track-changes` (`"accept"|"reject"|"all"`)
-: List of classes to be applied to indented Markdown code blocks.
+: Specifies what to do with insertions, deletions, and
+ comments produced by the MS Word "Track Changes" feature. Only
+ affects docx input.
-`abbreviations` (array of strings)
+`abbreviations` (file path)
: List of strings to be regarded as abbreviations when
parsing Markdown. See `--abbreviations` in `pandoc(1)` for
details.
-`defaultImageExtension` (string)
+`standalone` (boolean, default false)
-: Extension to be applied to image sources that lack extensions
- (e.g. `".jpg"`).
+: If true, causes a standalone document to be produced, using
+ the default template or the custom template specified using
+ `template`. If false, a fragment will be produced.
-`trackChanges` (`"accept"|"reject"|"all"`)
+`template` (string)
-: Specifies what to do with insertions, deletions, and
- comments produced by the MS Word "Track Changes" feature. Only
- affects docx input.
+: String contents of a document template (see Templates in
+ `pandoc(1)` for the format).
-`stripComments` (boolean, default false)
+`variables` (JSON map)
-: Causes HTML comments to be stripped in Markdown or Textile
- source, instead of being passed through to the output format.
+: Variables to be interpolated in the template. (See Templates
+ in `pandoc(1)`.)
-`citeproc` (boolean, default false)
+`dpi` (integer, default 96)
-: Causes citations to be processed using citeproc. See
- Citations in `pandoc(1)` for details.
+: Dots-per-inch to use for conversions between pixels and
+ other measurements (for image sizes).
-`citeMethod` (`"citeproc"|"natbib"|"biblatex"`)
+`wrap` (`"auto"|"preserve"|"none"`)
-: Determines how citations are formatted in LaTeX output.
+: 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).
-`tableOfContents` (boolean, default false)
+`columns` (integer, default 72)
-: Include a table of contents (in supported formats).
+: Column width (affects text wrapping and calculation of
+ table column widths in plain text formats)
-`tocDepth` (integer, default 3)
+`table-of-contents` (boolean, default false)
-: Depth of sections to include in the table of contents.
+: Include a table of contents (in supported formats).
-`numberSections` (boolean, default false)
+`toc-depth` (integer, default 3)
-: Automatically number sections (in supported formats).
+: Depth of sections to include in the table of contents.
-`numberOffset` (array of integers)
+`strip-comments` (boolean, default false)
-: 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."
+: Causes HTML comments to be stripped in Markdown or Textile
+ source, instead of being passed through to the output format.
-`identifierPrefix` (string)
+`highlight-style` (string, default `"pygments"`)
-: Prefix to be added to all automatically-generated identifiers.
+: Specify the style to use for syntax highlighting of code.
+ Standard styles are `"pygments"` (the default), `"kate"`,
+ `"monochrome"`, `"breezeDark"`, `"espresso"`, `"zenburn"`,
+ `"haddock"`, and `"tango"`. Alternatively, the path of
+ a `.theme` with a KDE syntax theme may be used (in this
+ case, the relevant file contents must also be included
+ in `files`, see below).
-`sectionDivs` (boolean, default false)
+`embed-resources`
-: Arrange the document into a hierarchy of nested sections
- based on the headings.
+: 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`.
-`htmlQTags` (boolean, default false)
+`html-q-tags` (boolean, default false)
: Use `<q>` elements in HTML instead of literal quotation marks.
-`listings` (boolean, default false)
+`ascii` (boolean, default false)
-: Use the `listings` package to format code in LaTeX output.
+: Use entities and escapes when possible to avoid non-ASCII
+ characters in the output.
-`referenceLinks` (boolean, default false)
+`reference-links` (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
@@ -205,72 +202,122 @@ the first one given is the default.
end of the block (e.g. paragraph), in
certain formats. (See `pandoc(1)` under `--reference-location`.)
+`setext-headers` (boolean, default false)
-`topLevelDivision` (`"default"|"part"|"chapter"|"section"`)
+: 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.
-`emailObfuscation` (`"none"|"references"|"javascript"`)
+`number-sections` (boolean, default false)
-: Determines how email addresses are obfuscated in HTML.
+: Automatically number sections (in supported formats).
-`htmlMathMethod` (`"plain"|"webtex"|"gladtex"|"mathml"|"mathjax"|"katex"`)
-: Determines how math is represented in HTML.
+`number-offset` (array of integers)
-`variables` (JSON mapping)
+: 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."
-: Variables to be interpolated in the template. (See Templates
- in `pandoc(1)`.)
+`html-math-method` (`"plain"|"webtex"|"gladtex"|"mathml"|"mathjax"|"katex"`)
-`dpi` (integer, default 96)
+: Determines how math is represented in HTML.
-: Dots-per-inch to use for conversions between pixels and
- other measurements (for image sizes).
+`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.
-`slideLevel` (integer)
+`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.
-`highlightStyle` (string, default `"pygments"`)
+`section-divs` (boolean, default false)
-: Specify the style to use for syntax highlighting of code.
- Standard styles are `"pygments"` (the default), `"kate"`,
- `"monochrome"`, `"breezeDark"`, `"espresso"`, `"zenburn"`,
- `"haddock"`, and `"tango"`. Alternatively, the path of
- a `.theme` with a KDE syntax theme may be used (in this
- case, the relevant file contents must also be included
- in `files`, see below).
+: 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`.
-`epubMetadata` (string)
+`epub-metadata` (file path)
-: Dublin core XML elements to be used for EPUB metadata.
+: Path of file containing Dublin core XML elements to be used for
+ EPUB metadata. The contents of the file must be included
+ under `files`.
-`epubChapterLevel` (integer, default 1)
+`epub-chapter-level` (integer, default 1)
: 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.
-`epubFonts` (array of file paths)
+`epub-fonts` (array of file paths)
: Fonts to include in the EPUB. The fonts themselves must be
included in `files` (see below).
-`referenceDoc` (file path)
+`ipynb-output` (`"best"|"all"|"none"`)
-: Reference doc to use in creating `docx` or `odt` or `pptx`.
- See `pandoc(1)` under `--reference-doc` for details.
+: Determines how ipynb output cells are treated. `all` means
+ 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)
@@ -280,7 +327,6 @@ the first one given is the default.
left as it is, unless it is *also* valid base 64 data,
in which case it will be interpreted that way.
-
## `/batch` endpoint
The `/batch` endpoint behaves like the root endpoint,
diff --git a/linux/pandoc.control.in b/linux/control.in
index 51a683629..51a683629 100644
--- a/linux/pandoc.control.in
+++ b/linux/control.in
diff --git a/linux/make_artifacts.sh b/linux/make_artifacts.sh
index 5e594b569..4653a0c19 100644
--- a/linux/make_artifacts.sh
+++ b/linux/make_artifacts.sh
@@ -27,42 +27,44 @@ ghc --version
cabal update
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
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
file $ARTIFACTS/pandoc | grep "statically linked"
-file $ARTIFACTS/pandoc-server | grep "statically linked"
-# make deb for EXE
make_deb() {
- VERSION=`$ARTIFACTS/$EXE --version | awk '{print $2; exit;}'`
+ VERSION=`$ARTIFACTS/pandoc --version | awk '{print $2; exit;}'`
REVISION=${REVISION:-1}
DEBVER=$VERSION-$REVISION
- BASE=$EXE-$DEBVER-$ARCHITECTURE
+ BASE=pandoc-$DEBVER-$ARCHITECTURE
DIST=/mnt/$BASE
DEST=$DIST/usr
- COPYRIGHT=$DEST/share/doc/$EXE/copyright
+ COPYRIGHT=$DEST/share/doc/pandoc/copyright
cd /mnt
mkdir -p $DEST/bin
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
- cp $ARTIFACTS/$EXE $DEST/bin/
- strip $DEST/bin/$EXE
- cp /mnt/man/$EXE.1 $DEST/share/man/man1/$EXE.1
- gzip -9 $DEST/share/man/man1/$EXE.1
+ cp $ARTIFACTS/pandoc $DEST/bin/
+ cd $DEST/bin
+ strip pandoc
+ 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
echo "" >> $COPYRIGHT
INSTALLED_SIZE=$(du -k -s $DEST | awk '{print $1}')
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/INSTALLED_SIZE/$INSTALLED_SIZE/" \
> $DIST/DEBIAN/control
@@ -73,26 +75,28 @@ make_deb() {
cp $BASE.deb $ARTIFACTS/
}
-# Make tarball for EXE
+# Make tarball for pandoc
make_tarball() {
- TARGET=$EXE-$VERSION
+ TARGET=pandoc-$VERSION
cd $ARTIFACTS
rm -rf $TARGET
mkdir $TARGET
mkdir $TARGET/bin $TARGET/share $TARGET/share/man $TARGET/share/man/man1
- cp /mnt/man/$EXE.1 $TARGET/share/man/man1
- mv $EXE $TARGET/bin
- strip $TARGET/bin/$EXE
- gzip -9 $TARGET/share/man/man1/$EXE.1
+ cp /mnt/man/pandoc.1 $TARGET/share/man/man1
+ cp /mnt/man/pandoc-server.1 $TARGET/share/man/man1
+ mv pandoc $TARGET/bin
+ 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
rm -r $TARGET
}
-for EXE in pandoc pandoc-server
-do
- make_deb
- make_tarball
-done
+make_deb
+make_tarball
exit 0
diff --git a/linux/pandoc-server.control.in b/linux/pandoc-server.control.in
deleted file mode 100644
index 348fd21c4..000000000
--- a/linux/pandoc-server.control.in
+++ /dev/null
@@ -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
diff --git a/pandoc.cabal b/pandoc.cabal
index 275e87f90..592956c55 100644
--- a/pandoc.cabal
+++ b/pandoc.cabal
@@ -429,10 +429,6 @@ flag lua53
Description: Embed Lua 5.3 instead of 5.4.
Default: False
-flag server
- Description: Build pandoc-server executable.
- Default: False
-
flag nightly
Description: Add '-nightly-COMPILEDATE' to the output of '--version'.
Default: False
@@ -538,7 +534,10 @@ library
xml-types >= 0.3 && < 0.4,
yaml >= 0.11 && < 0.12,
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)
build-depends: unix >= 2.4 && < 2.8
if flag(nightly)
@@ -564,6 +563,7 @@ library
Text.Pandoc.MediaBag,
Text.Pandoc.Error,
Text.Pandoc.Filter,
+ Text.Pandoc.Server,
Text.Pandoc.Readers,
Text.Pandoc.Readers.HTML,
Text.Pandoc.Readers.LaTeX,
@@ -789,32 +789,8 @@ executable pandoc
main-is: pandoc.hs
buildable: True
other-modules: Paths_pandoc
-
-executable pandoc-server
- 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
+ build-depends: wai-extra >= 3.0.24,
+ warp
test-suite test-pandoc
import: common-executable
diff --git a/server/Main.hs b/server/Main.hs
deleted file mode 100644
index 531a0b0a0..000000000
--- a/server/Main.hs
+++ /dev/null
@@ -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'
diff --git a/server/PandocServer.hs b/server/PandocServer.hs
deleted file mode 100644
index 295412c6d..000000000
--- a/server/PandocServer.hs
+++ /dev/null
@@ -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
diff --git a/src/Text/Pandoc/Server.hs b/src/Text/Pandoc/Server.hs
new file mode 100644
index 000000000..a7c46f93f
--- /dev/null
+++ b/src/Text/Pandoc/Server.hs
@@ -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
+
diff --git a/stack.yaml b/stack.yaml
index b0558fbb9..d0c551a8d 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,6 +1,5 @@
flags:
pandoc:
- server: false
embed_data_files: true
QuickCheck:
old-random: false