pdf-slave

Tool to generate PDF from haskintex templates and YAML input https://github.com/NCrashed/pdf-slave#readme

Latest on Hackage:1.3.1.0

This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow stackage.org to host generated Haddocks.

BSD3 licensed by Anton Gushcha
Maintained by ncrashed@gmail.com

pdf-slave

Build Status

Tool that compiles haskintex (TeX with embedded Haskell) files into PDF documents. Templates are described in YAML format and can define dependencies.

Features:

  • Input JSON file for htex file that holds template varying data.

  • Option for packing a template in all-in YAML bundle.

  • Support for template dependencies that include: - Bibtex files - Images, listings, other static files. - Other .htex templates that compiles into TeX and includes into parent template. - Other .htex templates that compiles into PDF and includes into parent template.

Description

In many real-world systems we need to produce reports, cheques and other documents to users. One possible solution is rendering LaTeX files, but it is not flexible enough as you need to solve burden with generation of LaTeX file from varying inputs, manage static files like images and handling dependencies between pieces of document.

The new tool takes the burden away. Now all static files and generation code is located within a single template project, that describes how to build your PDF document in a declaretive way.

Common work flow with pdf-slave: * Develop template from several LaTeX files with embedded Haskell (thanks to haskintex tool)

  • Pack the template project into bundle all-in file.

  • Distribute the bundle (for instance to your service server).

  • Call pdf-slave with bundle and inputs in JSON file to generate PDF document.

Template reference

Common template consists of several files:

  • template_input.json - Input data for template in JSON format.

    {
      "line-width": 2,
      "spiral-precision": 0.01,
      "spiral-interval": [0,4],
      "spiral-a": 0.1,
      "spiral-b": 4
    }
  • template.htex - TeX/LaTeX with embedded Haskell that reads input data from file template.json. You have to provide code at the beginning of file that reads the inputs, like that:

Make a Helper.hs file in directory with template: ``` haskell module Helper where

import qualified Data.ByteString.Lazy as BS import Data.Aeson import System.IO.Unsafe

defaultInput :: FromJSON a => a {-# NOINLINE defaultInput #-} defaultInput = case unsafePerformIO $ fmap eitherDecode' $ BS.readFile "input.json" of Left e -> error (show e) Right a -> a ```

Next in your .htex file: ``` Haskell \begin{document}

\begin{haskellpragmas} {-# LANGUAGE OverloadedStrings #-} \end{haskellpragmas} \begin{writehaskell} import Data.Aeson import Helper

data Input = Input { lineWidth :: Double , spiralPrecision :: Double , spiralInterval :: (Double, Double) , spiralA :: Double , spiralB :: Double }

instance FromJSON Input where parseJSON (Object o) = Input <$> o .: "line-width" <> o .: "spiral-precision" <> o .: "spiral-interval" <> o .: "spiral-a" <> o .: "spiral-b"

inpt :: Input inpt = defaultInput \end{writehaskell} ```

Note: The tool copies JSON with inputs to build folder with .htex twice, once with name specified at input key in template description and secondly at fixed input.json.

  • template.yaml - description of template and its dependencies.

    name:  template01             # name of template
    input: template01_input.json  # name of input file
    body:  template01.htex        # name of .htex file
    dependencies: {}              # dependency tree (see below)
    haskintex-opts: []            # additional flags to haskintex

    For input Helper.hs you should make a record to inform pdf-slave that the module should be copied to build directory:

    dependencies:
      Helper.hs:
        type: other

Dependencies

There are 4 different types of dependencies:

  • other - static files that don't require any processing. They could be images, listings etc. Example of YAML configuration:

` YAML dependencies: lambda.png: # name of dependency is equal to filename relative to parent template type: other # marks type of dependency code/demo.hs: type: other `

See examples/example02 for full example.

  • bibtex - bibliography files that require additional passes with bibtex. Example of YAML configuration:

    dependencies:
      biblio.bib: # name of dependency is equal to filename relative to parent template
        type: bibtex # marks type of dependency

    See examples/example03 for full example.

  • template - other .htex templates that are included in parent via \\input{...} or \\include{..}. Example of YAML configuration:

    dependencies:
      dep1: # name of dependency defines subfolder where the output tex file is located
        type:  template # marks type of dependency
        name:  dep1
        input: dep1_input.json
        body:  dep1.htex
      dep2:
        type:  template
        name:  dep2
        body:  dep2.htex
        dependencies:
          lambda.png:
            type: other
          code/demo.hs:
            type: other

    See examples/example04 for full example.

    Note that code/demo.hs subdependency should be included as dep2/code/demo.hs in dep2.htex as dep2.tex is inlined into parent.

  • template_pdf - other .htex templates that are included as PDFs into parent template. Example of YAML configuration:

    dependencies:
      template01: # name of dependency defines subfolder where the output tex file is located
        type:  template_pdf # marks type of dependency
        name:  template01
        input: template01_input.json
        body:  template01.htex
      template02:
        type:  template_pdf
        name:  template02
        body:  template02.htex
        dependencies:
          lambda.png:
            type: other
          code/demo.hs:
            type: other

    See examples/example05 for full example.

