config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

ox-koma-letter.el (40615B)


      1 ;;; ox-koma-letter.el --- KOMA Scrlttr2 Backend for Org Export Engine  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2007-2024 Free Software Foundation, Inc.
      4 
      5 ;; Author: Nicolas Goaziou <n.goaziou AT gmail DOT com>
      6 ;;         Alan Schmitt <alan.schmitt AT polytechnique DOT org>
      7 ;;         Viktor Rosenfeld <listuser36 AT gmail DOT com>
      8 ;;         Rasmus Pank Roulund <emacs AT pank DOT eu>
      9 ;; Maintainer: Marco Wahl <marcowahlsoft@gmail.com>
     10 ;; Keywords: org, text, tex
     11 
     12 ;; This file is part of GNU Emacs.
     13 
     14 ;; GNU Emacs is free software: you can redistribute it and/or modify
     15 ;; it under the terms of the GNU General Public License as published by
     16 ;; the Free Software Foundation, either version 3 of the License, or
     17 ;; (at your option) any later version.
     18 
     19 ;; GNU Emacs is distributed in the hope that it will be useful,
     20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     22 ;; GNU General Public License for more details.
     23 
     24 ;; You should have received a copy of the GNU General Public License
     25 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     26 
     27 ;;; Commentary:
     28 ;;
     29 ;; This library implements a KOMA Scrlttr2 backend, derived from the
     30 ;; LaTeX one.
     31 ;;
     32 ;; Depending on the desired output format, three commands are provided
     33 ;; for export: `org-koma-letter-export-as-latex' (temporary buffer),
     34 ;; `org-koma-letter-export-to-latex' ("tex" file) and
     35 ;; `org-koma-letter-export-to-pdf' ("pdf" file).
     36 ;;
     37 ;; On top of buffer keywords supported by `latex' backend (see
     38 ;; `org-latex-packages-alist'), this backend introduces the following
     39 ;; keywords:
     40 ;;   - CLOSING: see `org-koma-letter-closing',
     41 ;;   - FROM_ADDRESS: see `org-koma-letter-from-address',
     42 ;;   - LCO: see `org-koma-letter-class-option-file',
     43 ;;   - OPENING: see `org-koma-letter-opening',
     44 ;;   - PHONE_NUMBER: see `org-koma-letter-phone-number',
     45 ;;   - URL: see `org-koma-letter-url',
     46 ;;   - FROM_LOGO: see `org-koma-letter-from-logo',
     47 ;;   - SIGNATURE: see `org-koma-letter-signature',
     48 ;;   - PLACE: see `org-koma-letter-place',
     49 ;;   - LOCATION: see `org-koma-letter-location',
     50 ;;   - TO_ADDRESS:  If unspecified this is set to "\mbox{}".
     51 ;;
     52 ;; TO_ADDRESS, FROM_ADDRESS, LOCATION, CLOSING, and SIGNATURE can also
     53 ;; be specified using "special headings" with the special tags
     54 ;; specified in `org-koma-letter-special-tags-in-letter'.  LaTeX line
     55 ;; breaks are not necessary for TO_ADDRESS, FROM_ADDRESS and LOCATION.
     56 ;; If both a headline and a keyword specify a to or from address the
     57 ;; value is determined in accordance with
     58 ;; `org-koma-letter-prefer-special-headings'.
     59 ;;
     60 ;; A number of OPTIONS settings can be set to change which contents is
     61 ;; exported.
     62 ;;   - backaddress (see `org-koma-letter-use-backaddress')
     63 ;;   - foldmarks (see `org-koma-letter-use-foldmarks')
     64 ;;   - phone (see `org-koma-letter-use-phone')
     65 ;;   - url (see `org-koma-letter-use-url')
     66 ;;   - from-logo (see `org-koma-letter-use-from-logo')
     67 ;;   - email (see `org-koma-letter-use-email')
     68 ;;   - place (see `org-koma-letter-use-place')
     69 ;;   - location (see `org-koma-letter-location')
     70 ;;   - subject, a list of format options
     71 ;;     (see `org-koma-letter-subject-format')
     72 ;;   - after-closing-order, a list of the ordering of headings with
     73 ;;     special tags after closing (see
     74 ;;     `org-koma-letter-special-tags-after-closing')
     75 ;;   - after-letter-order, as above, but after the end of the letter
     76 ;;     (see `org-koma-letter-special-tags-after-letter').
     77 ;;
     78 ;; The following variables works differently from the main LaTeX class
     79 ;;   - AUTHOR: Default to user-full-name but may be disabled.
     80 ;;     (See also `org-koma-letter-author'.)
     81 ;;   - EMAIL: Same as AUTHOR.  (See also `org-koma-letter-email'.)
     82 ;;
     83 ;; FROM_LOGO uses LaTeX markup.  FROM_LOGO provides the
     84 ;; "includegraphics" command to tell LaTeX where to find the logo.
     85 ;; This command needs to know the logo's directory and file name.  The
     86 ;; directory can either be relative or absolute, just as you would
     87 ;; expect.  LaTeX can use three file types for the logo: PDF, JPEG, or
     88 ;; PNG.  The logo can either include or exclude its extension, which
     89 ;; might surprise you.  When you exclude its extension, LaTeX will
     90 ;; search the directory for the "best" quality graphics format.  For
     91 ;; example if it finds both logo.pdf and logo.png then it will
     92 ;; identify the PDF as "better", and include "logo.pdf".  This can be
     93 ;; useful, for example, when you are mocking up a logo in the PNG
     94 ;; raster format and then switch over to the higher quality PDF vector
     95 ;; format.  When you include the file extension then LaTeX will
     96 ;; include it without searching for higher quality file types.
     97 ;; Whatever file type you choose, it will probably require a few
     98 ;; design iterations to get the best looking logo size for your
     99 ;; letter.  Finally, the directory and file name are specified
    100 ;; *without* quotes.  Here are some examples with commentary, in the
    101 ;; location of your letter, with a logo named "logo", to get you
    102 ;; started:
    103 ;;
    104 ;;   Logo in the same directory: \includegraphics{logo}
    105 ;;       or a sub-directory:     \includegraphics{logos/production/logo}
    106 ;;
    107 ;;   Logos specified using absolute paths on Linux or Windows:
    108 ;;
    109 ;;       \includegraphics{~/correspondence/logo}
    110 ;;       \includegraphics{~/correspondence/logos/production/logo}
    111 ;;       \includegraphics{c:/you/correspondence/logo}
    112 ;;       \includegraphics{c:/you/correspondence/logos/production/logo}
    113 ;;
    114 ;;   Logos in the same directory where the "better" quality PDF will
    115 ;;   be chosen over the JPG:
    116 ;;
    117 ;;       \includegraphics{logo.pdf}
    118 ;;       \includegraphics{logo.png}
    119 ;;
    120 ;; Headlines are in general ignored.  However, headlines with special
    121 ;; tags can be used for specified contents like postscript (ps),
    122 ;; carbon copy (cc), enclosures (encl) and code to be inserted after
    123 ;; \end{letter} (after_letter).  Specials tags are defined in
    124 ;; `org-koma-letter-special-tags-after-closing' and
    125 ;; `org-koma-letter-special-tags-after-letter'.  Currently members of
    126 ;; `org-koma-letter-special-tags-after-closing' used as macros and the
    127 ;; content of the headline is the argument.
    128 ;;
    129 ;; Headlines with to and from may also be used rather than the keyword
    130 ;; approach described above.  If both a keyword and a headline with
    131 ;; information is present precedence is determined by
    132 ;; `org-koma-letter-prefer-special-headings'.
    133 ;;
    134 ;; You need an appropriate association in `org-latex-classes' in order
    135 ;; to use the KOMA Scrlttr2 class.  By default, a sparse scrlttr2
    136 ;; class is provided: "default-koma-letter".  You can also add you own
    137 ;; letter class.  For instance:
    138 ;;
    139 ;;   (add-to-list 'org-latex-classes
    140 ;;                '("my-letter"
    141 ;;                  "\\documentclass\[%
    142 ;;   DIV=14,
    143 ;;   fontsize=12pt,
    144 ;;   parskip=half,
    145 ;;   subject=titled,
    146 ;;   backaddress=false,
    147 ;;   fromalign=left,
    148 ;;   fromemail=true,
    149 ;;   fromphone=true\]\{scrlttr2\}
    150 ;;   \[DEFAULT-PACKAGES]
    151 ;;   \[PACKAGES]
    152 ;;   \[EXTRA]"))
    153 ;;
    154 ;; Then, in your Org document, be sure to require the proper class
    155 ;; with:
    156 ;;
    157 ;;    #+LATEX_CLASS: my-letter
    158 ;;
    159 ;; Or by setting `org-koma-letter-default-class'.
    160 ;;
    161 ;; You may have to load (LaTeX) Babel as well, e.g., by adding
    162 ;; it to `org-latex-packages-alist',
    163 ;;
    164 ;;    (add-to-list 'org-latex-packages-alist '("AUTO" "babel" nil))
    165 
    166 ;;; Code:
    167 
    168 (require 'org-macs)
    169 (org-assert-version)
    170 
    171 (require 'cl-lib)
    172 (require 'ox-latex)
    173 
    174 ;; Install a default letter class.
    175 (unless (assoc "default-koma-letter" org-latex-classes)
    176   (add-to-list 'org-latex-classes
    177                '("default-koma-letter" "\\documentclass[11pt]{scrlttr2}")))
    178 
    179 
    180 ;;; User-Configurable Variables
    181 
    182 (defgroup org-export-koma-letter nil
    183   "Options for exporting to KOMA scrlttr2 class in LaTeX export."
    184   :tag "Org Koma-Letter"
    185   :group 'org-export)
    186 
    187 (defcustom org-koma-letter-class-option-file "NF"
    188   "Letter Class Option File.
    189 This option can also be set with the LCO keyword."
    190   :type 'string)
    191 
    192 (defcustom org-koma-letter-author 'user-full-name
    193   "Sender's name.
    194 
    195 This variable defaults to calling the function `user-full-name'
    196 which just returns the current function `user-full-name'.
    197 Alternatively a string, nil or a function may be given.
    198 Functions must return a string.
    199 
    200 This option can also be set with the AUTHOR keyword."
    201   :type '(radio (function-item user-full-name)
    202                 (string)
    203                 (function)
    204                 (const :tag "Do not export author" nil)))
    205 
    206 (defcustom org-koma-letter-email 'org-koma-letter-email
    207   "Sender's email address.
    208 
    209 This variable defaults to the value `org-koma-letter-email' which
    210 returns `user-mail-address'.  Alternatively a string, nil or
    211 a function may be given.  Functions must return a string.
    212 
    213 This option can also be set with the EMAIL keyword."
    214   :type '(radio (function-item org-koma-letter-email)
    215                 (string)
    216                 (function)
    217                 (const :tag "Do not export email" nil)))
    218 
    219 (defcustom org-koma-letter-from-address ""
    220   "Sender's address, as a string.
    221 This option can also be set with one or more FROM_ADDRESS
    222 keywords."
    223   :type 'string)
    224 
    225 (defcustom org-koma-letter-phone-number ""
    226   "Sender's phone number, as a string.
    227 This option can also be set with the PHONE_NUMBER keyword."
    228   :type 'string)
    229 
    230 (defcustom org-koma-letter-url ""
    231   "Sender's URL, e. g., the URL of her homepage.
    232 This option can also be set with the URL keyword."
    233   :type 'string
    234   :safe #'stringp)
    235 
    236 (defcustom org-koma-letter-from-logo ""
    237   "Commands for inserting the sender's logo, e. g., \\includegraphics{logo}.
    238 This option can also be set with the FROM_LOGO keyword."
    239   :type 'string
    240   :safe #'stringp)
    241 
    242 (defcustom org-koma-letter-place ""
    243   "Place from which the letter is sent, as a string.
    244 This option can also be set with the PLACE keyword."
    245   :type 'string)
    246 
    247 (defcustom org-koma-letter-location ""
    248   "Sender's extension field, as a string.
    249 
    250 This option can also be set with the LOCATION keyword.
    251 Moreover, when:
    252   (1) Either `org-koma-letter-prefer-special-headings' is non-nil
    253       or there is no LOCATION keyword or the LOCATION keyword is
    254       empty;
    255   (2) the letter contains a headline with the special
    256       tag \"location\";
    257 then the location will be set as the content of the location
    258 special heading.
    259 
    260 The location field is typically printed right of the address
    261 field (See Figure 4.9. in the English manual of 2015-10-03)."
    262   :type 'string)
    263 
    264 (defcustom org-koma-letter-opening ""
    265   "Letter's opening, as a string.
    266 
    267 This option can also be set with the OPENING keyword.  Moreover,
    268 when:
    269   (1) Either `org-koma-letter-prefer-special-headings' is non-nil
    270       or the CLOSING keyword is empty
    271   (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
    272   (3) the letter contains a headline without a special
    273       tag (e.g. \"to\" or \"ps\");
    274 then the opening will be implicitly set as the untagged headline title."
    275   :type 'string)
    276 
    277 (defcustom org-koma-letter-closing ""
    278   "Letter's closing, as a string.
    279 This option can also be set with the CLOSING keyword.  Moreover,
    280 when:
    281   (1) Either `org-koma-letter-prefer-special-headings' is non-nil
    282       or the CLOSING keyword is empty;
    283   (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
    284   (3) the letter contains a headline with the special
    285       tag \"closing\";
    286 then the opening will be set as the title of the closing special
    287 heading title."
    288   :type 'string)
    289 
    290 (defcustom org-koma-letter-signature ""
    291   "Signature, as a string.
    292 This option can also be set with the SIGNATURE keyword.
    293 Moreover, when:
    294   (1) Either `org-koma-letter-prefer-special-headings' is non-nil
    295       or there is no CLOSING keyword or the CLOSING keyword is empty;
    296   (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
    297   (3) the letter contains a headline with the special
    298       tag \"closing\";
    299 then the signature will be  set as the content of the
    300 closing special heading.
    301 
    302 Note if the content is empty the signature will not be set."
    303   :type 'string)
    304 
    305 (defcustom org-koma-letter-prefer-special-headings nil
    306   "Non-nil means prefer headlines over keywords for TO and FROM.
    307 This option can also be set with the OPTIONS keyword, e.g.:
    308 \"special-headings:t\"."
    309   :type 'boolean)
    310 
    311 (defcustom org-koma-letter-subject-format t
    312   "Non-nil means include the subject.
    313 
    314 Support formatting options.
    315 
    316 When t, insert a subject using default options.  When nil, do not
    317 insert a subject at all.  It can also be a list of symbols among
    318 the following ones:
    319 
    320  `afteropening'  Subject after opening
    321  `beforeopening' Subject before opening
    322  `centered'      Subject centered
    323  `left'          Subject left-justified
    324  `right'         Subject right-justified
    325  `titled'        Add title/description to subject
    326  `underlined'    Set subject underlined
    327  `untitled'      Do not add title/description to subject
    328 
    329 Please refer to the KOMA-script manual (Table 4.16. in the
    330 English manual of 2012-07-22).
    331 
    332 This option can also be set with the OPTIONS keyword, e.g.:
    333 \"subject:(underlined centered)\"."
    334   :type
    335   '(choice
    336     (const :tag "No export" nil)
    337     (const :tag "Default options" t)
    338     (set :tag "Configure options"
    339          (const :tag "Subject after opening" afteropening)
    340          (const :tag "Subject before opening" beforeopening)
    341          (const :tag "Subject centered" centered)
    342          (const :tag "Subject left-justified" left)
    343          (const :tag "Subject right-justified" right)
    344          (const :tag "Add title or description to subject" underlined)
    345          (const :tag "Set subject underlined" titled)
    346          (const :tag "Do not add title or description to subject" untitled))))
    347 
    348 (defcustom org-koma-letter-use-backaddress nil
    349   "Non-nil prints return address in line above to address.
    350 This option can also be set with the OPTIONS keyword, e.g.:
    351 \"backaddress:t\"."
    352   :type 'boolean)
    353 
    354 (defcustom org-koma-letter-use-foldmarks t
    355   "Configure appearance of folding marks.
    356 
    357 When t, activate default folding marks.  When nil, do not insert
    358 folding marks at all.  It can also be a list of symbols among the
    359 following ones:
    360 
    361   `B'  Activate upper horizontal mark on left paper edge
    362   `b'  Deactivate upper horizontal mark on left paper edge
    363 
    364   `H'  Activate all horizontal marks on left paper edge
    365   `h'  Deactivate all horizontal marks on left paper edge
    366 
    367   `L'  Activate left vertical mark on upper paper edge
    368   `l'  Deactivate left vertical mark on upper paper edge
    369 
    370   `M'  Activate middle horizontal mark on left paper edge
    371   `m'  Deactivate middle horizontal mark on left paper edge
    372 
    373   `P'  Activate punch or center mark on left paper edge
    374   `p'  Deactivate punch or center mark on left paper edge
    375 
    376   `T'  Activate lower horizontal mark on left paper edge
    377    t   Deactivate lower horizontal mark on left paper edge
    378 
    379   `V'  Activate all vertical marks on upper paper edge
    380   `v'  Deactivate all vertical marks on upper paper edge
    381 
    382 This option can also be set with the OPTIONS keyword, e.g.:
    383 \"foldmarks:(b l m t)\"."
    384   :type '(choice
    385           (const :tag "Activate default folding marks" t)
    386           (const :tag "Deactivate folding marks" nil)
    387           (set
    388            :tag "Configure folding marks"
    389            (const :tag "Activate upper horizontal mark on left paper edge" B)
    390            (const :tag "Deactivate upper horizontal mark on left paper edge" b)
    391            (const :tag "Activate all horizontal marks on left paper edge" H)
    392            (const :tag "Deactivate all horizontal marks on left paper edge" h)
    393            (const :tag "Activate left vertical mark on upper paper edge" L)
    394            (const :tag "Deactivate left vertical mark on upper paper edge" l)
    395            (const :tag "Activate middle horizontal mark on left paper edge" M)
    396            (const :tag "Deactivate middle horizontal mark on left paper edge" m)
    397            (const :tag "Activate punch or center mark on left paper edge" P)
    398            (const :tag "Deactivate punch or center mark on left paper edge" p)
    399            (const :tag "Activate lower horizontal mark on left paper edge" T)
    400            (const :tag "Deactivate lower horizontal mark on left paper edge" t)
    401            (const :tag "Activate all vertical marks on upper paper edge" V)
    402            (const :tag "Deactivate all vertical marks on upper paper edge" v))))
    403 
    404 (defcustom org-koma-letter-use-phone nil
    405   "Non-nil prints sender's phone number.
    406 This option can also be set with the OPTIONS keyword, e.g.:
    407 \"phone:t\"."
    408   :type 'boolean)
    409 
    410 (defcustom org-koma-letter-use-url nil
    411   "Non-nil prints sender's URL.
    412 This option can also be set with the OPTIONS keyword, e.g.:
    413 \"url:t\"."
    414   :type 'boolean
    415   :safe #'booleanp)
    416 
    417 (defcustom org-koma-letter-use-from-logo nil
    418   "Non-nil prints sender's FROM_LOGO.
    419 This option can also be set with the OPTIONS keyword, e.g.:
    420 \"from-logo:t\"."
    421   :type 'boolean
    422   :safe #'booleanp)
    423 
    424 (defcustom org-koma-letter-use-email nil
    425   "Non-nil prints sender's email address.
    426 This option can also be set with the OPTIONS keyword, e.g.:
    427 \"email:t\"."
    428   :type 'boolean)
    429 
    430 (defcustom org-koma-letter-use-place t
    431   "Non-nil prints the letter's place next to the date.
    432 This option can also be set with the OPTIONS keyword, e.g.:
    433 \"place:nil\"."
    434   :type 'boolean)
    435 
    436 (defcustom org-koma-letter-default-class "default-koma-letter"
    437   "Default class for `org-koma-letter'.
    438 The value must be a member of `org-latex-classes'."
    439   :type 'string)
    440 
    441 (defcustom org-koma-letter-headline-is-opening-maybe t
    442   "Non-nil means a headline may be used as an opening and closing.
    443 See also `org-koma-letter-opening' and
    444 `org-koma-letter-closing'."
    445   :type 'boolean)
    446 
    447 (defcustom org-koma-letter-prefer-subject nil
    448   "Non-nil means title should be interpreted as subject if subject is missing.
    449 This option can also be set with the OPTIONS keyword,
    450 e.g. \"title-subject:t\"."
    451   :type 'boolean)
    452 
    453 (defconst org-koma-letter-special-tags-in-letter '(to from closing location)
    454   "Header tags related to the letter itself.")
    455 
    456 (defconst org-koma-letter-special-tags-after-closing '(after_closing ps encl cc)
    457   "Header tags to be inserted in the letter after closing.")
    458 
    459 (defconst org-koma-letter-special-tags-as-macro '(ps encl cc)
    460   "Header tags to be inserted as macros.")
    461 
    462 (defconst org-koma-letter-special-tags-after-letter '(after_letter)
    463   "Header tags to be inserted after the letter.")
    464 
    465 (defvar org-koma-letter-special-contents nil
    466   "Holds special content temporarily.")
    467 
    468 
    469 ;;; Define Backend
    470 
    471 (org-export-define-derived-backend 'koma-letter 'latex
    472   :options-alist
    473   '((:latex-class "LATEX_CLASS" nil org-koma-letter-default-class t)
    474     (:lco "LCO" nil org-koma-letter-class-option-file)
    475     (:author "AUTHOR" nil (org-koma-letter--get-value org-koma-letter-author) parse)
    476     (:author-changed-in-buffer-p "AUTHOR" nil nil t)
    477     (:from-address "FROM_ADDRESS" nil org-koma-letter-from-address newline)
    478     (:phone-number "PHONE_NUMBER" nil org-koma-letter-phone-number)
    479     (:url "URL" nil org-koma-letter-url)
    480     (:from-logo "FROM_LOGO" nil org-koma-letter-from-logo)
    481     (:email "EMAIL" nil (org-koma-letter--get-value org-koma-letter-email) t)
    482     (:to-address "TO_ADDRESS" nil nil newline)
    483     (:place "PLACE" nil org-koma-letter-place)
    484     (:location "LOCATION" nil org-koma-letter-location)
    485     (:subject "SUBJECT" nil nil parse)
    486     (:opening "OPENING" nil org-koma-letter-opening parse)
    487     (:closing "CLOSING" nil org-koma-letter-closing parse)
    488     (:signature "SIGNATURE" nil org-koma-letter-signature newline)
    489     (:special-headings nil "special-headings" org-koma-letter-prefer-special-headings)
    490     (:special-tags-as-macro nil nil org-koma-letter-special-tags-as-macro)
    491     (:special-tags-in-letter nil nil org-koma-letter-special-tags-in-letter)
    492     (:special-tags-after-closing nil "after-closing-order"
    493                                  org-koma-letter-special-tags-after-closing)
    494     (:special-tags-after-letter nil "after-letter-order"
    495                                 org-koma-letter-special-tags-after-letter)
    496     (:with-backaddress nil "backaddress" org-koma-letter-use-backaddress)
    497     (:with-email nil "email" org-koma-letter-use-email)
    498     (:with-foldmarks nil "foldmarks" org-koma-letter-use-foldmarks)
    499     (:with-phone nil "phone" org-koma-letter-use-phone)
    500     (:with-url nil "url" org-koma-letter-use-url)
    501     (:with-from-logo nil "from-logo" org-koma-letter-use-from-logo)
    502     (:with-place nil "place" org-koma-letter-use-place)
    503     (:with-subject nil "subject" org-koma-letter-subject-format)
    504     (:with-title-as-subject nil "title-subject" org-koma-letter-prefer-subject)
    505     (:with-headline-opening nil nil org-koma-letter-headline-is-opening-maybe)
    506     ;; Special properties non-nil when a setting happened in buffer.
    507     ;; They are used to prioritize in-buffer settings over "lco"
    508     ;; files.  See `org-koma-letter-template'.
    509     (:inbuffer-author "AUTHOR" nil 'koma-letter:empty)
    510     (:inbuffer-from "FROM" nil 'koma-letter:empty)
    511     (:inbuffer-email "EMAIL" nil 'koma-letter:empty)
    512     (:inbuffer-phone-number "PHONE_NUMBER" nil 'koma-letter:empty)
    513     (:inbuffer-url "URL" nil 'koma-letter:empty)
    514     (:inbuffer-from-logo "FROM_LOGO" nil 'koma-letter:empty)
    515     (:inbuffer-place "PLACE" nil 'koma-letter:empty)
    516     (:inbuffer-location "LOCATION" nil 'koma-letter:empty)
    517     (:inbuffer-signature "SIGNATURE" nil 'koma-letter:empty)
    518     (:inbuffer-with-backaddress nil "backaddress" 'koma-letter:empty)
    519     (:inbuffer-with-email nil "email" 'koma-letter:empty)
    520     (:inbuffer-with-foldmarks nil "foldmarks" 'koma-letter:empty)
    521     (:inbuffer-with-phone nil "phone" 'koma-letter:empty)
    522     (:inbuffer-with-url nil "url" 'koma-letter:empty)
    523     (:inbuffer-with-from-logo nil "from-logo" 'koma-letter:empty)
    524     (:inbuffer-with-place nil "place" 'koma-letter:empty))
    525   :translate-alist '((export-block . org-koma-letter-export-block)
    526                      (export-snippet . org-koma-letter-export-snippet)
    527                      (headline . org-koma-letter-headline)
    528                      (keyword . org-koma-letter-keyword)
    529                      (template . org-koma-letter-template))
    530   :menu-entry
    531   '(?k "Export with KOMA Scrlttr2"
    532        ((?L "As LaTeX buffer" org-koma-letter-export-as-latex)
    533         (?l "As LaTeX file" org-koma-letter-export-to-latex)
    534         (?p "As PDF file" org-koma-letter-export-to-pdf)
    535         (?o "As PDF file and open"
    536             (lambda (a s v b)
    537               (if a (org-koma-letter-export-to-pdf t s v b)
    538                 (org-open-file (org-koma-letter-export-to-pdf nil s v b))))))))
    539 
    540 
    541 
    542 ;;; Helper functions
    543 
    544 (defun org-koma-letter-email ()
    545   "Return the current `user-mail-address'."
    546   user-mail-address)
    547 
    548 ;; The following is taken from/inspired by ox-grof.el
    549 ;; Thanks, Luis!
    550 
    551 (defun org-koma-letter--get-tagged-contents (key)
    552   "Get contents from a headline tagged with KEY.
    553 The contents is stored in `org-koma-letter-special-contents'."
    554   (let ((value (cdr (assoc-string (org-koma-letter--get-value key)
    555                                   org-koma-letter-special-contents))))
    556     (when value (org-string-nw-p (org-trim value)))))
    557 
    558 (defun org-koma-letter--get-value (value)
    559   "Turn value into a string whenever possible.
    560 Determines if VALUE is nil, a string, a function or a symbol and
    561 return a string or nil."
    562   (when value
    563     (cond ((stringp value) value)
    564           ((functionp value) (funcall value))
    565           ((symbolp value) (symbol-name value))
    566           (t value))))
    567 
    568 (defun org-koma-letter--special-contents-inline (keywords info)
    569   "Process KEYWORDS members of `org-koma-letter-special-contents'.
    570 
    571 KEYWORDS is a list of symbols.  Return them as a string to be
    572 formatted.
    573 
    574 INFO is the information plist possibly holding :special-tags-as-macro
    575 property.  See `org-koma-letter-special-tags-as-macro'.
    576 
    577 The function is used for inserting content of special headings
    578 such as the one tagged with PS."
    579   (mapconcat
    580    (lambda (keyword)
    581      (let* ((name (org-koma-letter--get-value keyword))
    582             (value (org-koma-letter--get-tagged-contents name))
    583             (macrop (memq keyword (plist-get info :special-tags-as-macro))))
    584        (cond ((not value) nil)
    585              (macrop (format "\\%s{%s}\n" name value))
    586              (t value))))
    587    keywords
    588    "\n"))
    589 
    590 
    591 (defun org-koma-letter--add-latex-newlines (string)
    592   "Replace regular newlines with LaTeX newlines (i.e. `\\\\') in STRING.
    593 Return a new string."
    594   (let ((str (org-trim string)))
    595     (when (org-string-nw-p str)
    596       (replace-regexp-in-string "\n" "\\\\\\\\\n" str))))
    597 
    598 
    599 
    600 ;;; Transcode Functions
    601 
    602 ;;;; Export Block
    603 
    604 (defun org-koma-letter-export-block (export-block _contents _info)
    605   "Transcode an EXPORT-BLOCK element into KOMA Scrlttr2 code.
    606 CONTENTS is nil.  INFO is a plist used as a communication
    607 channel."
    608   (when (member (org-element-property :type export-block)
    609 		'("KOMA-LETTER" "LATEX"))
    610     (org-remove-indentation (org-element-property :value export-block))))
    611 
    612 ;;;; Export Snippet
    613 
    614 (defun org-koma-letter-export-snippet (export-snippet _contents _info)
    615   "Transcode an EXPORT-SNIPPET object into KOMA Scrlttr2 code.
    616 CONTENTS is nil.  INFO is a plist used as a communication
    617 channel."
    618   (when (memq (org-export-snippet-backend export-snippet) '(latex koma-letter))
    619     (org-element-property :value export-snippet)))
    620 
    621 ;;;; Keyword
    622 
    623 (defun org-koma-letter-keyword (keyword contents info)
    624   "Transcode a KEYWORD element into KOMA Scrlttr2 code.
    625 CONTENTS is nil.  INFO is a plist used as a communication
    626 channel."
    627   (let ((key (org-element-property :key keyword))
    628         (value (org-element-property :value keyword)))
    629     ;; Handle specifically KOMA-LETTER keywords.  Otherwise, fallback
    630     ;; to `latex' backend.
    631     (if (equal key "KOMA-LETTER") value
    632       (org-export-with-backend 'latex keyword contents info))))
    633 
    634 ;; Headline
    635 
    636 (defun org-koma-letter-headline (headline contents info)
    637   "Transcode a HEADLINE element from Org to LaTeX.
    638 CONTENTS holds the contents of the headline.  INFO is a plist
    639 holding contextual information.
    640 
    641 Note that if a headline is tagged with a tag from
    642 `org-koma-letter-special-tags' it will not be exported, but
    643 stored in `org-koma-letter-special-contents' and included at the
    644 appropriate place."
    645   (let ((special-tag (org-koma-letter--special-tag headline info)))
    646     (if (not special-tag)
    647         contents
    648       (push (cons special-tag contents) org-koma-letter-special-contents)
    649       "")))
    650 
    651 (defun org-koma-letter--special-tag (headline info)
    652   "Non-nil if HEADLINE is a special headline.
    653 INFO is a plist holding contextual information.  Return first
    654 special tag headline."
    655   (let ((special-tags (append
    656                        (plist-get info :special-tags-in-letter)
    657                        (plist-get info :special-tags-after-closing)
    658                        (plist-get info :special-tags-after-letter))))
    659     (cl-some (lambda (tag) (and (assoc-string tag special-tags) tag))
    660 	     (org-export-get-tags headline info))))
    661 
    662 (defun org-koma-letter--keyword-or-headline (plist-key pred info)
    663   "Return the correct version of opening or closing.
    664 PLIST-KEY should be a key in info, typically :opening
    665 or :closing.  PRED is a predicate run on headline to determine
    666 which title to use which takes two arguments, a headline element
    667 and an info plist.  INFO is a plist holding contextual
    668 information.  Return the preferred candidate for the exported of
    669 PLIST-KEY."
    670   (let* ((keyword-candidate (plist-get info plist-key))
    671          (headline-candidate (when (and (plist-get info :with-headline-opening)
    672                                         (or (plist-get info :special-headings)
    673                                             (not keyword-candidate)))
    674                                (org-element-map (plist-get info :parse-tree)
    675                                    'headline
    676                                  (lambda (h)
    677                                    (and (funcall pred h info)
    678 					(org-element-property :title h)))
    679                                  info t))))
    680     (org-export-data (or headline-candidate keyword-candidate "") info)))
    681 
    682 ;;;; Template
    683 
    684 (defun org-koma-letter-template (contents info)
    685   "Return complete document string after KOMA Scrlttr2 conversion.
    686 CONTENTS is the transcoded contents string.  INFO is a plist
    687 holding export options."
    688   (concat
    689    ;; Timestamp.
    690    (and (plist-get info :time-stamp-file)
    691         (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
    692    ;; LaTeX compiler
    693    (org-latex--insert-compiler info)
    694    ;; Document class and packages.
    695    (org-latex-make-preamble info)
    696    ;; Settings.  They can come from three locations, in increasing
    697    ;; order of precedence: global variables, LCO files and in-buffer
    698    ;; settings.  Thus, we first insert settings coming from global
    699    ;; variables, then we insert LCO files, and, eventually, we insert
    700    ;; settings coming from buffer keywords.
    701    (org-koma-letter--build-settings 'global info)
    702    (mapconcat (lambda (file) (format "\\LoadLetterOption{%s}\n" file))
    703               (split-string (or (plist-get info :lco) ""))
    704               "")
    705    (org-koma-letter--build-settings 'buffer info)
    706    ;; Date.
    707    (format "\\date{%s}\n" (org-export-data (org-export-get-date info) info))
    708    ;; Hyperref, document start, and subject and title.
    709    (let* ((with-subject (plist-get info :with-subject))
    710           (with-title (plist-get info :with-title))
    711           (title-as-subject (and with-subject
    712                                  (plist-get info :with-title-as-subject)))
    713           (subject* (org-string-nw-p
    714                      (org-export-data (plist-get info :subject) info)))
    715           (title* (and with-title
    716                        (org-string-nw-p
    717                         (org-export-data (plist-get info :title) info))))
    718           (subject (cond ((not with-subject) nil)
    719                          (title-as-subject (or subject* title*))
    720                          (t subject*)))
    721           (title (cond ((not with-title) nil)
    722                        (title-as-subject (and subject* title*))
    723                        (t title*)))
    724           (hyperref-template (plist-get info :latex-hyperref-template))
    725           (spec (append (list (cons ?t (or title subject "")))
    726                         (org-latex--format-spec info))))
    727      (concat
    728       (when (and with-subject (not (eq with-subject t)))
    729         (format "\\KOMAoption{subject}{%s}\n"
    730                 (if (symbolp with-subject) with-subject
    731                   (mapconcat #'symbol-name with-subject ","))))
    732       ;; Hyperref.
    733       (and (stringp hyperref-template)
    734 	   (format-spec hyperref-template spec))
    735       ;; Document start.
    736       "\\begin{document}\n\n"
    737       ;; Subject and title.
    738       (when subject (format "\\setkomavar{subject}{%s}\n" subject))
    739       (when title (format "\\setkomavar{title}{%s}\n" title))
    740       (when (or (org-string-nw-p title) (org-string-nw-p subject)) "\n")))
    741    ;; Letter start.
    742    (let ((keyword-val (plist-get info :to-address))
    743          (heading-val (org-koma-letter--get-tagged-contents 'to)))
    744      (format "\\begin{letter}{%%\n%s}\n\n"
    745              (org-koma-letter--add-latex-newlines
    746               (or (if (plist-get info :special-headings)
    747                       (or heading-val keyword-val)
    748                     (or keyword-val heading-val))
    749                   "\\mbox{}"))))
    750    ;; Opening.
    751    (format "\\opening{%s}\n\n"
    752            (org-koma-letter--keyword-or-headline
    753             :opening
    754 	    (lambda (h i)
    755 	      (not (org-koma-letter--special-tag h i)))
    756             info))
    757    ;; Letter body.
    758    contents
    759    ;; Closing.
    760    (format "\\closing{%s}\n"
    761 	   (org-koma-letter--keyword-or-headline
    762 	    :closing
    763 	    (lambda (h i)
    764 	      (let ((special-tag (org-koma-letter--special-tag h i)))
    765 		(and special-tag
    766 		     (string= "closing" special-tag))))
    767 	    info))
    768    (org-koma-letter--special-contents-inline
    769     (plist-get info :special-tags-after-closing) info)
    770    ;; Letter end.
    771    "\n\\end{letter}\n"
    772    (org-koma-letter--special-contents-inline
    773     (plist-get info :special-tags-after-letter) info)
    774    ;; Document end.
    775    "\n\\end{document}"))
    776 
    777 (defun org-koma-letter--build-settings (scope info)
    778   "Build settings string according to type.
    779 SCOPE is either `global' or `buffer'.  INFO is a plist used as
    780 a communication channel."
    781   (let* ((check-scope
    782           ;; Non-nil value when SETTING was defined in SCOPE.
    783           (lambda (setting)
    784             (let ((property (intern (format ":inbuffer-%s" setting))))
    785               (if (eq scope 'global)
    786                   (eq (plist-get info property) 'koma-letter:empty)
    787                 (not (eq (plist-get info property) 'koma-letter:empty))))))
    788          (heading-or-key-value
    789           (lambda (heading key &optional scoped)
    790             (let* ((heading-val
    791                     (org-koma-letter--get-tagged-contents heading))
    792                    (key-val (org-string-nw-p (plist-get info key)))
    793                    (scopedp (funcall check-scope (or scoped heading))))
    794               (and (or (and key-val scopedp) heading-val)
    795                    (not (and (eq scope 'global) heading-val))
    796                    (if scopedp key-val heading-val))))))
    797     (concat
    798      ;; Name.
    799      (let ((author (plist-get info :author)))
    800        (and author
    801             (funcall check-scope 'author)
    802             (format "\\setkomavar{fromname}{%s}\n"
    803                     (org-export-data author info))))
    804      ;; From.
    805      (let ((from (funcall heading-or-key-value 'from :from-address)))
    806        (and from
    807             (format "\\setkomavar{fromaddress}{%s}\n"
    808                     (org-koma-letter--add-latex-newlines from))))
    809      ;; Email.
    810      (let ((email (plist-get info :email)))
    811        (and email
    812             (funcall check-scope 'email)
    813             (format "\\setkomavar{fromemail}{%s}\n" email)))
    814      (and (funcall check-scope 'with-email)
    815           (format "\\KOMAoption{fromemail}{%s}\n"
    816                   (if (plist-get info :with-email) "true" "false")))
    817      ;; Phone number.
    818      (let ((phone-number (plist-get info :phone-number)))
    819        (and (org-string-nw-p phone-number)
    820             (funcall check-scope 'phone-number)
    821             (format "\\setkomavar{fromphone}{%s}\n" phone-number)))
    822      (and (funcall check-scope 'with-phone)
    823           (format "\\KOMAoption{fromphone}{%s}\n"
    824                   (if (plist-get info :with-phone) "true" "false")))
    825      ;; URL
    826      (let ((url (plist-get info :url)))
    827        (and (org-string-nw-p url)
    828             (funcall check-scope 'url)
    829             (format "\\setkomavar{fromurl}{%s}\n" url)))
    830      (and (funcall check-scope 'with-url)
    831           (format "\\KOMAoption{fromurl}{%s}\n"
    832                   (if (plist-get info :with-url) "true" "false")))
    833      ;; From Logo
    834      (let ((from-logo (plist-get info :from-logo)))
    835        (and (org-string-nw-p from-logo)
    836             (funcall check-scope 'from-logo)
    837             (format "\\setkomavar{fromlogo}{%s}\n" from-logo)))
    838      (and (funcall check-scope 'with-from-logo)
    839           (format "\\KOMAoption{fromlogo}{%s}\n"
    840                   (if (plist-get info :with-from-logo) "true" "false")))
    841      ;; Signature.
    842      (let* ((heading-val
    843              (and (plist-get info :with-headline-opening)
    844                   (pcase (org-koma-letter--get-tagged-contents 'closing)
    845                     ((and (pred org-string-nw-p) closing) (org-trim closing))
    846                     (_ nil))))
    847             (signature (org-string-nw-p (plist-get info :signature)))
    848             (signature-scope (funcall check-scope 'signature)))
    849        (and (or (and signature signature-scope)
    850                 heading-val)
    851             (not (and (eq scope 'global) heading-val))
    852             (format "\\setkomavar{signature}{%s}\n"
    853                     (if signature-scope signature heading-val))))
    854      ;; Back address.
    855      (and (funcall check-scope 'with-backaddress)
    856           (format "\\KOMAoption{backaddress}{%s}\n"
    857                   (if (plist-get info :with-backaddress) "true" "false")))
    858      ;; Place.
    859      (let ((with-place-set (funcall check-scope 'with-place))
    860            (place-set (funcall check-scope 'place)))
    861        (and (or (and with-place-set place-set)
    862                 (and (eq scope 'buffer) (or with-place-set place-set)))
    863             (format "\\setkomavar{place}{%s}\n"
    864                     (if (plist-get info :with-place) (plist-get info :place)
    865                       ""))))
    866      ;; Location.
    867      (let ((location (funcall heading-or-key-value 'location :location)))
    868        (and location
    869             (format "\\setkomavar{location}{%s}\n" location)))
    870      ;; Folding marks.
    871      (and (funcall check-scope 'with-foldmarks)
    872           (let ((foldmarks (plist-get info :with-foldmarks)))
    873             (cond ((consp foldmarks)
    874                    (format "\\KOMAoptions{foldmarks=true,foldmarks=%s}\n"
    875                            (mapconcat #'symbol-name foldmarks "")))
    876                   (foldmarks "\\KOMAoptions{foldmarks=true}\n")
    877                   (t "\\KOMAoptions{foldmarks=false}\n")))))))
    878 
    879 
    880 
    881 ;;; Commands
    882 
    883 ;;;###autoload
    884 (defun org-koma-letter-export-as-latex
    885     (&optional async subtreep visible-only body-only ext-plist)
    886   "Export current buffer as a KOMA Scrlttr2 letter.
    887 
    888 If narrowing is active in the current buffer, only export its
    889 narrowed part.
    890 
    891 If a region is active, export that region.
    892 
    893 A non-nil optional argument ASYNC means the process should happen
    894 asynchronously.  The resulting buffer should be accessible
    895 through the `org-export-stack' interface.
    896 
    897 When optional argument SUBTREEP is non-nil, export the sub-tree
    898 at point, extracting information from the headline properties
    899 first.
    900 
    901 When optional argument VISIBLE-ONLY is non-nil, don't export
    902 contents of hidden elements.
    903 
    904 When optional argument BODY-ONLY is non-nil, only write code
    905 between \"\\begin{letter}\" and \"\\end{letter}\".
    906 
    907 EXT-PLIST, when provided, is a property list with external
    908 parameters overriding Org default settings, but still inferior to
    909 file-local settings.
    910 
    911 Export is done in a buffer named \"*Org KOMA-LETTER Export*\".  It
    912 will be displayed if `org-export-show-temporary-export-buffer' is
    913 non-nil."
    914   (interactive)
    915   (let (org-koma-letter-special-contents)
    916     (org-export-to-buffer 'koma-letter "*Org KOMA-LETTER Export*"
    917       async subtreep visible-only body-only ext-plist
    918       (if (fboundp 'major-mode-remap)
    919           (major-mode-remap 'latex-mode)
    920         #'LaTeX-mode))))
    921 
    922 ;;;###autoload
    923 (defun org-koma-letter-export-to-latex
    924     (&optional async subtreep visible-only body-only ext-plist)
    925   "Export current buffer as a KOMA Scrlttr2 letter (tex).
    926 
    927 If narrowing is active in the current buffer, only export its
    928 narrowed part.
    929 
    930 If a region is active, export that region.
    931 
    932 A non-nil optional argument ASYNC means the process should happen
    933 asynchronously.  The resulting file should be accessible through
    934 the `org-export-stack' interface.
    935 
    936 When optional argument SUBTREEP is non-nil, export the sub-tree
    937 at point, extracting information from the headline properties
    938 first.
    939 
    940 When optional argument VISIBLE-ONLY is non-nil, don't export
    941 contents of hidden elements.
    942 
    943 When optional argument BODY-ONLY is non-nil, only write code
    944 between \"\\begin{letter}\" and \"\\end{letter}\".
    945 
    946 EXT-PLIST, when provided, is a property list with external
    947 parameters overriding Org default settings, but still inferior to
    948 file-local settings.
    949 
    950 When optional argument PUB-DIR is set, use it as the publishing
    951 directory.
    952 
    953 Return output file's name."
    954   (interactive)
    955   (let ((outfile (org-export-output-file-name ".tex" subtreep))
    956         (org-koma-letter-special-contents))
    957     (org-export-to-file 'koma-letter outfile
    958       async subtreep visible-only body-only ext-plist)))
    959 
    960 ;;;###autoload
    961 (defun org-koma-letter-export-to-pdf
    962     (&optional async subtreep visible-only body-only ext-plist)
    963   "Export current buffer as a KOMA Scrlttr2 letter (pdf).
    964 
    965 If narrowing is active in the current buffer, only export its
    966 narrowed part.
    967 
    968 If a region is active, export that region.
    969 
    970 A non-nil optional argument ASYNC means the process should happen
    971 asynchronously.  The resulting file should be accessible through
    972 the `org-export-stack' interface.
    973 
    974 When optional argument SUBTREEP is non-nil, export the sub-tree
    975 at point, extracting information from the headline properties
    976 first.
    977 
    978 When optional argument VISIBLE-ONLY is non-nil, don't export
    979 contents of hidden elements.
    980 
    981 When optional argument BODY-ONLY is non-nil, only write code
    982 between \"\\begin{letter}\" and \"\\end{letter}\".
    983 
    984 EXT-PLIST, when provided, is a property list with external
    985 parameters overriding Org default settings, but still inferior to
    986 file-local settings.
    987 
    988 Return PDF file's name."
    989   (interactive)
    990   (let ((file (org-export-output-file-name ".tex" subtreep))
    991         (org-koma-letter-special-contents))
    992     (org-export-to-file 'koma-letter file
    993       async subtreep visible-only body-only ext-plist
    994       #'org-latex-compile)))
    995 
    996 
    997 (provide 'ox-koma-letter)
    998 ;;; ox-koma-letter.el ends here