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