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