ox-koma-letter: Add after_closing and cleanup
[org-mode.git] / contrib / lisp / ox-koma-letter.el
blob619d9c4a51c64c001d9de267c62eb9d83b386dbb
1 ;;; ox-koma-letter.el --- KOMA Scrlttr2 Back-End for Org Export Engine
3 ;; Copyright (C) 2007-2016 Free Software Foundation, Inc.
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 ;; Keywords: org, wp, tex
11 ;; This program is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;; This library implements a KOMA Scrlttr2 back-end, derived from the
27 ;; LaTeX one.
29 ;; Depending on the desired output format, three commands are provided
30 ;; for export: `org-koma-letter-export-as-latex' (temporary buffer),
31 ;; `org-koma-letter-export-to-latex' ("tex" file) and
32 ;; `org-koma-letter-export-to-pdf' ("pdf" file).
34 ;; On top of buffer keywords supported by `latex' back-end (see
35 ;; `org-latex-options-alist'), this back-end introduces the following
36 ;; keywords:
37 ;; - CLOSING: see `org-koma-letter-closing',
38 ;; - FROM_ADDRESS: see `org-koma-letter-from-address',
39 ;; - LCO: see `org-koma-letter-class-option-file',
40 ;; - OPENING: see `org-koma-letter-opening',
41 ;; - PHONE_NUMBER: see `org-koma-letter-phone-number',
42 ;; - SIGNATURE: see `org-koma-letter-signature',
43 ;; - PLACE: see `org-koma-letter-place',
44 ;; - TO_ADDRESS: If unspecified this is set to "\mbox{}".
46 ;; TO_ADDRESS and FROM_ADDRESS can also be specified using heading
47 ;; with the special tags specified in
48 ;; `org-koma-letter-special-tags-in-letter', namely "to" and "from".
49 ;; LaTeX line breaks are not necessary if using these headings. If
50 ;; both a headline and a keyword specify a to or from address the
51 ;; value is determined in accordance with
52 ;; `org-koma-letter-prefer-special-headings'.
54 ;; A number of OPTIONS settings can be set to change which contents is
55 ;; exported.
56 ;; - backaddress (see `org-koma-letter-use-backaddress')
57 ;; - foldmarks (see `org-koma-letter-use-foldmarks')
58 ;; - phone (see `org-koma-letter-use-phone')
59 ;; - email (see `org-koma-letter-use-email')
60 ;; - place (see `org-koma-letter-use-place')
61 ;; - subject, a list of format options
62 ;; (see `org-koma-letter-subject-format')
63 ;; - after-closing-order, a list of the ordering of headings with
64 ;; special tags after closing (see
65 ;; `org-koma-letter-special-tags-after-closing')
66 ;; - after-letter-order, as above, but after the end of the letter
67 ;; (see `org-koma-letter-special-tags-after-letter').
69 ;; The following variables works differently from the main LaTeX class
70 ;; - AUTHOR: Default to user-full-name but may be disabled.
71 ;; (See also `org-koma-letter-author'),
72 ;; - EMAIL: Same as AUTHOR. (see also `org-koma-letter-email'),
74 ;; Headlines are in general ignored. However, headlines with special
75 ;; tags can be used for specified contents like postscript (ps),
76 ;; carbon copy (cc), enclosures (encl) and code to be inserted after
77 ;; \end{letter} (after_letter). Specials tags are defined in
78 ;; `org-koma-letter-special-tags-after-closing' and
79 ;; `org-koma-letter-special-tags-after-letter'. Currently members of
80 ;; `org-koma-letter-special-tags-after-closing' used as macros and the
81 ;; content of the headline is the argument.
83 ;; Headlines with to and from may also be used rather than the keyword
84 ;; approach described above. If both a keyword and a headline with
85 ;; information is present precedence is determined by
86 ;; `org-koma-letter-prefer-special-headings'.
88 ;; You need an appropriate association in `org-latex-classes' in order
89 ;; to use the KOMA Scrlttr2 class. By default, a sparse scrlttr2
90 ;; class is provided: "default-koma-letter". You can also add you own
91 ;; letter class. For instance:
93 ;; (add-to-list 'org-latex-classes
94 ;; '("my-letter"
95 ;; "\\documentclass\[%
96 ;; DIV=14,
97 ;; fontsize=12pt,
98 ;; parskip=half,
99 ;; subject=titled,
100 ;; backaddress=false,
101 ;; fromalign=left,
102 ;; fromemail=true,
103 ;; fromphone=true\]\{scrlttr2\}
104 ;; \[DEFAULT-PACKAGES]
105 ;; \[PACKAGES]
106 ;; \[EXTRA]"))
108 ;; Then, in your Org document, be sure to require the proper class
109 ;; with:
111 ;; #+LATEX_CLASS: my-letter
113 ;; Or by setting `org-koma-letter-default-class'.
115 ;; You may have to load (LaTeX) Babel as well, e.g., by adding
116 ;; it to `org-latex-packages-alist',
118 ;; (add-to-list 'org-latex-packages-alist '("AUTO" "babel" nil))
120 ;;; Code:
122 (require 'ox-latex)
124 ;; Install a default letter class.
125 (unless (assoc "default-koma-letter" org-latex-classes)
126 (add-to-list 'org-latex-classes
127 '("default-koma-letter" "\\documentclass[11pt]{scrlttr2}")))
130 ;;; User-Configurable Variables
132 (defgroup org-export-koma-letter nil
133 "Options for exporting to KOMA scrlttr2 class in LaTeX export."
134 :tag "Org Koma-Letter"
135 :group 'org-export)
137 (defcustom org-koma-letter-class-option-file "NF"
138 "Letter Class Option File.
139 This option can also be set with the LCO keyword."
140 :group 'org-export-koma-letter
141 :type 'string)
143 (defcustom org-koma-letter-author 'user-full-name
144 "Sender's name.
146 This variable defaults to calling the function `user-full-name'
147 which just returns the current function `user-full-name'.
148 Alternatively a string, nil or a function may be given.
149 Functions must return a string.
151 This option can also be set with the AUTHOR keyword."
152 :group 'org-export-koma-letter
153 :type '(radio (function-item user-full-name)
154 (string)
155 (function)
156 (const :tag "Do not export author" nil)))
158 (defcustom org-koma-letter-email 'org-koma-letter-email
159 "Sender's email address.
161 This variable defaults to the value `org-koma-letter-email' which
162 returns `user-mail-address'. Alternatively a string, nil or
163 a function may be given. Functions must return a string.
165 This option can also be set with the EMAIL keyword."
166 :group 'org-export-koma-letter
167 :type '(radio (function-item org-koma-letter-email)
168 (string)
169 (function)
170 (const :tag "Do not export email" nil)))
172 (defcustom org-koma-letter-from-address ""
173 "Sender's address, as a string.
174 This option can also be set with one or more FROM_ADDRESS
175 keywords."
176 :group 'org-export-koma-letter
177 :type 'string)
179 (defcustom org-koma-letter-phone-number ""
180 "Sender's phone number, as a string.
181 This option can also be set with the PHONE_NUMBER keyword."
182 :group 'org-export-koma-letter
183 :type 'string)
185 (defcustom org-koma-letter-place ""
186 "Place from which the letter is sent, as a string.
187 This option can also be set with the PLACE keyword."
188 :group 'org-export-koma-letter
189 :type 'string)
191 (defcustom org-koma-letter-opening ""
192 "Letter's opening, as a string.
194 This option can also be set with the OPENING keyword. Moreover,
195 when:
196 (1) Either `org-koma-letter-prefer-special-headings' is non-nil
197 or the CLOSING keyword is empty
198 (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
199 (3) the letter contains a headline without a special
200 tag (e.g. \"to\" or \"ps\");
201 then the opening will be implicitly set as the untagged headline title."
202 :group 'org-export-koma-letter
203 :type 'string)
205 (defcustom org-koma-letter-closing ""
206 "Letter's closing, as a string.
207 This option can also be set with the CLOSING keyword. Moreover,
208 when:
209 (1) Either `org-koma-letter-prefer-special-headings' is non-nil
210 or the CLOSING keyword is empty;
211 (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
212 (3) the letter contains a headline with the special
213 tag \"closing\";
214 then the opening will be set as the title of the closing special
215 heading title."
216 :group 'org-export-koma-letter
217 :type 'string)
219 (defcustom org-koma-letter-signature ""
220 "Signature, as a string.
221 This option can also be set with the SIGNATURE keyword.
222 Moreover, when:
223 (1) Either `org-koma-letter-prefer-special-headings' is non-nil
224 or there is no CLOSING keyword or the CLOSING keyword is empty;
225 (2) `org-koma-letter-headline-is-opening-maybe' is non-nil;
226 (3) the letter contains a headline with the special
227 tag \"closing\";
228 then the signature will be set as the content of the
229 closing special heading.
231 Note if the content is empty the signature will not be set."
232 :group 'org-export-koma-letter
233 :type 'string)
235 (defcustom org-koma-letter-prefer-special-headings nil
236 "Non-nil means prefer headlines over keywords for TO and FROM.
237 This option can also be set with the OPTIONS keyword, e.g.:
238 \"special-headings:t\"."
239 :group 'org-export-koma-letter
240 :type 'boolean)
242 (defcustom org-koma-letter-subject-format t
243 "Non-nil means include the subject.
245 Support formatting options.
247 When t, insert a subject using default options. When nil, do not
248 insert a subject at all. It can also be a list of symbols among
249 the following ones:
251 `afteropening' Subject after opening
252 `beforeopening' Subject before opening
253 `centered' Subject centered
254 `left' Subject left-justified
255 `right' Subject right-justified
256 `titled' Add title/description to subject
257 `underlined' Set subject underlined
258 `untitled' Do not add title/description to subject
260 Please refer to the KOMA-script manual (Table 4.16. in the
261 English manual of 2012-07-22).
263 This option can also be set with the OPTIONS keyword, e.g.:
264 \"subject:(underlined centered)\"."
265 :type
266 '(choice
267 (const :tag "No export" nil)
268 (const :tag "Default options" t)
269 (set :tag "Configure options"
270 (const :tag "Subject after opening" afteropening)
271 (const :tag "Subject before opening" beforeopening)
272 (const :tag "Subject centered" centered)
273 (const :tag "Subject left-justified" left)
274 (const :tag "Subject right-justified" right)
275 (const :tag "Add title or description to subject" underlined)
276 (const :tag "Set subject underlined" titled)
277 (const :tag "Do not add title or description to subject" untitled)))
278 :group 'org-export-koma-letter)
280 (defcustom org-koma-letter-use-backaddress nil
281 "Non-nil prints return address in line above to address.
282 This option can also be set with the OPTIONS keyword, e.g.:
283 \"backaddress:t\"."
284 :group 'org-export-koma-letter
285 :type 'boolean)
287 (defcustom org-koma-letter-use-foldmarks t
288 "Configure appearance of folding marks.
290 When t, activate default folding marks. When nil, do not insert
291 folding marks at all. It can also be a list of symbols among the
292 following ones:
294 `B' Activate upper horizontal mark on left paper edge
295 `b' Deactivate upper horizontal mark on left paper edge
297 `H' Activate all horizontal marks on left paper edge
298 `h' Deactivate all horizontal marks on left paper edge
300 `L' Activate left vertical mark on upper paper edge
301 `l' Deactivate left vertical mark on upper paper edge
303 `M' Activate middle horizontal mark on left paper edge
304 `m' Deactivate middle horizontal mark on left paper edge
306 `P' Activate punch or center mark on left paper edge
307 `p' Deactivate punch or center mark on left paper edge
309 `T' Activate lower horizontal mark on left paper edge
310 `t' Deactivate lower horizontal mark on left paper edge
312 `V' Activate all vertical marks on upper paper edge
313 `v' Deactivate all vertical marks on upper paper edge
315 This option can also be set with the OPTIONS keyword, e.g.:
316 \"foldmarks:(b l m t)\"."
317 :group 'org-export-koma-letter
318 :type '(choice
319 (const :tag "Activate default folding marks" t)
320 (const :tag "Deactivate folding marks" nil)
321 (set
322 :tag "Configure folding marks"
323 (const :tag "Activate upper horizontal mark on left paper edge" B)
324 (const :tag "Deactivate upper horizontal mark on left paper edge" b)
325 (const :tag "Activate all horizontal marks on left paper edge" H)
326 (const :tag "Deactivate all horizontal marks on left paper edge" h)
327 (const :tag "Activate left vertical mark on upper paper edge" L)
328 (const :tag "Deactivate left vertical mark on upper paper edge" l)
329 (const :tag "Activate middle horizontal mark on left paper edge" M)
330 (const :tag "Deactivate middle horizontal mark on left paper edge" m)
331 (const :tag "Activate punch or center mark on left paper edge" P)
332 (const :tag "Deactivate punch or center mark on left paper edge" p)
333 (const :tag "Activate lower horizontal mark on left paper edge" T)
334 (const :tag "Deactivate lower horizontal mark on left paper edge" t)
335 (const :tag "Activate all vertical marks on upper paper edge" V)
336 (const :tag "Deactivate all vertical marks on upper paper edge" v))))
338 (defcustom org-koma-letter-use-phone nil
339 "Non-nil prints sender's phone number.
340 This option can also be set with the OPTIONS keyword, e.g.:
341 \"phone:t\"."
342 :group 'org-export-koma-letter
343 :type 'boolean)
345 (defcustom org-koma-letter-use-email nil
346 "Non-nil prints sender's email address.
347 This option can also be set with the OPTIONS keyword, e.g.:
348 \"email:t\"."
349 :group 'org-export-koma-letter
350 :type 'boolean)
352 (defcustom org-koma-letter-use-place t
353 "Non-nil prints the letter's place next to the date.
354 This option can also be set with the OPTIONS keyword, e.g.:
355 \"place:nil\"."
356 :group 'org-export-koma-letter
357 :type 'boolean)
359 (defcustom org-koma-letter-default-class "default-koma-letter"
360 "Default class for `org-koma-letter'.
361 The value must be a member of `org-latex-classes'."
362 :group 'org-export-koma-letter
363 :type 'string)
365 (defcustom org-koma-letter-headline-is-opening-maybe t
366 "Non-nil means a headline may be used as an opening and closing.
367 See also `org-koma-letter-opening' and
368 `org-koma-letter-closing'."
369 :group 'org-export-koma-letter
370 :type 'boolean)
372 (defcustom org-koma-letter-prefer-subject nil
373 "Non-nil means title should be interpreted as subject if subject is missing.
374 This option can also be set with the OPTIONS keyword,
375 e.g. \"title-subject:t\"."
376 :group 'org-export-koma-letter
377 :type 'boolean)
379 (defconst org-koma-letter-special-tags-in-letter '(to from closing)
380 "Header tags related to the letter itself.")
382 (defconst org-koma-letter-special-tags-after-closing '(after_closing ps encl cc)
383 "Header tags to be inserted in the letter after closing.")
385 (defconst org-koma-letter-special-tags-as-macro '(ps encl cc)
386 "Header tags to be inserted as macros")
388 (defconst org-koma-letter-special-tags-after-letter '(after_letter)
389 "Header tags to be inserted after the letter.")
391 (defvar org-koma-letter-special-contents nil
392 "Holds special content temporarily.")
394 (make-obsolete-variable 'org-koma-letter-use-title
395 'org-export-with-title
396 "25.1" 'set)
399 ;;; Define Back-End
401 (org-export-define-derived-backend 'koma-letter 'latex
402 :options-alist
403 '((:latex-class "LATEX_CLASS" nil org-koma-letter-default-class t)
404 (:lco "LCO" nil org-koma-letter-class-option-file)
405 (:author "AUTHOR" nil (org-koma-letter--get-value org-koma-letter-author) parse)
406 (:author-changed-in-buffer-p "AUTHOR" nil nil t)
407 (:from-address "FROM_ADDRESS" nil org-koma-letter-from-address newline)
408 (:phone-number "PHONE_NUMBER" nil org-koma-letter-phone-number)
409 (:email "EMAIL" nil (org-koma-letter--get-value org-koma-letter-email) t)
410 (:to-address "TO_ADDRESS" nil nil newline)
411 (:place "PLACE" nil org-koma-letter-place)
412 (:subject "SUBJECT" nil nil parse)
413 (:opening "OPENING" nil org-koma-letter-opening parse)
414 (:closing "CLOSING" nil org-koma-letter-closing parse)
415 (:signature "SIGNATURE" nil org-koma-letter-signature newline)
416 (:special-headings nil "special-headings" org-koma-letter-prefer-special-headings)
417 (:special-tags-as-macro nil nil org-koma-letter-special-tags-as-macro)
418 (:special-tags-in-letter nil nil org-koma-letter-special-tags-in-letter)
419 (:special-tags-after-closing nil "after-closing-order"
420 org-koma-letter-special-tags-after-closing)
421 (:special-tags-after-letter nil "after-letter-order"
422 org-koma-letter-special-tags-after-letter)
423 (:with-backaddress nil "backaddress" org-koma-letter-use-backaddress)
424 (:with-email nil "email" org-koma-letter-use-email)
425 (:with-foldmarks nil "foldmarks" org-koma-letter-use-foldmarks)
426 (:with-phone nil "phone" org-koma-letter-use-phone)
427 (:with-place nil "place" org-koma-letter-use-place)
428 (:with-subject nil "subject" org-koma-letter-subject-format)
429 (:with-title-as-subject nil "title-subject" org-koma-letter-prefer-subject)
430 (:with-headline-opening nil nil org-koma-letter-headline-is-opening-maybe)
431 ;; Special properties non-nil when a setting happened in buffer.
432 ;; They are used to prioritize in-buffer settings over "lco"
433 ;; files. See `org-koma-letter-template'.
434 (:inbuffer-author "AUTHOR" nil 'koma-letter:empty)
435 (:inbuffer-email "EMAIL" nil 'koma-letter:empty)
436 (:inbuffer-phone-number "PHONE_NUMBER" nil 'koma-letter:empty)
437 (:inbuffer-place "PLACE" nil 'koma-letter:empty)
438 (:inbuffer-signature "SIGNATURE" nil 'koma-letter:empty)
439 (:inbuffer-with-backaddress nil "backaddress" 'koma-letter:empty)
440 (:inbuffer-with-email nil "email" 'koma-letter:empty)
441 (:inbuffer-with-foldmarks nil "foldmarks" 'koma-letter:empty)
442 (:inbuffer-with-phone nil "phone" 'koma-letter:empty)
443 (:inbuffer-with-place nil "place" 'koma-letter:empty))
444 :translate-alist '((export-block . org-koma-letter-export-block)
445 (export-snippet . org-koma-letter-export-snippet)
446 (headline . org-koma-letter-headline)
447 (keyword . org-koma-letter-keyword)
448 (template . org-koma-letter-template))
449 :menu-entry
450 '(?k "Export with KOMA Scrlttr2"
451 ((?L "As LaTeX buffer" org-koma-letter-export-as-latex)
452 (?l "As LaTeX file" org-koma-letter-export-to-latex)
453 (?p "As PDF file" org-koma-letter-export-to-pdf)
454 (?o "As PDF file and open"
455 (lambda (a s v b)
456 (if a (org-koma-letter-export-to-pdf t s v b)
457 (org-open-file (org-koma-letter-export-to-pdf nil s v b))))))))
461 ;;; Helper functions
463 (defun org-koma-letter-email ()
464 "Return the current `user-mail-address'."
465 user-mail-address)
467 ;; The following is taken from/inspired by ox-grof.el
468 ;; Thanks, Luis!
470 (defun org-koma-letter--get-tagged-contents (key)
471 "Get contents from a headline tagged with KEY.
472 The contents is stored in `org-koma-letter-special-contents'."
473 (cdr (assoc-string (org-koma-letter--get-value key)
474 org-koma-letter-special-contents)))
476 (defun org-koma-letter--get-value (value)
477 "Turn value into a string whenever possible.
478 Determines if VALUE is nil, a string, a function or a symbol and
479 return a string or nil."
480 (when value
481 (cond ((stringp value) value)
482 ((functionp value) (funcall value))
483 ((symbolp value) (symbol-name value))
484 (t value))))
486 (defun org-koma-letter--special-contents-inline (keywords info)
487 "Process KEYWORDS members of `org-koma-letter-special-contents'.
488 KEYWORDS is a list of symbols. Return them as a string to be
489 formatted.
491 The function is used for inserting content of special headings
492 such as the one tagged with PS.
494 (mapconcat
495 (lambda (keyword)
496 (let* ((name (org-koma-letter--get-value keyword))
497 (value (org-koma-letter--get-tagged-contents name))
498 (macrop (memq keyword (plist-get info :special-tags-as-macro))))
499 (cond ((not value) nil)
500 (macrop (format "\\%s{%s}\n" name value))
501 (t value))))
502 keywords
503 ""))
505 (defun org-koma-letter--determine-to-and-from (info key)
506 "Given INFO determine KEY for the letter.
507 KEY should be `to' or `from'.
509 `ox-koma-letter' allows two ways to specify TO and FROM. If both
510 are present return the preferred one as determined by
511 `org-koma-letter-prefer-special-headings'."
512 (let ((option (org-string-nw-p
513 (plist-get info (if (eq key 'to) :to-address :from-address))))
514 (headline (org-koma-letter--get-tagged-contents key)))
515 (replace-regexp-in-string
516 "\n" "\\\\\\\\\n"
517 (org-trim
518 (if (plist-get info :special-headings) (or headline option "")
519 (or option headline ""))))))
523 ;;; Transcode Functions
525 ;;;; Export Block
527 (defun org-koma-letter-export-block (export-block contents info)
528 "Transcode an EXPORT-BLOCK element into KOMA Scrlttr2 code.
529 CONTENTS is nil. INFO is a plist used as a communication
530 channel."
531 (when (member (org-element-property :type export-block) '("KOMA-LETTER" "LATEX"))
532 (org-remove-indentation (org-element-property :value export-block))))
534 ;;;; Export Snippet
536 (defun org-koma-letter-export-snippet (export-snippet contents info)
537 "Transcode an EXPORT-SNIPPET object into KOMA Scrlttr2 code.
538 CONTENTS is nil. INFO is a plist used as a communication
539 channel."
540 (when (memq (org-export-snippet-backend export-snippet) '(latex koma-letter))
541 (org-element-property :value export-snippet)))
543 ;;;; Keyword
545 (defun org-koma-letter-keyword (keyword contents info)
546 "Transcode a KEYWORD element into KOMA Scrlttr2 code.
547 CONTENTS is nil. INFO is a plist used as a communication
548 channel."
549 (let ((key (org-element-property :key keyword))
550 (value (org-element-property :value keyword)))
551 ;; Handle specifically KOMA-LETTER keywords. Otherwise, fallback
552 ;; to `latex' back-end.
553 (if (equal key "KOMA-LETTER") value
554 (org-export-with-backend 'latex keyword contents info))))
556 ;; Headline
558 (defun org-koma-letter-headline (headline contents info)
559 "Transcode a HEADLINE element from Org to LaTeX.
560 CONTENTS holds the contents of the headline. INFO is a plist
561 holding contextual information.
563 Note that if a headline is tagged with a tag from
564 `org-koma-letter-special-tags' it will not be exported, but
565 stored in `org-koma-letter-special-contents' and included at the
566 appropriate place."
567 (let ((special-tag (org-koma-letter--special-tag headline info)))
568 (if (not special-tag)
569 contents
570 (push (cons special-tag contents) org-koma-letter-special-contents)
571 "")))
573 (defun org-koma-letter--special-tag (headline info)
574 "Non-nil if HEADLINE is a special headline.
575 INFO is a plist holding contextual information. Return first
576 special tag headline."
577 (let ((special-tags (append
578 (plist-get info :special-tags-in-letter)
579 (plist-get info :special-tags-after-closing)
580 (plist-get info :special-tags-after-letter))))
581 (catch 'exit
582 (dolist (tag (org-export-get-tags headline info))
583 (let ((tag (assoc-string tag special-tags)))
584 (when tag (throw 'exit tag)))))))
586 (defun org-koma-letter--keyword-or-headline (plist-key pred info)
587 "Return the correct version of opening or closing.
588 PLIST-KEY should be a key in info, typically :opening
589 or :closing. PRED is a predicate run on headline to determine
590 which title to use which takes two arguments, a headline element
591 and an info plist. INFO is a plist holding contextual
592 information. Return the preferred candidate for the exported of
593 PLIST-KEY."
594 (let* ((keyword-candidate (plist-get info plist-key))
595 (headline-candidate (when (and (plist-get info :with-headline-opening)
596 (or (plist-get info :special-headings)
597 (not keyword-candidate)))
598 (org-element-map (plist-get info :parse-tree)
599 'headline
600 (lambda (head)
601 (when (funcall pred head info)
602 (org-element-property :title head)))
603 info t))))
604 (org-export-data (or headline-candidate keyword-candidate "") info)))
606 ;;;; Template
608 (defun org-koma-letter-template (contents info)
609 "Return complete document string after KOMA Scrlttr2 conversion.
610 CONTENTS is the transcoded contents string. INFO is a plist
611 holding export options."
612 (concat
613 ;; Time-stamp.
614 (and (plist-get info :time-stamp-file)
615 (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
616 ;; LaTeX compiler
617 (org-latex--insert-compiler info)
618 ;; Document class and packages.
619 (org-latex--make-preamble info)
620 ;; Settings. They can come from three locations, in increasing
621 ;; order of precedence: global variables, LCO files and in-buffer
622 ;; settings. Thus, we first insert settings coming from global
623 ;; variables, then we insert LCO files, and, eventually, we insert
624 ;; settings coming from buffer keywords.
625 (org-koma-letter--build-settings 'global info)
626 (mapconcat #'(lambda (file) (format "\\LoadLetterOption{%s}\n" file))
627 (org-split-string (or (plist-get info :lco) "") " ")
629 (org-koma-letter--build-settings 'buffer info)
630 ;; From address.
631 (let ((from-address (org-koma-letter--determine-to-and-from info 'from)))
632 (when (org-string-nw-p from-address)
633 (format "\\setkomavar{fromaddress}{%s}\n" from-address)))
634 ;; Date.
635 (format "\\date{%s}\n" (org-export-data (org-export-get-date info) info))
636 ;; Hyperref, document start, and subject and title.
637 (let* ((with-subject (plist-get info :with-subject))
638 (with-title (plist-get info :with-title))
639 (title-as-subject (and with-subject
640 (plist-get info :with-title-as-subject)))
641 (subject* (org-string-nw-p
642 (org-export-data (plist-get info :subject) info)))
643 (title* (and with-title
644 (org-string-nw-p
645 (org-export-data (plist-get info :title) info))))
646 (subject (cond ((not with-subject) nil)
647 (title-as-subject (or subject* title*))
648 (t subject*)))
649 (title (cond ((not with-title) nil)
650 (title-as-subject (and subject* title*))
651 (t title*)))
652 (hyperref-template (plist-get info :latex-hyperref-template))
653 (spec (append (list (cons ?t (or title subject "")))
654 (org-latex--format-spec info))))
655 (concat
656 (when (and with-subject (not (eq with-subject t)))
657 (format "\\KOMAoption{subject}{%s}\n"
658 (if (symbolp with-subject) with-subject
659 (mapconcat #'symbol-name with-subject ","))))
660 ;; Hyperref.
661 (format-spec hyperref-template spec)
662 ;; Document start.
663 "\\begin{document}\n\n"
664 ;; Subject and title.
665 (when subject (format "\\setkomavar{subject}{%s}\n" subject))
666 (when title (format "\\setkomavar{title}{%s}\n" title))
667 (when (or (org-string-nw-p title) (org-string-nw-p subject)) "\n")))
668 ;; Letter start.
669 (format "\\begin{letter}{%%\n%s}\n\n"
670 (org-koma-letter--determine-to-and-from info 'to))
671 ;; Opening.
672 (format "\\opening{%s}\n\n"
673 (org-koma-letter--keyword-or-headline
674 :opening (lambda (h i) (not (org-koma-letter--special-tag h i)))
675 info))
676 ;; Letter body.
677 contents
678 ;; Closing.
679 (format "\\closing{%s}\n"
680 (org-koma-letter--keyword-or-headline
681 :closing (lambda (h i) (eq (org-koma-letter--special-tag h i)
682 'closing))
683 info))
684 (org-koma-letter--special-contents-inline
685 (plist-get info :special-tags-after-closing) info)
686 ;; Letter end.
687 "\n\\end{letter}\n"
688 (org-koma-letter--special-contents-inline
689 (plist-get info :special-tags-after-letter) info)
690 ;; Document end.
691 "\n\\end{document}"))
693 (defun org-koma-letter--build-settings (scope info)
694 "Build settings string according to type.
695 SCOPE is either `global' or `buffer'. INFO is a plist used as
696 a communication channel."
697 (let ((check-scope
698 (function
699 ;; Non-nil value when SETTING was defined in SCOPE.
700 (lambda (setting)
701 (let ((property (intern (format ":inbuffer-%s" setting))))
702 (if (eq scope 'global)
703 (eq (plist-get info property) 'koma-letter:empty)
704 (not (eq (plist-get info property) 'koma-letter:empty))))))))
705 (concat
706 ;; Name.
707 (let ((author (plist-get info :author)))
708 (and author
709 (funcall check-scope 'author)
710 (format "\\setkomavar{fromname}{%s}\n"
711 (org-export-data author info))))
712 ;; Email.
713 (let ((email (plist-get info :email)))
714 (and email
715 (funcall check-scope 'email)
716 (format "\\setkomavar{fromemail}{%s}\n" email)))
717 (and (funcall check-scope 'with-email)
718 (format "\\KOMAoption{fromemail}{%s}\n"
719 (if (plist-get info :with-email) "true" "false")))
720 ;; Phone number.
721 (let ((phone-number (plist-get info :phone-number)))
722 (and (org-string-nw-p phone-number)
723 (funcall check-scope 'phone-number)
724 (format "\\setkomavar{fromphone}{%s}\n" phone-number)))
725 (and (funcall check-scope 'with-phone)
726 (format "\\KOMAoption{fromphone}{%s}\n"
727 (if (plist-get info :with-phone) "true" "false")))
728 ;; Signature.
729 (let* ((heading-val
730 (and (plist-get info :with-headline-opening)
731 (org-string-nw-p
732 (org-trim
733 (org-export-data
734 (org-koma-letter--get-tagged-contents 'closing)
735 info)))))
736 (signature (org-string-nw-p (plist-get info :signature)))
737 (signature-scope (funcall check-scope 'signature)))
738 (and (or (and signature signature-scope)
739 heading-val)
740 (not (and (eq scope 'global) heading-val))
741 (format "\\setkomavar{signature}{%s}\n"
742 (if signature-scope signature heading-val))))
743 ;; Back address.
744 (and (funcall check-scope 'with-backaddress)
745 (format "\\KOMAoption{backaddress}{%s}\n"
746 (if (plist-get info :with-backaddress) "true" "false")))
747 ;; Place.
748 (let ((with-place-set (funcall check-scope 'with-place))
749 (place-set (funcall check-scope 'place)))
750 (and (or (and with-place-set place-set)
751 (and (eq scope 'buffer) (or with-place-set place-set)))
752 (format "\\setkomavar{place}{%s}\n"
753 (if (plist-get info :with-place) (plist-get info :place)
754 ""))))
755 ;; Folding marks.
756 (and (funcall check-scope 'with-foldmarks)
757 (let ((foldmarks (plist-get info :with-foldmarks)))
758 (cond ((consp foldmarks)
759 (format "\\KOMAoptions{foldmarks=true,foldmarks=%s}\n"
760 (mapconcat #'symbol-name foldmarks "")))
761 (foldmarks "\\KOMAoptions{foldmarks=true}\n")
762 (t "\\KOMAoptions{foldmarks=false}\n")))))))
766 ;;; Commands
768 ;;;###autoload
769 (defun org-koma-letter-export-as-latex
770 (&optional async subtreep visible-only body-only ext-plist)
771 "Export current buffer as a KOMA Scrlttr2 letter.
773 If narrowing is active in the current buffer, only export its
774 narrowed part.
776 If a region is active, export that region.
778 A non-nil optional argument ASYNC means the process should happen
779 asynchronously. The resulting buffer should be accessible
780 through the `org-export-stack' interface.
782 When optional argument SUBTREEP is non-nil, export the sub-tree
783 at point, extracting information from the headline properties
784 first.
786 When optional argument VISIBLE-ONLY is non-nil, don't export
787 contents of hidden elements.
789 When optional argument BODY-ONLY is non-nil, only write code
790 between \"\\begin{letter}\" and \"\\end{letter}\".
792 EXT-PLIST, when provided, is a proeprty list with external
793 parameters overriding Org default settings, but still inferior to
794 file-local settings.
796 Export is done in a buffer named \"*Org KOMA-LETTER Export*\". It
797 will be displayed if `org-export-show-temporary-export-buffer' is
798 non-nil."
799 (interactive)
800 (let (org-koma-letter-special-contents)
801 (org-export-to-buffer 'koma-letter "*Org KOMA-LETTER Export*"
802 async subtreep visible-only body-only ext-plist
803 (lambda () (LaTeX-mode)))))
805 ;;;###autoload
806 (defun org-koma-letter-export-to-latex
807 (&optional async subtreep visible-only body-only ext-plist)
808 "Export current buffer as a KOMA Scrlttr2 letter (tex).
810 If narrowing is active in the current buffer, only export its
811 narrowed part.
813 If a region is active, export that region.
815 A non-nil optional argument ASYNC means the process should happen
816 asynchronously. The resulting file should be accessible through
817 the `org-export-stack' interface.
819 When optional argument SUBTREEP is non-nil, export the sub-tree
820 at point, extracting information from the headline properties
821 first.
823 When optional argument VISIBLE-ONLY is non-nil, don't export
824 contents of hidden elements.
826 When optional argument BODY-ONLY is non-nil, only write code
827 between \"\\begin{letter}\" and \"\\end{letter}\".
829 EXT-PLIST, when provided, is a property list with external
830 parameters overriding Org default settings, but still inferior to
831 file-local settings.
833 When optional argument PUB-DIR is set, use it as the publishing
834 directory.
836 Return output file's name."
837 (interactive)
838 (let ((outfile (org-export-output-file-name ".tex" subtreep))
839 (org-koma-letter-special-contents))
840 (org-export-to-file 'koma-letter outfile
841 async subtreep visible-only body-only ext-plist)))
843 ;;;###autoload
844 (defun org-koma-letter-export-to-pdf
845 (&optional async subtreep visible-only body-only ext-plist)
846 "Export current buffer as a KOMA Scrlttr2 letter (pdf).
848 If narrowing is active in the current buffer, only export its
849 narrowed part.
851 If a region is active, export that region.
853 A non-nil optional argument ASYNC means the process should happen
854 asynchronously. The resulting file should be accessible through
855 the `org-export-stack' interface.
857 When optional argument SUBTREEP is non-nil, export the sub-tree
858 at point, extracting information from the headline properties
859 first.
861 When optional argument VISIBLE-ONLY is non-nil, don't export
862 contents of hidden elements.
864 When optional argument BODY-ONLY is non-nil, only write code
865 between \"\\begin{letter}\" and \"\\end{letter}\".
867 EXT-PLIST, when provided, is a property list with external
868 parameters overriding Org default settings, but still inferior to
869 file-local settings.
871 Return PDF file's name."
872 (interactive)
873 (let ((file (org-export-output-file-name ".tex" subtreep))
874 (org-koma-letter-special-contents))
875 (org-export-to-file 'koma-letter file
876 async subtreep visible-only body-only ext-plist
877 (lambda (file) (org-latex-compile file)))))
880 (provide 'ox-koma-letter)
881 ;;; ox-koma-letter.el ends here