ox-man.el (38958B)
1 ;;; ox-man.el --- Man Backend for Org Export Engine -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2011-2024 Free Software Foundation, Inc. 4 5 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com> 6 ;; Luis R Anaya <papoanaya aroba hot mail punto com> 7 ;; Keywords: outlines, hypermedia, calendar, text 8 9 ;; This file is part of GNU Emacs. 10 11 ;; GNU Emacs is free software: you can redistribute it and/or modify 12 ;; it under the terms of the GNU General Public License as published by 13 ;; the Free Software Foundation, either version 3 of the License, or 14 ;; (at your option) any later version. 15 16 ;; GNU Emacs is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 21 ;; You should have received a copy of the GNU General Public License 22 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 23 24 ;;; Commentary: 25 ;; 26 ;; This library implements a Man backend for Org generic exporter. 27 ;; 28 ;; To test it, run 29 ;; 30 ;; M-: (org-export-to-buffer 'man "*Test Man*") RET 31 ;; 32 ;; in an Org buffer then switch to the buffer to see the Man export. 33 ;; See ox.el for more details on how this exporter works. 34 ;; 35 ;; It introduces one new buffer keywords: 36 ;; "MAN_CLASS_OPTIONS". 37 38 ;;; Code: 39 40 (require 'org-macs) 41 (org-assert-version) 42 43 (require 'cl-lib) 44 (require 'ox) 45 46 ;;; Function Declarations 47 48 (defvar org-export-man-default-packages-alist) 49 (defvar org-export-man-packages-alist) 50 (defvar orgtbl-exp-regexp) 51 52 53 54 ;;; Define Backend 55 56 (org-export-define-backend 'man 57 '((babel-call . org-man-babel-call) 58 (bold . org-man-bold) 59 (center-block . org-man-center-block) 60 (code . org-man-code) 61 (drawer . org-man-drawer) 62 (dynamic-block . org-man-dynamic-block) 63 (entity . org-man-entity) 64 (example-block . org-man-example-block) 65 (export-block . org-man-export-block) 66 (export-snippet . org-man-export-snippet) 67 (fixed-width . org-man-fixed-width) 68 (footnote-definition . org-man-footnote-definition) 69 (footnote-reference . org-man-footnote-reference) 70 (headline . org-man-headline) 71 (horizontal-rule . org-man-horizontal-rule) 72 (inline-babel-call . org-man-inline-babel-call) 73 (inline-src-block . org-man-inline-src-block) 74 (inlinetask . org-man-inlinetask) 75 (italic . org-man-italic) 76 (item . org-man-item) 77 (keyword . org-man-keyword) 78 (line-break . org-man-line-break) 79 (link . org-man-link) 80 (node-property . org-man-node-property) 81 (paragraph . org-man-paragraph) 82 (plain-list . org-man-plain-list) 83 (plain-text . org-man-plain-text) 84 (planning . org-man-planning) 85 (property-drawer . org-man-property-drawer) 86 (quote-block . org-man-quote-block) 87 (radio-target . org-man-radio-target) 88 (section . org-man-section) 89 (special-block . org-man-special-block) 90 (src-block . org-man-src-block) 91 (statistics-cookie . org-man-statistics-cookie) 92 (strike-through . org-man-strike-through) 93 (subscript . org-man-subscript) 94 (superscript . org-man-superscript) 95 (table . org-man-table) 96 (table-cell . org-man-table-cell) 97 (table-row . org-man-table-row) 98 (target . org-man-target) 99 (template . org-man-template) 100 (timestamp . org-man-timestamp) 101 (underline . org-man-underline) 102 (verbatim . org-man-verbatim) 103 (verse-block . org-man-verse-block)) 104 :menu-entry 105 '(?M "Export to MAN" 106 ((?m "As MAN file" org-man-export-to-man) 107 (?p "As PDF file" org-man-export-to-pdf) 108 (?o "As PDF file and open" 109 (lambda (a s v b) 110 (if a (org-man-export-to-pdf t s v b) 111 (org-open-file (org-man-export-to-pdf nil s v b))))))) 112 :options-alist 113 '((:man-class "MAN_CLASS" nil nil t) 114 (:man-class-options "MAN_CLASS_OPTIONS" nil nil t) 115 (:man-header-extra "MAN_HEADER" nil nil newline) 116 ;; Other variables. 117 (:man-tables-centered nil nil org-man-tables-centered) 118 (:man-tables-verbatim nil nil org-man-tables-verbatim) 119 (:man-table-scientific-notation nil nil org-man-table-scientific-notation) 120 (:man-source-highlight nil nil org-man-source-highlight) 121 (:man-source-highlight-langs nil nil org-man-source-highlight-langs))) 122 123 124 125 ;;; User Configurable Variables 126 127 (defgroup org-export-man nil 128 "Options for exporting Org mode files to Man." 129 :tag "Org Export Man" 130 :group 'org-export) 131 132 ;;; Tables 133 134 (defcustom org-man-tables-centered t 135 "When non-nil, tables are exported in a center environment." 136 :group 'org-export-man 137 :version "24.4" 138 :package-version '(Org . "8.0") 139 :type 'boolean) 140 141 (defcustom org-man-tables-verbatim nil 142 "When non-nil, tables are exported verbatim." 143 :group 'org-export-man 144 :version "24.4" 145 :package-version '(Org . "8.0") 146 :type 'boolean) 147 148 149 (defcustom org-man-table-scientific-notation "%sE%s" 150 "Format string to display numbers in scientific notation. 151 The format should have \"%s\" twice, for mantissa and exponent 152 \(i.e. \"%s\\\\times10^{%s}\"). 153 154 When nil, no transformation is made." 155 :group 'org-export-man 156 :version "24.4" 157 :package-version '(Org . "8.0") 158 :type '(choice 159 (string :tag "Format string") 160 (const :tag "No formatting"))) 161 162 163 ;;; Inlinetasks 164 ;; Src blocks 165 166 (defcustom org-man-source-highlight nil 167 "Use GNU source highlight to embellish source blocks." 168 :group 'org-export-man 169 :version "24.4" 170 :package-version '(Org . "8.0") 171 :type 'boolean) 172 173 174 (defcustom org-man-source-highlight-langs 175 '((emacs-lisp "lisp") (lisp "lisp") (clojure "lisp") 176 (scheme "scheme") 177 (c "c") (cc "cpp") (csharp "csharp") (d "d") 178 (fortran "fortran") (cobol "cobol") (pascal "pascal") 179 (ada "ada") (asm "asm") 180 (perl "perl") (cperl "perl") 181 (python "python") (ruby "ruby") (tcl "tcl") (lua "lua") 182 (java "java") (javascript "javascript") 183 (tex "latex") 184 (shell-script "sh") (awk "awk") (diff "diff") (m4 "m4") 185 (ocaml "caml") (caml "caml") 186 (sql "sql") (sqlite "sql") 187 (html "html") (css "css") (xml "xml") 188 (bat "bat") (bison "bison") (clipper "clipper") 189 (ldap "ldap") (opa "opa") 190 (php "php") (postscript "postscript") (prolog "prolog") 191 (properties "properties") (makefile "makefile") 192 (tml "tml") (vbscript "vbscript") (xorg "xorg")) 193 "Alist mapping languages to their listing language counterpart. 194 The key is a symbol, the major mode symbol without the \"-mode\". 195 The value is the string that should be inserted as the language 196 parameter for the listings package. If the mode name and the 197 listings name are the same, the language does not need an entry 198 in this list - but it does not hurt if it is present." 199 :group 'org-export-man 200 :version "24.4" 201 :package-version '(Org . "8.0") 202 :type '(repeat 203 (list 204 (symbol :tag "Major mode ") 205 (string :tag "Listings language")))) 206 207 208 ;;; Compilation 209 210 (defcustom org-man-pdf-process 211 '("tbl %f | eqn | groff -man | ps2pdf - > %b.pdf" 212 "tbl %f | eqn | groff -man | ps2pdf - > %b.pdf" 213 "tbl %f | eqn | groff -man | ps2pdf - > %b.pdf") 214 215 "Commands to process a Man file to a PDF file. 216 217 This is a list of strings, each of them will be given to the 218 shell as a command. %f in the command will be replaced by the 219 relative file name, %F by the absolute file name, %b by the file 220 base name (i.e. without directory and extension parts), %o by the 221 base directory of the file and %O by the absolute file name of 222 the output file. 223 224 By default, Org uses 3 runs of to do the processing. 225 226 Alternatively, this may be a Lisp function that does the 227 processing. This function should accept the file name as 228 its single argument." 229 :group 'org-export-man 230 :version "24.4" 231 :package-version '(Org . "8.0") 232 :type '(choice 233 (repeat :tag "Shell command sequence" 234 (string :tag "Shell command")) 235 (const :tag "2 runs of pdfgroff" 236 ("tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf" 237 "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf" )) 238 (const :tag "3 runs of pdfgroff" 239 ("tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf" 240 "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf" 241 "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf")) 242 (function))) 243 244 (defcustom org-man-logfiles-extensions 245 '("log" "out" "toc") 246 "The list of file extensions to consider as Man logfiles." 247 :group 'org-export-man 248 :version "24.4" 249 :package-version '(Org . "8.0") 250 :type '(repeat (string :tag "Extension"))) 251 252 (defcustom org-man-remove-logfiles t 253 "Non-nil means remove the logfiles produced by PDF production. 254 These are the .aux, .log, .out, and .toc files." 255 :group 'org-export-man 256 :version "24.4" 257 :package-version '(Org . "8.0") 258 :type 'boolean) 259 260 261 262 ;;; Internal Functions 263 264 (defun org-man--caption/label-string (element info) 265 "Return caption and label Man string for ELEMENT. 266 267 INFO is a plist holding contextual information. If there's no 268 caption nor label, return the empty string. 269 270 For non-floats, see `org-man--wrap-label'." 271 (let ((label (org-element-property :label element)) 272 (main (org-export-get-caption element)) 273 (short (org-export-get-caption element t))) 274 (cond ((and (not main) (not label)) "") 275 ((not main) (format "\\fI%s\\fP" label)) 276 ;; Option caption format with short name. 277 (short (format "\\fR%s\\fP - \\fI\\P - %s\n" 278 (org-export-data short info) 279 (org-export-data main info))) 280 ;; Standard caption format. 281 (t (format "\\fR%s\\fP" (org-export-data main info)))))) 282 283 (defun org-man--wrap-label (element output) 284 "Wrap label associated to ELEMENT around OUTPUT, if appropriate. 285 This function shouldn't be used for floats. See 286 `org-man--caption/label-string'." 287 (let ((label (org-element-property :name element))) 288 (if (or (not output) (not label) (string= output "") (string= label "")) 289 output 290 (concat (format "%s\n.br\n" label) output)))) 291 292 (defun org-man--protect-text (text) 293 "Protect minus and backslash characters in string TEXT." 294 (replace-regexp-in-string "-" "\\-" text nil t)) 295 296 (defun org-man--protect-example (text) 297 "Escape necessary characters for verbatim TEXT." 298 ;; See man groff_man_style; \e must be used to render backslash. 299 ;; Note that groff's .eo (disable backslash) and .ec (re-enable 300 ;; backslash) cannot be used as per the same man page. 301 (replace-regexp-in-string "\\\\" "\\e" text nil t)) 302 303 304 305 ;;; Template 306 307 (defun org-man-template (contents info) 308 "Return complete document string after Man conversion. 309 CONTENTS is the transcoded contents string. INFO is a plist 310 holding export options." 311 (let* ((title (when (plist-get info :with-title) 312 (org-export-data (plist-get info :title) info))) 313 (attr (read (format "(%s)" 314 (mapconcat 315 #'identity 316 (list (plist-get info :man-class-options)) 317 " ")))) 318 (section-item (plist-get attr :section-id))) 319 320 (concat 321 322 (cond 323 ((and title (stringp section-item)) 324 (format ".TH \"%s\" \"%s\" \n" title section-item)) 325 ((and (string= "" title) (stringp section-item)) 326 (format ".TH \"%s\" \"%s\" \n" " " section-item)) 327 (title 328 (format ".TH \"%s\" \"1\" \n" title)) 329 (t 330 ".TH \" \" \"1\" ")) 331 contents))) 332 333 334 335 336 ;;; Transcode Functions 337 338 ;;; Babel Call 339 ;; 340 ;; Babel Calls are ignored. 341 342 343 ;;; Bold 344 345 (defun org-man-bold (_bold contents _info) 346 "Transcode BOLD from Org to Man. 347 CONTENTS is the text with bold markup. INFO is a plist holding 348 contextual information." 349 (format "\\fB%s\\fP" contents)) 350 351 352 ;;; Center Block 353 354 (defun org-man-center-block (center-block contents _info) 355 "Transcode a CENTER-BLOCK element from Org to Man. 356 CONTENTS holds the contents of the center block. INFO is a plist 357 holding contextual information." 358 (org-man--wrap-label 359 center-block 360 (format ".ce %d\n.nf\n%s\n.fi" 361 (- (length (split-string contents "\n")) 1 ) 362 contents))) 363 364 365 ;;; Code 366 367 (defun org-man-code (code _contents _info) 368 "Transcode a CODE object from Org to Man." 369 (format "\\fC%s\\fP" 370 (org-man--protect-text (org-element-property :value code)))) 371 372 373 ;;; Drawer 374 375 (defun org-man-drawer (_drawer contents _info) 376 "Transcode a DRAWER element from Org to Man. 377 DRAWER holds the drawer information 378 CONTENTS holds the contents of the block. 379 INFO is a plist holding contextual information." 380 contents) 381 382 383 ;;; Dynamic Block 384 385 (defun org-man-dynamic-block (dynamic-block contents _info) 386 "Transcode a DYNAMIC-BLOCK element from Org to Man. 387 CONTENTS holds the contents of the block. INFO is a plist 388 holding contextual information. See `org-export-data'." 389 (org-man--wrap-label dynamic-block contents)) 390 391 392 ;;; Entity 393 394 (defun org-man-entity (entity _contents _info) 395 "Transcode an ENTITY object from Org to Man. 396 CONTENTS are the definition itself. INFO is a plist holding 397 contextual information." 398 (org-element-property :utf-8 entity)) 399 400 401 ;;; Example Block 402 403 (defun org-man-example-block (example-block _contents info) 404 "Transcode an EXAMPLE-BLOCK element from Org to Man. 405 CONTENTS is nil. INFO is a plist holding contextual 406 information." 407 (org-man--wrap-label 408 example-block 409 (format ".RS\n.nf\n%s\n.fi\n.RE" 410 (org-man--protect-example (org-export-format-code-default example-block info))))) 411 412 413 ;;; Export Block 414 415 (defun org-man-export-block (export-block _contents _info) 416 "Transcode a EXPORT-BLOCK element from Org to Man. 417 CONTENTS is nil. INFO is a plist holding contextual information." 418 (when (string= (org-element-property :type export-block) "MAN") 419 (org-remove-indentation (org-element-property :value export-block)))) 420 421 422 ;;; Export Snippet 423 424 (defun org-man-export-snippet (export-snippet _contents _info) 425 "Transcode a EXPORT-SNIPPET object from Org to Man. 426 CONTENTS is nil. INFO is a plist holding contextual information." 427 (when (eq (org-export-snippet-backend export-snippet) 'man) 428 (org-element-property :value export-snippet))) 429 430 431 ;;; Fixed Width 432 433 (defun org-man-fixed-width (fixed-width _contents _info) 434 "Transcode a FIXED-WIDTH element from Org to Man. 435 CONTENTS is nil. INFO is a plist holding contextual information." 436 (org-man--wrap-label 437 fixed-width 438 (format "\\fC\n%s\n\\fP" 439 (org-remove-indentation 440 (org-element-property :value fixed-width))))) 441 442 443 ;;; Footnote Definition 444 ;; 445 ;; Footnote Definitions are ignored. 446 447 ;;; Footnote References 448 ;; 449 ;; Footnote References are Ignored 450 451 452 ;;; Headline 453 454 (defun org-man-headline (headline contents info) 455 "Transcode a HEADLINE element from Org to Man. 456 CONTENTS holds the contents of the headline. INFO is a plist 457 holding contextual information." 458 (let* ((level (org-export-get-relative-level headline info)) 459 ;; Section formatting will set two placeholders: one for the 460 ;; title and the other for the contents. 461 (section-fmt 462 (pcase level 463 (1 ".SH \"%s\"\n%s") 464 (2 ".SS \"%s\"\n%s") 465 (3 ".SS \"%s\"\n%s") 466 (_ nil))) 467 (text (org-export-data (org-element-property :title headline) info))) 468 469 (cond 470 ;; Case 1: This is a footnote section: ignore it. 471 ((org-element-property :footnote-section-p headline) nil) 472 473 ;; Case 2. This is a deep sub-tree: export it as a list item. 474 ;; Also export as items headlines for which no section 475 ;; format has been found. 476 ((or (not section-fmt) (org-export-low-level-p headline info)) 477 ;; Build the real contents of the sub-tree. 478 (let ((low-level-body 479 (concat 480 ;; If the headline is the first sibling, start a list. 481 (when (org-export-first-sibling-p headline info) 482 (format "%s\n" ".RS")) 483 ;; Itemize headline 484 ".TP\n.ft I\n" text "\n.ft\n" 485 contents ".RE"))) 486 ;; If headline is not the last sibling simply return 487 ;; LOW-LEVEL-BODY. Otherwise, also close the list, before any 488 ;; blank line. 489 (if (not (org-export-last-sibling-p headline info)) low-level-body 490 (replace-regexp-in-string 491 "[ \t\n]*\\'" "" 492 low-level-body)))) 493 494 ;; Case 3. Standard headline. Export it as a section. 495 (t (format section-fmt text contents ))))) 496 497 ;;; Horizontal Rule 498 ;; Not supported 499 500 ;;; Inline Babel Call 501 ;; 502 ;; Inline Babel Calls are ignored. 503 504 ;;; Inline Src Block 505 506 (defun org-man-inline-src-block (inline-src-block _contents info) 507 "Transcode an INLINE-SRC-BLOCK element from Org to Man. 508 CONTENTS holds the contents of the item. INFO is a plist holding 509 contextual information." 510 (let* ((code (org-element-property :value inline-src-block))) 511 (cond 512 ((plist-get info :man-source-highlight) 513 (let* ((tmpdir temporary-file-directory) 514 (in-file (make-temp-name 515 (expand-file-name "srchilite" tmpdir))) 516 (out-file (make-temp-name 517 (expand-file-name "reshilite" tmpdir))) 518 (org-lang (org-element-property :language inline-src-block)) 519 (lst-lang 520 (and org-lang 521 (cadr (assq (intern org-lang) 522 (plist-get info :man-source-highlight-langs))))) 523 524 (cmd (concat (expand-file-name "source-highlight") 525 " -s " lst-lang 526 " -f groff_man" 527 " -i " in-file 528 " -o " out-file ))) 529 530 (if lst-lang 531 (let ((code-block "" )) 532 (with-temp-file in-file (insert code)) 533 (shell-command cmd) 534 (setq code-block (org-file-contents out-file)) 535 (delete-file in-file) 536 (delete-file out-file) 537 code-block) 538 (format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n" 539 (org-man--protect-example code))))) 540 541 ;; Do not use a special package: transcode it verbatim. 542 (t 543 (concat ".RS\n.nf\n" "\\fC" "\n" (org-man--protect-example code) "\n" 544 "\\fP\n.fi\n.RE\n"))))) 545 546 547 ;;; Inlinetask 548 ;;; Italic 549 550 (defun org-man-italic (_italic contents _info) 551 "Transcode ITALIC from Org to Man. 552 CONTENTS is the text with italic markup. INFO is a plist holding 553 contextual information." 554 (format "\\fI%s\\fP" contents)) 555 556 557 ;;; Item 558 559 560 (defun org-man-item (item contents info) 561 "Transcode an ITEM element from Org to Man. 562 CONTENTS holds the contents of the item. INFO is a plist holding 563 contextual information." 564 (let* ((bullet (org-element-property :bullet item)) 565 (type (org-element-property :type (org-element-parent item))) 566 (checkbox (pcase (org-element-property :checkbox item) 567 (`on "\\o'\\(sq\\(mu'") 568 (`off "\\(sq ") 569 (`trans "\\o'\\(sq\\(mi'"))) 570 571 (tag (let ((tag (org-element-property :tag item))) 572 ;; Check-boxes must belong to the tag. 573 (and tag (format "\\fB%s\\fP" 574 (concat checkbox 575 (org-export-data tag info))))))) 576 577 (if (and (null tag) (null checkbox)) 578 (let* ((bullet (org-trim bullet)) 579 (marker (cond ((string= "-" bullet) "\\(em") 580 ((string= "*" bullet) "\\(bu") 581 ((eq type 'ordered) 582 (format "%s " (org-trim bullet))) 583 (t "\\(dg")))) 584 (concat ".IP " marker " 4\n" 585 (org-trim (or contents " " )))) 586 (concat ".TP\n" (or tag (concat " " checkbox)) "\n" 587 (org-trim (or contents " " )))))) 588 589 ;;; Keyword 590 591 592 (defun org-man-keyword (keyword _contents _info) 593 "Transcode a KEYWORD element from Org to Man. 594 CONTENTS is nil. INFO is a plist holding contextual information." 595 (let ((key (org-element-property :key keyword)) 596 (value (org-element-property :value keyword))) 597 (cond 598 ((string= key "MAN") value) 599 ((string= key "INDEX") nil) 600 ((string= key "TOC" ) nil)))) 601 602 603 ;;; Line Break 604 605 (defun org-man-line-break (_line-break _contents _info) 606 "Transcode a LINE-BREAK object from Org to Man. 607 CONTENTS is nil. INFO is a plist holding contextual information." 608 "\n.br\n") 609 610 611 ;;; Link 612 613 614 (defun org-man-link (link desc info) 615 "Transcode a LINK object from Org to Man. 616 617 DESC is the description part of the link, or the empty string. 618 INFO is a plist holding contextual information. See 619 `org-export-data'." 620 (let* ((type (org-element-property :type link)) 621 (raw-path (org-element-property :path link)) 622 ;; Ensure DESC really exists, or set it to nil. 623 (desc (and (not (string= desc "")) desc)) 624 (path (pcase type 625 ("file" (org-export-file-uri raw-path)) 626 (_ (concat type ":" raw-path))))) 627 (cond 628 ;; Link type is handled by a special function. 629 ((org-export-custom-protocol-maybe link desc 'man info)) 630 ;; External link with a description part. 631 ((and path desc) (format "%s \\fBat\\fP \\fI%s\\fP" path desc)) 632 ;; External link without a description part. 633 (path (format "\\fI%s\\fP" path)) 634 ;; No path, only description. Try to do something useful. 635 (t (format "\\fI%s\\fP" desc))))) 636 637 ;;;; Node Property 638 639 (defun org-man-node-property (node-property _contents _info) 640 "Transcode a NODE-PROPERTY element from Org to Man. 641 CONTENTS is nil. INFO is a plist holding contextual 642 information." 643 (format "%s:%s" 644 (org-element-property :key node-property) 645 (let ((value (org-element-property :value node-property))) 646 (if value (concat " " value) "")))) 647 648 ;;; Paragraph 649 650 (defun org-man-paragraph (paragraph contents _info) 651 "Transcode a PARAGRAPH element from Org to Man. 652 CONTENTS is the contents of the paragraph, as a string. INFO is 653 the plist used as a communication channel." 654 (let ((parent (org-element-parent paragraph))) 655 (when parent 656 (let ((parent-type (org-element-type parent)) 657 (fixed-paragraph "")) 658 (cond ((and (eq parent-type 'item) 659 (org-element-property :bullet parent)) 660 (setq fixed-paragraph (concat "" contents))) 661 ((eq parent-type 'section) 662 (setq fixed-paragraph (concat ".PP\n" contents))) 663 ((eq parent-type 'footnote-definition) 664 (setq fixed-paragraph contents)) 665 (t (setq fixed-paragraph (concat "" contents)))) 666 fixed-paragraph)))) 667 668 669 ;;; Plain List 670 671 (defun org-man-plain-list (_plain-list contents _info) 672 "Transcode a PLAIN-LIST element from Org to Man. 673 CONTENTS is the contents of the list. INFO is a plist holding 674 contextual information." 675 contents) 676 677 ;;; Plain Text 678 679 (defun org-man-plain-text (text info) 680 "Transcode a TEXT string from Org to Man. 681 TEXT is the string to transcode. INFO is a plist holding 682 contextual information." 683 (let ((output text)) 684 ;; Protect various chars. 685 (setq output (replace-regexp-in-string 686 "\\(?:[^\\]\\|^\\)\\(\\\\\\)\\(?:[^%$#&{}~^_\\]\\|$\\)" 687 "$\\" output nil t 1)) 688 ;; Activate smart quotes. Be sure to provide original TEXT string 689 ;; since OUTPUT may have been modified. 690 (when (plist-get info :with-smart-quotes) 691 (setq output (org-export-activate-smart-quotes output :utf-8 info text))) 692 ;; Handle break preservation if required. 693 (when (plist-get info :preserve-breaks) 694 (setq output (replace-regexp-in-string "\\(\\\\\\\\\\)?[ \t]*\n" ".br\n" 695 output))) 696 ;; Return value. 697 output)) 698 699 700 701 ;;; Planning 702 703 704 ;;; Property Drawer 705 706 (defun org-man-property-drawer (_property-drawer contents _info) 707 "Transcode a PROPERTY-DRAWER element from Org to Man. 708 CONTENTS holds the contents of the drawer. INFO is a plist 709 holding contextual information." 710 (and (org-string-nw-p contents) 711 (format ".RS\n.nf\n%s\n.fi\n.RE" contents))) 712 713 ;;; Quote Block 714 715 (defun org-man-quote-block (quote-block contents _info) 716 "Transcode a QUOTE-BLOCK element from Org to Man. 717 CONTENTS holds the contents of the block. INFO is a plist 718 holding contextual information." 719 (org-man--wrap-label 720 quote-block 721 (format ".RS\n%s\n.RE" contents))) 722 723 724 ;;; Radio Target 725 726 (defun org-man-radio-target (_radio-target text _info) 727 "Transcode a RADIO-TARGET object from Org to Man. 728 TEXT is the text of the target. INFO is a plist holding 729 contextual information." 730 text) 731 732 733 ;;; Section 734 735 (defun org-man-section (_section contents _info) 736 "Transcode a SECTION element from Org to Man. 737 CONTENTS holds the contents of the section. INFO is a plist 738 holding contextual information." 739 contents) 740 741 742 ;;; Special Block 743 744 (defun org-man-special-block (special-block contents _info) 745 "Transcode a SPECIAL-BLOCK element from Org to Man. 746 CONTENTS holds the contents of the block. INFO is a plist 747 holding contextual information." 748 (org-man--wrap-label special-block (format "%s\n" contents))) 749 750 751 ;;; Src Block 752 753 (defun org-man-src-block (src-block _contents info) 754 "Transcode a SRC-BLOCK element from Org to Man. 755 CONTENTS holds the contents of the item. INFO is a plist holding 756 contextual information." 757 (if (not (plist-get info :man-source-highlight)) 758 (format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n" 759 (org-man--protect-example (org-export-format-code-default src-block info))) 760 (let* ((tmpdir temporary-file-directory) 761 (in-file (make-temp-name (expand-file-name "srchilite" tmpdir))) 762 (out-file (make-temp-name (expand-file-name "reshilite" tmpdir))) 763 (code (org-element-property :value src-block)) 764 (org-lang (org-element-property :language src-block)) 765 (lst-lang 766 (and org-lang 767 (cadr (assq (intern org-lang) 768 (plist-get info :man-source-highlight-langs))))) 769 (cmd (concat "source-highlight" 770 " -s " lst-lang 771 " -f groff_man " 772 " -i " in-file 773 " -o " out-file))) 774 (if lst-lang 775 (let ((code-block "")) 776 (with-temp-file in-file (insert code)) 777 (shell-command cmd) 778 (setq code-block (org-file-contents out-file)) 779 (delete-file in-file) 780 (delete-file out-file) 781 code-block) 782 (format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code)))))) 783 784 785 ;;; Statistics Cookie 786 787 (defun org-man-statistics-cookie (statistics-cookie _contents _info) 788 "Transcode a STATISTICS-COOKIE object from Org to Man. 789 CONTENTS is nil. INFO is a plist holding contextual information." 790 (org-element-property :value statistics-cookie)) 791 792 793 ;;; Strike-Through 794 795 (defun org-man-strike-through (_strike-through contents _info) 796 "Transcode STRIKE-THROUGH from Org to Man. 797 CONTENTS is the text with strike-through markup. INFO is a plist 798 holding contextual information." 799 (format "\\fI%s\\fP" contents)) 800 801 ;;; Subscript 802 803 (defun org-man-subscript (_subscript contents _info) 804 "Transcode a SUBSCRIPT object from Org to Man. 805 CONTENTS is the contents of the object. INFO is a plist holding 806 contextual information." 807 (format "\\d\\s-2%s\\s+2\\u" contents)) 808 809 ;;; Superscript "^_%s$ 810 811 (defun org-man-superscript (_superscript contents _info) 812 "Transcode a SUPERSCRIPT object from Org to Man. 813 CONTENTS is the contents of the object. INFO is a plist holding 814 contextual information." 815 (format "\\u\\s-2%s\\s+2\\d" contents)) 816 817 818 ;;; Table 819 ;; 820 ;; `org-man-table' is the entry point for table transcoding. It 821 ;; takes care of tables with a "verbatim" attribute. Otherwise, it 822 ;; delegates the job to either `org-man-table--table.el-table' or 823 ;; `org-man-table--org-table' functions, depending of the type of 824 ;; the table. 825 ;; 826 ;; `org-man-table--align-string' is a subroutine used to build 827 ;; alignment string for Org tables. 828 829 (defun org-man-table (table contents info) 830 "Transcode a TABLE element from Org to Man. 831 CONTENTS is the contents of the table. INFO is a plist holding 832 contextual information." 833 (cond 834 ;; Case 1: verbatim table. 835 ((or (plist-get info :man-tables-verbatim) 836 (let ((attr (read (format "(%s)" 837 (mapconcat 838 #'identity 839 (org-element-property :attr_man table) 840 " "))))) 841 842 (and attr (plist-get attr :verbatim)))) 843 844 (format ".nf\n\\fC%s\\fP\n.fi" 845 ;; Re-create table, without affiliated keywords. 846 (org-man--protect-example 847 (org-trim 848 (org-element-interpret-data 849 `(table nil ,@(org-element-contents table))))))) 850 ;; Case 2: Standard table. 851 (t (org-man-table--org-table table contents info)))) 852 853 (defun org-man-table--align-string (divider table info) 854 "Return an appropriate Man alignment string. 855 TABLE is the considered table. INFO is a plist used as 856 a communication channel." 857 (let (alignment) 858 ;; Extract column groups and alignment from first (non-rule) row. 859 (org-element-map 860 (org-element-map table 'table-row 861 (lambda (row) 862 (and (eq (org-element-property :type row) 'standard) row)) 863 info 'first-match) 864 'table-cell 865 (lambda (cell) 866 (let* ((borders (org-export-table-cell-borders cell info)) 867 (raw-width (org-export-table-cell-width cell info)) 868 (width-cm (when raw-width (/ raw-width 5))) 869 (width (if raw-width (format "w(%dc)" 870 (if (< width-cm 1) 1 width-cm)) ""))) 871 ;; Check left border for the first cell only. 872 (when (and (memq 'left borders) (not alignment)) 873 (push "|" alignment)) 874 (push 875 (concat (pcase (org-export-table-cell-alignment cell info) 876 (`left "l") (`right "r") (`center "c")) 877 width 878 divider) 879 alignment) 880 (when (memq 'right borders) (push "|" alignment)))) 881 info) 882 (apply #'concat (reverse alignment)))) 883 884 (defun org-man-table--org-table (table contents info) 885 "Return appropriate Man code for an Org table. 886 887 TABLE is the table type element to transcode. CONTENTS is its 888 contents, as a string. INFO is a plist used as a communication 889 channel. 890 891 This function assumes TABLE has `org' as its `:type' attribute." 892 (let* ((attr (org-export-read-attribute :attr_man table)) 893 (caption (and (not (plist-get attr :disable-caption)) 894 (org-man--caption/label-string table info))) 895 (divider (if (plist-get attr :divider) "|" " ")) 896 897 ;; Determine alignment string. 898 (alignment (org-man-table--align-string divider table info)) 899 ;; Extract others display options. 900 901 (lines (org-split-string contents "\n")) 902 903 (attr-list 904 (delq nil 905 (list 906 (and (plist-get attr :expand) "expand") 907 (let ((placement (plist-get attr :placement))) 908 (cond ((string= placement 'center) "center") 909 ((string= placement 'left) nil) 910 ((plist-get info :man-tables-centered) "center") 911 (t ""))) 912 (or (plist-get attr :boxtype) "box")))) 913 914 (title-line (plist-get attr :title-line)) 915 (long-cells (plist-get attr :long-cells)) 916 917 (table-format (concat 918 (format "%s" (or (car attr-list) "" )) 919 (or 920 (let ((output-list '())) 921 (when (cdr attr-list) 922 (dolist (attr-item (cdr attr-list)) 923 (setq output-list (concat output-list (format ",%s" attr-item))))) 924 output-list) 925 ""))) 926 927 (first-line (when lines (org-split-string (car lines) "\t")))) 928 ;; Prepare the final format string for the table. 929 930 931 (cond 932 ;; Others. 933 (lines (concat ".TS\n " table-format ";\n" 934 935 (format "%s.\n" 936 (let ((final-line "")) 937 (when title-line 938 (dotimes (_ (length first-line)) 939 (setq final-line (concat final-line "cb" divider)))) 940 941 (setq final-line (concat final-line "\n")) 942 943 (if alignment 944 (setq final-line (concat final-line alignment)) 945 (dotimes (_ (length first-line)) 946 (setq final-line (concat final-line "c" divider)))) 947 final-line )) 948 949 (format "%s.TE\n" 950 (let ((final-line "") 951 (long-line "") 952 (lines (org-split-string contents "\n"))) 953 954 (dolist (line-item lines) 955 (setq long-line "") 956 957 (if long-cells 958 (progn 959 (if (string= line-item "_") 960 (setq long-line (format "%s\n" line-item)) 961 ;; else string = 962 (let ((cell-item-list (org-split-string line-item "\t"))) 963 (dolist (cell-item cell-item-list) 964 965 (cond ((eq cell-item (car (last cell-item-list))) 966 (setq long-line (concat long-line 967 (format "T{\n%s\nT}\t\n" cell-item )))) 968 (t 969 (setq long-line (concat long-line 970 (format "T{\n%s\nT}\t" cell-item )))))) 971 long-line)) 972 ;; else long cells 973 (setq final-line (concat final-line long-line ))) 974 975 (setq final-line (concat final-line line-item "\n")))) 976 final-line)) 977 978 (and caption (format ".TB \"%s\"" caption))))))) 979 980 ;;; Table Cell 981 982 (defun org-man-table-cell (table-cell contents info) 983 "Transcode a TABLE-CELL element from Org to Man. 984 CONTENTS is the cell contents. INFO is a plist used as 985 a communication channel." 986 (concat 987 (let ((scientific-format (plist-get info :man-table-scientific-notation))) 988 (if (and contents 989 scientific-format 990 (string-match orgtbl-exp-regexp contents)) 991 ;; Use appropriate format string for scientific notation. 992 (format scientific-format 993 (match-string 1 contents) 994 (match-string 2 contents)) 995 contents)) 996 (when (org-export-get-next-element table-cell info) "\t"))) 997 998 999 ;;; Table Row 1000 1001 (defun org-man-table-row (table-row contents info) 1002 "Transcode a TABLE-ROW element from Org to Man. 1003 CONTENTS is the contents of the row. INFO is a plist used as 1004 a communication channel." 1005 ;; Rules are ignored since table separators are deduced from borders 1006 ;; of the current row. 1007 (when (eq (org-element-property :type table-row) 'standard) 1008 (let ((borders 1009 ;; TABLE-ROW's borders are extracted from its first cell. 1010 (org-export-table-cell-borders 1011 (car (org-element-contents table-row)) info))) 1012 (concat 1013 (cond ((and (memq 'top borders) (memq 'above borders)) "_\n")) 1014 contents 1015 (cond ((and (memq 'bottom borders) (memq 'below borders)) "\n_") 1016 ((memq 'below borders) "\n_")))))) 1017 1018 1019 ;;; Target 1020 1021 (defun org-man-target (target _contents info) 1022 "Transcode a TARGET object from Org to Man. 1023 CONTENTS is nil. INFO is a plist holding contextual 1024 information." 1025 (format "\\fI%s\\fP" (org-export-get-reference target info))) 1026 1027 1028 ;;; Timestamp 1029 1030 (defun org-man-timestamp (_timestamp _contents _info) 1031 "Transcode a TIMESTAMP object from Org to Man. 1032 CONTENTS is nil. INFO is a plist holding contextual information." 1033 "") 1034 1035 1036 ;;; Underline 1037 1038 (defun org-man-underline (_underline contents _info) 1039 "Transcode UNDERLINE from Org to Man. 1040 CONTENTS is the text with underline markup. INFO is a plist 1041 holding contextual information." 1042 (format "\\fI%s\\fP" contents)) 1043 1044 1045 ;;; Verbatim 1046 1047 (defun org-man-verbatim (verbatim _contents _info) 1048 "Transcode a VERBATIM object from Org to Man." 1049 (format "\\fI%s\\fP" 1050 (org-man--protect-text (org-element-property :value verbatim)))) 1051 1052 1053 ;;; Verse Block 1054 1055 (defun org-man-verse-block (_verse-block contents _info) 1056 "Transcode a VERSE-BLOCK element from Org to Man. 1057 CONTENTS is verse block contents. INFO is a plist holding 1058 contextual information." 1059 (format ".RS\n.ft I\n%s\n.ft\n.RE" contents)) 1060 1061 1062 1063 ;;; Interactive functions 1064 1065 (defun org-man-export-to-man 1066 (&optional async subtreep visible-only body-only ext-plist) 1067 "Export current buffer to a Man file. 1068 1069 If narrowing is active in the current buffer, only export its 1070 narrowed part. 1071 1072 If a region is active, export that region. 1073 1074 A non-nil optional argument ASYNC means the process should happen 1075 asynchronously. The resulting file should be accessible through 1076 the `org-export-stack' interface. 1077 1078 When optional argument SUBTREEP is non-nil, export the sub-tree 1079 at point, extracting information from the headline properties 1080 first. 1081 1082 When optional argument VISIBLE-ONLY is non-nil, don't export 1083 contents of hidden elements. 1084 1085 When optional argument BODY-ONLY is non-nil, only the body 1086 without any markers. 1087 1088 EXT-PLIST, when provided, is a property list with external 1089 parameters overriding Org default settings, but still inferior to 1090 file-local settings. 1091 1092 Return output file's name." 1093 (interactive) 1094 (let ((outfile (org-export-output-file-name ".man" subtreep))) 1095 (org-export-to-file 'man outfile 1096 async subtreep visible-only body-only ext-plist))) 1097 1098 (defun org-man-export-to-pdf 1099 (&optional async subtreep visible-only body-only ext-plist) 1100 "Export current buffer to Groff then process through to PDF. 1101 1102 If narrowing is active in the current buffer, only export its 1103 narrowed part. 1104 1105 If a region is active, export that region. 1106 1107 A non-nil optional argument ASYNC means the process should happen 1108 asynchronously. The resulting file should be accessible through 1109 the `org-export-stack' interface. 1110 1111 When optional argument SUBTREEP is non-nil, export the sub-tree 1112 at point, extracting information from the headline properties 1113 first. 1114 1115 When optional argument VISIBLE-ONLY is non-nil, don't export 1116 contents of hidden elements. 1117 1118 When optional argument BODY-ONLY is non-nil, only write between 1119 markers. 1120 1121 EXT-PLIST, when provided, is a property list with external 1122 parameters overriding Org default settings, but still inferior to 1123 file-local settings. 1124 1125 Return PDF file's name." 1126 (interactive) 1127 (let ((outfile (org-export-output-file-name ".man" subtreep))) 1128 (org-export-to-file 'man outfile 1129 async subtreep visible-only body-only ext-plist 1130 #'org-latex-compile))) 1131 1132 (defun org-man-compile (file) 1133 "Compile a Groff file. 1134 1135 FILE is the name of the file being compiled. Processing is done 1136 through the command specified in `org-man-pdf-process'. 1137 1138 Return PDF file name or an error if it couldn't be produced." 1139 (message "Processing Groff file %s..." file) 1140 (let ((output (org-compile-file file org-man-pdf-process "pdf"))) 1141 (when org-man-remove-logfiles 1142 (let ((base (file-name-sans-extension output))) 1143 (dolist (ext org-man-logfiles-extensions) 1144 (let ((file (concat base "." ext))) 1145 (when (file-exists-p file) (delete-file file)))))) 1146 (message "Process completed.") 1147 output)) 1148 1149 (provide 'ox-man) 1150 1151 ;;; ox-man.el ends here