summaryrefslogtreecommitdiff
path: root/paper/lua-filters/scrlttr2
diff options
context:
space:
mode:
Diffstat (limited to 'paper/lua-filters/scrlttr2')
-rw-r--r--paper/lua-filters/scrlttr2/Makefile9
-rw-r--r--paper/lua-filters/scrlttr2/README.md60
-rw-r--r--paper/lua-filters/scrlttr2/expected-strings.sh31
-rw-r--r--paper/lua-filters/scrlttr2/sample.md16
-rw-r--r--paper/lua-filters/scrlttr2/scrlttr2.lua161
5 files changed, 277 insertions, 0 deletions
diff --git a/paper/lua-filters/scrlttr2/Makefile b/paper/lua-filters/scrlttr2/Makefile
new file mode 100644
index 0000000..acd4c7e
--- /dev/null
+++ b/paper/lua-filters/scrlttr2/Makefile
@@ -0,0 +1,9 @@
+test: sample.md scrlttr2.lua sample.pdf
+ @pandoc --to=latex --lua-filter=scrlttr2.lua -s sample.md | \
+ sh expected-strings.sh
+ @rm sample.pdf
+
+%.pdf: %.md scrlttr2.lua
+ @pandoc --lua-filter=scrlttr2.lua --output=$@ $<
+
+.PHONY: test
diff --git a/paper/lua-filters/scrlttr2/README.md b/paper/lua-filters/scrlttr2/README.md
new file mode 100644
index 0000000..2a4e440
--- /dev/null
+++ b/paper/lua-filters/scrlttr2/README.md
@@ -0,0 +1,60 @@
+# scrlttr2
+
+This filter allows to write DIN 5008 letter using the [scrlttr2]
+LaTeX document class from KOMA script. It converts metadata to
+the appropriate KOMA variables and allows using the default LaTeX
+template shipped with pandoc.
+
+[scrlttr2]: https://www.ctan.org/pkg/scrlttr2
+
+## Base variables
+
+ - `opening`: phrase used as an opening;
+ defaults to "Dear Sir/Madam,"
+ - `closing`: closing phrase; defaults to "Sincerely,"
+ - `address`: recipient's street address;
+ defaults to "no address given"
+ - `date`: the date of the letter; defaults to the current day.
+
+## KOMA Variables
+
+Currently, the following metadata fields are translated to KOMA
+variables:
+
+- `fromaddress` (alias: `return-address`): address of the sender
+- `fromfax` (alias: `fax`): sender's fax number
+- `fromemail` (alias: `email`): sender's email
+- `fromlogo` (alias: `logo`): image to be used as the sender's logo
+- `fromname` (alias: `author`): sender name
+- `fromphone` (alias: `phone`): sender's phone number
+- `fromurl` (alias: `url`): sender's URL
+- `customer`: customer number
+- `invoice`: invoice number
+- `myref`: sender's reference
+- `place`: sender's place used near date
+- `signature`: sender's signature
+- `subject`: letter's subject
+- `title`: letter title
+- `yourref`: addressee's reference
+
+The values of these variables are converted to MetaInlines. If a
+list is given, then each list item is used as a line, e.g.,
+
+ fromaddress:
+ - 35 Industry Way
+ - Springfield
+
+The `KOMAoptions` value is inferred from the given variables, but
+can be overwritten by specifying it explicitly.
+
+See the scrlttr2 documentation for details.
+
+## Intended Usage
+
+Many sender variables don't change, so it is sensible to provide
+default values for these. Authors using Markdown to draft letters
+can use a separate YAML file for this. E.g., if there is a file
+`default.yml` which contains the sender's details, then only the
+addressee's data must be specified.
+
+ pandoc --lua-filter=scrlttr2 letter.md default.yml -o out.pdf
diff --git a/paper/lua-filters/scrlttr2/expected-strings.sh b/paper/lua-filters/scrlttr2/expected-strings.sh
new file mode 100644
index 0000000..f2b54c2
--- /dev/null
+++ b/paper/lua-filters/scrlttr2/expected-strings.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+latex_result="$(cat -)"
+
+assert_contains ()
+{
+ printf '%s' "$latex_result" | grep -qF "$1" -
+ if [ $? -ne 0 ]; then
+ printf 'Output does not contain `%s`.\n' "$1" >&2
+ exit 1
+ fi
+}
+
+# whether we are using the scrlttr2 class
+assert_contains '{scrlttr2}'
+
+assert_contains '\setkomavar{fromname}{Jane Doe}'
+assert_contains '\setkomavar{fromaddress}{35 Industry Way\\ Springfield}'
+assert_contains '\setkomavar{subject}{Letter of Reference}'
+assert_contains '\setkomavar{date}{February 29, 2020}'
+
+# Custom opening and default closing
+assert_contains '\opening{To Whom It May Concern,}'
+assert_contains '\closing{Sincerely,}'
+
+# Author and date
+assert_contains '\author{Jane Doe}'
+assert_contains '\date{February 29, 2020}'
+
+# Recipient address
+assert_contains '\begin{letter}{Fireworks Inc.\\ 123 Fake St\\ 58008 Springfield}'
diff --git a/paper/lua-filters/scrlttr2/sample.md b/paper/lua-filters/scrlttr2/sample.md
new file mode 100644
index 0000000..5f13554
--- /dev/null
+++ b/paper/lua-filters/scrlttr2/sample.md
@@ -0,0 +1,16 @@
+---
+author: Jane Doe
+fromaddress:
+ - 35 Industry Way
+ - Springfield
+opening: To Whom It May Concern,
+subject: Letter of Reference
+date: February 29, 2020
+address:
+ - Fireworks Inc.
+ - 123 Fake St
+ - 58008 Springfield
+...
+
+I strongly recommend to embiggen your team by giving John Doe the position of a
+yak shaver. He has shown cromulent performance as a bike shedder.
diff --git a/paper/lua-filters/scrlttr2/scrlttr2.lua b/paper/lua-filters/scrlttr2/scrlttr2.lua
new file mode 100644
index 0000000..78f38fd
--- /dev/null
+++ b/paper/lua-filters/scrlttr2/scrlttr2.lua
@@ -0,0 +1,161 @@
+-- Ensure unpack also works if pandoc was compiled against Lua 5.1
+local unpack = unpack or table.unpack
+local List = require 'pandoc.List'
+local stringify = (require 'pandoc.utils')['stringify']
+
+--- Set some default options
+local default = {
+ opening = 'Dear Sir/Madam,',
+ closing = 'Sincerely,',
+ address = 'no address given'
+}
+
+--- Return a list of inlines representing a call to a latex command.
+local function latex_command (command, ...)
+ local entry = {
+ pandoc.RawInline('latex', '\\' .. command),
+ }
+ for _, arg in ipairs{...} do
+ entry[#entry + 1] = pandoc.RawInline('latex', '{')
+ if type(arg) ~= 'table' then
+ entry[#entry + 1] = pandoc.RawInline('latex', tostring(arg))
+ else
+ List.extend(entry, arg)
+ end
+ entry[#entry + 1] = pandoc.RawInline('latex', '}')
+ end
+ return entry
+end
+
+--- Convert the given meta-value to a list of inlines
+local function ensure_inlines (val)
+ if not val or type(val) == 'string' or type(val) == 'boolean' then
+ return pandoc.MetaInlines{pandoc.Str(tostring(val))}
+ elseif type(val) == 'table' and val.t == 'MetaInlines' then
+ return val
+ elseif type(val) == 'table' then
+ local res = List:new{}
+ for i = 1, #val do
+ res:extend(val[i])
+ res[#res + 1] = pandoc.RawInline('latex', '\\\\ ')
+ end
+ res[#res] = nil -- drop last linebreak
+ return pandoc.MetaInlines(res)
+ else
+ return pandoc.MetaInlines{pandoc.Str(pandoc.utils.stringify(val))}
+ end
+end
+
+--- Convert the given value to a MetaList
+local function ensure_meta_list (val)
+ if not val or val.t ~= 'MetaList' then
+ return pandoc.MetaList{}
+ else
+ return val
+ end
+end
+
+--- Set supported variables as KOMA variables.
+function setkomavar_commands (meta)
+ local set_vars = {}
+ local res = {}
+ local function set_koma_var (name, value, enable)
+ if value ~= nil then
+ res[#res + 1] = latex_command('setkomavar', name, ensure_inlines(value))
+ if enable then
+ set_vars[#set_vars + 1] = name
+ end
+ end
+ end
+
+ set_koma_var('fromname', meta.fromname or meta.author)
+ set_koma_var('fromaddress', meta.fromaddress or meta['return-address'])
+ set_koma_var('subject', meta.subject)
+ set_koma_var('title', meta.title)
+ set_koma_var('signature', meta.signature)
+ set_koma_var('customer', meta.customer)
+ set_koma_var('yourref', meta.yourref)
+ set_koma_var('myref', meta.myref)
+ set_koma_var('invoice', meta.invoice)
+ set_koma_var('place', meta.place)
+
+ set_koma_var('fromfax', meta.fromfax or meta.fax, true)
+ set_koma_var('fromurl', meta.fromurl or meta.url, true)
+ set_koma_var('fromlogo', meta.fromlogo or meta.logo, true)
+ set_koma_var('fromemail', meta.fromemail or meta.email, true)
+ set_koma_var('fromphone', meta.fromphone or meta.phone, true)
+
+ -- don't set date if date is set to `false`
+ if meta.date == nil or meta.date == true then
+ if meta['date-format'] then
+ set_koma_var('date', os.date(stringify(date_format)))
+ else
+ set_koma_var('date', pandoc.MetaInlines{pandoc.RawInline('latex', '\\today')})
+ end
+ elseif meta.date then
+ set_koma_var('date', meta.date)
+ end
+
+ if meta['KOMAoptions'] or #set_vars >= 1 then
+ res[#res + 1] = latex_command(
+ 'KOMAoptions',
+ meta['KOMAoptions']
+ or table.concat(set_vars, '=true,') .. '=true'
+ )
+ end
+
+ return res
+end
+
+--- Bring Metadata in a form suitable for the scrlttr KOMA class
+local function make_koma_metadata(meta)
+ local header_includes = ensure_meta_list(meta['header-includes'])
+ List.extend(header_includes, setkomavar_commands(meta))
+
+ local include_before = ensure_meta_list(meta['include-before'])
+ List.extend(
+ include_before,
+ {
+ pandoc.MetaInlines(
+ latex_command(
+ 'begin',
+ 'letter',
+ ensure_inlines(meta.address or default.address)
+ )
+ ),
+
+ pandoc.MetaInlines(
+ latex_command('opening', meta.opening or default.opening)
+ ),
+ }
+ )
+
+ local include_after = ensure_meta_list(meta['include-after'])
+ List.extend(
+ include_after,
+ {
+ pandoc.MetaInlines(
+ latex_command('closing', meta.closing or default.closing)
+ ),
+ pandoc.MetaInlines(latex_command('end', 'letter')),
+ }
+ )
+
+ -- unset or reset some unwanted vars
+ meta.data = nil -- set via komavar 'date'
+ meta.title = nil -- set via komavar 'subject'
+ meta.indent = true -- disable parskib
+ -- set documentclass to scrlttr2 if it's unset
+ meta.documentclass = meta.documentclass or pandoc.MetaString'scrlttr2'
+
+
+ meta['header-includes'] = header_includes
+ meta['include-before'] = include_before
+ meta['include-after'] = include_after
+
+ return meta
+end
+
+return {
+ {Meta = make_koma_metadata}
+}