ox-texinfo.el (78346B)
1 ;;; ox-texinfo.el --- Texinfo Backend for Org Export Engine -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2012-2024 Free Software Foundation, Inc. 4 ;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com> 5 ;; Keywords: outlines, hypermedia, calendar, text 6 7 ;; This file is part of GNU Emacs. 8 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. 13 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. 18 19 ;; You should have received a copy of the GNU General Public License 20 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 21 22 ;;; Commentary: 23 ;; 24 ;; See Org manual for details. 25 26 ;;; Code: 27 28 (require 'org-macs) 29 (org-assert-version) 30 31 (require 'cl-lib) 32 (require 'ox) 33 (require 'org-element-ast) 34 35 (eval-when-compile (require 'subr-x)) 36 37 (defvar orgtbl-exp-regexp) 38 (defvar org-texinfo-supports-math--cache) 39 40 41 ;;; Define Backend 42 43 (org-export-define-backend 'texinfo 44 '((bold . org-texinfo-bold) 45 (center-block . org-texinfo-center-block) 46 (clock . org-texinfo-clock) 47 (code . org-texinfo-code) 48 (drawer . org-texinfo-drawer) 49 (dynamic-block . org-texinfo-dynamic-block) 50 (entity . org-texinfo-entity) 51 (example-block . org-texinfo-example-block) 52 (export-block . org-texinfo-export-block) 53 (export-snippet . org-texinfo-export-snippet) 54 (fixed-width . org-texinfo-fixed-width) 55 (footnote-definition . org-texinfo-footnote-definition) 56 (footnote-reference . org-texinfo-footnote-reference) 57 (headline . org-texinfo-headline) 58 (inline-src-block . org-texinfo-inline-src-block) 59 (inlinetask . org-texinfo-inlinetask) 60 (italic . org-texinfo-italic) 61 (item . org-texinfo-item) 62 (keyword . org-texinfo-keyword) 63 (latex-environment . org-texinfo-latex-environment) 64 (latex-fragment . org-texinfo-latex-fragment) 65 (line-break . org-texinfo-line-break) 66 (link . org-texinfo-link) 67 (node-property . org-texinfo-node-property) 68 (paragraph . org-texinfo-paragraph) 69 (plain-list . org-texinfo-plain-list) 70 (plain-text . org-texinfo-plain-text) 71 (planning . org-texinfo-planning) 72 (property-drawer . org-texinfo-property-drawer) 73 (quote-block . org-texinfo-quote-block) 74 (radio-target . org-texinfo-radio-target) 75 (section . org-texinfo-section) 76 (special-block . org-texinfo-special-block) 77 (src-block . org-texinfo-src-block) 78 (statistics-cookie . org-texinfo-statistics-cookie) 79 (strike-through . org-texinfo-strike-through) 80 (subscript . org-texinfo-subscript) 81 (superscript . org-texinfo-superscript) 82 (table . org-texinfo-table) 83 (table-cell . org-texinfo-table-cell) 84 (table-row . org-texinfo-table-row) 85 (target . org-texinfo-target) 86 (template . org-texinfo-template) 87 (timestamp . org-texinfo-timestamp) 88 (underline . org-texinfo-underline) 89 (verbatim . org-texinfo-verbatim) 90 (verse-block . org-texinfo-verse-block)) 91 :filters-alist 92 '((:filter-headline . org-texinfo--filter-section-blank-lines) 93 (:filter-parse-tree . (org-texinfo--normalize-headlines 94 org-texinfo--separate-definitions)) 95 (:filter-section . org-texinfo--filter-section-blank-lines) 96 (:filter-final-output . org-texinfo--untabify)) 97 :menu-entry 98 '(?i "Export to Texinfo" 99 ((?t "As TEXI file" org-texinfo-export-to-texinfo) 100 (?i "As INFO file" org-texinfo-export-to-info) 101 (?o "As INFO file and open" 102 (lambda (a s v b) 103 (if a (org-texinfo-export-to-info t s v b) 104 (org-open-file (org-texinfo-export-to-info nil s v b))))))) 105 :options-alist 106 '((:texinfo-filename "TEXINFO_FILENAME" nil nil t) 107 (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t) 108 (:texinfo-header "TEXINFO_HEADER" nil nil newline) 109 (:texinfo-post-header "TEXINFO_POST_HEADER" nil nil newline) 110 (:subtitle "SUBTITLE" nil nil parse) 111 (:subauthor "SUBAUTHOR" nil nil newline) 112 (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t) 113 (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t) ;Obsolete. 114 (:texinfo-dirname "TEXINFO_DIR_NAME" nil nil t) 115 (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t) 116 (:texinfo-printed-title "TEXINFO_PRINTED_TITLE" nil nil t) 117 ;; Other variables. 118 (:texinfo-classes nil nil org-texinfo-classes) 119 (:texinfo-format-headline-function nil nil org-texinfo-format-headline-function) 120 (:texinfo-node-description-column nil nil org-texinfo-node-description-column) 121 (:texinfo-active-timestamp-format nil nil org-texinfo-active-timestamp-format) 122 (:texinfo-inactive-timestamp-format nil nil org-texinfo-inactive-timestamp-format) 123 (:texinfo-diary-timestamp-format nil nil org-texinfo-diary-timestamp-format) 124 (:texinfo-link-with-unknown-path-format nil nil org-texinfo-link-with-unknown-path-format) 125 (:texinfo-tables-verbatim nil nil org-texinfo-tables-verbatim) 126 (:texinfo-table-scientific-notation nil nil org-texinfo-table-scientific-notation) 127 (:texinfo-table-default-markup nil nil org-texinfo-table-default-markup) 128 (:texinfo-text-markup-alist nil nil org-texinfo-text-markup-alist) 129 (:texinfo-format-drawer-function nil nil org-texinfo-format-drawer-function) 130 (:texinfo-format-inlinetask-function nil nil org-texinfo-format-inlinetask-function) 131 (:texinfo-compact-itemx nil "compact-itemx" org-texinfo-compact-itemx) 132 ;; Redefine regular options. 133 (:with-latex nil "tex" org-texinfo-with-latex))) 134 135 136 ;;; User Configurable Variables 137 138 (defgroup org-export-texinfo nil 139 "Options for exporting Org mode files to Texinfo." 140 :tag "Org Export Texinfo" 141 :version "24.4" 142 :package-version '(Org . "8.0") 143 :group 'org-export) 144 145 ;;;; Preamble 146 147 (defcustom org-texinfo-coding-system nil 148 "Default document encoding for Texinfo output. 149 150 If nil it will default to `buffer-file-coding-system'." 151 :type 'coding-system) 152 153 (defcustom org-texinfo-default-class "info" 154 "The default Texinfo class." 155 :type '(string :tag "Texinfo class")) 156 157 (defcustom org-texinfo-classes 158 '(("info" 159 "@documentencoding AUTO\n@documentlanguage AUTO" 160 ("@chapter %s" "@unnumbered %s" "@chapheading %s" "@appendix %s") 161 ("@section %s" "@unnumberedsec %s" "@heading %s" "@appendixsec %s") 162 ("@subsection %s" "@unnumberedsubsec %s" "@subheading %s" 163 "@appendixsubsec %s") 164 ("@subsubsection %s" "@unnumberedsubsubsec %s" "@subsubheading %s" 165 "@appendixsubsubsec %s"))) 166 "Alist of Texinfo classes and associated header and structure. 167 If #+TEXINFO_CLASS is set in the buffer, use its value and the 168 associated information. Here is the structure of a class 169 definition: 170 171 (class-name 172 header-string 173 (numbered-1 unnumbered-1 unnumbered-no-toc-1 appendix-1) 174 (numbered-2 unnumbered-2 unnumbered-no-toc-2 appendix-2) 175 ...) 176 177 178 The header string 179 ----------------- 180 181 The header string is inserted in the header of the generated 182 document, right after \"@setfilename\" and \"@settitle\" 183 commands. 184 185 If it contains the special string 186 187 \"@documentencoding AUTO\" 188 189 \"AUTO\" will be replaced with an appropriate coding system. See 190 `org-texinfo-coding-system' for more information. Likewise, if 191 the string contains the special string 192 193 \"@documentlanguage AUTO\" 194 195 \"AUTO\" will be replaced with the language defined in the 196 buffer, through #+LANGUAGE keyword, or globally, with 197 `org-export-default-language', which see. 198 199 200 The sectioning structure 201 ------------------------ 202 203 The sectioning structure of the class is given by the elements 204 following the header string. For each sectioning level, a number 205 of strings is specified. A %s formatter is mandatory in each 206 section string and will be replaced by the title of the section." 207 :version "27.1" 208 :package-version '(Org . "9.2") 209 :type '(repeat 210 (list (string :tag "Texinfo class") 211 (string :tag "Texinfo header") 212 (repeat :tag "Levels" :inline t 213 (choice 214 (list :tag "Heading" 215 (string :tag " numbered") 216 (string :tag " unnumbered") 217 (string :tag "unnumbered-no-toc") 218 (string :tag " appendix"))))))) 219 220 ;;;; Headline 221 222 (defcustom org-texinfo-format-headline-function 223 'org-texinfo-format-headline-default-function 224 "Function to format headline text. 225 226 This function will be called with 5 arguments: 227 TODO the todo keyword (string or nil). 228 TODO-TYPE the type of todo (symbol: `todo', `done', nil) 229 PRIORITY the priority of the headline (integer or nil) 230 TEXT the main headline text (string). 231 TAGS the tags as a list of strings (list of strings or nil). 232 233 The function result will be used in the section format string." 234 :type 'function 235 :version "26.1" 236 :package-version '(Org . "8.3")) 237 238 ;;;; Node listing (menu) 239 240 (defcustom org-texinfo-node-description-column 32 241 "Column at which to start the description in the node listings. 242 If a node title is greater than this length, the description will 243 be placed after the end of the title." 244 :type 'integer) 245 246 ;;;; Timestamps 247 248 (defcustom org-texinfo-active-timestamp-format "@emph{%s}" 249 "A printf format string to be applied to active timestamps." 250 :type 'string) 251 252 (defcustom org-texinfo-inactive-timestamp-format "@emph{%s}" 253 "A printf format string to be applied to inactive timestamps." 254 :type 'string) 255 256 (defcustom org-texinfo-diary-timestamp-format "@emph{%s}" 257 "A printf format string to be applied to diary timestamps." 258 :type 'string) 259 260 ;;;; Links 261 262 (defcustom org-texinfo-link-with-unknown-path-format "@indicateurl{%s}" 263 "Format string for links with unknown path type." 264 :type 'string) 265 266 ;;;; Tables 267 268 (defcustom org-texinfo-tables-verbatim nil 269 "When non-nil, tables are exported verbatim." 270 :type 'boolean) 271 272 (defcustom org-texinfo-table-scientific-notation nil 273 "Format string to display numbers in scientific notation. 274 275 The format should have \"%s\" twice, for mantissa and exponent 276 \(i.e. \"%s\\\\times10^{%s}\"). 277 278 When nil, no transformation is made." 279 :type '(choice 280 (string :tag "Format string") 281 (const :tag "No formatting" nil))) 282 283 (defcustom org-texinfo-table-default-markup "@asis" 284 "Default markup for first column in two-column tables. 285 286 This should an indicating command, e.g., \"@code\", \"@kbd\" or 287 \"@samp\". 288 289 It can be overridden locally using the \":indic\" attribute." 290 :type 'string 291 :version "26.1" 292 :package-version '(Org . "9.1") 293 :safe #'stringp) 294 295 ;;;; Text markup 296 297 (defcustom org-texinfo-text-markup-alist '((bold . "@strong{%s}") 298 (code . code) 299 (italic . "@emph{%s}") 300 (verbatim . samp)) 301 "Alist of Texinfo expressions to convert text markup. 302 303 The key must be a symbol among `bold', `code', `italic', 304 `strike-through', `underscore' and `verbatim'. The value is 305 a formatting string to wrap fontified text with. 306 307 Value can also be set to the following symbols: `verb', `samp' 308 and `code'. With the first one, Org uses \"@verb\" to create 309 a format string and selects a delimiter character that isn't in 310 the string. For the other two, Org uses \"@samp\" or \"@code\" 311 to typeset and protects special characters. 312 313 When no association is found for a given markup, text is returned 314 as-is." 315 :version "26.1" 316 :package-version '(Org . "9.1") 317 :type 'alist 318 :options '(bold code italic strike-through underscore verbatim)) 319 320 ;;;; Drawers 321 322 (defcustom org-texinfo-format-drawer-function (lambda (_name contents) contents) 323 "Function called to format a drawer in Texinfo code. 324 325 The function must accept two parameters: 326 NAME the drawer name, like \"LOGBOOK\" 327 CONTENTS the contents of the drawer. 328 329 The function should return the string to be exported. 330 331 The default function simply returns the value of CONTENTS." 332 :version "24.4" 333 :package-version '(Org . "8.2") 334 :type 'function) 335 336 ;;;; Inlinetasks 337 338 (defcustom org-texinfo-format-inlinetask-function 339 'org-texinfo-format-inlinetask-default-function 340 "Function called to format an inlinetask in Texinfo code. 341 342 The function must accept six parameters: 343 TODO the todo keyword, as a string 344 TODO-TYPE the todo type, a symbol among `todo', `done' and nil. 345 PRIORITY the inlinetask priority, as a string 346 NAME the inlinetask name, as a string. 347 TAGS the inlinetask tags, as a list of strings. 348 CONTENTS the contents of the inlinetask, as a string. 349 350 The function should return the string to be exported." 351 :type 'function) 352 353 ;;;; LaTeX 354 355 (defcustom org-texinfo-with-latex (and org-export-with-latex 'detect) 356 "When non-nil, the Texinfo exporter attempts to process LaTeX math. 357 358 When set to t, the exporter will process LaTeX environments and 359 fragments as Texinfo \"@displaymath\" and \"@math\" commands 360 respectively. Alternatively, when set to `detect', the exporter 361 does so only if the installed version of Texinfo supports the 362 necessary commands." 363 :package-version '(Org . "9.6") 364 :type '(choice 365 (const :tag "Detect" detect) 366 (const :tag "Yes" t) 367 (const :tag "No" nil))) 368 369 ;;;; Itemx 370 371 (defcustom org-texinfo-compact-itemx nil 372 "Non-nil means certain items in description list become `@itemx'. 373 374 If this is non-nil and an item in a description list has no 375 body but is followed by another item, then the second item is 376 transcoded to `@itemx'. See info node `(org)Plain lists in 377 Texinfo export' for how to enable this for individual lists." 378 :package-version '(Org . "9.6") 379 :type 'boolean 380 :safe t) 381 382 ;;;; Compilation 383 384 (defcustom org-texinfo-info-process '("makeinfo --no-split %f") 385 "Commands to process a Texinfo file to an INFO file. 386 387 This is a list of strings, each of them will be given to the 388 shell as a command. %f in the command will be replaced by the 389 relative file name, %F by the absolute file name, %b by the file 390 base name (i.e. without directory and extension parts), %o by the 391 base directory of the file and %O by the absolute file name of 392 the output file." 393 :version "26.1" 394 :package-version '(Org . "9.1") 395 :type '(repeat :tag "Shell command sequence" 396 (string :tag "Shell command"))) 397 398 (defcustom org-texinfo-logfiles-extensions 399 '("aux" "toc" "cp" "fn" "ky" "pg" "tp" "vr") 400 "The list of file extensions to consider as Texinfo logfiles. 401 The logfiles will be remove if `org-texinfo-remove-logfiles' is 402 non-nil." 403 :type '(repeat (string :tag "Extension"))) 404 405 (defcustom org-texinfo-remove-logfiles t 406 "Non-nil means remove the logfiles produced by compiling a Texinfo file. 407 By default, logfiles are files with these extensions: .aux, .toc, 408 .cp, .fn, .ky, .pg and .tp. To define the set of logfiles to remove, 409 set `org-texinfo-logfiles-extensions'." 410 :group 'org-export-latex 411 :type 'boolean) 412 413 ;;; Constants 414 415 (defconst org-texinfo-max-toc-depth 4 416 "Maximum depth for creation of detailed menu listings. 417 Beyond this depth, Texinfo will not recognize the nodes and will 418 cause errors. Left as a constant in case this value ever 419 changes.") 420 421 (defconst org-texinfo-supported-coding-systems 422 '("US-ASCII" "UTF-8" "ISO-8859-15" "ISO-8859-1" "ISO-8859-2" "koi8-r" "koi8-u") 423 "List of coding systems supported by Texinfo, as strings. 424 Specified coding system will be matched against these strings. 425 If two strings share the same prefix (e.g. \"ISO-8859-1\" and 426 \"ISO-8859-15\"), the most specific one has to be listed first.") 427 428 (defconst org-texinfo-inline-image-rules 429 (list (cons "file" 430 (regexp-opt '("eps" "pdf" "png" "jpg" "jpeg" "gif" "svg")))) 431 "Rules characterizing image files that can be inlined.") 432 433 (defvar org-texinfo--quoted-keys-regexp 434 (regexp-opt '("BS" "TAB" "RET" "ESC" "SPC" "DEL" 435 "LFD" "DELETE" "SHIFT" "Ctrl" "Meta" "Alt" 436 "Cmd" "Super" "UP" "LEFT" "RIGHT" "DOWN") 437 'words) 438 "Regexp matching keys that have to be quoted using @key{KEY}.") 439 440 (defconst org-texinfo--definition-command-alist 441 '(("deffn Command" . "Command") 442 ("defun" . "Function") 443 ("defmac" . "Macro") 444 ("defspec" . "Special Form") 445 ("defvar" . "Variable") 446 ("defopt" . "User Option") 447 (nil . "Key")) 448 "Alist mapping Texinfo definition commands to output in Info files.") 449 450 (defconst org-texinfo--definition-command-regexp 451 (format "\\`%s: \\(.+\\)" 452 (regexp-opt 453 (delq nil (mapcar #'cdr org-texinfo--definition-command-alist)) 454 t)) 455 "Regexp used to match definition commands in descriptive lists.") 456 457 458 ;;; Internal Functions 459 460 (defun org-texinfo--untabify (s _backend _info) 461 "Remove TAB characters in string S." 462 (replace-regexp-in-string "\t" (make-string tab-width ?\s) s)) 463 464 (defun org-texinfo--filter-section-blank-lines (headline _backend _info) 465 "Filter controlling number of blank lines after a section." 466 (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" "\n\n" headline)) 467 468 (defun org-texinfo--normalize-headlines (tree _backend info) 469 "Normalize headlines in TREE. 470 471 BACKEND is the symbol specifying backend used for export. 472 INFO is a plist used as a communication channel. 473 474 Make sure every headline in TREE contains a section, since those 475 are required to install a menu. Also put exactly one blank line 476 at the end of each section. 477 478 Return new tree." 479 (org-element-map tree 'headline 480 (lambda (hl) 481 (org-element-put-property hl :post-blank 1) 482 (let ((contents (org-element-contents hl))) 483 (when contents 484 (let ((first (org-element-map contents '(headline section) 485 #'identity info t))) 486 (unless (org-element-type-p first 'section) 487 (apply #'org-element-set-contents 488 hl 489 (org-element-create 'section `(:parent ,hl)) contents)))))) 490 info) 491 tree) 492 493 (defun org-texinfo--find-verb-separator (s) 494 "Return a character not used in string S. 495 This is used to choose a separator for constructs like \\verb." 496 (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}")) 497 (cl-loop for c across ll 498 when (not (string-match (regexp-quote (char-to-string c)) s)) 499 return (char-to-string c)))) 500 501 (defun org-texinfo--text-markup (text markup _info) 502 "Format TEXT depending on MARKUP text markup. 503 INFO is a plist used as a communication channel. See 504 `org-texinfo-text-markup-alist' for details." 505 (pcase (cdr (assq markup org-texinfo-text-markup-alist)) 506 (`nil text) ;no markup: return raw text 507 (`code (format "@code{%s}" (org-texinfo--sanitize-content text))) 508 (`samp (format "@samp{%s}" (org-texinfo--sanitize-content text))) 509 (`verb 510 (let ((separator (org-texinfo--find-verb-separator text))) 511 (format "@verb{%s%s%s}" separator text separator))) 512 ;; Else use format string. 513 (fmt (format fmt text)))) 514 515 (defun org-texinfo--get-node (datum info) 516 "Return node or anchor associated to DATUM. 517 DATUM is a headline, a radio-target or a target. INFO is a plist 518 used as a communication channel. The function guarantees the 519 node or anchor name is unique." 520 (let ((cache (plist-get info :texinfo-node-cache))) 521 (or (cdr (assq datum cache)) 522 (let* ((salt 0) 523 (basename 524 (org-texinfo--sanitize-node 525 (pcase (org-element-type datum) 526 (`headline 527 (org-texinfo--sanitize-title 528 (org-export-get-alt-title datum info) info)) 529 (`radio-target 530 (org-export-data (org-element-contents datum) info)) 531 (`target 532 (org-element-property :value datum)) 533 (_ 534 (or (org-element-property :name datum) 535 (org-export-get-reference datum info)))))) 536 (name basename)) 537 ;; Org exports deeper elements before their parents. If two 538 ;; node names collide -- e.g., they have the same title -- 539 ;; within the same hierarchy, the second one would get the 540 ;; smaller node name. This is counter-intuitive. 541 ;; Consequently, we ensure that every parent headline gets 542 ;; its node beforehand. As a recursive operation, this 543 ;; achieves the desired effect. 544 (let ((parent (org-element-lineage datum 'headline))) 545 (when (and parent (not (assq parent cache))) 546 (org-texinfo--get-node parent info) 547 (setq cache (plist-get info :texinfo-node-cache)))) 548 ;; Ensure NAME is unique and not reserved node name "Top", 549 ;; no matter what case is used. 550 (while (or (string-equal "Top" (capitalize name)) 551 (rassoc name cache)) 552 (setq name (concat basename (format " (%d)" (cl-incf salt))))) 553 (plist-put info :texinfo-node-cache (cons (cons datum name) cache)) 554 name)))) 555 556 (defun org-texinfo--sanitize-node (title) 557 "Bend string TITLE to node line requirements. 558 Trim string and collapse multiple whitespace characters as they 559 are not significant. Replace leading left parenthesis, when 560 followed by a right parenthesis, with a square bracket. Remove 561 periods, commas and colons." 562 (org-trim 563 (replace-regexp-in-string 564 "[ \t]+" " " 565 (replace-regexp-in-string 566 "[:,.]" "" 567 (replace-regexp-in-string "\\`(\\(.*?)\\)" "[\\1" title))))) 568 569 (defun org-texinfo--sanitize-title (title info) 570 "Make TITLE suitable as a section name. 571 TITLE is a string or a secondary string. INFO is the current 572 export state, as a plist." 573 (org-export-data-with-backend 574 title (org-export-toc-entry-backend 'texinfo) info)) 575 576 (defun org-texinfo--sanitize-content (text) 577 "Escape special characters in string TEXT. 578 Special characters are: @ { }" 579 (replace-regexp-in-string "[@{}]" "@\\&" text)) 580 581 (defun org-texinfo--wrap-float (value info &optional type label caption short) 582 "Wrap string VALUE within a @float command. 583 INFO is the current export state, as a plist. TYPE is float 584 type, as a string. LABEL is the cross reference label for the 585 float, as a string. CAPTION and SHORT are, respectively, the 586 caption and shortcaption used for the float, as secondary 587 strings (e.g., returned by `org-export-get-caption')." 588 (let* ((backend 589 (org-export-toc-entry-backend 'texinfo 590 (cons 'footnote-reference 591 (lambda (f c i) (org-export-with-backend 'texinfo f c i))))) 592 (short-backend 593 (org-export-toc-entry-backend 'texinfo 594 '(inline-src-block . ignore) 595 '(verbatim . ignore))) 596 (short-str 597 (if (and short caption) 598 (format "@shortcaption{%s}\n" 599 (org-export-data-with-backend short short-backend info)) 600 "")) 601 (caption-str 602 (if (or short caption) 603 (format "@caption{%s}\n" 604 (org-export-data-with-backend 605 (or caption short) 606 (if (equal short-str "") short-backend backend) 607 info)) 608 ""))) 609 (format "@float %s%s\n%s\n%s%s@end float" 610 type (if label (concat "," label) "") value caption-str short-str))) 611 612 (defun org-texinfo--sectioning-structure (info) 613 "Return sectioning structure used in the document. 614 INFO is a plist holding export options." 615 (let ((class (plist-get info :texinfo-class))) 616 (pcase (assoc class (plist-get info :texinfo-classes)) 617 (`(,_ ,_ . ,sections) sections) 618 (_ (user-error "Unknown Texinfo class: %S" class))))) 619 620 (defun org-texinfo--separate-definitions (tree _backend info) 621 "Split up descriptive lists in TREE that contain Texinfo definition commands. 622 INFO is a plist used as a communication channel. 623 Return new tree." 624 (org-element-map tree 'plain-list 625 (lambda (plain-list) 626 (when (eq (org-element-property :type plain-list) 'descriptive) 627 (let ((contents (org-element-contents plain-list)) 628 (items nil)) 629 (dolist (item contents) 630 (pcase-let ((`(,cmd . ,args) (org-texinfo--match-definition item))) 631 (cond 632 (cmd 633 (when items 634 (org-texinfo--split-plain-list plain-list (nreverse items)) 635 (setq items nil)) 636 (org-texinfo--split-definition plain-list item cmd args)) 637 (t 638 (when args 639 (org-texinfo--massage-key-item plain-list item args info)) 640 (push item items))))) 641 (unless (org-element-contents plain-list) 642 (org-element-extract plain-list))))) 643 info) 644 tree) 645 646 (defun org-texinfo--match-definition (item) 647 "Return a cons-cell if ITEM specifies a Texinfo definition command. 648 The car is the command and the cdr is its arguments." 649 (let ((tag (car-safe (org-element-property :tag item)))) 650 (and tag 651 (stringp tag) 652 (string-match org-texinfo--definition-command-regexp tag) 653 (pcase-let* 654 ((cmd (car (rassoc (match-string-no-properties 1 tag) 655 org-texinfo--definition-command-alist))) 656 (`(,cmd ,category) 657 (and cmd (save-match-data (split-string cmd " ")))) 658 (args (match-string-no-properties 2 tag))) 659 (cons cmd (if category (concat category " " args) args)))))) 660 661 (defun org-texinfo--split-definition (plain-list item cmd args) 662 "Insert a definition command before list PLAIN-LIST. 663 Replace list item ITEM with a special-block that inherits the 664 contents of ITEM and whose type and Texinfo attributes are 665 specified by CMD and ARGS." 666 (let ((contents (org-element-contents item))) 667 (org-element-insert-before 668 (apply #'org-element-create 'special-block 669 (list :type cmd 670 :attr_texinfo (list (format ":options %s" args)) 671 :post-blank (if contents 1 0)) 672 (mapc #'org-element-extract contents)) 673 plain-list)) 674 (org-element-extract item)) 675 676 (defun org-texinfo--split-plain-list (plain-list items) 677 "Insert a new plain list before the plain list PLAIN-LIST. 678 Remove ITEMS from PLAIN-LIST and use them as the contents of the 679 new plain list." 680 (org-element-insert-before 681 (apply #'org-element-create 'plain-list 682 (list :type 'descriptive 683 :attr_texinfo (org-element-property :attr_texinfo plain-list) 684 :post-blank 1) 685 (mapc #'org-element-extract items)) 686 plain-list)) 687 688 (defun org-texinfo--massage-key-item (plain-list item args info) 689 "In PLAIN-LIST modify ITEM based on ARGS. 690 691 Reformat ITEM's tag property and determine the arguments for the 692 `@findex' and `@kindex' commands for ITEM and store them in ITEM 693 using the `:findex' and `:kindex' properties. 694 695 If PLAIN-LIST is a description list whose `:compact' attribute is 696 non-nil and ITEM has no content but is followed by another item, 697 then store the `@findex' and `@kindex' values in the next item. 698 If the previous item stored its respective values in this item, 699 then move them to the next item. 700 701 INFO is a plist used as a communication channel." 702 (let ((key nil) 703 (cmd nil)) 704 (if (string-match (rx (+ " ") 705 "(" (group (+ (not (any "()")))) ")" 706 (* " ") 707 eos) 708 args) 709 (setq key (substring args 0 (match-beginning 0)) 710 cmd (match-string 1 args)) 711 (setq key args)) 712 (org-element-put-property 713 item :tag 714 (cons (org-export-raw-string (org-texinfo-kbd-macro key t)) 715 (and cmd `(" (" (code (:value ,cmd :post-blank 0)) ")")))) 716 (let ((findex (org-element-property :findex item)) 717 (kindex (org-element-property :kindex item)) 718 (next-item (org-export-get-next-element item nil)) 719 (mx (string-prefix-p "M-x " key))) 720 (when (and (not cmd) mx) 721 (setq cmd (substring key 4))) 722 (when (and cmd (not (member cmd findex))) 723 (setq findex (nconc findex (list cmd)))) 724 (unless mx 725 (setq kindex (nconc kindex (list key)))) 726 (cond 727 ((and next-item 728 (or (plist-get info :texinfo-compact-itemx) 729 (org-not-nil 730 (org-export-read-attribute :attr_texinfo plain-list :compact))) 731 (not (org-element-contents item)) 732 (eq 1 (org-element-post-blank item))) 733 (org-element-put-property next-item :findex findex) 734 (org-element-put-property next-item :kindex kindex) 735 (org-element-put-property item :findex nil) 736 (org-element-put-property item :kindex nil)) 737 (t 738 (org-element-set-contents 739 item 740 (nconc (mapcar (lambda (key) `(keyword (:key "KINDEX" :value ,key))) kindex) 741 (mapcar (lambda (cmd) `(keyword (:key "FINDEX" :value ,cmd))) findex) 742 (org-element-contents item)))))))) 743 744 ;;; Template 745 746 (defun org-texinfo-template (contents info) 747 "Return complete document string after Texinfo conversion. 748 CONTENTS is the transcoded contents string. INFO is a plist 749 holding export options." 750 (let ((title (org-export-data (plist-get info :title) info)) 751 ;; Copying data is the contents of the first headline in 752 ;; parse tree with a non-nil copying property. 753 (copying (org-element-map (plist-get info :parse-tree) 'headline 754 (lambda (hl) 755 (and (org-not-nil (org-element-property :COPYING hl)) 756 (org-element-contents hl))) 757 info t))) 758 (concat 759 "\\input texinfo @c -*- texinfo -*-\n" 760 "@c %**start of header\n" 761 (let ((file (or (org-strip-quotes (plist-get info :texinfo-filename)) 762 (let ((f (plist-get info :output-file))) 763 (and f (concat (file-name-sans-extension f) ".info")))))) 764 (and file (format "@setfilename %s\n" file))) 765 (format "@settitle %s\n" title) 766 ;; Insert class-defined header. 767 (org-element-normalize-string 768 (let ((header (nth 1 (assoc (plist-get info :texinfo-class) 769 org-texinfo-classes))) 770 (coding 771 (catch 'coding-system 772 (let ((case-fold-search t) 773 (name (symbol-name (or org-texinfo-coding-system 774 buffer-file-coding-system)))) 775 (dolist (system org-texinfo-supported-coding-systems "UTF-8") 776 (when (string-match-p (regexp-quote system) name) 777 (throw 'coding-system system)))))) 778 (language (plist-get info :language)) 779 (case-fold-search nil)) 780 ;; Auto coding system. 781 (replace-regexp-in-string 782 "^@documentencoding \\(AUTO\\)$" 783 coding 784 (replace-regexp-in-string 785 "^@documentlanguage \\(AUTO\\)$" language header t nil 1) 786 t nil 1))) 787 ;; Additional header options set by #+TEXINFO_HEADER. 788 (let ((texinfo-header (plist-get info :texinfo-header))) 789 (and texinfo-header (org-element-normalize-string texinfo-header))) 790 "@c %**end of header\n\n" 791 ;; Additional options set by #+TEXINFO_POST_HEADER. 792 (let ((texinfo-post-header (plist-get info :texinfo-post-header))) 793 (and texinfo-post-header 794 (org-element-normalize-string texinfo-post-header))) 795 ;; Copying. 796 (and copying 797 (format "@copying\n%s@end copying\n\n" 798 (org-element-normalize-string 799 (org-export-data copying info)))) 800 (let* ((dircat (or (plist-get info :texinfo-dircat) "Misc")) 801 (file (or (org-strip-quotes (plist-get info :texinfo-filename)) 802 (plist-get info :output-file))) 803 (file (if file (file-name-sans-extension file))) 804 (dn (or (plist-get info :texinfo-dirname) 805 (plist-get info :texinfo-dirtitle))) ;Obsolete name. 806 ;; Strip any terminating `.' from `dn'. 807 (dn (if (and dn (string-match "\\.\\'" dn)) (substring dn 0 -1) dn)) 808 ;; The direntry we need to produce has the shape: 809 ;; * DIRNAME: NODE. DESCRIPTION. 810 ;; where NODE is usually just `(FILENAME)', and where 811 ;; `* FILENAME.' is a shorthand for `* FILENAME: (FILENAME).' 812 (dirname 813 (cond 814 ((and dn (string-match 815 (eval-when-compile 816 (concat "\\`\\(?:" 817 "\\* \\(?1:.*\\)" ;Starts with `* ' or 818 "\\|\\(?1:.*(.*).*\\)" ;contains parens. 819 "\\)\\'")) 820 dn)) 821 ;; When users provide a `dn' that looks like a complete 822 ;; `* DIRNAME: (FILENAME).' thingy, we just trust them to 823 ;; provide something valid (just making sure it starts 824 ;; with `* ' and ends with `.'). 825 (format "* %s." (match-string 1 dn))) 826 ;; `dn' is presumed to be just the DIRNAME part, so generate 827 ;; either `* DIRNAME: (FILENAME).' or `* FILENAME.', whichever 828 ;; is shortest. 829 ((and dn (not (equal dn file))) 830 (format "* %s: (%s)." dn (or file dn))) 831 (t (format "* %s." file))))) 832 (concat "@dircategory " dircat "\n" 833 "@direntry\n" 834 (let ((dirdesc 835 (let ((desc (or (plist-get info :texinfo-dirdesc) 836 title))) 837 (cond ((not desc) nil) 838 ((string-suffix-p "." desc) desc) 839 (t (concat desc ".")))))) 840 (if dirdesc (format "%-23s %s" dirname dirdesc) dirname)) 841 "\n" 842 "@end direntry\n\n")) 843 ;; Title 844 "@finalout\n" 845 "@titlepage\n" 846 (when (plist-get info :with-title) 847 (concat 848 (format "@title %s\n" 849 (or (plist-get info :texinfo-printed-title) title "")) 850 (let ((subtitle (plist-get info :subtitle))) 851 (when subtitle 852 (format "@subtitle %s\n" 853 (org-export-data subtitle info)))))) 854 (when (plist-get info :with-author) 855 (concat 856 ;; Primary author. 857 (let ((author (org-string-nw-p 858 (org-export-data (plist-get info :author) info))) 859 (email (and (plist-get info :with-email) 860 (org-string-nw-p 861 (org-export-data (plist-get info :email) info))))) 862 (cond ((and author email) 863 (format "@author %s (@email{%s})\n" author email)) 864 (author (format "@author %s\n" author)) 865 (email (format "@author @email{%s}\n" email)))) 866 ;; Other authors. 867 (let ((subauthor (plist-get info :subauthor))) 868 (and subauthor 869 (org-element-normalize-string 870 (replace-regexp-in-string "^" "@author " subauthor)))))) 871 (and copying "@page\n@vskip 0pt plus 1filll\n@insertcopying\n") 872 "@end titlepage\n\n" 873 ;; Table of contents. 874 (and (plist-get info :with-toc) "@contents\n\n") 875 ;; Configure Top Node when not for TeX. Also include contents 876 ;; from the first section of the document. 877 "@ifnottex\n" 878 "@node Top\n" 879 (format "@top %s\n" title) 880 (let* ((first-section 881 (org-element-map (plist-get info :parse-tree) 'section 882 #'identity info t '(headline))) 883 (top-contents 884 (org-export-data (org-element-contents first-section) info))) 885 (and (org-string-nw-p top-contents) (concat "\n" top-contents))) 886 "@end ifnottex\n\n" 887 ;; Menu. 888 (org-texinfo-make-menu (plist-get info :parse-tree) info 'master) 889 "\n" 890 ;; Document's body. 891 contents "\n" 892 ;; Creator. 893 (and (plist-get info :with-creator) 894 (concat (plist-get info :creator) "\n")) 895 ;; Document end. 896 "@bye"))) 897 898 899 900 ;;; Transcode Functions 901 902 ;;;; Bold 903 904 (defun org-texinfo-bold (_bold contents info) 905 "Transcode BOLD from Org to Texinfo. 906 CONTENTS is the text with bold markup. INFO is a plist holding 907 contextual information." 908 (org-texinfo--text-markup contents 'bold info)) 909 910 ;;;; Center Block 911 912 (defun org-texinfo-center-block (_center-block contents _info) 913 "Transcode a CENTER-BLOCK element from Org to Texinfo. 914 CONTENTS holds the contents of the block. INFO is a plist used 915 as a communication channel." 916 (replace-regexp-in-string "\\(^\\).*?\\S-" "@center " contents nil nil 1)) 917 918 ;;;; Clock 919 920 (defun org-texinfo-clock (clock _contents info) 921 "Transcode a CLOCK element from Org to Texinfo. 922 CONTENTS is nil. INFO is a plist holding contextual 923 information." 924 (concat 925 "@noindent" 926 (format "@strong{%s} " org-clock-string) 927 (format (plist-get info :texinfo-inactive-timestamp-format) 928 (concat (org-timestamp-translate (org-element-property :value clock)) 929 (let ((time (org-element-property :duration clock))) 930 (and time (format " (%s)" time))))) 931 "@*")) 932 933 ;;;; Code 934 935 (defun org-texinfo-code (code _contents info) 936 "Transcode a CODE object from Org to Texinfo. 937 CONTENTS is nil. INFO is a plist used as a communication 938 channel." 939 (org-texinfo--text-markup (org-element-property :value code) 'code info)) 940 941 ;;;; Drawer 942 943 (defun org-texinfo-drawer (drawer contents info) 944 "Transcode a DRAWER element from Org to Texinfo. 945 CONTENTS holds the contents of the block. INFO is a plist 946 holding contextual information." 947 (let* ((name (org-element-property :drawer-name drawer)) 948 (output (funcall (plist-get info :texinfo-format-drawer-function) 949 name contents))) 950 output)) 951 952 ;;;; Dynamic Block 953 954 (defun org-texinfo-dynamic-block (_dynamic-block contents _info) 955 "Transcode a DYNAMIC-BLOCK element from Org to Texinfo. 956 CONTENTS holds the contents of the block. INFO is a plist 957 holding contextual information." 958 contents) 959 960 ;;;; Entity 961 962 (defun org-texinfo-entity (entity _contents _info) 963 "Transcode an ENTITY object from Org to Texinfo." 964 ;; Since there is not specific Texinfo entry in entities, use 965 ;; Texinfo-specific commands whenever possible, and fallback to 966 ;; UTF-8 otherwise. 967 (pcase (org-element-property :name entity) 968 ("AElig" "@AE{}") 969 ("aelig" "@ae{}") 970 ((or "bull" "bullet") "@bullet{}") 971 ("copy" "@copyright{}") 972 ("deg" "@textdegree{}") 973 ((or "dots" "hellip") "@dots{}") 974 ("equiv" "@equiv{}") 975 ((or "euro" "EUR") "@euro{}") 976 ((or "ge" "geq") "@geq{}") 977 ("laquo" "@guillemetleft{}") 978 ("iexcl" "@exclamdown{}") 979 ("imath" "@dotless{i}") 980 ("iquest" "@questiondown{}") 981 ("jmath" "@dotless{j}") 982 ((or "le" "leq") "@leq{}") 983 ("lsaquo" "@guilsinglleft{}") 984 ("mdash" "---") 985 ("minus" "@minus{}") 986 ("nbsp" "@tie{}") 987 ("ndash" "--") 988 ("OElig" "@OE{}") 989 ("oelig" "@oe{}") 990 ("ordf" "@ordf{}") 991 ("ordm" "@ordm{}") 992 ("pound" "@pound{}") 993 ("raquo" "@guillemetright{}") 994 ((or "rArr" "Rightarrow") "@result{}") 995 ("reg" "@registeredsymbol{}") 996 ((or "rightarrow" "to" "rarr") "@arrow{}") 997 ("rsaquo" "@guilsinglright{}") 998 ("thorn" "@th{}") 999 ("THORN" "@TH{}") 1000 ((and (pred (string-prefix-p "_")) name) ;spacing entities 1001 (format "@w{%s}" (substring name 1))) 1002 (_ (org-element-property :utf-8 entity)))) 1003 1004 ;;;; Example Block 1005 1006 (defun org-texinfo-example-block (example-block _contents info) 1007 "Transcode an EXAMPLE-BLOCK element from Org to Texinfo. 1008 CONTENTS is nil. INFO is a plist holding contextual 1009 information." 1010 (format "@example\n%s@end example" 1011 (org-texinfo--sanitize-content 1012 (org-export-format-code-default example-block info)))) 1013 1014 ;;; Export Block 1015 1016 (defun org-texinfo-export-block (export-block _contents _info) 1017 "Transcode a EXPORT-BLOCK element from Org to Texinfo. 1018 CONTENTS is nil. INFO is a plist holding contextual information." 1019 (when (string= (org-element-property :type export-block) "TEXINFO") 1020 (org-remove-indentation (org-element-property :value export-block)))) 1021 1022 ;;; Export Snippet 1023 1024 (defun org-texinfo-export-snippet (export-snippet _contents _info) 1025 "Transcode a EXPORT-SNIPPET object from Org to Texinfo. 1026 CONTENTS is nil. INFO is a plist holding contextual information." 1027 (when (eq (org-export-snippet-backend export-snippet) 'texinfo) 1028 (org-element-property :value export-snippet))) 1029 1030 ;;;; Fixed Width 1031 1032 (defun org-texinfo-fixed-width (fixed-width _contents _info) 1033 "Transcode a FIXED-WIDTH element from Org to Texinfo. 1034 CONTENTS is nil. INFO is a plist holding contextual information." 1035 (format "@example\n%s\n@end example" 1036 (org-remove-indentation 1037 (org-texinfo--sanitize-content 1038 (org-element-property :value fixed-width))))) 1039 1040 ;;;; Footnote Reference 1041 1042 (defun org-texinfo-footnote-reference (footnote _contents info) 1043 "Create a footnote reference for FOOTNOTE. 1044 1045 FOOTNOTE is the footnote to define. CONTENTS is nil. INFO is a 1046 plist holding contextual information." 1047 (let* ((contents (org-export-get-footnote-definition footnote info)) 1048 (data (org-export-data contents info))) 1049 (format "@footnote{%s}" 1050 ;; It is invalid to close a footnote on a line starting 1051 ;; with "@end". As a safety net, we leave a newline 1052 ;; character before the closing brace. However, when the 1053 ;; footnote ends with a paragraph, it is visually pleasing 1054 ;; to move the brace right after its end. 1055 (if (org-element-type-p (org-last contents) 'paragraph) 1056 (org-trim data) 1057 data)))) 1058 1059 ;;;; Headline 1060 1061 (defun org-texinfo-headline (headline contents info) 1062 "Transcode a HEADLINE element from Org to Texinfo. 1063 CONTENTS holds the contents of the headline. INFO is a plist 1064 holding contextual information." 1065 (cond 1066 ((org-element-property :footnote-section-p headline) nil) 1067 ((org-not-nil (org-export-get-node-property :COPYING headline t)) nil) 1068 (t 1069 (let* ((index (let ((i (org-export-get-node-property :INDEX headline t))) 1070 (and (member i '("cp" "fn" "ky" "pg" "tp" "vr")) i))) 1071 (numbered? (org-export-numbered-headline-p headline info)) 1072 (notoc? (org-export-excluded-from-toc-p headline info)) 1073 (command 1074 (and 1075 (not (org-export-low-level-p headline info)) 1076 (let ((sections (org-texinfo--sectioning-structure info))) 1077 (pcase (nth (1- (org-export-get-relative-level headline info)) 1078 sections) 1079 (`(,numbered ,unnumbered ,unnumbered-no-toc ,appendix) 1080 (cond 1081 ((org-not-nil 1082 (org-export-get-node-property :APPENDIX headline t)) 1083 appendix) 1084 (numbered? numbered) 1085 (index unnumbered) 1086 (notoc? unnumbered-no-toc) 1087 (t unnumbered))) 1088 (`nil nil) 1089 (_ (user-error "Invalid Texinfo class specification: %S" 1090 (plist-get info :texinfo-class))))))) 1091 (todo 1092 (and (plist-get info :with-todo-keywords) 1093 (let ((todo (org-element-property :todo-keyword headline))) 1094 (and todo (org-export-data todo info))))) 1095 (todo-type (and todo (org-element-property :todo-type headline))) 1096 (tags (and (plist-get info :with-tags) 1097 (org-export-get-tags headline info))) 1098 (priority (and (plist-get info :with-priority) 1099 (org-element-property :priority headline))) 1100 (text (org-texinfo--sanitize-title 1101 (org-element-property :title headline) info)) 1102 (full-text 1103 (funcall (plist-get info :texinfo-format-headline-function) 1104 todo todo-type priority text tags)) 1105 (contents 1106 (concat "\n" 1107 (if (org-string-nw-p contents) (concat "\n" contents) "") 1108 (and index (format "\n@printindex %s\n" index)))) 1109 (node (org-texinfo--get-node headline info))) 1110 (if (not command) 1111 (concat (and (org-export-first-sibling-p headline info) 1112 (format "@%s\n" (if numbered? 'enumerate 'itemize))) 1113 (format "@item\n@anchor{%s}%s\n" node full-text) 1114 contents 1115 (if (org-export-last-sibling-p headline info) 1116 (format "@end %s" (if numbered? 'enumerate 'itemize)) 1117 "\n")) 1118 (concat 1119 ;; Even if HEADLINE is using @subheading and al., leave an 1120 ;; anchor so cross-references in the Org document still work. 1121 (format (if notoc? "@anchor{%s}\n" "@node %s\n") node) 1122 (format command full-text) 1123 contents)))))) 1124 1125 (defun org-texinfo-format-headline-default-function 1126 (todo _todo-type priority text tags) 1127 "Default format function for a headline. 1128 See `org-texinfo-format-headline-function' for details." 1129 (concat (and todo (format "@strong{%s} " todo)) 1130 (and priority (format "@emph{#%s} " priority)) 1131 text 1132 (and tags (concat " " (org-make-tag-string tags))))) 1133 1134 ;;;; Inline Src Block 1135 1136 (defun org-texinfo-inline-src-block (inline-src-block _contents _info) 1137 "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo. 1138 CONTENTS holds the contents of the item. INFO is a plist holding 1139 contextual information." 1140 (format "@code{%s}" 1141 (org-texinfo--sanitize-content 1142 (org-element-property :value inline-src-block)))) 1143 1144 ;;;; Inlinetask 1145 1146 (defun org-texinfo-inlinetask (inlinetask contents info) 1147 "Transcode an INLINETASK element from Org to Texinfo. 1148 CONTENTS holds the contents of the block. INFO is a plist 1149 holding contextual information." 1150 (let ((title (org-export-data (org-element-property :title inlinetask) info)) 1151 (todo (and (plist-get info :with-todo-keywords) 1152 (let ((todo (org-element-property :todo-keyword inlinetask))) 1153 (and todo (org-export-data todo info))))) 1154 (todo-type (org-element-property :todo-type inlinetask)) 1155 (tags (and (plist-get info :with-tags) 1156 (org-export-get-tags inlinetask info))) 1157 (priority (and (plist-get info :with-priority) 1158 (org-element-property :priority inlinetask)))) 1159 (funcall (plist-get info :texinfo-format-inlinetask-function) 1160 todo todo-type priority title tags contents))) 1161 1162 (defun org-texinfo-format-inlinetask-default-function 1163 (todo _todo-type priority title tags contents) 1164 "Default format function for inlinetasks. 1165 See `org-texinfo-format-inlinetask-function' for details." 1166 (let ((full-title 1167 (concat (when todo (format "@strong{%s} " todo)) 1168 (when priority (format "#%c " priority)) 1169 title 1170 (when tags (org-make-tag-string tags))))) 1171 (format "@center %s\n\n%s\n" full-title contents))) 1172 1173 ;;;; Italic 1174 1175 (defun org-texinfo-italic (_italic contents info) 1176 "Transcode ITALIC from Org to Texinfo. 1177 CONTENTS is the text with italic markup. INFO is a plist holding 1178 contextual information." 1179 (org-texinfo--text-markup contents 'italic info)) 1180 1181 ;;;; Item 1182 1183 (defun org-texinfo-item (item contents info) 1184 "Transcode an ITEM element from Org to Texinfo. 1185 CONTENTS holds the contents of the item. INFO is a plist holding 1186 contextual information." 1187 (let* ((tag (org-element-property :tag item)) 1188 (plain-list (org-element-parent item)) 1189 (compact (and (eq (org-element-property :type plain-list) 'descriptive) 1190 (or (plist-get info :texinfo-compact-itemx) 1191 (org-not-nil (org-export-read-attribute 1192 :attr_texinfo plain-list :compact))))) 1193 (previous-item nil)) 1194 (when (and compact 1195 (org-export-get-next-element item info) 1196 (not (org-element-contents item)) 1197 (eq 1 (org-element-post-blank item))) 1198 (org-element-put-property item :post-blank 0)) 1199 (if (and compact 1200 (setq previous-item (org-export-get-previous-element item info)) 1201 (not (org-element-contents previous-item)) 1202 (eq 0 (org-element-post-blank previous-item))) 1203 (format "@itemx%s\n%s" 1204 (if tag (concat " " (org-export-data tag info)) "") 1205 (or contents "")) 1206 (let* ((split (org-string-nw-p (org-export-read-attribute 1207 :attr_texinfo plain-list :sep))) 1208 (items (and tag 1209 (let ((tag (org-export-data tag info))) 1210 (if split 1211 (split-string tag (regexp-quote split) 1212 t "[ \t\n]+") 1213 (list tag)))))) 1214 (format "%s\n%s" 1215 (pcase items 1216 (`nil "@item") 1217 (`(,item) (concat "@item " item)) 1218 (`(,item . ,items) 1219 (concat "@item " item "\n" 1220 (mapconcat (lambda (i) (concat "@itemx " i)) 1221 items 1222 "\n")))) 1223 (or contents "")))))) 1224 1225 ;;;; Keyword 1226 1227 (defun org-texinfo-keyword (keyword _contents info) 1228 "Transcode a KEYWORD element from Org to Texinfo. 1229 CONTENTS is nil. INFO is a plist holding contextual information." 1230 (let ((value (org-element-property :value keyword))) 1231 (pcase (org-element-property :key keyword) 1232 ("TEXINFO" value) 1233 ("CINDEX" (format "@cindex %s" value)) 1234 ("FINDEX" (format "@findex %s" value)) 1235 ("KINDEX" (format "@kindex %s" value)) 1236 ("PINDEX" (format "@pindex %s" value)) 1237 ("TINDEX" (format "@tindex %s" value)) 1238 ("VINDEX" (format "@vindex %s" value)) 1239 ("TOC" 1240 (cond ((string-match-p "\\<tables\\>" value) 1241 (concat "@listoffloats " 1242 (org-export-translate "Table" :utf-8 info))) 1243 ((string-match-p "\\<listings\\>" value) 1244 (concat "@listoffloats " 1245 (org-export-translate "Listing" :utf-8 info)))))))) 1246 1247 ;;;; LaTeX Environment 1248 1249 (defun org-texinfo-latex-environment (environment _contents info) 1250 "Transcode a LaTeX ENVIRONMENT from Org to Texinfo. 1251 CONTENTS is ignored. INFO is a plist holding contextual information." 1252 (let ((with-latex (plist-get info :with-latex))) 1253 (when (or (eq with-latex t) 1254 (and (eq with-latex 'detect) 1255 (org-texinfo-supports-math-p))) 1256 (let ((value (org-element-property :value environment))) 1257 (string-join (list "@displaymath" 1258 (string-trim (org-remove-indentation value)) 1259 "@end displaymath") 1260 "\n"))))) 1261 1262 ;;;; LaTeX Fragment 1263 1264 (defun org-texinfo-latex-fragment (fragment _contents info) 1265 "Transcode a LaTeX FRAGMENT from Org to Texinfo. 1266 INFO is a plist holding contextual information." 1267 (let ((with-latex (plist-get info :with-latex))) 1268 (when (or (eq with-latex t) 1269 (and (eq with-latex 'detect) 1270 (org-texinfo-supports-math-p))) 1271 (let ((value (org-remove-indentation 1272 (org-element-property :value fragment)))) 1273 (cond 1274 ((or (string-match-p "^\\\\\\[" value) 1275 (string-match-p "^\\$\\$" value)) 1276 (concat "\n" 1277 "@displaymath" 1278 "\n" 1279 (string-trim (substring value 2 -2)) 1280 "\n" 1281 "@end displaymath" 1282 "\n")) 1283 ((string-match-p "^\\$" value) 1284 (concat "@math{" 1285 (string-trim (substring value 1 -1)) 1286 "}")) 1287 ((string-match-p "^\\\\(" value) 1288 (concat "@math{" 1289 (string-trim (substring value 2 -2)) 1290 "}")) 1291 (t value)))))) 1292 1293 ;;;; Line Break 1294 1295 (defun org-texinfo-line-break (_line-break _contents _info) 1296 "Transcode a LINE-BREAK object from Org to Texinfo. 1297 CONTENTS is nil. INFO is a plist holding contextual information." 1298 "@*\n") 1299 1300 ;;;; Link 1301 1302 (defun org-texinfo--@ref (datum description info) 1303 "Return @ref command for element or object DATUM. 1304 DESCRIPTION is the printed name of the section, as a string, or 1305 nil." 1306 (let ((node-name (org-texinfo--get-node datum info)) 1307 ;; Sanitize DESCRIPTION for cross-reference use. In 1308 ;; particular, remove colons as they seem to cause pain (even 1309 ;; within @asis{...}) to the Texinfo reader. 1310 (title (and description 1311 (replace-regexp-in-string 1312 "[ \t]*:+" "" 1313 (replace-regexp-in-string "," "@comma{}" description))))) 1314 (if (or (not title) (equal title node-name)) 1315 (format "@ref{%s}" node-name) 1316 (format "@ref{%s, , %s}" node-name title)))) 1317 1318 (defun org-texinfo-link (link desc info) 1319 "Transcode a LINK object from Org to Texinfo. 1320 DESC is the description part of the link, or the empty string. 1321 INFO is a plist holding contextual information. See 1322 `org-export-data'." 1323 (let* ((type (org-element-property :type link)) 1324 (raw-path (org-element-property :path link)) 1325 ;; Ensure DESC really exists, or set it to nil. 1326 (desc (and (not (string= desc "")) desc)) 1327 (path (org-texinfo--sanitize-content 1328 (cond 1329 ((string-equal type "file") 1330 (org-export-file-uri raw-path)) 1331 (t (concat type ":" raw-path)))))) 1332 (cond 1333 ((org-export-custom-protocol-maybe link desc 'texinfo info)) 1334 ((org-export-inline-image-p link org-texinfo-inline-image-rules) 1335 (org-texinfo--inline-image link info)) 1336 ((equal type "radio") 1337 (let ((destination (org-export-resolve-radio-link link info))) 1338 (if (not destination) desc 1339 (org-texinfo--@ref destination desc info)))) 1340 ((member type '("custom-id" "id" "fuzzy")) 1341 (let ((destination 1342 (if (equal type "fuzzy") 1343 (org-export-resolve-fuzzy-link link info) 1344 (org-export-resolve-id-link link info)))) 1345 (pcase (org-element-type destination) 1346 (`nil 1347 (format org-texinfo-link-with-unknown-path-format path)) 1348 ;; Id link points to an external file. 1349 (`plain-text 1350 (if desc (format "@uref{file://%s,%s}" destination desc) 1351 (format "@uref{file://%s}" destination))) 1352 ((or `headline 1353 ;; Targets within headlines cannot be turned into 1354 ;; @anchor{}, so we refer to the headline parent 1355 ;; directly. 1356 (and `target 1357 (guard 1358 (org-element-type-p 1359 (org-element-parent destination) 1360 'headline)))) 1361 (let ((headline (org-element-lineage destination 'headline t))) 1362 (org-texinfo--@ref headline desc info))) 1363 (_ (org-texinfo--@ref destination desc info))))) 1364 ((string= type "mailto") 1365 (format "@email{%s}" 1366 (concat path (and desc (concat ", " desc))))) 1367 ;; External link with a description part. 1368 ((and path desc) (format "@uref{%s, %s}" path desc)) 1369 ;; External link without a description part. 1370 (path (format "@uref{%s}" path)) 1371 ;; No path, only description. Try to do something useful. 1372 (t 1373 (format (plist-get info :texinfo-link-with-unknown-path-format) desc))))) 1374 1375 (defun org-texinfo--inline-image (link info) 1376 "Return Texinfo code for an inline image. 1377 LINK is the link pointing to the inline image. INFO is the 1378 current state of the export, as a plist." 1379 (let* ((parent (org-element-parent-element link)) 1380 (label (and (org-element-property :name parent) 1381 (org-texinfo--get-node parent info))) 1382 (caption (org-export-get-caption parent)) 1383 (shortcaption (org-export-get-caption parent t)) 1384 (path (org-element-property :path link)) 1385 (filename 1386 (file-name-sans-extension 1387 (if (file-name-absolute-p path) 1388 (expand-file-name path) 1389 (file-relative-name path)))) 1390 (extension (file-name-extension path)) 1391 (attributes (org-export-read-attribute :attr_texinfo parent)) 1392 (height (or (plist-get attributes :height) "")) 1393 (width (or (plist-get attributes :width) "")) 1394 (alt (or (plist-get attributes :alt) "")) 1395 (image (format "@image{%s,%s,%s,%s,%s}" 1396 filename width height alt extension))) 1397 (cond ((or caption shortcaption) 1398 (org-texinfo--wrap-float image 1399 info 1400 (org-export-translate "Figure" :utf-8 info) 1401 label 1402 caption 1403 shortcaption)) 1404 (label (concat "@anchor{" label "}\n" image)) 1405 (t image)))) 1406 1407 1408 ;;;; Menu 1409 1410 (defun org-texinfo-make-menu (scope info &optional master) 1411 "Create the menu for inclusion in the Texinfo document. 1412 1413 SCOPE is a headline or a full parse tree. INFO is the 1414 communication channel, as a plist. 1415 1416 When optional argument MASTER is non-nil, generate a master menu, 1417 including detailed node listing." 1418 (let ((menu (org-texinfo--build-menu scope info))) 1419 (when (org-string-nw-p menu) 1420 (org-element-normalize-string 1421 (format 1422 "@menu\n%s@end menu" 1423 (concat menu 1424 (when master 1425 (let ((detailmenu 1426 (org-texinfo--build-menu 1427 scope info 1428 (let ((toc-depth (plist-get info :with-toc))) 1429 (if (wholenump toc-depth) toc-depth 1430 org-texinfo-max-toc-depth))))) 1431 (when (org-string-nw-p detailmenu) 1432 (concat "\n@detailmenu\n" 1433 "--- The Detailed Node Listing ---\n\n" 1434 detailmenu 1435 "@end detailmenu\n")))))))))) 1436 1437 (defun org-texinfo--build-menu (scope info &optional level) 1438 "Build menu for entries within SCOPE. 1439 SCOPE is a headline or a full parse tree. INFO is a plist 1440 containing contextual information. When optional argument LEVEL 1441 is an integer, build the menu recursively, down to this depth." 1442 (cond 1443 ((not level) 1444 (org-texinfo--format-entries (org-texinfo--menu-entries scope info) info)) 1445 ((zerop level) "\n") 1446 (t 1447 (mapconcat 1448 (lambda (h) 1449 (let ((entries (org-texinfo--menu-entries h info))) 1450 (when entries 1451 (concat 1452 (format "%s\n\n%s\n" 1453 (org-export-data (org-export-get-alt-title h info) info) 1454 (org-texinfo--format-entries entries info)) 1455 (org-texinfo--build-menu h info (1- level)))))) 1456 (org-texinfo--menu-entries scope info) 1457 "")))) 1458 1459 (defun org-texinfo--format-entries (entries info) 1460 "Format all direct menu entries in SCOPE, as a string. 1461 SCOPE is either a headline or a full Org document. INFO is 1462 a plist containing contextual information." 1463 (org-element-normalize-string 1464 (mapconcat 1465 (lambda (h) 1466 (let* ((title 1467 ;; Colons are used as a separator between title and node 1468 ;; name. Remove them. 1469 (replace-regexp-in-string 1470 "[ \t]*:+" "" 1471 (org-texinfo--sanitize-title 1472 (org-export-get-alt-title h info) info))) 1473 (node (org-texinfo--get-node h info)) 1474 (entry (concat "* " title ":" 1475 (if (string= title node) ":" 1476 (concat " " node ". ")))) 1477 (desc (org-element-property :DESCRIPTION h))) 1478 (if (not desc) entry 1479 (format (format "%%-%ds %%s" org-texinfo-node-description-column) 1480 entry desc)))) 1481 entries "\n"))) 1482 1483 (defun org-texinfo--menu-entries (scope info) 1484 "List direct children in SCOPE needing a menu entry. 1485 SCOPE is a headline or a full parse tree. INFO is a plist 1486 holding contextual information." 1487 (let* ((cache (or (plist-get info :texinfo-entries-cache) 1488 (plist-get (plist-put info :texinfo-entries-cache 1489 (make-hash-table :test #'eq)) 1490 :texinfo-entries-cache))) 1491 (cached-entries (gethash scope cache 'no-cache))) 1492 (if (not (eq cached-entries 'no-cache)) cached-entries 1493 (let* ((sections (org-texinfo--sectioning-structure info)) 1494 (max-depth (length sections))) 1495 (puthash scope 1496 (cl-remove-if 1497 (lambda (h) 1498 (or (org-not-nil (org-export-get-node-property :COPYING h t)) 1499 (< max-depth (org-export-get-relative-level h info)))) 1500 (org-export-collect-headlines info 1 scope)) 1501 cache))))) 1502 1503 ;;;; Node Property 1504 1505 (defun org-texinfo-node-property (node-property _contents _info) 1506 "Transcode a NODE-PROPERTY element from Org to Texinfo. 1507 CONTENTS is nil. INFO is a plist holding contextual 1508 information." 1509 (format "%s:%s" 1510 (org-element-property :key node-property) 1511 (let ((value (org-element-property :value node-property))) 1512 (if value (concat " " value) "")))) 1513 1514 ;;;; Paragraph 1515 1516 (defun org-texinfo-paragraph (_paragraph contents _info) 1517 "Transcode a PARAGRAPH element from Org to Texinfo. 1518 CONTENTS is the contents of the paragraph, as a string. INFO is 1519 the plist used as a communication channel." 1520 ;; Ensure that we do not create multiple paragraphs, when a single 1521 ;; paragraph is expected. 1522 ;; Multiple newlines may appear in CONTENTS, for example, when 1523 ;; certain objects are stripped from export, leaving single newlines 1524 ;; before and after. 1525 (org-remove-blank-lines contents)) 1526 1527 ;;;; Plain List 1528 1529 (defun org-texinfo-plain-list (plain-list contents info) 1530 "Transcode a PLAIN-LIST element from Org to Texinfo. 1531 CONTENTS is the contents of the list. INFO is a plist holding 1532 contextual information." 1533 (let* ((attr (org-export-read-attribute :attr_texinfo plain-list)) 1534 (indic (let ((i (or (plist-get attr :indic) 1535 (plist-get info :texinfo-table-default-markup)))) 1536 ;; Allow indicating commands with missing @ sign. 1537 (if (string-prefix-p "@" i) i (concat "@" i)))) 1538 (table-type (plist-get attr :table-type)) 1539 (type (org-element-property :type plain-list)) 1540 (enum 1541 (cond ((not (eq type 'ordered)) nil) 1542 ((plist-member attr :enum) (plist-get attr :enum)) 1543 (t 1544 ;; Texinfo only supports initial counters, i.e., it 1545 ;; cannot change the numbering mid-list. 1546 (let ((first-item (car (org-element-contents plain-list)))) 1547 (org-element-property :counter first-item))))) 1548 (list-type (cond 1549 ((eq type 'ordered) "enumerate") 1550 ((eq type 'unordered) "itemize") 1551 ((member table-type '("ftable" "vtable")) table-type) 1552 (t "table")))) 1553 (format "@%s\n%s@end %s" 1554 (cond ((eq type 'descriptive) (concat list-type " " indic)) 1555 (enum (format "%s %s" list-type enum)) 1556 (t list-type)) 1557 contents 1558 list-type))) 1559 1560 ;;;; Plain Text 1561 1562 (defun org-texinfo-plain-text (text info) 1563 "Transcode a TEXT string from Org to Texinfo. 1564 TEXT is the string to transcode. INFO is a plist holding 1565 contextual information." 1566 ;; First protect @, { and }. 1567 (let ((output (org-texinfo--sanitize-content text))) 1568 ;; Activate smart quotes. Be sure to provide original TEXT string 1569 ;; since OUTPUT may have been modified. 1570 (when (plist-get info :with-smart-quotes) 1571 (setq output 1572 (org-export-activate-smart-quotes output :texinfo info text))) 1573 ;; LaTeX into @LaTeX{} and TeX into @TeX{} 1574 (let ((case-fold-search nil)) 1575 (setq output (replace-regexp-in-string "\\(?:La\\)?TeX" "@\\&{}" output))) 1576 ;; Convert special strings. 1577 (when (plist-get info :with-special-strings) 1578 (setq output 1579 (replace-regexp-in-string 1580 "\\.\\.\\." "@dots{}" 1581 (replace-regexp-in-string "\\\\-" "@-" output)))) 1582 ;; Handle break preservation if required. 1583 (when (plist-get info :preserve-breaks) 1584 (setq output (replace-regexp-in-string 1585 "\\(\\\\\\\\\\)?[ \t]*\n" " @*\n" output))) 1586 ;; Reverse sentence ending. A sentence can end with a capital 1587 ;; letter. Use non-breaking space if it shouldn't. 1588 (let ((case-fold-search nil)) 1589 (replace-regexp-in-string 1590 "[A-Z]\\([.?!]\\)\\(?:[])]\\|'\\{1,2\\}\\)?\\(?: \\|$\\)" 1591 "@\\1" 1592 output nil nil 1)))) 1593 1594 ;;;; Planning 1595 1596 (defun org-texinfo-planning (planning _contents info) 1597 "Transcode a PLANNING element from Org to Texinfo. 1598 CONTENTS is nil. INFO is a plist holding contextual 1599 information." 1600 (concat 1601 "@noindent" 1602 (mapconcat 1603 #'identity 1604 (delq nil 1605 (list 1606 (let ((closed (org-element-property :closed planning))) 1607 (when closed 1608 (concat 1609 (format "@strong{%s} " org-closed-string) 1610 (format (plist-get info :texinfo-inactive-timestamp-format) 1611 (org-timestamp-translate closed))))) 1612 (let ((deadline (org-element-property :deadline planning))) 1613 (when deadline 1614 (concat 1615 (format "@strong{%s} " org-deadline-string) 1616 (format (plist-get info :texinfo-active-timestamp-format) 1617 (org-timestamp-translate deadline))))) 1618 (let ((scheduled (org-element-property :scheduled planning))) 1619 (when scheduled 1620 (concat 1621 (format "@strong{%s} " org-scheduled-string) 1622 (format (plist-get info :texinfo-active-timestamp-format) 1623 (org-timestamp-translate scheduled))))))) 1624 " ") 1625 "@*")) 1626 1627 ;;;; Property Drawer 1628 1629 (defun org-texinfo-property-drawer (_property-drawer contents _info) 1630 "Transcode a PROPERTY-DRAWER element from Org to Texinfo. 1631 CONTENTS holds the contents of the drawer. INFO is a plist 1632 holding contextual information." 1633 (and (org-string-nw-p contents) 1634 (format "@verbatim\n%s@end verbatim" contents))) 1635 1636 ;;;; Quote Block 1637 1638 (defun org-texinfo-quote-block (quote-block contents _info) 1639 "Transcode a QUOTE-BLOCK element from Org to Texinfo. 1640 CONTENTS holds the contents of the block. INFO is a plist 1641 holding contextual information." 1642 (let ((tag (org-export-read-attribute :attr_texinfo quote-block :tag)) 1643 (author (org-export-read-attribute :attr_texinfo quote-block :author))) 1644 (format "@quotation%s\n%s%s\n@end quotation" 1645 (if tag (concat " " tag) "") 1646 contents 1647 (if author (concat "\n@author " author) "")))) 1648 1649 ;;;; Radio Target 1650 1651 (defun org-texinfo-radio-target (radio-target text info) 1652 "Transcode a RADIO-TARGET object from Org to Texinfo. 1653 TEXT is the text of the target. INFO is a plist holding 1654 contextual information." 1655 (format "@anchor{%s}%s" 1656 (org-texinfo--get-node radio-target info) 1657 text)) 1658 1659 ;;;; Section 1660 1661 (defun org-texinfo-section (section contents info) 1662 "Transcode a SECTION element from Org to Texinfo. 1663 CONTENTS holds the contents of the section. INFO is a plist 1664 holding contextual information." 1665 (let ((parent (org-element-lineage section 'headline))) 1666 (when parent ;first section is handled in `org-texinfo-template' 1667 (org-trim 1668 (concat contents 1669 "\n" 1670 (and (not (org-export-excluded-from-toc-p parent info)) 1671 (org-texinfo-make-menu parent info))))))) 1672 1673 ;;;; Special Block 1674 1675 (defun org-texinfo-special-block (special-block contents _info) 1676 "Transcode a SPECIAL-BLOCK element from Org to Texinfo. 1677 CONTENTS holds the contents of the block. INFO is a plist used 1678 as a communication channel." 1679 (let ((opt (org-export-read-attribute :attr_texinfo special-block :options)) 1680 (type (org-element-property :type special-block))) 1681 (format "@%s%s\n%s@end %s" 1682 type 1683 (if opt (concat " " opt) "") 1684 (or contents "") 1685 type))) 1686 1687 ;;;; Src Block 1688 1689 (defun org-texinfo-src-block (src-block _contents info) 1690 "Transcode a SRC-BLOCK element from Org to Texinfo. 1691 CONTENTS holds the contents of the item. INFO is a plist holding 1692 contextual information." 1693 (let* ((lisp (string-match-p 1694 "lisp" 1695 (or (org-element-property :language src-block) ""))) 1696 (code (org-texinfo--sanitize-content 1697 (org-export-format-code-default src-block info))) 1698 (value (format 1699 (if lisp "@lisp\n%s@end lisp" "@example\n%s@end example") 1700 code)) 1701 (caption (org-export-get-caption src-block)) 1702 (shortcaption (org-export-get-caption src-block t))) 1703 (if (not (or caption shortcaption)) value 1704 (org-texinfo--wrap-float value 1705 info 1706 (org-export-translate "Listing" :utf-8 info) 1707 (org-texinfo--get-node src-block info) 1708 caption 1709 shortcaption)))) 1710 1711 ;;;; Statistics Cookie 1712 1713 (defun org-texinfo-statistics-cookie (statistics-cookie _contents _info) 1714 "Transcode a STATISTICS-COOKIE object from Org to Texinfo. 1715 CONTENTS is nil. INFO is a plist holding contextual information." 1716 (org-element-property :value statistics-cookie)) 1717 1718 1719 ;;;; Strike-through 1720 1721 (defun org-texinfo-strike-through (_strike-through contents info) 1722 "Transcode STRIKE-THROUGH from Org to Texinfo. 1723 CONTENTS is the text with strike-through markup. INFO is a plist 1724 holding contextual information." 1725 (org-texinfo--text-markup contents 'strike-through info)) 1726 1727 ;;;; Subscript 1728 1729 (defun org-texinfo-subscript (_subscript contents _info) 1730 "Transcode a SUBSCRIPT object from Org to Texinfo. 1731 CONTENTS is the contents of the object. INFO is a plist holding 1732 contextual information." 1733 (format "@math{_%s}" contents)) 1734 1735 ;;;; Superscript 1736 1737 (defun org-texinfo-superscript (_superscript contents _info) 1738 "Transcode a SUPERSCRIPT object from Org to Texinfo. 1739 CONTENTS is the contents of the object. INFO is a plist holding 1740 contextual information." 1741 (format "@math{^%s}" contents)) 1742 1743 ;;;; Table 1744 1745 (defun org-texinfo-table (table contents info) 1746 "Transcode a TABLE element from Org to Texinfo. 1747 CONTENTS is the contents of the table. INFO is a plist holding 1748 contextual information." 1749 (if (eq (org-element-property :type table) 'table.el) 1750 (format "@verbatim\n%s@end verbatim" 1751 (org-element-normalize-string 1752 (org-element-property :value table))) 1753 (let* ((col-width (org-export-read-attribute :attr_texinfo table :columns)) 1754 (columns 1755 (if col-width (format "@columnfractions %s" col-width) 1756 (org-texinfo-table-column-widths table info))) 1757 (caption (org-export-get-caption table)) 1758 (shortcaption (org-export-get-caption table t)) 1759 (table-str (format "@multitable %s\n%s@end multitable" 1760 columns 1761 contents))) 1762 (if (not (or caption shortcaption)) table-str 1763 (org-texinfo--wrap-float table-str 1764 info 1765 (org-export-translate "Table" :utf-8 info) 1766 (org-texinfo--get-node table info) 1767 caption 1768 shortcaption))))) 1769 1770 (defun org-texinfo-table-column-widths (table info) 1771 "Determine the largest table cell in each column to process alignment. 1772 TABLE is the table element to transcode. INFO is a plist used as 1773 a communication channel." 1774 (let ((widths (make-vector (cdr (org-export-table-dimensions table info)) 0))) 1775 (org-element-map table 'table-row 1776 (lambda (row) 1777 (let ((idx 0)) 1778 (org-element-map row 'table-cell 1779 (lambda (cell) 1780 ;; Length of the cell in the original buffer is only an 1781 ;; approximation of the length of the cell in the 1782 ;; output. It can sometimes fail (e.g. it considers 1783 ;; "/a/" being larger than "ab"). 1784 (let ((w (- (org-element-contents-end cell) 1785 (org-element-contents-begin cell)))) 1786 (aset widths idx (max w (aref widths idx)))) 1787 (cl-incf idx)) 1788 info))) 1789 info) 1790 (format "{%s}" (mapconcat (lambda (w) (make-string w ?a)) widths "} {")))) 1791 1792 ;;;; Table Cell 1793 1794 (defun org-texinfo-table-cell (table-cell contents info) 1795 "Transcode a TABLE-CELL element from Org to Texinfo. 1796 CONTENTS is the cell contents. INFO is a plist used as 1797 a communication channel." 1798 (concat 1799 (let ((scientific-notation 1800 (plist-get info :texinfo-table-scientific-notation))) 1801 (if (and contents 1802 scientific-notation 1803 (string-match orgtbl-exp-regexp contents)) 1804 ;; Use appropriate format string for scientific notation. 1805 (format scientific-notation 1806 (match-string 1 contents) 1807 (match-string 2 contents)) 1808 contents)) 1809 (when (org-export-get-next-element table-cell info) "\n@tab "))) 1810 1811 ;;;; Table Row 1812 1813 (defun org-texinfo-table-row (table-row contents info) 1814 "Transcode a TABLE-ROW element from Org to Texinfo. 1815 CONTENTS is the contents of the row. INFO is a plist used as 1816 a communication channel." 1817 ;; Rules are ignored since table separators are deduced from 1818 ;; borders of the current row. 1819 (when (eq (org-element-property :type table-row) 'standard) 1820 (let ((rowgroup-tag 1821 (if (and (= 1 (org-export-table-row-group table-row info)) 1822 (org-export-table-has-header-p 1823 (org-element-lineage table-row 'table) info)) 1824 "@headitem " 1825 "@item "))) 1826 (concat rowgroup-tag contents "\n")))) 1827 1828 ;;;; Target 1829 1830 (defun org-texinfo-target (target _contents info) 1831 "Transcode a TARGET object from Org to Texinfo. 1832 CONTENTS is nil. INFO is a plist holding contextual 1833 information." 1834 (format "@anchor{%s}" (org-texinfo--get-node target info))) 1835 1836 ;;;; Timestamp 1837 1838 (defun org-texinfo-timestamp (timestamp _contents info) 1839 "Transcode a TIMESTAMP object from Org to Texinfo. 1840 CONTENTS is nil. INFO is a plist holding contextual 1841 information." 1842 (let ((value (org-texinfo-plain-text 1843 (org-timestamp-translate timestamp) info))) 1844 (pcase (org-element-property :type timestamp) 1845 ((or `active `active-range) 1846 (format (plist-get info :texinfo-active-timestamp-format) value)) 1847 ((or `inactive `inactive-range) 1848 (format (plist-get info :texinfo-inactive-timestamp-format) value)) 1849 (_ (format (plist-get info :texinfo-diary-timestamp-format) value))))) 1850 1851 ;;;; Underline 1852 1853 (defun org-texinfo-underline (_underline contents info) 1854 "Transcode UNDERLINE from Org to Texinfo. 1855 CONTENTS is the text with underline markup. INFO is a plist 1856 holding contextual information." 1857 (org-texinfo--text-markup contents 'underline info)) 1858 1859 ;;;; Verbatim 1860 1861 (defun org-texinfo-verbatim (verbatim _contents info) 1862 "Transcode a VERBATIM object from Org to Texinfo. 1863 CONTENTS is nil. INFO is a plist used as a communication 1864 channel." 1865 (org-texinfo--text-markup 1866 (org-element-property :value verbatim) 'verbatim info)) 1867 1868 ;;;; Verse Block 1869 1870 (defun org-texinfo-verse-block (_verse-block contents _info) 1871 "Transcode a VERSE-BLOCK element from Org to Texinfo. 1872 CONTENTS is verse block contents. INFO is a plist holding 1873 contextual information." 1874 (format "@display\n%s@end display" contents)) 1875 1876 1877 ;;; Public Functions 1878 1879 (defun org-texinfo-kbd-macro (key &optional noquote) 1880 "Quote KEY using @kbd{...} and if necessary @key{...}. 1881 1882 This is intended to be used as an Org macro like so: 1883 1884 #+macro: kbd (eval (org-texinfo-kbd-macro $1)) 1885 Type {{{kbd(C-c SPC)}}}. 1886 1887 Also see info node `(org)Key bindings in Texinfo export'. 1888 1889 If optional NOQOUTE is non-nil, then do not add the quoting 1890 that is necessary when using this in an Org macro." 1891 (format (if noquote "@kbd{%s}" "@@texinfo:@kbd{@@%s@@texinfo:}@@") 1892 (let ((case-fold-search nil)) 1893 (replace-regexp-in-string 1894 org-texinfo--quoted-keys-regexp 1895 (if noquote "@key{\\&}" "@@texinfo:@key{@@\\&@@texinfo:}@@") 1896 key t)))) 1897 1898 ;;; Interactive Functions 1899 1900 ;;;###autoload 1901 (defun org-texinfo-export-to-texinfo 1902 (&optional async subtreep visible-only body-only ext-plist) 1903 "Export current buffer to a Texinfo file. 1904 1905 If narrowing is active in the current buffer, only export its 1906 narrowed part. 1907 1908 If a region is active, export that region. 1909 1910 A non-nil optional argument ASYNC means the process should happen 1911 asynchronously. The resulting file should be accessible through 1912 the `org-export-stack' interface. 1913 1914 When optional argument SUBTREEP is non-nil, export the sub-tree 1915 at point, extracting information from the headline properties 1916 first. 1917 1918 When optional argument VISIBLE-ONLY is non-nil, don't export 1919 contents of hidden elements. 1920 1921 When optional argument BODY-ONLY is non-nil, only write code 1922 between \"\\begin{document}\" and \"\\end{document}\". 1923 1924 EXT-PLIST, when provided, is a property list with external 1925 parameters overriding Org default settings, but still inferior to 1926 file-local settings. 1927 1928 Return output file's name." 1929 (interactive) 1930 (let ((outfile (org-export-output-file-name ".texi" subtreep)) 1931 (org-export-coding-system org-texinfo-coding-system)) 1932 (org-export-to-file 'texinfo outfile 1933 async subtreep visible-only body-only ext-plist))) 1934 1935 (defun org-texinfo-export-to-texinfo-batch () 1936 "Export Org file INFILE to Texinfo file OUTFILE, in batch mode. 1937 Overwrites existing output file. 1938 Usage: emacs -batch -f org-texinfo-export-to-texinfo-batch INFILE OUTFILE" 1939 (or noninteractive (user-error "Batch mode use only")) 1940 (let ((infile (pop command-line-args-left)) 1941 (outfile (pop command-line-args-left)) 1942 (org-export-coding-system org-texinfo-coding-system) 1943 (make-backup-files nil)) 1944 (unless (file-readable-p infile) 1945 (message "File `%s' not readable" infile) 1946 (kill-emacs 1)) 1947 (with-temp-buffer 1948 (insert-file-contents infile) 1949 (org-export-to-file 'texinfo outfile)))) 1950 1951 ;;;###autoload 1952 (defun org-texinfo-export-to-info 1953 (&optional async subtreep visible-only body-only ext-plist) 1954 "Export current buffer to Texinfo then process through to INFO. 1955 1956 If narrowing is active in the current buffer, only export its 1957 narrowed part. 1958 1959 If a region is active, export that region. 1960 1961 A non-nil optional argument ASYNC means the process should happen 1962 asynchronously. The resulting file should be accessible through 1963 the `org-export-stack' interface. 1964 1965 When optional argument SUBTREEP is non-nil, export the sub-tree 1966 at point, extracting information from the headline properties 1967 first. 1968 1969 When optional argument VISIBLE-ONLY is non-nil, don't export 1970 contents of hidden elements. 1971 1972 When optional argument BODY-ONLY is non-nil, only write code 1973 between \"\\begin{document}\" and \"\\end{document}\". 1974 1975 EXT-PLIST, when provided, is a property list with external 1976 parameters overriding Org default settings, but still inferior to 1977 file-local settings. 1978 1979 Return INFO file's name." 1980 (interactive) 1981 (let ((outfile (org-export-output-file-name ".texi" subtreep)) 1982 (org-export-coding-system org-texinfo-coding-system)) 1983 (org-export-to-file 'texinfo outfile 1984 async subtreep visible-only body-only ext-plist 1985 #'org-texinfo-compile))) 1986 1987 ;;;###autoload 1988 (defun org-texinfo-publish-to-texinfo (plist filename pub-dir) 1989 "Publish an org file to Texinfo. 1990 1991 FILENAME is the filename of the Org file to be published. PLIST 1992 is the property list for the given project. PUB-DIR is the 1993 publishing directory. 1994 1995 Return output file name." 1996 (org-publish-org-to 'texinfo filename ".texi" plist pub-dir)) 1997 1998 ;;;###autoload 1999 (defun org-texinfo-convert-region-to-texinfo () 2000 "Assume the current region has Org syntax, and convert it to Texinfo. 2001 This can be used in any buffer. For example, you can write an 2002 itemized list in Org syntax in an Texinfo buffer and use this 2003 command to convert it." 2004 (interactive) 2005 (org-export-replace-region-by 'texinfo)) 2006 2007 (defalias 'org-export-region-to-texinfo #'org-texinfo-convert-region-to-texinfo) 2008 2009 (defun org-texinfo-compile (file) 2010 "Compile a texinfo file. 2011 2012 FILE is the name of the file being compiled. Processing is done 2013 through the command specified in `org-texinfo-info-process', 2014 which see. Output is redirected to \"*Org INFO Texinfo Output*\" 2015 buffer. 2016 2017 Return INFO file name or an error if it couldn't be produced." 2018 (message "Processing Texinfo file %s..." file) 2019 (let* ((log-name "*Org INFO Texinfo Output*") 2020 (log (get-buffer-create log-name)) 2021 (output 2022 (org-compile-file file org-texinfo-info-process "info" 2023 (format "See %S for details" log-name) 2024 log))) 2025 (when org-texinfo-remove-logfiles 2026 (let ((base (file-name-sans-extension output))) 2027 (dolist (ext org-texinfo-logfiles-extensions) 2028 (let ((file (concat base "." ext))) 2029 (when (file-exists-p file) (delete-file file)))))) 2030 (message "Process completed.") 2031 output)) 2032 2033 (defun org-texinfo-supports-math-p () 2034 "Return t if the installed version of Texinfo supports \"@math\". 2035 2036 Once computed, the results remain cached." 2037 (unless (boundp 'org-texinfo-supports-math--cache) 2038 (setq org-texinfo-supports-math--cache 2039 (let ((math-example "1 + 1 = 2")) 2040 (let* ((input-file (make-temp-file "test" nil ".texi")) 2041 (output-file 2042 (concat (file-name-sans-extension input-file) ".info")) 2043 (input-content (string-join 2044 (list (format "@setfilename %s" output-file) 2045 "@node Top" 2046 "@displaymath" 2047 math-example 2048 "@end displaymath") 2049 "\n"))) 2050 (with-temp-file input-file 2051 (insert input-content)) 2052 (when-let* ((output-file 2053 ;; If compilation fails, consider math to 2054 ;; be not supported. 2055 (ignore-errors (let ((inhibit-message t)) 2056 (org-texinfo-compile input-file)))) 2057 (output-content (with-temp-buffer 2058 (insert-file-contents output-file) 2059 (buffer-string)))) 2060 (let ((result (string-match-p (regexp-quote math-example) 2061 output-content))) 2062 (delete-file input-file) 2063 (delete-file output-file) 2064 (if result t nil))))))) 2065 org-texinfo-supports-math--cache) 2066 2067 (provide 'ox-texinfo) 2068 2069 ;; Local variables: 2070 ;; generated-autoload-file: "org-loaddefs.el" 2071 ;; End: 2072 2073 ;;; ox-texinfo.el ends here