diff options
Diffstat (limited to 'paper/lua-filters/scrlttr2')
-rw-r--r-- | paper/lua-filters/scrlttr2/Makefile | 9 | ||||
-rw-r--r-- | paper/lua-filters/scrlttr2/README.md | 60 | ||||
-rw-r--r-- | paper/lua-filters/scrlttr2/expected-strings.sh | 31 | ||||
-rw-r--r-- | paper/lua-filters/scrlttr2/sample.md | 16 | ||||
-rw-r--r-- | paper/lua-filters/scrlttr2/scrlttr2.lua | 161 |
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} +} |