Input propagation

When you work with dependencies you have two options how to handle inputs:

  • Define dependency inputs in its own file:

    dependencies:
      dep1:
        type:  template
        name:  dep1
        input: dep1_input.json # private input file
        body:  dep1.htex
  • Define dependency inputs in parent file:

    name:  template06
    input: template06_input.json # contains inputs for dep1
    body:  template06.htex
    dependencies:
      dep1:
        type:  template
        name:  dep1
        body:  dep1.htex

    Contents of template06_input.json:

    {
      "dep1": {
        "line-width": 2,
        "spiral-precision": 0.01,
        "spiral-interval": [0,4],
        "spiral-a": 0.1,
        "spiral-b": 4
      }
    }

    Note that key of subsection must be equal to name of dependency.

    See examples/example06 for full example.

Making bundles

One can pack all .htex, .json, .yaml and all dependencies in single YAML bundle that can be easily distributed, transmitted between services and stored:

cd examples/template01
pdf-slave --template template01.yaml --output template01_bundle.yaml pack

As modification of such bundles isn't handy, one can unpack bundle:

pdf-slave --template template01_bundle.yaml --output template01_directory unpack

Rendering of bundles is handled with the same command that is used for ordinary templates.

Compilation

You need:

  • LaTeX distribution (for instance, texlive or miktex)

  • stack or system wide GHC 8.0.1 + Cabal 1.24.0.

Compilation with stack:

git clone https://github.com/NCrashed/pdf-slave.git
cd pdf-slave
stack install

Compilation with cabal:

git clone https://github.com/NCrashed/pdf-slave.git
cd pdf-slave
cabal sandbox init
cabal install --dependencies-only
cabal install

Running examples

You need:

  • LaTeX distribution (for instance, texlive or miktex)

  • stack or GHC+Cabal (yes, you need GHC to evaluate templates at runtime)

  • pdf-slave and haskintex executables in PATH

Stack users:

cd examples/template01
stack install aeson HaTeX
stack exec -- pdf-slave --template template01.yaml --output output.pdf pdf && xdg-open output.pdf

Cabal users:

cd examples/template01
cabal sandbox init
cabal install aeson HaTeX
pdf-slave --template template01.yaml --output output.pdf pdf && xdg-open output.pdf

Docker build

  1. Run cook_doocker.sh script. This will build two images, one pdf-slave-build for compilation of binaries and the second one pdf-slave for production usage.

  2. Usage:

` bash docker run -it --rm -v $(pwd)/examples/template01:/data/examples pdf-slave pdf --template examples/template01.yaml --output examples/output.pdf xdg-open examples/template01/output.pdf `

  1. Or download precompiled container from Docker Hub.

Credits

  • Daniel Díaz - author of haskintex tool that is core of the package.

  • Alexander Vershilov aka qnikst - help with debugging and new Helper.hs module.

Changes

1.3.1.0

  • Added flag json to output bundles in JSON format instead of YAML.

1.3.0.0

  • Default input file name is now input.json, not <template name>_input.json.

  • Changed recommended way of reading input via Helper.hs

  • Added flag preserve-temp to not nuke temporary files after execution.

1.2.3.0

  • Support compilation with GHC 7.10.

1.2.2.0

  • Always copy input file to <template_name>_input.json.

1.2.1.0

  • Execute haskintex with -werror flag.

1.2.0.0

  • Add version command to CLI.

  • Fix: renderBundleToPDF doesn't take base directory.

  • Fix: loadTemplateInMemory now takes base directory.

  • Strong distinguish between bundle and ordinary template format. Bundle templates have to define bundle: true inside the YAML files.

1.1.0.0

  • Factor out pdf-slave-template package for compiler agnostic format definition.

1.0.1.0

  • Added missing reexports into head module.

1.0.0.0

  • Initial release.
comments powered byDisqus