dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

ox-koma-letter.el (40392B)


      1 ;;; ox-koma-letter.el --- KOMA Scrlttr2 Back-End for Org Export Engine  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2007-2023 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, wp, 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 back-end, 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' back-end (see
     38 ;; `org-latex-packages-alist'), this back-end 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 Back-End
    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 The function is used for inserting content of special headings
    575 such as the one tagged with PS."
    576   (mapconcat
    577    (lambda (keyword)
    578      (let* ((name (org-koma-letter--get-value keyword))
    579             (value (org-koma-letter--get-tagged-contents name))
    580             (macrop (memq keyword (plist-get info :special-tags-as-macro))))
    581        (cond ((not value) nil)
    582              (macrop (format "\\%s{%s}\n" name value))
    583              (t value))))
    584    keywords
    585    "\n"))
    586 
    587 
    588 (defun org-koma-letter--add-latex-newlines (string)
    589   "Replace regular newlines with LaTeX newlines (i.e. `\\\\')."
    590   (let ((str (org-trim string)))
    591     (when (org-string-nw-p str)
    592       (replace-regexp-in-string "\n" "\\\\\\\\\n" str))))
    593 
    594 
    595 
    596 ;;; Transcode Functions
    597 
    598 ;;;; Export Block
    599 
    600 (defun org-koma-letter-export-block (export-block _contents _info)
    601   "Transcode an EXPORT-BLOCK element into KOMA Scrlttr2 code.
    602 CONTENTS is nil.  INFO is a plist used as a communication
    603 channel."
    604   (when (member (org-element-property :type export-block)
    605 		'("KOMA-LETTER" "LATEX"))
    606     (org-remove-indentation (org-element-property :value export-block))))
    607 
    608 ;;;; Export Snippet
    609 
    610 (defun org-koma-letter-export-snippet (export-snippet _contents _info)
    611   "Transcode an EXPORT-SNIPPET object into KOMA Scrlttr2 code.
    612 CONTENTS is nil.  INFO is a plist used as a communication
    613 channel."
    614   (when (memq (org-export-snippet-backend export-snippet) '(latex koma-letter))
    615     (org-element-property :value export-snippet)))
    616 
    617 ;;;; Keyword
    618 
    619 (defun org-koma-letter-keyword (keyword contents info)
    620   "Transcode a KEYWORD element into KOMA Scrlttr2 code.
    621 CONTENTS is nil.  INFO is a plist used as a communication
    622 channel."
    623   (let ((key (org-element-property :key keyword))
    624         (value (org-element-property :value keyword)))
    625     ;; Handle specifically KOMA-LETTER keywords.  Otherwise, fallback
    626     ;; to `latex' back-end.
    627     (if (equal key "KOMA-LETTER") value
    628       (org-export-with-backend 'latex keyword contents info))))
    629 
    630 ;; Headline
    631 
    632 (defun org-koma-letter-headline (headline contents info)
    633   "Transcode a HEADLINE element from Org to LaTeX.
    634 CONTENTS holds the contents of the headline.  INFO is a plist
    635 holding contextual information.
    636 
    637 Note that if a headline is tagged with a tag from
    638 `org-koma-letter-special-tags' it will not be exported, but
    639 stored in `org-koma-letter-special-contents' and included at the
    640 appropriate place."
    641   (let ((special-tag (org-koma-letter--special-tag headline info)))
    642     (if (not special-tag)
    643         contents
    644       (push (cons special-tag contents) org-koma-letter-special-contents)
    645       "")))
    646 
    647 (defun org-koma-letter--special-tag (headline info)
    648   "Non-nil if HEADLINE is a special headline.
    649 INFO is a plist holding contextual information.  Return first
    650 special tag headline."
    651   (let ((special-tags (append
    652                        (plist-get info :special-tags-in-letter)
    653                        (plist-get info :special-tags-after-closing)
    654                        (plist-get info :special-tags-after-letter))))
    655     (cl-some (lambda (tag) (and (assoc-string tag special-tags) tag))
    656 	     (org-export-get-tags headline info))))
    657 
    658 (defun org-koma-letter--keyword-or-headline (plist-key pred info)
    659   "Return the correct version of opening or closing.
    660 PLIST-KEY should be a key in info, typically :opening
    661 or :closing.  PRED is a predicate run on headline to determine
    662 which title to use which takes two arguments, a headline element
    663 and an info plist.  INFO is a plist holding contextual
    664 information.  Return the preferred candidate for the exported of
    665 PLIST-KEY."
    666   (let* ((keyword-candidate (plist-get info plist-key))
    667          (headline-candidate (when (and (plist-get info :with-headline-opening)
    668                                         (or (plist-get info :special-headings)
    669                                             (not keyword-candidate)))
    670                                (org-element-map (plist-get info :parse-tree)
    671                                    'headline
    672                                  (lambda (h)
    673                                    (and (funcall pred h info)
    674 					(org-element-property :title h)))
    675                                  info t))))
    676     (org-export-data (or headline-candidate keyword-candidate "") info)))
    677 
    678 ;;;; Template
    679 
    680 (defun org-koma-letter-template (contents info)
    681   "Return complete document string after KOMA Scrlttr2 conversion.
    682 CONTENTS is the transcoded contents string.  INFO is a plist
    683 holding export options."
    684   (concat
    685    ;; Time-stamp.
    686    (and (plist-get info :time-stamp-file)
    687         (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
    688    ;; LaTeX compiler
    689    (org-latex--insert-compiler info)
    690    ;; Document class and packages.
    691    (org-latex-make-preamble info)
    692    ;; Settings.  They can come from three locations, in increasing
    693    ;; order of precedence: global variables, LCO files and in-buffer
    694    ;; settings.  Thus, we first insert settings coming from global
    695    ;; variables, then we insert LCO files, and, eventually, we insert
    696    ;; settings coming from buffer keywords.
    697    (org-koma-letter--build-settings 'global info)
    698    (mapconcat (lambda (file) (format "\\LoadLetterOption{%s}\n" file))
    699               (split-string (or (plist-get info :lco) ""))
    700               "")
    701    (org-koma-letter--build-settings 'buffer info)
    702    ;; Date.
    703    (format "\\date{%s}\n" (org-export-data (org-export-get-date info) info))
    704    ;; Hyperref, document start, and subject and title.
    705    (let* ((with-subject (plist-get info :with-subject))
    706           (with-title (plist-get info :with-title))
    707           (title-as-subject (and with-subject
    708                                  (plist-get info :with-title-as-subject)))
    709           (subject* (org-string-nw-p
    710                      (org-export-data (plist-get info :subject) info)))
    711           (title* (and with-title
    712                        (org-string-nw-p
    713                         (org-export-data (plist-get info :title) info))))
    714           (subject (cond ((not with-subject) nil)
    715                          (title-as-subject (or subject* title*))
    716                          (t subject*)))
    717           (title (cond ((not with-title) nil)
    718                        (title-as-subject (and subject* title*))
    719                        (t title*)))
    720           (hyperref-template (plist-get info :latex-hyperref-template))
    721           (spec (append (list (cons ?t (or title subject "")))
    722                         (org-latex--format-spec info))))
    723      (concat
    724       (when (and with-subject (not (eq with-subject t)))
    725         (format "\\KOMAoption{subject}{%s}\n"
    726                 (if (symbolp with-subject) with-subject
    727                   (mapconcat #'symbol-name with-subject ","))))
    728       ;; Hyperref.
    729       (and (stringp hyperref-template)
    730 	   (format-spec hyperref-template spec))
    731       ;; Document start.
    732       "\\begin{document}\n\n"
    733       ;; Subject and title.
    734       (when subject (format "\\setkomavar{subject}{%s}\n" subject))
    735       (when title (format "\\setkomavar{title}{%s}\n" title))
    736       (when (or (org-string-nw-p title) (org-string-nw-p subject)) "\n")))
    737    ;; Letter start.
    738    (let ((keyword-val (plist-get info :to-address))
    739          (heading-val (org-koma-letter--get-tagged-contents 'to)))
    740      (format "\\begin{letter}{%%\n%s}\n\n"
    741              (org-koma-letter--add-latex-newlines
    742               (or (if (plist-get info :special-headings)
    743                       (or heading-val keyword-val)
    744                     (or keyword-val heading-val))
    745                   "\\mbox{}"))))
    746    ;; Opening.
    747    (format "\\opening{%s}\n\n"
    748            (org-koma-letter--keyword-or-headline
    749             :opening
    750 	    (lambda (h i)
    751 	      (not (org-koma-letter--special-tag h i)))
    752             info))
    753    ;; Letter body.
    754    contents
    755    ;; Closing.
    756    (format "\\closing{%s}\n"
    757 	   (org-koma-letter--keyword-or-headline
    758 	    :closing
    759 	    (lambda (h i)
    760 	      (let ((special-tag (org-koma-letter--special-tag h i)))
    761 		(and special-tag
    762 		     (string= "closing" special-tag))))
    763 	    info))
    764    (org-koma-letter--special-contents-inline
    765     (plist-get info :special-tags-after-closing) info)
    766    ;; Letter end.
    767    "\n\\end{letter}\n"
    768    (org-koma-letter--special-contents-inline
    769     (plist-get info :special-tags-after-letter) info)
    770    ;; Document end.
    771    "\n\\end{document}"))
    772 
    773 (defun org-koma-letter--build-settings (scope info)
    774   "Build settings string according to type.
    775 SCOPE is either `global' or `buffer'.  INFO is a plist used as
    776 a communication channel."
    777   (let* ((check-scope
    778           ;; Non-nil value when SETTING was defined in SCOPE.
    779           (lambda (setting)
    780             (let ((property (intern (format ":inbuffer-%s" setting))))
    781               (if (eq scope 'global)
    782                   (eq (plist-get info property) 'koma-letter:empty)
    783                 (not (eq (plist-get info property) 'koma-letter:empty))))))
    784          (heading-or-key-value
    785           (lambda (heading key &optional scoped)
    786             (let* ((heading-val
    787                     (org-koma-letter--get-tagged-contents heading))
    788                    (key-val (org-string-nw-p (plist-get info key)))
    789                    (scopedp (funcall check-scope (or scoped heading))))
    790               (and (or (and key-val scopedp) heading-val)
    791                    (not (and (eq scope 'global) heading-val))
    792                    (if scopedp key-val heading-val))))))
    793     (concat
    794      ;; Name.
    795      (let ((author (plist-get info :author)))
    796        (and author
    797             (funcall check-scope 'author)
    798             (format "\\setkomavar{fromname}{%s}\n"
    799                     (org-export-data author info))))
    800      ;; From.
    801      (let ((from (funcall heading-or-key-value 'from :from-address)))
    802        (and from
    803             (format "\\setkomavar{fromaddress}{%s}\n"
    804                     (org-koma-letter--add-latex-newlines from))))
    805      ;; Email.
    806      (let ((email (plist-get info :email)))
    807        (and email
    808             (funcall check-scope 'email)
    809             (format "\\setkomavar{fromemail}{%s}\n" email)))
    810      (and (funcall check-scope 'with-email)
    811           (format "\\KOMAoption{fromemail}{%s}\n"
    812                   (if (plist-get info :with-email) "true" "false")))
    813      ;; Phone number.
    814      (let ((phone-number (plist-get info :phone-number)))
    815        (and (org-string-nw-p phone-number)
    816             (funcall check-scope 'phone-number)
    817             (format "\\setkomavar{fromphone}{%s}\n" phone-number)))
    818      (and (funcall check-scope 'with-phone)
    819           (format "\\KOMAoption{fromphone}{%s}\n"
    820                   (if (plist-get info :with-phone) "true" "false")))
    821      ;; URL
    822      (let ((url (plist-get info :url)))
    823        (and (org-string-nw-p url)
    824             (funcall check-scope 'url)
    825             (format "\\setkomavar{fromurl}{%s}\n" url)))
    826      (and (funcall check-scope 'with-url)
    827           (format "\\KOMAoption{fromurl}{%s}\n"
    828                   (if (plist-get info :with-url) "true" "false")))
    829      ;; From Logo
    830      (let ((from-logo (plist-get info :from-logo)))
    831        (and (org-string-nw-p from-logo)
    832             (funcall check-scope 'from-logo)
    833             (format "\\setkomavar{fromlogo}{%s}\n" from-logo)))
    834      (and (funcall check-scope 'with-from-logo)
    835           (format "\\KOMAoption{fromlogo}{%s}\n"
    836                   (if (plist-get info :with-from-logo) "true" "false")))
    837      ;; Signature.
    838      (let* ((heading-val
    839              (and (plist-get info :with-headline-opening)
    840                   (pcase (org-koma-letter--get-tagged-contents 'closing)
    841                     ((and (pred org-string-nw-p) closing) (org-trim closing))
    842                     (_ nil))))
    843             (signature (org-string-nw-p (plist-get info :signature)))
    844             (signature-scope (funcall check-scope 'signature)))
    845        (and (or (and signature signature-scope)
    846                 heading-val)
    847             (not (and (eq scope 'global) heading-val))
    848             (format "\\setkomavar{signature}{%s}\n"
    849                     (if signature-scope signature heading-val))))
    850      ;; Back address.
    851      (and (funcall check-scope 'with-backaddress)
    852           (format "\\KOMAoption{backaddress}{%s}\n"
    853                   (if (plist-get info :with-backaddress) "true" "false")))
    854      ;; Place.
    855      (let ((with-place-set (funcall check-scope 'with-place))
    856            (place-set (funcall check-scope 'place)))
    857        (and (or (and with-place-set place-set)
    858                 (and (eq scope 'buffer) (or with-place-set place-set)))
    859             (format "\\setkomavar{place}{%s}\n"
    860                     (if (plist-get info :with-place) (plist-get info :place)
    861                       ""))))
    862      ;; Location.
    863      (let ((location (funcall heading-or-key-value 'location :location)))
    864        (and location
    865             (format "\\setkomavar{location}{%s}\n" location)))
    866      ;; Folding marks.
    867      (and (funcall check-scope 'with-foldmarks)
    868           (let ((foldmarks (plist-get info :with-foldmarks)))
    869             (cond ((consp foldmarks)
    870                    (format "\\KOMAoptions{foldmarks=true,foldmarks=%s}\n"
    871                            (mapconcat #'symbol-name foldmarks "")))
    872                   (foldmarks "\\KOMAoptions{foldmarks=true}\n")
    873                   (t "\\KOMAoptions{foldmarks=false}\n")))))))
    874 
    875 
    876 
    877 ;;; Commands
    878 
    879 ;;;###autoload
    880 (defun org-koma-letter-export-as-latex
    881     (&optional async subtreep visible-only body-only ext-plist)
    882   "Export current buffer as a KOMA Scrlttr2 letter.
    883 
    884 If narrowing is active in the current buffer, only export its
    885 narrowed part.
    886 
    887 If a region is active, export that region.
    888 
    889 A non-nil optional argument ASYNC means the process should happen
    890 asynchronously.  The resulting buffer should be accessible
    891 through the `org-export-stack' interface.
    892 
    893 When optional argument SUBTREEP is non-nil, export the sub-tree
    894 at point, extracting information from the headline properties
    895 first.
    896 
    897 When optional argument VISIBLE-ONLY is non-nil, don't export
    898 contents of hidden elements.
    899 
    900 When optional argument BODY-ONLY is non-nil, only write code
    901 between \"\\begin{letter}\" and \"\\end{letter}\".
    902 
    903 EXT-PLIST, when provided, is a property list with external
    904 parameters overriding Org default settings, but still inferior to
    905 file-local settings.
    906 
    907 Export is done in a buffer named \"*Org KOMA-LETTER Export*\".  It
    908 will be displayed if `org-export-show-temporary-export-buffer' is
    909 non-nil."
    910   (interactive)
    911   (let (org-koma-letter-special-contents)
    912     (org-export-to-buffer 'koma-letter "*Org KOMA-LETTER Export*"
    913       async subtreep visible-only body-only ext-plist
    914       (lambda () (LaTeX-mode)))))
    915 
    916 ;;;###autoload
    917 (defun org-koma-letter-export-to-latex
    918     (&optional async subtreep visible-only body-only ext-plist)
    919   "Export current buffer as a KOMA Scrlttr2 letter (tex).
    920 
    921 If narrowing is active in the current buffer, only export its
    922 narrowed part.
    923 
    924 If a region is active, export that region.
    925 
    926 A non-nil optional argument ASYNC means the process should happen
    927 asynchronously.  The resulting file should be accessible through
    928 the `org-export-stack' interface.
    929 
    930 When optional argument SUBTREEP is non-nil, export the sub-tree
    931 at point, extracting information from the headline properties
    932 first.
    933 
    934 When optional argument VISIBLE-ONLY is non-nil, don't export
    935 contents of hidden elements.
    936 
    937 When optional argument BODY-ONLY is non-nil, only write code
    938 between \"\\begin{letter}\" and \"\\end{letter}\".
    939 
    940 EXT-PLIST, when provided, is a property list with external
    941 parameters overriding Org default settings, but still inferior to
    942 file-local settings.
    943 
    944 When optional argument PUB-DIR is set, use it as the publishing
    945 directory.
    946 
    947 Return output file's name."
    948   (interactive)
    949   (let ((outfile (org-export-output-file-name ".tex" subtreep))
    950         (org-koma-letter-special-contents))
    951     (org-export-to-file 'koma-letter outfile
    952       async subtreep visible-only body-only ext-plist)))
    953 
    954 ;;;###autoload
    955 (defun org-koma-letter-export-to-pdf
    956     (&optional async subtreep visible-only body-only ext-plist)
    957   "Export current buffer as a KOMA Scrlttr2 letter (pdf).
    958 
    959 If narrowing is active in the current buffer, only export its
    960 narrowed part.
    961 
    962 If a region is active, export that region.
    963 
    964 A non-nil optional argument ASYNC means the process should happen
    965 asynchronously.  The resulting file should be accessible through
    966 the `org-export-stack' interface.
    967 
    968 When optional argument SUBTREEP is non-nil, export the sub-tree
    969 at point, extracting information from the headline properties
    970 first.
    971 
    972 When optional argument VISIBLE-ONLY is non-nil, don't export
    973 contents of hidden elements.
    974 
    975 When optional argument BODY-ONLY is non-nil, only write code
    976 between \"\\begin{letter}\" and \"\\end{letter}\".
    977 
    978 EXT-PLIST, when provided, is a property list with external
    979 parameters overriding Org default settings, but still inferior to
    980 file-local settings.
    981 
    982 Return PDF file's name."
    983   (interactive)
    984   (let ((file (org-export-output-file-name ".tex" subtreep))
    985         (org-koma-letter-special-contents))
    986     (org-export-to-file 'koma-letter file
    987       async subtreep visible-only body-only ext-plist
    988       #'org-latex-compile)))
    989 
    990 
    991 (provide 'ox-koma-letter)
    992 ;;; ox-koma-letter.el ends here