Merge branch 'maint'
[org-mode/org-tableheadings.git] / lisp / ox-texinfo.el
blob4cae991fc00e4037531c54da57c9f30715b8e815
1 ;;; ox-texinfo.el --- Texinfo Back-End for Org Export Engine -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2012-2016 Free Software Foundation, Inc.
4 ;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com>
5 ;; Keywords: outlines, hypermedia, calendar, wp
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22 ;;; Commentary:
24 ;; See Org manual for details.
26 ;;; Code:
28 (eval-when-compile (require 'cl))
29 (require 'ox)
31 (defvar orgtbl-exp-regexp)
35 ;;; Define Back-End
37 (org-export-define-backend 'texinfo
38 '((bold . org-texinfo-bold)
39 (center-block . org-texinfo-center-block)
40 (clock . org-texinfo-clock)
41 (code . org-texinfo-code)
42 (drawer . org-texinfo-drawer)
43 (dynamic-block . org-texinfo-dynamic-block)
44 (entity . org-texinfo-entity)
45 (example-block . org-texinfo-example-block)
46 (export-block . org-texinfo-export-block)
47 (export-snippet . org-texinfo-export-snippet)
48 (fixed-width . org-texinfo-fixed-width)
49 (footnote-definition . org-texinfo-footnote-definition)
50 (footnote-reference . org-texinfo-footnote-reference)
51 (headline . org-texinfo-headline)
52 (inline-src-block . org-texinfo-inline-src-block)
53 (inlinetask . org-texinfo-inlinetask)
54 (italic . org-texinfo-italic)
55 (item . org-texinfo-item)
56 (keyword . org-texinfo-keyword)
57 (line-break . org-texinfo-line-break)
58 (link . org-texinfo-link)
59 (node-property . org-texinfo-node-property)
60 (paragraph . org-texinfo-paragraph)
61 (plain-list . org-texinfo-plain-list)
62 (plain-text . org-texinfo-plain-text)
63 (planning . org-texinfo-planning)
64 (property-drawer . org-texinfo-property-drawer)
65 (quote-block . org-texinfo-quote-block)
66 (radio-target . org-texinfo-radio-target)
67 (section . org-texinfo-section)
68 (special-block . org-texinfo-special-block)
69 (src-block . org-texinfo-src-block)
70 (statistics-cookie . org-texinfo-statistics-cookie)
71 (subscript . org-texinfo-subscript)
72 (superscript . org-texinfo-superscript)
73 (table . org-texinfo-table)
74 (table-cell . org-texinfo-table-cell)
75 (table-row . org-texinfo-table-row)
76 (target . org-texinfo-target)
77 (template . org-texinfo-template)
78 (timestamp . org-texinfo-timestamp)
79 (verbatim . org-texinfo-verbatim)
80 (verse-block . org-texinfo-verse-block))
81 :filters-alist
82 '((:filter-headline . org-texinfo--filter-section-blank-lines)
83 (:filter-parse-tree . org-texinfo--normalize-headlines)
84 (:filter-section . org-texinfo--filter-section-blank-lines))
85 :menu-entry
86 '(?i "Export to Texinfo"
87 ((?t "As TEXI file" org-texinfo-export-to-texinfo)
88 (?i "As INFO file" org-texinfo-export-to-info)
89 (?o "As INFO file and open"
90 (lambda (a s v b)
91 (if a (org-texinfo-export-to-info t s v b)
92 (org-open-file (org-texinfo-export-to-info nil s v b)))))))
93 :options-alist
94 '((:texinfo-filename "TEXINFO_FILENAME" nil nil t)
95 (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t)
96 (:texinfo-header "TEXINFO_HEADER" nil nil newline)
97 (:texinfo-post-header "TEXINFO_POST_HEADER" nil nil newline)
98 (:subtitle "SUBTITLE" nil nil parse)
99 (:subauthor "SUBAUTHOR" nil nil newline)
100 (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t)
101 (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t)
102 (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t)
103 (:texinfo-printed-title "TEXINFO_PRINTED_TITLE" nil nil t)
104 ;; Other variables.
105 (:texinfo-classes nil nil org-texinfo-classes)
106 (:texinfo-format-headline-function nil nil org-texinfo-format-headline-function)
107 (:texinfo-node-description-column nil nil org-texinfo-node-description-column)
108 (:texinfo-active-timestamp-format nil nil org-texinfo-active-timestamp-format)
109 (:texinfo-inactive-timestamp-format nil nil org-texinfo-inactive-timestamp-format)
110 (:texinfo-diary-timestamp-format nil nil org-texinfo-diary-timestamp-format)
111 (:texinfo-link-with-unknown-path-format nil nil org-texinfo-link-with-unknown-path-format)
112 (:texinfo-tables-verbatim nil nil org-texinfo-tables-verbatim)
113 (:texinfo-table-scientific-notation nil nil org-texinfo-table-scientific-notation)
114 (:texinfo-def-table-markup nil nil org-texinfo-def-table-markup)
115 (:texinfo-text-markup-alist nil nil org-texinfo-text-markup-alist)
116 (:texinfo-format-drawer-function nil nil org-texinfo-format-drawer-function)
117 (:texinfo-format-inlinetask-function nil nil org-texinfo-format-inlinetask-function)))
121 ;;; User Configurable Variables
123 (defgroup org-export-texinfo nil
124 "Options for exporting Org mode files to Texinfo."
125 :tag "Org Export Texinfo"
126 :version "24.4"
127 :package-version '(Org . "8.0")
128 :group 'org-export)
130 ;;;; Preamble
132 (defcustom org-texinfo-coding-system nil
133 "Default document encoding for Texinfo output.
135 If nil it will default to `buffer-file-coding-system'."
136 :group 'org-export-texinfo
137 :type 'coding-system)
139 (defcustom org-texinfo-default-class "info"
140 "The default Texinfo class."
141 :group 'org-export-texinfo
142 :type '(string :tag "Texinfo class"))
144 (defcustom org-texinfo-classes
145 '(("info"
146 "@documentencoding AUTO\n@documentlanguage AUTO"
147 ("@chapter %s" . "@unnumbered %s")
148 ("@section %s" . "@unnumberedsec %s")
149 ("@subsection %s" . "@unnumberedsubsec %s")
150 ("@subsubsection %s" . "@unnumberedsubsubsec %s")))
151 "Alist of Texinfo classes and associated header and structure.
152 If #+TEXINFO_CLASS is set in the buffer, use its value and the
153 associated information. Here is the structure of each cell:
155 (class-name
156 header-string
157 (numbered-section . unnumbered-section)
158 ...)
161 The header string
162 -----------------
164 The header string is inserted in the header of the generated
165 document, right after \"@setfilename\" and \"@settitle\"
166 commands.
168 If it contains the special string
170 \"@documentencoding AUTO\"
172 \"AUTO\" will be replaced with an appropriate coding system. See
173 `org-texinfo-coding-system' for more information. Likewise, if
174 the string contains the special string
176 \"@documentlanguage AUTO\"
178 \"AUTO\" will be replaced with the language defined in the
179 buffer, through #+LANGUAGE keyword, or globally, with
180 `org-export-default-language', which see.
183 The sectioning structure
184 ------------------------
186 The sectioning structure of the class is given by the elements
187 following the header string. For each sectioning level, a number
188 of strings is specified. A %s formatter is mandatory in each
189 section string and will be replaced by the title of the section.
191 Instead of a list of sectioning commands, you can also specify
192 a function name. That function will be called with two
193 parameters, the reduced) level of the headline, and a predicate
194 non-nil when the headline should be numbered. It must return
195 a format string in which the section title will be added."
196 :group 'org-export-texinfo
197 :version "24.4"
198 :package-version '(Org . "8.2")
199 :type '(repeat
200 (list (string :tag "Texinfo class")
201 (string :tag "Texinfo header")
202 (repeat :tag "Levels" :inline t
203 (choice
204 (cons :tag "Heading"
205 (string :tag " numbered")
206 (string :tag "unnumbered"))
207 (function :tag "Hook computing sectioning"))))))
209 ;;;; Headline
211 (defcustom org-texinfo-format-headline-function
212 'org-texinfo-format-headline-default-function
213 "Function to format headline text.
215 This function will be called with 5 arguments:
216 TODO the todo keyword (string or nil).
217 TODO-TYPE the type of todo (symbol: `todo', `done', nil)
218 PRIORITY the priority of the headline (integer or nil)
219 TEXT the main headline text (string).
220 TAGS the tags as a list of strings (list of strings or nil).
222 The function result will be used in the section format string."
223 :group 'org-export-texinfo
224 :type 'function
225 :version "25.1"
226 :package-version '(Org . "8.3"))
228 ;;;; Node listing (menu)
230 (defcustom org-texinfo-node-description-column 32
231 "Column at which to start the description in the node listings.
232 If a node title is greater than this length, the description will
233 be placed after the end of the title."
234 :group 'org-export-texinfo
235 :type 'integer)
237 ;;;; Timestamps
239 (defcustom org-texinfo-active-timestamp-format "@emph{%s}"
240 "A printf format string to be applied to active timestamps."
241 :group 'org-export-texinfo
242 :type 'string)
244 (defcustom org-texinfo-inactive-timestamp-format "@emph{%s}"
245 "A printf format string to be applied to inactive timestamps."
246 :group 'org-export-texinfo
247 :type 'string)
249 (defcustom org-texinfo-diary-timestamp-format "@emph{%s}"
250 "A printf format string to be applied to diary timestamps."
251 :group 'org-export-texinfo
252 :type 'string)
254 ;;;; Links
256 (defcustom org-texinfo-link-with-unknown-path-format "@indicateurl{%s}"
257 "Format string for links with unknown path type."
258 :group 'org-export-texinfo
259 :type 'string)
261 ;;;; Tables
263 (defcustom org-texinfo-tables-verbatim nil
264 "When non-nil, tables are exported verbatim."
265 :group 'org-export-texinfo
266 :type 'boolean)
268 (defcustom org-texinfo-table-scientific-notation "%s\\,(%s)"
269 "Format string to display numbers in scientific notation.
270 The format should have \"%s\" twice, for mantissa and exponent
271 \(i.e. \"%s\\\\times10^{%s}\").
273 When nil, no transformation is made."
274 :group 'org-export-texinfo
275 :type '(choice
276 (string :tag "Format string")
277 (const :tag "No formatting" nil)))
279 (defcustom org-texinfo-def-table-markup "@samp"
280 "Default setting for @table environments."
281 :group 'org-export-texinfo
282 :type 'string)
284 ;;;; Text markup
286 (defcustom org-texinfo-text-markup-alist '((bold . "@strong{%s}")
287 (code . code)
288 (italic . "@emph{%s}")
289 (verbatim . verb)
290 (comment . "@c %s"))
291 "Alist of Texinfo expressions to convert text markup.
293 The key must be a symbol among `bold', `italic' and `comment'.
294 The value is a formatting string to wrap fontified text with.
296 Value can also be set to the following symbols: `verb' and
297 `code'. For the former, Org will use \"@verb\" to
298 create a format string and select a delimiter character that
299 isn't in the string. For the latter, Org will use \"@code\"
300 to typeset and try to protect special characters.
302 If no association can be found for a given markup, text will be
303 returned as-is."
304 :group 'org-export-texinfo
305 :type 'alist
306 :options '(bold code italic verbatim comment))
308 ;;;; Drawers
310 (defcustom org-texinfo-format-drawer-function (lambda (_name contents) contents)
311 "Function called to format a drawer in Texinfo code.
313 The function must accept two parameters:
314 NAME the drawer name, like \"LOGBOOK\"
315 CONTENTS the contents of the drawer.
317 The function should return the string to be exported.
319 The default function simply returns the value of CONTENTS."
320 :group 'org-export-texinfo
321 :version "24.4"
322 :package-version '(Org . "8.2")
323 :type 'function)
325 ;;;; Inlinetasks
327 (defcustom org-texinfo-format-inlinetask-function
328 'org-texinfo-format-inlinetask-default-function
329 "Function called to format an inlinetask in Texinfo code.
331 The function must accept six parameters:
332 TODO the todo keyword, as a string
333 TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
334 PRIORITY the inlinetask priority, as a string
335 NAME the inlinetask name, as a string.
336 TAGS the inlinetask tags, as a list of strings.
337 CONTENTS the contents of the inlinetask, as a string.
339 The function should return the string to be exported."
340 :group 'org-export-texinfo
341 :type 'function)
343 ;;;; Compilation
345 (defcustom org-texinfo-info-process '("makeinfo %f")
346 "Commands to process a Texinfo file to an INFO file.
347 This is list of strings, each of them will be given to the shell
348 as a command. %f in the command will be replaced by the full
349 file name, %b by the file base name (i.e without extension) and
350 %o by the base directory of the file."
351 :group 'org-export-texinfo
352 :type '(repeat :tag "Shell command sequence"
353 (string :tag "Shell command")))
355 (defcustom org-texinfo-logfiles-extensions
356 '("aux" "toc" "cp" "fn" "ky" "pg" "tp" "vr")
357 "The list of file extensions to consider as Texinfo logfiles.
358 The logfiles will be remove if `org-texinfo-remove-logfiles' is
359 non-nil."
360 :group 'org-export-texinfo
361 :type '(repeat (string :tag "Extension")))
363 (defcustom org-texinfo-remove-logfiles t
364 "Non-nil means remove the logfiles produced by compiling a Texinfo file.
365 By default, logfiles are files with these extensions: .aux, .toc,
366 .cp, .fn, .ky, .pg and .tp. To define the set of logfiles to remove,
367 set `org-texinfo-logfiles-extensions'."
368 :group 'org-export-latex
369 :type 'boolean)
371 ;;; Constants
373 (defconst org-texinfo-max-toc-depth 4
374 "Maximum depth for creation of detailed menu listings.
375 Beyond this depth, Texinfo will not recognize the nodes and will
376 cause errors. Left as a constant in case this value ever
377 changes.")
379 (defconst org-texinfo-supported-coding-systems
380 '("US-ASCII" "UTF-8" "ISO-8859-15" "ISO-8859-1" "ISO-8859-2" "koi8-r" "koi8-u")
381 "List of coding systems supported by Texinfo, as strings.
382 Specified coding system will be matched against these strings.
383 If two strings share the same prefix (e.g. \"ISO-8859-1\" and
384 \"ISO-8859-15\"), the most specific one has to be listed first.")
386 (defconst org-texinfo-inline-image-rules
387 (list (cons "file"
388 (regexp-opt '("eps" "pdf" "png" "jpg" "jpeg" "gif" "svg"))))
389 "Rules characterizing image files that can be inlined.")
392 ;;; Internal Functions
394 (defun org-texinfo--filter-section-blank-lines (headline _backend _info)
395 "Filter controlling number of blank lines after a section."
396 (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" "\n\n" headline))
398 (defun org-texinfo--normalize-headlines (tree _backend info)
399 "Normalize headlines in TREE.
401 BACK-END is the symbol specifying back-end used for export. INFO
402 is a plist used as a communication channel.
404 Make sure every headline in TREE contains a section, since those
405 are required to install a menu. Also put exactly one blank line
406 at the end of each section.
408 Return new tree."
409 (org-element-map tree 'headline
410 (lambda (hl)
411 (org-element-put-property hl :post-blank 1)
412 (let ((contents (org-element-contents hl)))
413 (when contents
414 (let ((first (org-element-map contents '(headline section)
415 #'identity info t)))
416 (unless (eq (org-element-type first) 'section)
417 (apply #'org-element-set-contents
419 (cons `(section (:parent ,hl)) contents)))))))
420 info)
421 tree)
423 (defun org-texinfo--find-verb-separator (s)
424 "Return a character not used in string S.
425 This is used to choose a separator for constructs like \\verb."
426 (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
427 (loop for c across ll
428 when (not (string-match (regexp-quote (char-to-string c)) s))
429 return (char-to-string c))))
431 (defun org-texinfo--text-markup (text markup _info)
432 "Format TEXT depending on MARKUP text markup.
433 INFO is a plist used as a communication channel. See
434 `org-texinfo-text-markup-alist' for details."
435 (pcase (cdr (assq markup org-texinfo-text-markup-alist))
436 ;; No format string: Return raw text.
437 (`nil text)
438 (`verb
439 (let ((separator (org-texinfo--find-verb-separator text)))
440 (concat "@verb{" separator text separator "}")))
441 (`code
442 (format "@code{%s}" (replace-regexp-in-string "[@{}]" "@\\&" text)))
443 ;; Else use format string.
444 (fmt (format fmt text))))
446 (defun org-texinfo--get-node (blob info)
447 "Return node or anchor associated to BLOB.
448 BLOB is an element or object. INFO is a plist used as
449 a communication channel. The function guarantees the node or
450 anchor name is unique."
451 (let ((cache (plist-get info :texinfo-node-cache)))
452 (or (cdr (assq blob cache))
453 (let ((name
454 (org-texinfo--sanitize-node
455 (if (eq (org-element-type blob) 'headline)
456 (org-export-data (org-export-get-alt-title blob info) info)
457 (org-export-get-reference blob info)))))
458 ;; Ensure NAME is unique.
459 (while (rassoc name cache) (setq name (concat name "x")))
460 (plist-put info :texinfo-node-cache (cons (cons blob name) cache))
461 name))))
463 (defun org-texinfo--sanitize-node (title)
464 "Bend string TITLE to node line requirements.
465 Trim string and collapse multiple whitespace characters as they
466 are not significant. Also remove the following characters: @
467 { } ( ) : . ,"
468 (replace-regexp-in-string
469 "[:,.]" ""
470 (replace-regexp-in-string
471 "\\`(\\(.*)\\)" "[\\1"
472 (org-trim (replace-regexp-in-string "[ \t]\\{2,\\}" " " title)))))
474 (defun org-texinfo--sanitize-content (text)
475 "Escape special characters in string TEXT.
476 Special characters are: @ { }"
477 (replace-regexp-in-string "[@{}]" "@\\&" text))
479 (defun org-texinfo--wrap-float (value info &optional type label caption short)
480 "Wrap string VALUE within a @float command.
481 INFO is the current export state, as a plist. TYPE is float
482 type, as a string. LABEL is the cross reference label for the
483 float, as a string. CAPTION and SHORT are, respectively, the
484 caption and shortcaption used for the float, as secondary
485 strings (e.g., returned by `org-export-get-caption')."
486 (let* ((backend
487 (org-export-create-backend
488 :parent 'texinfo
489 :transcoders '((link . (lambda (object c i) c))
490 (radio-target . (lambda (object c i) c))
491 (target . ignore))))
492 (short-backend
493 (org-export-create-backend
494 :parent 'texinfo
495 :transcoders '((footnote-reference . ignore)
496 (inline-src-block . ignore)
497 (link . (lambda (object c i) c))
498 (radio-target . (lambda (object c i) c))
499 (target . ignore)
500 (verbatim . ignore))))
501 (short-str
502 (if (and short caption)
503 (format "@shortcaption{%s}\n"
504 (org-export-data-with-backend short short-backend info))
505 ""))
506 (caption-str
507 (if (or short caption)
508 (format "@caption{%s}\n"
509 (org-export-data-with-backend
510 (or caption short)
511 (if (equal short-str "") short-backend backend)
512 info))
513 "")))
514 (format "@float %s%s\n%s\n%s%s@end float"
515 type (if label (concat "," label) "") value caption-str short-str)))
517 ;;; Template
519 (defun org-texinfo-template (contents info)
520 "Return complete document string after Texinfo conversion.
521 CONTENTS is the transcoded contents string. INFO is a plist
522 holding export options."
523 (let ((title (org-export-data (plist-get info :title) info))
524 ;; Copying data is the contents of the first headline in
525 ;; parse tree with a non-nil copying property.
526 (copying (org-element-map (plist-get info :parse-tree) 'headline
527 (lambda (hl)
528 (and (org-not-nil (org-element-property :COPYING hl))
529 (org-element-contents hl)))
530 info t)))
531 (concat
532 "\\input texinfo @c -*- texinfo -*-\n"
533 "@c %**start of header\n"
534 (let ((file (or (plist-get info :texinfo-filename)
535 (let ((f (plist-get info :output-file)))
536 (and f (concat (file-name-sans-extension f) ".info"))))))
537 (and file (format "@setfilename %s\n" file)))
538 (format "@settitle %s\n" title)
539 ;; Insert class-defined header.
540 (org-element-normalize-string
541 (let ((header (nth 1 (assoc (plist-get info :texinfo-class)
542 org-texinfo-classes)))
543 (coding
544 (catch 'coding-system
545 (let ((case-fold-search t)
546 (name (symbol-name (or org-texinfo-coding-system
547 buffer-file-coding-system))))
548 (dolist (system org-texinfo-supported-coding-systems "UTF-8")
549 (when (org-string-match-p (regexp-quote system) name)
550 (throw 'coding-system system))))))
551 (language (plist-get info :language))
552 (case-fold-search nil))
553 ;; Auto coding system.
554 (replace-regexp-in-string
555 "^@documentencoding \\(AUTO\\)$"
556 coding
557 (replace-regexp-in-string
558 "^@documentlanguage \\(AUTO\\)$" language header t nil 1) t nil 1)))
559 ;; Additional header options set by #+TEXINFO_HEADER.
560 (let ((texinfo-header (plist-get info :texinfo-header)))
561 (and texinfo-header (org-element-normalize-string texinfo-header)))
562 "@c %**end of header\n\n"
563 ;; Additional options set by #+TEXINFO_POST_HEADER.
564 (let ((texinfo-post-header (plist-get info :texinfo-post-header)))
565 (and texinfo-post-header
566 (org-element-normalize-string texinfo-post-header)))
567 ;; Copying.
568 (and copying
569 (format "@copying\n%s@end copying\n\n"
570 (org-element-normalize-string
571 (org-export-data copying info))))
572 ;; Info directory information. Only supply if both title and
573 ;; category are provided.
574 (let ((dircat (plist-get info :texinfo-dircat))
575 (dirtitle
576 (let ((title (plist-get info :texinfo-dirtitle)))
577 (and title
578 (string-match "^\\(?:\\* \\)?\\(.*?\\)\\(\\.\\)?$" title)
579 (format "* %s." (match-string 1 title))))))
580 (when (and dircat dirtitle)
581 (concat "@dircategory " dircat "\n"
582 "@direntry\n"
583 (let ((dirdesc
584 (let ((desc (plist-get info :texinfo-dirdesc)))
585 (cond ((not desc) nil)
586 ((org-string-match-p "\\.$" desc) desc)
587 (t (concat desc "."))))))
588 (if dirdesc (format "%-23s %s" dirtitle dirdesc) dirtitle))
589 "\n"
590 "@end direntry\n\n")))
591 ;; Title
592 "@finalout\n"
593 "@titlepage\n"
594 (when (plist-get info :with-title)
595 (concat
596 (format "@title %s\n" (or (plist-get info :texinfo-printed-title) title ""))
597 (let ((subtitle (plist-get info :subtitle)))
598 (when subtitle
599 (format "@subtitle %s\n"
600 (org-export-data subtitle info))))))
601 (when (plist-get info :with-author)
602 (concat
603 ;; Primary author.
604 (let ((author (org-string-nw-p
605 (org-export-data (plist-get info :author) info)))
606 (email (and (plist-get info :with-email)
607 (org-string-nw-p
608 (org-export-data (plist-get info :email) info)))))
609 (cond ((and author email)
610 (format "@author %s (@email{%s})\n" author email))
611 (author (format "@author %s\n" author))
612 (email (format "@author @email{%s}\n" email))))
613 ;; Other authors.
614 (let ((subauthor (plist-get info :subauthor)))
615 (and subauthor
616 (org-element-normalize-string
617 (replace-regexp-in-string "^" "@author " subauthor))))))
618 (and copying "@page\n@vskip 0pt plus 1filll\n@insertcopying\n")
619 "@end titlepage\n\n"
620 ;; Table of contents.
621 (and (plist-get info :with-toc) "@contents\n\n")
622 ;; Configure Top Node when not for Tex
623 "@ifnottex\n"
624 "@node Top\n"
625 (format "@top %s\n" title)
626 (and copying "@insertcopying\n")
627 "@end ifnottex\n\n"
628 ;; Menu.
629 (org-texinfo-make-menu (plist-get info :parse-tree) info 'master)
630 "\n"
631 ;; Document's body.
632 contents "\n"
633 ;; Creator.
634 (and (plist-get info :with-creator)
635 (concat (plist-get info :creator) "\n"))
636 ;; Document end.
637 "@bye")))
641 ;;; Transcode Functions
643 ;;;; Bold
645 (defun org-texinfo-bold (_bold contents info)
646 "Transcode BOLD from Org to Texinfo.
647 CONTENTS is the text with bold markup. INFO is a plist holding
648 contextual information."
649 (org-texinfo--text-markup contents 'bold info))
651 ;;;; Center Block
653 (defun org-texinfo-center-block (_center-block contents _info)
654 "Transcode a CENTER-BLOCK element from Org to Texinfo.
655 CONTENTS holds the contents of the block. INFO is a plist used
656 as a communication channel."
657 contents)
659 ;;;; Clock
661 (defun org-texinfo-clock (clock _contents info)
662 "Transcode a CLOCK element from Org to Texinfo.
663 CONTENTS is nil. INFO is a plist holding contextual
664 information."
665 (concat
666 "@noindent"
667 (format "@strong{%s} " org-clock-string)
668 (format (plist-get info :texinfo-inactive-timestamp-format)
669 (concat (org-timestamp-translate (org-element-property :value clock))
670 (let ((time (org-element-property :duration clock)))
671 (and time (format " (%s)" time)))))
672 "@*"))
674 ;;;; Code
676 (defun org-texinfo-code (code _contents info)
677 "Transcode a CODE object from Org to Texinfo.
678 CONTENTS is nil. INFO is a plist used as a communication
679 channel."
680 (org-texinfo--text-markup (org-element-property :value code) 'code info))
682 ;;;; Drawer
684 (defun org-texinfo-drawer (drawer contents info)
685 "Transcode a DRAWER element from Org to Texinfo.
686 CONTENTS holds the contents of the block. INFO is a plist
687 holding contextual information."
688 (let* ((name (org-element-property :drawer-name drawer))
689 (output (funcall (plist-get info :texinfo-format-drawer-function)
690 name contents)))
691 output))
693 ;;;; Dynamic Block
695 (defun org-texinfo-dynamic-block (_dynamic-block contents _info)
696 "Transcode a DYNAMIC-BLOCK element from Org to Texinfo.
697 CONTENTS holds the contents of the block. INFO is a plist
698 holding contextual information."
699 contents)
701 ;;;; Entity
703 (defun org-texinfo-entity (entity _contents _info)
704 "Transcode an ENTITY object from Org to Texinfo.
705 CONTENTS are the definition itself. INFO is a plist holding
706 contextual information."
707 (let ((ent (org-element-property :latex entity)))
708 (if (org-element-property :latex-math-p entity) (format "@math{%s}" ent) ent)))
710 ;;;; Example Block
712 (defun org-texinfo-example-block (example-block _contents info)
713 "Transcode an EXAMPLE-BLOCK element from Org to Texinfo.
714 CONTENTS is nil. INFO is a plist holding contextual
715 information."
716 (format "@verbatim\n%s@end verbatim"
717 (org-export-format-code-default example-block info)))
719 ;;; Export Block
721 (defun org-texinfo-export-block (export-block _contents _info)
722 "Transcode a EXPORT-BLOCK element from Org to Texinfo.
723 CONTENTS is nil. INFO is a plist holding contextual information."
724 (when (string= (org-element-property :type export-block) "TEXINFO")
725 (org-remove-indentation (org-element-property :value export-block))))
727 ;;; Export Snippet
729 (defun org-texinfo-export-snippet (export-snippet _contents _info)
730 "Transcode a EXPORT-SNIPPET object from Org to Texinfo.
731 CONTENTS is nil. INFO is a plist holding contextual information."
732 (when (eq (org-export-snippet-backend export-snippet) 'texinfo)
733 (org-element-property :value export-snippet)))
735 ;;;; Fixed Width
737 (defun org-texinfo-fixed-width (fixed-width _contents _info)
738 "Transcode a FIXED-WIDTH element from Org to Texinfo.
739 CONTENTS is nil. INFO is a plist holding contextual information."
740 (format "@example\n%s\n@end example"
741 (org-remove-indentation
742 (org-texinfo--sanitize-content
743 (org-element-property :value fixed-width)))))
745 ;;;; Footnote Reference
747 (defun org-texinfo-footnote-reference (footnote _contents info)
748 "Create a footnote reference for FOOTNOTE.
750 FOOTNOTE is the footnote to define. CONTENTS is nil. INFO is a
751 plist holding contextual information."
752 (let ((def (org-export-get-footnote-definition footnote info)))
753 (format "@footnote{%s}"
754 (org-trim (org-export-data def info)))))
756 ;;;; Headline
758 (defun org-texinfo-headline (headline contents info)
759 "Transcode a HEADLINE element from Org to Texinfo.
760 CONTENTS holds the contents of the headline. INFO is a plist
761 holding contextual information."
762 (let* ((class (plist-get info :texinfo-class))
763 (level (org-export-get-relative-level headline info))
764 (numberedp (org-export-numbered-headline-p headline info))
765 (class-sectioning (assoc class (plist-get info :texinfo-classes)))
766 ;; Find the index type, if any.
767 (index (org-element-property :INDEX headline))
768 ;; Create node info, to insert it before section formatting.
769 ;; Use custom menu title if present.
770 (node (format "@node %s\n" (org-texinfo--get-node headline info)))
771 ;; Section formatting will set two placeholders: one for the
772 ;; title and the other for the contents.
773 (section-fmt
774 (if (org-not-nil (org-element-property :APPENDIX headline))
775 "@appendix %s\n%s"
776 (let ((sec (if (and (symbolp (nth 2 class-sectioning))
777 (fboundp (nth 2 class-sectioning)))
778 (funcall (nth 2 class-sectioning) level numberedp)
779 (nth (1+ level) class-sectioning))))
780 (cond
781 ;; No section available for that LEVEL.
782 ((not sec) nil)
783 ;; Section format directly returned by a function.
784 ((stringp sec) sec)
785 ;; (numbered-section . unnumbered-section)
786 ((not (consp (cdr sec)))
787 (concat (if (or index (not numberedp)) (cdr sec) (car sec))
788 "\n%s"))))))
789 (todo
790 (and (plist-get info :with-todo-keywords)
791 (let ((todo (org-element-property :todo-keyword headline)))
792 (and todo (org-export-data todo info)))))
793 (todo-type (and todo (org-element-property :todo-type headline)))
794 (tags (and (plist-get info :with-tags)
795 (org-export-get-tags headline info)))
796 (priority (and (plist-get info :with-priority)
797 (org-element-property :priority headline)))
798 (text (org-export-data (org-element-property :title headline) info))
799 (full-text (funcall (plist-get info :texinfo-format-headline-function)
800 todo todo-type priority text tags))
801 (contents (if (org-string-nw-p contents) (concat "\n" contents) "")))
802 (cond
803 ;; Case 1: This is a footnote section: ignore it.
804 ((org-element-property :footnote-section-p headline) nil)
805 ;; Case 2: This is the `copying' section: ignore it
806 ;; This is used elsewhere.
807 ((org-not-nil (org-element-property :COPYING headline)) nil)
808 ;; Case 3: An index. If it matches one of the known indexes,
809 ;; print it as such following the contents, otherwise
810 ;; print the contents and leave the index up to the user.
811 (index
812 (concat node
813 (format
814 section-fmt
815 full-text
816 (concat contents
817 (and (member index '("cp" "fn" "ky" "pg" "tp" "vr"))
818 (concat "\n@printindex " index))))))
819 ;; Case 4: This is a deep sub-tree: export it as a list item.
820 ;; Also export as items headlines for which no section
821 ;; format has been found.
822 ((or (not section-fmt) (org-export-low-level-p headline info))
823 ;; Build the real contents of the sub-tree.
824 (concat (and (org-export-first-sibling-p headline info)
825 (format "@%s\n" (if numberedp 'enumerate 'itemize)))
826 "@item\n" full-text "\n"
827 contents
828 (if (org-export-last-sibling-p headline info)
829 (format "@end %s" (if numberedp 'enumerate 'itemize))
830 "\n")))
831 ;; Case 5: Standard headline. Export it as a section.
832 (t (concat node (format section-fmt full-text contents))))))
834 (defun org-texinfo-format-headline-default-function
835 (todo _todo-type priority text tags)
836 "Default format function for a headline.
837 See `org-texinfo-format-headline-function' for details."
838 (concat (when todo (format "@strong{%s} " todo))
839 (when priority (format "@emph{#%s} " priority))
840 text
841 (when tags (format " :%s:" (mapconcat 'identity tags ":")))))
843 ;;;; Inline Src Block
845 (defun org-texinfo-inline-src-block (inline-src-block _contents _info)
846 "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo.
847 CONTENTS holds the contents of the item. INFO is a plist holding
848 contextual information."
849 (let* ((code (org-element-property :value inline-src-block))
850 (separator (org-texinfo--find-verb-separator code)))
851 (concat "@verb{" separator code separator "}")))
853 ;;;; Inlinetask
855 (defun org-texinfo-inlinetask (inlinetask contents info)
856 "Transcode an INLINETASK element from Org to Texinfo.
857 CONTENTS holds the contents of the block. INFO is a plist
858 holding contextual information."
859 (let ((title (org-export-data (org-element-property :title inlinetask) info))
860 (todo (and (plist-get info :with-todo-keywords)
861 (let ((todo (org-element-property :todo-keyword inlinetask)))
862 (and todo (org-export-data todo info)))))
863 (todo-type (org-element-property :todo-type inlinetask))
864 (tags (and (plist-get info :with-tags)
865 (org-export-get-tags inlinetask info)))
866 (priority (and (plist-get info :with-priority)
867 (org-element-property :priority inlinetask))))
868 (funcall (plist-get info :texinfo-format-inlinetask-function)
869 todo todo-type priority title tags contents)))
871 (defun org-texinfo-format-inlinetask-default-function
872 (todo _todo-type priority title tags contents)
873 "Default format function for a inlinetasks.
874 See `org-texinfo-format-inlinetask-function' for details."
875 (let ((full-title
876 (concat (when todo (format "@strong{%s} " todo))
877 (when priority (format "#%c " priority))
878 title
879 (when tags (format ":%s:" (mapconcat #'identity tags ":"))))))
880 (format "@center %s\n\n%s\n" full-title contents)))
882 ;;;; Italic
884 (defun org-texinfo-italic (_italic contents info)
885 "Transcode ITALIC from Org to Texinfo.
886 CONTENTS is the text with italic markup. INFO is a plist holding
887 contextual information."
888 (org-texinfo--text-markup contents 'italic info))
890 ;;;; Item
892 (defun org-texinfo-item (item contents info)
893 "Transcode an ITEM element from Org to Texinfo.
894 CONTENTS holds the contents of the item. INFO is a plist holding
895 contextual information."
896 (format "@item%s\n%s"
897 (let ((tag (org-element-property :tag item)))
898 (if tag (concat " " (org-export-data tag info)) ""))
899 (or contents "")))
901 ;;;; Keyword
903 (defun org-texinfo-keyword (keyword _contents info)
904 "Transcode a KEYWORD element from Org to Texinfo.
905 CONTENTS is nil. INFO is a plist holding contextual information."
906 (let ((key (org-element-property :key keyword))
907 (value (org-element-property :value keyword)))
908 (cond
909 ((string= key "TEXINFO") value)
910 ((string= key "CINDEX") (format "@cindex %s" value))
911 ((string= key "FINDEX") (format "@findex %s" value))
912 ((string= key "KINDEX") (format "@kindex %s" value))
913 ((string= key "PINDEX") (format "@pindex %s" value))
914 ((string= key "TINDEX") (format "@tindex %s" value))
915 ((string= key "VINDEX") (format "@vindex %s" value))
916 ((string= key "TOC")
917 (cond ((org-string-match-p "\\<tables\\>" value)
918 (concat "@listoffloats "
919 (org-export-translate "Table" :utf-8 info)))
920 ((org-string-match-p "\\<listings\\>" value)
921 (concat "@listoffloats "
922 (org-export-translate "Listing" :utf-8 info))))))))
924 ;;;; Line Break
926 (defun org-texinfo-line-break (_line-break _contents _info)
927 "Transcode a LINE-BREAK object from Org to Texinfo.
928 CONTENTS is nil. INFO is a plist holding contextual information."
929 "@*\n")
931 ;;;; Link
933 (defun org-texinfo-link (link desc info)
934 "Transcode a LINK object from Org to Texinfo.
936 DESC is the description part of the link, or the empty string.
937 INFO is a plist holding contextual information. See
938 `org-export-data'."
939 (let* ((type (org-element-property :type link))
940 (raw-path (org-element-property :path link))
941 ;; Ensure DESC really exists, or set it to nil.
942 (desc (and (not (string= desc "")) desc))
943 (path (cond
944 ((member type '("http" "https" "ftp"))
945 (concat type ":" raw-path))
946 ((string= type "file") (org-export-file-uri raw-path))
947 (t raw-path))))
948 (cond
949 ((org-export-custom-protocol-maybe link desc 'texinfo))
950 ((org-export-inline-image-p link org-texinfo-inline-image-rules)
951 (org-texinfo--inline-image link info))
952 ((equal type "radio")
953 (let ((destination (org-export-resolve-radio-link link info)))
954 (if (not destination) desc
955 (format "@ref{%s,,%s}"
956 (org-texinfo--get-node destination info)
957 desc))))
958 ((member type '("custom-id" "id" "fuzzy"))
959 (let ((destination
960 (if (equal type "fuzzy")
961 (org-export-resolve-fuzzy-link link info)
962 (org-export-resolve-id-link link info))))
963 (case (org-element-type destination)
964 ((nil)
965 (format org-texinfo-link-with-unknown-path-format
966 (org-texinfo--sanitize-content path)))
967 ;; Id link points to an external file.
968 (plain-text
969 (if desc (format "@uref{file://%s,%s}" destination desc)
970 (format "@uref{file://%s}" destination)))
971 (headline
972 (format "@ref{%s,%s}"
973 (org-texinfo--get-node destination info)
974 (cond
975 (desc)
976 ((org-export-numbered-headline-p destination info)
977 (mapconcat
978 #'number-to-string
979 (org-export-get-headline-number destination info) "."))
980 (t (org-export-data
981 (org-element-property :title destination) info)))))
982 (otherwise
983 (format "@ref{%s,,%s}"
984 (org-texinfo--get-node destination info)
985 (cond
986 (desc)
987 ;; No description is provided: first try to
988 ;; associate destination to a number.
989 ((let ((n (org-export-get-ordinal destination info)))
990 (cond ((not n) nil)
991 ((integerp n) n)
992 (t (mapconcat #'number-to-string n ".")))))
993 ;; Then grab title of headline containing
994 ;; DESTINATION.
995 ((let ((h (org-element-lineage destination '(headline) t)))
996 (and h
997 (org-export-data
998 (org-element-property :title destination) info))))
999 ;; Eventually, just return "Top" to refer to the
1000 ;; beginning of the info file.
1001 (t "Top")))))))
1002 ((equal type "info")
1003 (let* ((info-path (split-string path "[:#]"))
1004 (info-manual (car info-path))
1005 (info-node (or (cadr info-path) "Top"))
1006 (title (or desc "")))
1007 (format "@ref{%s,%s,,%s,}" info-node title info-manual)))
1008 ((string= type "mailto")
1009 (format "@email{%s}"
1010 (concat (org-texinfo--sanitize-content path)
1011 (and desc (concat "," desc)))))
1012 ;; External link with a description part.
1013 ((and path desc) (format "@uref{%s,%s}" path desc))
1014 ;; External link without a description part.
1015 (path (format "@uref{%s}" path))
1016 ;; No path, only description. Try to do something useful.
1018 (format (plist-get info :texinfo-link-with-unknown-path-format) desc)))))
1020 (defun org-texinfo--inline-image (link info)
1021 "Return Texinfo code for an inline image.
1022 LINK is the link pointing to the inline image. INFO is the
1023 current state of the export, as a plist."
1024 (let* ((parent (org-export-get-parent-element link))
1025 (label (and (org-element-property :name parent)
1026 (org-texinfo--get-node parent info)))
1027 (caption (org-export-get-caption parent))
1028 (shortcaption (org-export-get-caption parent t))
1029 (path (org-element-property :path link))
1030 (filename
1031 (file-name-sans-extension
1032 (if (file-name-absolute-p path) (expand-file-name path) path)))
1033 (extension (file-name-extension path))
1034 (attributes (org-export-read-attribute :attr_texinfo parent))
1035 (height (or (plist-get attributes :height) ""))
1036 (width (or (plist-get attributes :width) ""))
1037 (alt (or (plist-get attributes :alt) ""))
1038 (image (format "@image{%s,%s,%s,%s,%s}"
1039 filename width height alt extension)))
1040 (cond ((or caption shortcaption)
1041 (org-texinfo--wrap-float image
1042 info
1043 (org-export-translate "Figure" :utf-8 info)
1044 label
1045 caption
1046 shortcaption))
1047 (label (concat "@anchor{" label "}\n" image))
1048 (t image))))
1051 ;;;; Menu
1053 (defun org-texinfo-make-menu (scope info &optional master)
1054 "Create the menu for inclusion in the Texinfo document.
1056 SCOPE is a headline or a full parse tree. INFO is the
1057 communication channel, as a plist.
1059 When optional argument MASTER is non-nil, generate a master menu,
1060 including detailed node listing."
1061 (let ((menu (org-texinfo--build-menu scope info)))
1062 (when (org-string-nw-p menu)
1063 (org-element-normalize-string
1064 (format
1065 "@menu\n%s@end menu"
1066 (concat menu
1067 (when master
1068 (let ((detailmenu
1069 (org-texinfo--build-menu
1070 scope info
1071 (let ((toc-depth (plist-get info :with-toc)))
1072 (if (wholenump toc-depth) toc-depth
1073 org-texinfo-max-toc-depth)))))
1074 (when (org-string-nw-p detailmenu)
1075 (concat "\n@detailmenu\n"
1076 "--- The Detailed Node Listing ---\n\n"
1077 detailmenu
1078 "@end detailmenu\n"))))))))))
1080 (defun org-texinfo--build-menu (scope info &optional level)
1081 "Build menu for entries within SCOPE.
1082 SCOPE is a headline or a full parse tree. INFO is a plist
1083 containing contextual information. When optional argument LEVEL
1084 is an integer, build the menu recursively, down to this depth."
1085 (cond
1086 ((not level)
1087 (org-texinfo--format-entries (org-texinfo--menu-entries scope info) info))
1088 ((zerop level) nil)
1090 (org-element-normalize-string
1091 (mapconcat
1092 (lambda (h)
1093 (let ((entries (org-texinfo--menu-entries h info)))
1094 (when entries
1095 (concat
1096 (format "%s\n\n%s\n"
1097 (org-export-data (org-export-get-alt-title h info) info)
1098 (org-texinfo--format-entries entries info))
1099 (org-texinfo--build-menu h info (1- level))))))
1100 (org-texinfo--menu-entries scope info) "\n")))))
1102 (defun org-texinfo--format-entries (entries info)
1103 "Format all direct menu entries in SCOPE, as a string.
1104 SCOPE is either a headline or a full Org document. INFO is
1105 a plist containing contextual information."
1106 (org-element-normalize-string
1107 (mapconcat
1108 (lambda (h)
1109 (let* ((title (org-export-data
1110 (org-export-get-alt-title h info) info))
1111 (node (org-texinfo--get-node h info))
1112 (entry (concat "* " title ":"
1113 (if (string= title node) ":"
1114 (concat " " node ". "))))
1115 (desc (org-element-property :DESCRIPTION h)))
1116 (if (not desc) entry
1117 (format (format "%%-%ds %%s" org-texinfo-node-description-column)
1118 entry desc))))
1119 entries "\n")))
1121 (defun org-texinfo--menu-entries (scope info)
1122 "List direct children in SCOPE needing a menu entry.
1123 SCOPE is a headline or a full parse tree. INFO is a plist
1124 holding contextual information."
1125 (let* ((cache (or (plist-get info :texinfo-entries-cache)
1126 (plist-get (plist-put info :texinfo-entries-cache
1127 (make-hash-table :test #'eq))
1128 :texinfo-entries-cache)))
1129 (cached-entries (gethash scope cache 'no-cache)))
1130 (if (not (eq cached-entries 'no-cache)) cached-entries
1131 (puthash scope
1132 (org-element-map (org-element-contents scope) 'headline
1133 (lambda (h)
1134 (and (not (org-not-nil (org-element-property :COPYING h)))
1135 (not (org-element-property :footnote-section-p h))
1136 (not (org-export-low-level-p h info))
1138 info nil 'headline)
1139 cache))))
1141 ;;;; Node Property
1143 (defun org-texinfo-node-property (node-property _contents _info)
1144 "Transcode a NODE-PROPERTY element from Org to Texinfo.
1145 CONTENTS is nil. INFO is a plist holding contextual
1146 information."
1147 (format "%s:%s"
1148 (org-element-property :key node-property)
1149 (let ((value (org-element-property :value node-property)))
1150 (if value (concat " " value) ""))))
1152 ;;;; Paragraph
1154 (defun org-texinfo-paragraph (_paragraph contents _info)
1155 "Transcode a PARAGRAPH element from Org to Texinfo.
1156 CONTENTS is the contents of the paragraph, as a string. INFO is
1157 the plist used as a communication channel."
1158 contents)
1160 ;;;; Plain List
1162 (defun org-texinfo-plain-list (plain-list contents info)
1163 "Transcode a PLAIN-LIST element from Org to Texinfo.
1164 CONTENTS is the contents of the list. INFO is a plist holding
1165 contextual information."
1166 (let* ((attr (org-export-read-attribute :attr_texinfo plain-list))
1167 (indic (or (plist-get attr :indic)
1168 (plist-get info :texinfo-def-table-markup)))
1169 (table-type (plist-get attr :table-type))
1170 (type (org-element-property :type plain-list))
1171 (list-type (cond
1172 ((eq type 'ordered) "enumerate")
1173 ((eq type 'unordered) "itemize")
1174 ((member table-type '("ftable" "vtable")) table-type)
1175 (t "table"))))
1176 (format "@%s\n%s@end %s"
1177 (if (eq type 'descriptive) (concat list-type " " indic) list-type)
1178 contents
1179 list-type)))
1181 ;;;; Plain Text
1183 (defun org-texinfo-plain-text (text info)
1184 "Transcode a TEXT string from Org to Texinfo.
1185 TEXT is the string to transcode. INFO is a plist holding
1186 contextual information."
1187 ;; First protect @, { and }.
1188 (let ((output (org-texinfo--sanitize-content text)))
1189 ;; Activate smart quotes. Be sure to provide original TEXT string
1190 ;; since OUTPUT may have been modified.
1191 (when (plist-get info :with-smart-quotes)
1192 (setq output
1193 (org-export-activate-smart-quotes output :texinfo info text)))
1194 ;; LaTeX into @LaTeX{} and TeX into @TeX{}
1195 (let ((case-fold-search nil)
1196 (start 0))
1197 (while (string-match "\\(\\(?:La\\)?TeX\\)" output start)
1198 (setq output (replace-match
1199 (format "@%s{}" (match-string 1 output)) nil t output)
1200 start (match-end 0))))
1201 ;; Convert special strings.
1202 (when (plist-get info :with-special-strings)
1203 (while (string-match (regexp-quote "...") output)
1204 (setq output (replace-match "@dots{}" nil t output))))
1205 ;; Handle break preservation if required.
1206 (when (plist-get info :preserve-breaks)
1207 (setq output (replace-regexp-in-string
1208 "\\(\\\\\\\\\\)?[ \t]*\n" " @*\n" output)))
1209 ;; Return value.
1210 output))
1212 ;;;; Planning
1214 (defun org-texinfo-planning (planning _contents info)
1215 "Transcode a PLANNING element from Org to Texinfo.
1216 CONTENTS is nil. INFO is a plist holding contextual
1217 information."
1218 (concat
1219 "@noindent"
1220 (mapconcat
1221 'identity
1222 (delq nil
1223 (list
1224 (let ((closed (org-element-property :closed planning)))
1225 (when closed
1226 (concat
1227 (format "@strong{%s} " org-closed-string)
1228 (format (plist-get info :texinfo-inactive-timestamp-format)
1229 (org-timestamp-translate closed)))))
1230 (let ((deadline (org-element-property :deadline planning)))
1231 (when deadline
1232 (concat
1233 (format "@strong{%s} " org-deadline-string)
1234 (format (plist-get info :texinfo-active-timestamp-format)
1235 (org-timestamp-translate deadline)))))
1236 (let ((scheduled (org-element-property :scheduled planning)))
1237 (when scheduled
1238 (concat
1239 (format "@strong{%s} " org-scheduled-string)
1240 (format (plist-get info :texinfo-active-timestamp-format)
1241 (org-timestamp-translate scheduled)))))))
1242 " ")
1243 "@*"))
1245 ;;;; Property Drawer
1247 (defun org-texinfo-property-drawer (_property-drawer contents _info)
1248 "Transcode a PROPERTY-DRAWER element from Org to Texinfo.
1249 CONTENTS holds the contents of the drawer. INFO is a plist
1250 holding contextual information."
1251 (and (org-string-nw-p contents)
1252 (format "@verbatim\n%s@end verbatim" contents)))
1254 ;;;; Quote Block
1256 (defun org-texinfo-quote-block (quote-block contents _info)
1257 "Transcode a QUOTE-BLOCK element from Org to Texinfo.
1258 CONTENTS holds the contents of the block. INFO is a plist
1259 holding contextual information."
1260 (let* ((title (org-element-property :name quote-block))
1261 (start-quote (concat "@quotation"
1262 (if title
1263 (format " %s" title)))))
1264 (format "%s\n%s@end quotation" start-quote contents)))
1266 ;;;; Radio Target
1268 (defun org-texinfo-radio-target (radio-target text info)
1269 "Transcode a RADIO-TARGET object from Org to Texinfo.
1270 TEXT is the text of the target. INFO is a plist holding
1271 contextual information."
1272 (format "@anchor{%s}%s"
1273 (org-export-get-reference radio-target info)
1274 text))
1276 ;;;; Section
1278 (defun org-texinfo-section (section contents info)
1279 "Transcode a SECTION element from Org to Texinfo.
1280 CONTENTS holds the contents of the section. INFO is a plist
1281 holding contextual information."
1282 (org-trim
1283 (concat contents
1284 "\n"
1285 (let ((parent (org-export-get-parent-headline section)))
1286 (and parent (org-texinfo-make-menu parent info))))))
1288 ;;;; Special Block
1290 (defun org-texinfo-special-block (special-block contents _info)
1291 "Transcode a SPECIAL-BLOCK element from Org to Texinfo.
1292 CONTENTS holds the contents of the block. INFO is a plist used
1293 as a communication channel."
1294 (let ((opt (org-export-read-attribute :attr_texinfo special-block :options))
1295 (type (org-element-property :type special-block)))
1296 (format "@%s%s\n%s@end %s"
1297 type
1298 (if opt (concat " " opt) opt)
1299 (or contents "")
1300 type)))
1302 ;;;; Src Block
1304 (defun org-texinfo-src-block (src-block _contents info)
1305 "Transcode a SRC-BLOCK element from Org to Texinfo.
1306 CONTENTS holds the contents of the item. INFO is a plist holding
1307 contextual information."
1308 (let* ((lisp (org-string-match-p "lisp"
1309 (org-element-property :language src-block)))
1310 (code (org-texinfo--sanitize-content
1311 (org-export-format-code-default src-block info)))
1312 (value (format
1313 (if lisp "@lisp\n%s@end lisp" "@example\n%s@end example")
1314 code))
1315 (caption (org-export-get-caption src-block))
1316 (shortcaption (org-export-get-caption src-block t)))
1317 (if (not (or caption shortcaption)) value
1318 (org-texinfo--wrap-float value
1319 info
1320 (org-export-translate "Listing" :utf-8 info)
1321 (org-export-get-reference src-block info)
1322 caption
1323 shortcaption))))
1325 ;;;; Statistics Cookie
1327 (defun org-texinfo-statistics-cookie (statistics-cookie _contents _info)
1328 "Transcode a STATISTICS-COOKIE object from Org to Texinfo.
1329 CONTENTS is nil. INFO is a plist holding contextual information."
1330 (org-element-property :value statistics-cookie))
1332 ;;;; Subscript
1334 (defun org-texinfo-subscript (_subscript contents _info)
1335 "Transcode a SUBSCRIPT object from Org to Texinfo.
1336 CONTENTS is the contents of the object. INFO is a plist holding
1337 contextual information."
1338 (format "@math{_%s}" contents))
1340 ;;;; Superscript
1342 (defun org-texinfo-superscript (_superscript contents _info)
1343 "Transcode a SUPERSCRIPT object from Org to Texinfo.
1344 CONTENTS is the contents of the object. INFO is a plist holding
1345 contextual information."
1346 (format "@math{^%s}" contents))
1348 ;;;; Table
1350 (defun org-texinfo-table (table contents info)
1351 "Transcode a TABLE element from Org to Texinfo.
1352 CONTENTS is the contents of the table. INFO is a plist holding
1353 contextual information."
1354 (if (eq (org-element-property :type table) 'table.el)
1355 (format "@verbatim\n%s@end verbatim"
1356 (org-element-normalize-string
1357 (org-element-property :value table)))
1358 (let* ((col-width (org-export-read-attribute :attr_texinfo table :columns))
1359 (columns
1360 (if col-width (format "@columnfractions %s" col-width)
1361 (org-texinfo-table-column-widths table info)))
1362 (caption (org-export-get-caption table))
1363 (shortcaption (org-export-get-caption table t))
1364 (table-str (format "@multitable %s\n%s@end multitable"
1365 columns
1366 contents)))
1367 (if (not (or caption shortcaption)) table-str
1368 (org-texinfo--wrap-float table-str
1369 info
1370 (org-export-translate "Table" :utf-8 info)
1371 (org-export-get-reference table info)
1372 caption
1373 shortcaption)))))
1375 (defun org-texinfo-table-column-widths (table info)
1376 "Determine the largest table cell in each column to process alignment.
1377 TABLE is the table element to transcode. INFO is a plist used as
1378 a communication channel."
1379 (let ((widths (make-vector (cdr (org-export-table-dimensions table info)) 0)))
1380 (org-element-map table 'table-row
1381 (lambda (row)
1382 (let ((idx 0))
1383 (org-element-map row 'table-cell
1384 (lambda (cell)
1385 ;; Length of the cell in the original buffer is only an
1386 ;; approximation of the length of the cell in the
1387 ;; output. It can sometimes fail (e.g. it considers
1388 ;; "/a/" being larger than "ab").
1389 (let ((w (- (org-element-property :contents-end cell)
1390 (org-element-property :contents-begin cell))))
1391 (aset widths idx (max w (aref widths idx))))
1392 (incf idx))
1393 info)))
1394 info)
1395 (format "{%s}" (mapconcat (lambda (w) (make-string w ?a)) widths "} {"))))
1397 ;;;; Table Cell
1399 (defun org-texinfo-table-cell (table-cell contents info)
1400 "Transcode a TABLE-CELL element from Org to Texinfo.
1401 CONTENTS is the cell contents. INFO is a plist used as
1402 a communication channel."
1403 (concat
1404 (let ((scientific-notation
1405 (plist-get info :texinfo-table-scientific-notation)))
1406 (if (and contents
1407 scientific-notation
1408 (string-match orgtbl-exp-regexp contents))
1409 ;; Use appropriate format string for scientific notation.
1410 (format scientific-notation
1411 (match-string 1 contents)
1412 (match-string 2 contents))
1413 contents))
1414 (when (org-export-get-next-element table-cell info) "\n@tab ")))
1416 ;;;; Table Row
1418 (defun org-texinfo-table-row (table-row contents info)
1419 "Transcode a TABLE-ROW element from Org to Texinfo.
1420 CONTENTS is the contents of the row. INFO is a plist used as
1421 a communication channel."
1422 ;; Rules are ignored since table separators are deduced from
1423 ;; borders of the current row.
1424 (when (eq (org-element-property :type table-row) 'standard)
1425 (let ((rowgroup-tag
1426 (if (and (= 1 (org-export-table-row-group table-row info))
1427 (org-export-table-has-header-p
1428 (org-export-get-parent-table table-row) info))
1429 "@headitem "
1430 "@item ")))
1431 (concat rowgroup-tag contents "\n"))))
1433 ;;;; Target
1435 (defun org-texinfo-target (target _contents info)
1436 "Transcode a TARGET object from Org to Texinfo.
1437 CONTENTS is nil. INFO is a plist holding contextual
1438 information."
1439 (format "@anchor{%s}" (org-export-get-reference target info)))
1441 ;;;; Timestamp
1443 (defun org-texinfo-timestamp (timestamp _contents info)
1444 "Transcode a TIMESTAMP object from Org to Texinfo.
1445 CONTENTS is nil. INFO is a plist holding contextual
1446 information."
1447 (let ((value (org-texinfo-plain-text
1448 (org-timestamp-translate timestamp) info)))
1449 (case (org-element-property :type timestamp)
1450 ((active active-range)
1451 (format (plist-get info :texinfo-active-timestamp-format) value))
1452 ((inactive inactive-range)
1453 (format (plist-get info :texinfo-inactive-timestamp-format) value))
1454 (t (format (plist-get info :texinfo-diary-timestamp-format) value)))))
1456 ;;;; Verbatim
1458 (defun org-texinfo-verbatim (verbatim _contents info)
1459 "Transcode a VERBATIM object from Org to Texinfo.
1460 CONTENTS is nil. INFO is a plist used as a communication
1461 channel."
1462 (org-texinfo--text-markup
1463 (org-element-property :value verbatim) 'verbatim info))
1465 ;;;; Verse Block
1467 (defun org-texinfo-verse-block (_verse-block contents _info)
1468 "Transcode a VERSE-BLOCK element from Org to Texinfo.
1469 CONTENTS is verse block contents. INFO is a plist holding
1470 contextual information."
1471 (format "@display\n%s@end display" contents))
1474 ;;; Interactive functions
1476 (defun org-texinfo-export-to-texinfo
1477 (&optional async subtreep visible-only body-only ext-plist)
1478 "Export current buffer to a Texinfo file.
1480 If narrowing is active in the current buffer, only export its
1481 narrowed part.
1483 If a region is active, export that region.
1485 A non-nil optional argument ASYNC means the process should happen
1486 asynchronously. The resulting file should be accessible through
1487 the `org-export-stack' interface.
1489 When optional argument SUBTREEP is non-nil, export the sub-tree
1490 at point, extracting information from the headline properties
1491 first.
1493 When optional argument VISIBLE-ONLY is non-nil, don't export
1494 contents of hidden elements.
1496 When optional argument BODY-ONLY is non-nil, only write code
1497 between \"\\begin{document}\" and \"\\end{document}\".
1499 EXT-PLIST, when provided, is a property list with external
1500 parameters overriding Org default settings, but still inferior to
1501 file-local settings.
1503 Return output file's name."
1504 (interactive)
1505 (let ((outfile (org-export-output-file-name ".texi" subtreep))
1506 (org-export-coding-system org-texinfo-coding-system))
1507 (org-export-to-file 'texinfo outfile
1508 async subtreep visible-only body-only ext-plist)))
1510 (defun org-texinfo-export-to-info
1511 (&optional async subtreep visible-only body-only ext-plist)
1512 "Export current buffer to Texinfo then process through to INFO.
1514 If narrowing is active in the current buffer, only export its
1515 narrowed part.
1517 If a region is active, export that region.
1519 A non-nil optional argument ASYNC means the process should happen
1520 asynchronously. The resulting file should be accessible through
1521 the `org-export-stack' interface.
1523 When optional argument SUBTREEP is non-nil, export the sub-tree
1524 at point, extracting information from the headline properties
1525 first.
1527 When optional argument VISIBLE-ONLY is non-nil, don't export
1528 contents of hidden elements.
1530 When optional argument BODY-ONLY is non-nil, only write code
1531 between \"\\begin{document}\" and \"\\end{document}\".
1533 EXT-PLIST, when provided, is a property list with external
1534 parameters overriding Org default settings, but still inferior to
1535 file-local settings.
1537 When optional argument PUB-DIR is set, use it as the publishing
1538 directory.
1540 Return INFO file's name."
1541 (interactive)
1542 (let ((outfile (org-export-output-file-name ".texi" subtreep))
1543 (org-export-coding-system org-texinfo-coding-system))
1544 (org-export-to-file 'texinfo outfile
1545 async subtreep visible-only body-only ext-plist
1546 (lambda (file) (org-texinfo-compile file)))))
1548 ;;;###autoload
1549 (defun org-texinfo-publish-to-texinfo (plist filename pub-dir)
1550 "Publish an org file to Texinfo.
1552 FILENAME is the filename of the Org file to be published. PLIST
1553 is the property list for the given project. PUB-DIR is the
1554 publishing directory.
1556 Return output file name."
1557 (org-publish-org-to 'texinfo filename ".texi" plist pub-dir))
1559 ;;;###autoload
1560 (defun org-texinfo-convert-region-to-texinfo ()
1561 "Assume the current region has org-mode syntax, and convert it to Texinfo.
1562 This can be used in any buffer. For example, you can write an
1563 itemized list in org-mode syntax in an Texinfo buffer and use
1564 this command to convert it."
1565 (interactive)
1566 (org-export-replace-region-by 'texinfo))
1568 (defun org-texinfo-compile (file)
1569 "Compile a texinfo file.
1571 FILE is the name of the file being compiled. Processing is done
1572 through the command specified in `org-texinfo-info-process',
1573 which see. Output is redirected to \"*Org INFO Texinfo Output*\"
1574 buffer.
1576 Return INFO file name or an error if it couldn't be produced."
1577 (message "Processing Texinfo file %s..." file)
1578 (let* ((log-name "*Org INFO Texinfo Output*")
1579 (log (get-buffer-create log-name))
1580 (output
1581 (org-compile-file file org-texinfo-info-process "info"
1582 (format "See %S for details" log-name)
1583 log)))
1584 (when org-texinfo-remove-logfiles
1585 (let ((base (file-name-sans-extension output)))
1586 (dolist (ext org-texinfo-logfiles-extensions)
1587 (let ((file (concat base "." ext)))
1588 (when (file-exists-p file) (delete-file file))))))
1589 (message "Process completed.")
1590 output))
1593 (provide 'ox-texinfo)
1595 ;; Local variables:
1596 ;; generated-autoload-file: "org-loaddefs.el"
1597 ;; End:
1599 ;;; ox-texinfo.el ends here