1 ;;; org-export-latex.el --- LaTeX exporter for Org-mode
2 ;; Copyright (C) 2007 Free Software Foundation, Inc.
4 ;; Author: Bastien Guerry <bzg AT altern DOT org>
5 ;; Maintainer: Bastien Guerry <bzg AT altern DOT org>
6 ;; Version: $Id: org-export-latex.el,v 0.26b 2007/08/21 14:46:58 guerry Exp guerry $
7 ;; Keywords: org organizer latex export convert
8 ;; X-URL: <http://www.cognition.ens.fr/~guerry/u/org-export-latex.el>
10 ;; This file is part of GNU Emacs.
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program; if not, write to the Free Software
24 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 ;; This library is a LaTeX exporter for org-mode.
30 ;; Put this file into your load-path and the following into your ~/.emacs:
31 ;; (require 'org-export-latex)
33 ;; The interactive functions are similar to those of the HTML exporter:
35 ;; M-x `org-export-as-latex'
36 ;; M-x `org-export-as-latex-batch'
37 ;; M-x `org-export-as-latex-to-buffer'
38 ;; M-x `org-export-region-as-latex'
39 ;; M-x `org-replace-region-by-latex'
43 ;; I started this piece of code in may 2007. Special thanks to Carsten
44 ;; Dominik for helping me on this.
52 (defvar org-latex-options-plist nil
)
53 (defvar org-latex-todo-keywords-1 nil
)
54 (defvar org-latex-all-targets-regexp nil
)
55 (defvar org-latex-add-level
0)
56 (defvar org-latex-sectioning-depth
0)
58 (defvar org-latex-special-string-regexps
63 "A list of regexps to convert as special keywords.")
65 (defcustom org-export-latex-sectioning-alist
66 '((1 "\\section{%s}" "\\section*{%s}")
67 (2 "\\subsection{%s}" "\\subsection*{%s}")
68 (3 "\\subsubsection{%s}" "\\subsubsection*{%s}")
69 (4 "\\paragraph{%s}" "\\paragraph*{%s}")
70 (5 "\\subparagraph{%s}" "\\subparagraph*{%s}"))
71 "Alist of LaTeX commands for inserting sections.
72 Here is the structure of each cell:
74 \(level unnumbered-section numbered-section\)
76 The %s formatter will be replaced by the title of the section."
77 :group
'org-export-latex
80 (defcustom org-export-latex-emphasis-alist
81 '(("*" "\\textbf{%s}")
83 ("_" "\\underline{%s}")
86 "Alist of LaTeX expressions to convert emphasis fontifiers."
87 :group
'org-export-latex
90 (defcustom org-export-latex-preamble
91 "\\documentclass[11pt,a4paper]{article}
92 \\usepackage[utf8]{inputenc}
93 \\usepackage[T1]{fontenc}
94 \\usepackage{hyperref}"
95 "Preamble to be inserted at the very beginning of the LaTeX export."
96 :group
'org-export-latex
99 (defcustom org-export-latex-date-format nil
100 "Format string for \\date{...}."
101 :group
'org-export-latex
104 (defcustom org-export-latex-packages-alist nil
105 "Alist of packages to be inserted in the preamble.
106 Each cell is of the forma \( option . package \).
110 \(setq org-export-latex-packages-alist
111 '((\"french\" \"babel\"))"
112 :group
'org-export-latex
115 (defcustom org-export-latex-low-levels
'description
116 "Choice for converting sections that are below the current
117 admitted level of sectioning. This can be either nil (ignore the
118 sections), 'description (convert them as description lists) or a
119 string to be used instead of \\section{%s} (a %s for inserted the
120 headline is mandatory)."
121 :group
'org-export-latex
122 :type
'(choice (const :tag
"Ignore" nil
)
123 (symbol :tag
"Convert as descriptive list" description
)
124 (string :tag
"Use a section string" :value
"\\subparagraph{%s}")))
126 (defcustom org-export-latex-remove-from-headines
127 '(:todo t
:priority t
:tags t
)
128 "A plist of keywords to remove from headlines.
129 Non-nil means remove this keyword type from the headline.
131 Don't remove the keys, just change their values."
133 :group
'org-export-latex
)
135 (defcustom org-export-latex-quotation-marks-convention
"en"
136 "Convention for conversion of the quotation marks.
137 This value is overriden by any infile language setup."
138 :group
'org-export-latex
139 :type
'(choice (string :tag
"english" "en")
140 (string :tag
"french" "fr")))
142 (defcustom org-export-latex-image-default-option
"width=10em"
143 "Default option for images."
144 :group
'org-export-latex
147 (defcustom org-export-latex-coding-system nil
148 "Coding system for the exported LaTex file."
149 :group
'org-export-latex
150 :type
'coding-system
)
152 ;; FIXME Do we want this one?
153 ;; (defun org-export-as-latex-and-open (arg) ...)
156 (defun org-export-as-latex-batch ()
157 "Call `org-export-as-latex', may be used in batch processing as
159 --load=$HOME/lib/emacs/org.el
160 --eval \"(setq org-export-headline-levels 2)\"
161 --visit=MyFile --funcall org-export-as-latex-batch"
162 (org-export-as-latex org-export-headline-levels
'hidden
))
165 (defun org-export-as-latex-to-buffer (arg)
166 "Call `org-exort-as-latex` with output to a temporary buffer.
167 No file is created. The prefix ARG is passed through to `org-export-as-latex'."
169 (org-export-as-latex arg nil nil
"*Org LaTeX Export*")
170 (switch-to-buffer-other-window "*Org LaTeX Export*"))
173 (defun org-replace-region-by-latex (beg end
)
174 "Replace the region from BEG to END with its LaTeX export.
175 It assumes the region has `org-mode' syntax, and then convert it to
176 LaTeX. This can be used in any buffer. For example, you could
177 write an itemized list in `org-mode' syntax in an LaTeX buffer and
178 then use this command to convert it."
181 (save-window-excursion
183 (setq latex
(org-export-region-as-latex
185 (setq reg
(buffer-substring beg end
)
186 buf
(get-buffer-create "*Org tmp*"))
192 (setq latex
(org-export-region-as-latex
193 (point-min) (point-max) t
'string
)))
195 (delete-region beg end
)
199 (defun org-export-region-as-latex (beg end
&optional body-only buffer
)
200 "Convert region from BEG to END in `org-mode' buffer to LaTeX.
201 If prefix arg BODY-ONLY is set, omit file header, footer, and table of
202 contents, and only produce the region of converted text, useful for
203 cut-and-paste operations.
204 If BUFFER is a buffer or a string, use/create that buffer as a target
205 of the converted LaTeX. If BUFFER is the symbol `string', return the
206 produced LaTeX as a string and leave not buffer behind. For example,
207 a Lisp program could call this function in the following way:
209 (setq latex (org-export-region-as-latex beg end t 'string))
211 When called interactively, the output buffer is selected, and shown
212 in a window. A non-interactive call will only retunr the buffer."
214 (when (interactive-p)
215 (setq buffer
"*Org LaTeX Export*"))
216 (let ((transient-mark-mode t
) (zmacs-regions t
)
219 (set-mark (point)) ;; to activate the region
221 (setq rtn
(org-export-as-latex
224 (if (fboundp 'deactivate-mark
) (deactivate-mark))
225 (if (and (interactive-p) (bufferp rtn
))
226 (switch-to-buffer-other-window rtn
)
230 (defun org-export-as-latex (arg &optional hidden ext-plist
232 "Export current buffer to a LaTeX file."
234 ;; Make sure we have a file name when we need it.
235 (when (and (not (or to-buffer body-only
))
236 (not buffer-file-name
))
237 (if (buffer-base-buffer)
238 (org-set-local 'buffer-file-name
239 (with-current-buffer (buffer-base-buffer)
241 (error "Need a file name to be able to export")))
243 (message "Exporting to LaTeX...")
244 (org-update-radio-target-regexp)
245 (org-export-latex-set-initial-vars ext-plist
)
246 (let* ((opt-plist org-latex-options-plist
)
247 (filename (concat (file-name-as-directory
248 (org-export-directory :LaTeX ext-plist
))
249 (file-name-sans-extension
250 (file-name-nondirectory ;sans-extension
251 buffer-file-name
)) ".tex"))
252 (filename (if (equal (file-truename filename
)
253 (file-truename buffer-file-name
))
254 (concat filename
".tex")
256 (buffer (if to-buffer
258 ((eq to-buffer
'string
) (get-buffer-create
259 "*Org LaTeX Export*"))
260 (t (get-buffer-create to-buffer
)))
261 (find-file-noselect filename
)))
262 (region-p (org-region-active-p))
263 (odd org-odd-levels-only
)
264 (preamble (org-export-latex-make-preamble opt-plist
))
265 (skip (plist-get opt-plist
:skip-before-1st-heading
))
266 (text (plist-get opt-plist
:text
))
267 (first-lines (if skip
"" (org-export-latex-first-lines)))
268 (coding-system (and (boundp 'buffer-file-coding-system
)
269 buffer-file-coding-system
))
270 (coding-system-for-write (or org-export-latex-coding-system
272 (save-buffer-coding-system (or org-export-latex-coding-system
274 (region (buffer-substring
275 (if region-p
(region-beginning) (point-min))
276 (if region-p
(region-end) (point-max))))
278 ;; FIXME Use org-cleaned-string-for-export instead, only when
279 ;; everyone uses Org >5.04
280 (org-latex-cleaned-string-for-export
284 :skip-before-1st-heading nil
285 :LaTeX-fragments nil
)))
288 (unless body-only
(insert preamble
))
289 (when text
(insert (org-export-latex-content text
) "\n\n"))
290 (unless skip
(insert first-lines
))
292 ;; handle the case where the region does not begin with a section
294 (insert (with-temp-buffer
295 (insert string-for-export
)
296 (org-export-latex-first-lines))))
298 (org-export-latex-global
300 (insert string-for-export
)
301 (goto-char (point-min))
302 (re-search-forward "^\\(\\*+\\) " nil t
)
303 (let* ((asters (length (match-string 1)))
304 (level (if odd
(- asters
2) (- asters
1))))
305 (setq org-latex-add-level
306 (if odd
(1- (/ (1+ asters
) 2)) (1- asters
)))
307 (org-export-latex-parse-global level odd
))))
309 (unless body-only
(insert "\n\\end{document}"))
310 (or to-buffer
(write-file filename
))
311 (goto-char (point-min))
312 (message "Exporting to LaTeX...done")
313 (if (eq to-buffer
'string
)
314 (prog1 (buffer-substring (point-min) (point-max))
315 (kill-buffer (current-buffer)))
318 (defun org-export-latex-set-initial-vars (ext-plist)
319 "Store org local variables required for LaTeX export.
320 EXT-PLIST is an optional additional plist."
321 (setq org-latex-todo-keywords-1 org-todo-keywords-1
322 org-latex-all-targets-regexp
323 (org-make-target-link-regexp (org-all-targets))
324 org-latex-options-plist
325 (org-combine-plists (org-default-export-plist) ext-plist
326 (org-infile-export-plist))
327 org-latex-sectioning-depth
328 (let ((hl-levels (plist-get org-latex-options-plist
:headline-levels
))
329 (sec-depth (length org-export-latex-sectioning-alist
)))
330 ;; Fall back on org-export-latex-sectioning-alist length if
331 ;; headline-levels goes beyond it
332 (if (> hl-levels sec-depth
) sec-depth hl-levels
))))
334 (defun org-export-latex-make-preamble (opt-plist)
335 "Make the LaTeX preamble and return it as a string.
336 Argument OPT-PLIST is the options plist for current buffer."
337 (let ((toc (plist-get opt-plist
:table-of-contents
)))
338 (format (concat org-export-latex-preamble
351 (if org-export-latex-packages-alist
352 (mapconcat (lambda(p)
353 (if (equal "" (car p
))
354 (format "\\usepackage{%s}" (cadr p
))
355 (format "\\usepackage[%s]{%s}"
357 org-export-latex-packages-alist
"\n") "")
358 (or (plist-get opt-plist
:title
)
360 (plist-get opt-plist
:skip-before-1st-heading
))
361 (org-export-grab-title-from-buffer))
362 (and buffer-file-name
363 (file-name-sans-extension
364 (file-name-nondirectory buffer-file-name
)))
366 (if (plist-get opt-plist
:author-info
)
367 (format "\\author{%s}"
368 (or (plist-get opt-plist
:author
) user-full-name
))
369 (format "%%\\author{%s}"
370 (or (plist-get opt-plist
:author
) user-full-name
)))
371 (if (plist-get opt-plist
:timestamps
)
373 (format-time-string (or org-export-latex-date-format
374 (car org-time-stamp-formats
))))
376 (if (and (plist-get opt-plist
:section-numbers
) toc
)
377 (format "\\setcounter{tocdepth}{%s}"
378 (plist-get opt-plist
:headline-levels
)) "")
379 (if (and (plist-get opt-plist
:section-numbers
) toc
)
380 "\\tableofcontents" ""))))
382 (defun org-export-latex-first-lines (&optional comments
)
383 "Export the first lines before first headline.
384 COMMENTS is either nil to replace them with the empty string or a
385 formatting string like %%%%s if we want to comment them out."
387 (goto-char (point-min))
388 (let* ((end (if (re-search-forward "^\\*" nil t
)
389 (goto-char (match-beginning 0))
390 (goto-char (point-max)))))
391 (org-export-latex-content
392 (org-latex-cleaned-string-for-export
393 (buffer-substring (point-min) end
)
397 :skip-before-1st-heading nil
398 :LaTeX-fragments nil
)))))
400 (defun org-export-latex-parse-global (level odd
)
401 "Parse the current buffer recursively, starting at LEVEL.
402 If ODD is non-nil, assume the buffer only contains odd sections.
403 Return A list reflecting the document structure."
405 (goto-char (point-min))
406 (let* ((cnt 0) output
407 (depth org-latex-sectioning-depth
))
408 (while (re-search-forward
409 (concat "^\\(\\(?:\\*\\)\\{"
410 (number-to-string (+ (if odd
2 1) level
))
412 ;; make sure that there is no upper heading
417 (concat "^\\(\\(?:\\*\\)\\{"
418 (number-to-string level
)
419 "\\}\\) \\(.*\\)$") nil t
)))) t
)
421 (let* ((pos (match-beginning 0))
422 (heading (match-string 2))
423 (nlevel (if odd
(/ (+ 3 level
) 2) (1+ level
))))
428 (if (re-search-forward
429 (concat "^\\(\\(?:\\*\\)\\{"
430 (number-to-string (+ (if odd
2 1) level
))
431 "\\}\\) \\(.*\\)$") nil t
)
434 (goto-char (point-min))
442 `(heading .
,heading
)
443 `(content .
,(org-export-latex-parse-content))
444 `(subcontent .
,(org-export-latex-parse-subcontent
449 (defun org-export-latex-parse-content ()
450 "Extract the content of a section."
452 (end (if (re-search-forward "^\\(\\*\\)+ .*$" nil t
)
453 (progn (beginning-of-line) (point))
455 (buffer-substring beg end
)))
457 (defun org-export-latex-parse-subcontent (level odd
)
458 "Extract the subcontent of a section at LEVEL.
459 If ODD Is non-nil, assume subcontent only contains odd sections."
460 (if (not (re-search-forward
461 (concat "^\\(\\(?:\\*\\)\\{"
462 (number-to-string (+ (if odd
4 2) level
))
465 nil
; subcontent is nil
466 (org-export-latex-parse-global (+ (if odd
2 1) level
) odd
)))
468 (defun org-export-latex-global (content)
469 "Export CONTENT to LaTeX.
470 CONTENT is an element of the list produced by
471 `org-export-latex-parse-global'."
472 (if (eq (car content
) 'subcontent
)
473 (mapc 'org-export-latex-sub
(cdr content
))
474 (org-export-latex-sub (car content
))))
476 (defun org-export-latex-sub (subcontent)
477 "Export the list SUBCONTENT to LaTeX.
478 SUBCONTENT is an alist containing information about the headline
480 (mapc (lambda(x) (org-export-latex-subcontent x
)) subcontent
))
482 (defun org-export-latex-subcontent (subcontent)
483 "Export each cell of SUBCONTENT to LaTeX."
484 (let ((heading (org-export-latex-fontify-headline
485 (cdr (assoc 'heading subcontent
))))
486 (level (- (cdr (assoc 'level subcontent
))
487 org-latex-add-level
))
488 (occur (number-to-string (cdr (assoc 'occur subcontent
))))
489 (content (cdr (assoc 'content subcontent
)))
490 (subcontent (cadr (assoc 'subcontent subcontent
)))
491 (num (plist-get org-latex-options-plist
:section-numbers
)))
494 ((<= level org-latex-sectioning-depth
)
495 (let ((sec (assoc level org-export-latex-sectioning-alist
)))
496 (insert (format (if num
(cadr sec
) (caddr sec
)) heading
) "\n"))
497 (insert (org-export-latex-content content
))
498 (cond ((stringp subcontent
) (insert subcontent
))
499 ((listp subcontent
) (org-export-latex-sub subcontent
))))
500 ;; At a level under the hl option: we can drop this subsection
501 ((> level org-latex-sectioning-depth
)
502 (cond ((eq org-export-latex-low-levels
'description
)
503 (insert (format "\\begin{description}\n\n\\item[%s]\n\n" heading
))
504 (insert (org-export-latex-content content
))
505 (cond ((stringp subcontent
) (insert subcontent
))
506 ((listp subcontent
) (org-export-latex-sub subcontent
)))
507 (insert "\\end{description}\n"))
508 ((stringp org-export-latex-low-levels
)
509 (insert (format org-export-latex-low-levels heading
) "\n")
510 (insert (org-export-latex-content content
))
511 (cond ((stringp subcontent
) (insert subcontent
))
512 ((listp subcontent
) (org-export-latex-sub subcontent
)))))))))
514 (defun org-export-latex-special-keywords-maybe (remove-list)
515 "Maybe remove keywords depending on rules in REMOVE-LIST."
516 (goto-char (point-min))
517 (let ((re-todo (mapconcat 'identity org-latex-todo-keywords-1
"\\|")))
518 ;; convert TODO keywords
519 (when (re-search-forward (concat "^\\(" re-todo
"\\)") nil t
)
520 (if (plist-get remove-list
:todo
)
522 (replace-match (format "\\texttt{%s}" (match-string 1)) t t
)))
523 ;; convert priority string
524 (when (re-search-forward "\\[\\\\#.\\]" nil t
)
525 (if (plist-get remove-list
:priority
)
527 (replace-match (format "\\texttt{%s}" (match-string 0)) t t
)))
529 (when (re-search-forward "\\(:[a-zA-Z0-9]+\\)+:" nil t
)
530 (if (plist-get remove-list
:tags
)
532 (replace-match (format "\\texttt{%s}" (match-string 0)) t t
)))))
534 (defun org-export-latex-fontify-headline (headline)
535 "Fontify special words in a HEADLINE."
537 ;; FIXME: org-inside-LaTeX-fragment-p doesn't work when the $...$ is at
538 ;; the beginning of the buffer - inserting "\n" is safe here though.
539 (insert "\n" headline
)
540 (goto-char (point-min))
541 (org-export-latex-special-chars
542 (plist-get org-latex-options-plist
:sub-superscript
))
543 (when (plist-get org-latex-options-plist
:emphasize
)
544 (org-export-latex-fontify))
545 (org-export-latex-special-keywords-maybe
546 org-export-latex-remove-from-headines
)
547 (org-export-latex-links)
548 (org-trim (buffer-substring-no-properties (point-min) (point-max)))))
550 (defun org-export-latex-content (content)
551 "Convert CONTENT string to LaTeX."
554 (org-export-latex-quotation-marks)
555 (org-export-latex-special-chars
556 (plist-get org-latex-options-plist
:sub-superscript
))
557 (when (plist-get org-latex-options-plist
:emphasize
)
558 (org-export-latex-fontify))
559 (org-export-latex-links)
560 (org-export-latex-special-keywords)
561 (org-export-latex-itemize)
562 (org-export-latex-enumerate)
563 (org-export-latex-tables
564 (plist-get org-latex-options-plist
:tables
))
565 (org-export-latex-fixed-width
566 (plist-get org-latex-options-plist
:fixed-width
))
567 (org-export-fix-invisible-strings)
568 (buffer-substring (point-min) (point-max))))
570 (defun org-export-fix-invisible-strings ()
571 "Comment out (INVISIBLE) warnings."
572 (goto-char (point-min))
573 (while (re-search-forward "(INVISIBLE)" nil t
)
574 (replace-match "%\\&")))
576 (defun org-export-latex-quotation-marks ()
577 "Export question marks depending on language conventions.
578 Local definition of the language overrides
579 `org-export-latex-quotation-marks-convention' which overrides
580 `org-export-default-language'."
581 (let* ((lang (or (plist-get org-latex-options-plist
:language
)
582 org-export-latex-quotation-marks-convention
))
583 (quote-rpl (if (equal lang
"fr")
584 '(("\\(\\s-\\)\"" "«~")
585 ("\\(\\S-\\)\"" "~»")
587 '(("\\(\\s-\\)\"" "``")
588 ("\\(\\S-\\)\"" "''")
589 ("\\(\\s-\\)'" "`")))))
590 (mapc (lambda(l) (goto-char (point-min))
591 (while (re-search-forward (car l
) nil t
)
592 (let ((rpl (concat (match-string 1) (cadr l
))))
593 (org-latex-protect rpl
)
595 (replace-match rpl t t
))))) quote-rpl
)))
597 ;; | chars/string in Org | normal environment | math environment |
598 ;; |-----------------------+-----------------------+-----------------------|
599 ;; | & # % $ | \& \# \% \$ | \& \# \% \$ |
600 ;; | { } _ ^ \ | \ { \ } \_ \^ \\ | { } _ ^ \ |
601 ;; |-----------------------+-----------------------+-----------------------|
602 ;; | a_b and a^b | $a_b$ and $a^b$ | a_b and a^b |
603 ;; | a_abc and a_{abc} | $a_a$bc and $a_{abc}$ | a_abc and a_{abc} |
604 ;; | \tau and \mu | $\tau$ and $\mu$ | \tau and \mu |
605 ;; |-----------------------+-----------------------+-----------------------|
606 ;; | \_ \^ | \_ \^ | \_ \^ |
607 ;; | \(a=\mu\mbox{m}\) | \(a=\mu\mbox{m}\) | \(a=\mu\mbox{m}\) |
608 ;; | \[\beta^2-a=0\] | \[\beta^2-a=0\] | \[\beta^2-a=0\] |
609 ;; | $x=22\tau$ | $x=22\tau$ | $x=22\tau$ |
610 ;; | $$\alpha=\sqrt{a^3}$$ | $$\alpha=\sqrt{a^3}$$ | $$\alpha=\sqrt{a^3}$$ |
612 (defun org-export-latex-special-chars (sub-superscript)
613 "Export special characters to LaTeX.
614 If SUB-SUPERSCRIPT is non-nil, convert \\ and ^.
615 See the `org-export-latex.el' code for a complete conversion table."
616 (goto-char (point-min))
618 (goto-char (point-min))
619 (while (re-search-forward c nil t
)
620 ;; Put the point where to check for org-protected
621 (unless (get-text-property (match-beginning 2) 'org-protected
)
622 (cond ((member (match-string 2) '("\\$" "$"))
623 (if (equal (match-string 2) "\\$")
624 (replace-match (concat (match-string 1) "$"
625 (match-string 3)) t t
)
626 (replace-match (concat (match-string 1) "\\$"
627 (match-string 3)) t t
)))
628 ((member (match-string 2) '("&" "#" "%"))
629 (if (equal (match-string 1) "\\")
630 (replace-match (match-string 2) t t
)
631 (replace-match (concat (match-string 1) "\\"
632 (match-string 2)) t t
)))
633 ((equal (match-string 2) "~")
634 ;; FIXME protect ~ in links
635 ;; (unless (get-text-property 0 'org-protected (match-string 2))
636 ;; (unless (eq 'org-link (get-text-property 0 'face (match-string 2)))
637 (cond ((equal (match-string 1) "\\") nil
)
638 ((eq 'org-link
(get-text-property 0 'face
(match-string 2)))
639 (replace-match (concat (match-string 1) "\\~") t t
))
643 (concat (match-string 1) "\\textasciitilde{}")) t t
))))
644 ((member (match-string 2) '("{" "}"))
645 (unless (save-match-data (org-inside-LaTeX-fragment-p))
646 (if (equal (match-string 1) "\\")
647 (replace-match (match-string 2) t t
)
648 (replace-match (concat (match-string 1) "\\"
649 (match-string 2)) t t
)))))
650 (unless (save-match-data (org-inside-LaTeX-fragment-p))
651 (cond ((equal (match-string 2) "\\")
652 (replace-match (or (save-match-data
653 (org-export-latex-treat-backslash-char
655 (match-string 3))) "") t t
))
656 ((member (match-string 2) '("_" "^"))
657 (replace-match (or (save-match-data
658 (org-export-latex-treat-sub-super-char
662 (match-string 3))) "") t t
)))))))
663 '("^\\([^\n$]*?\\|^\\)\\(\\\\?\\$\\)\\([^\n$]*\\)$"
664 "\\([a-za-z0-9]+\\|[ \t\n]\\|\\\\\\)\\(_\\|\\^\\)\\([a-za-z0-9]+\\|[ \t\n]\\|[:punct:]\\|{[a-za-z0-9]+}\\|([a-za-z0-9]+)\\)"
665 "\\(.\\|^\\)\\(\\\\\\)\\([ \t\n]\\|[a-za-z&#%{}]+\\)"
671 "\\(.\\|^\\)\\(~\\)")))
673 (defun org-export-latex-treat-sub-super-char
674 (subsup string-before char string-after
)
675 "Convert the \"_\" and \"^\" characters to LaTeX.
676 SUBSUP corresponds to the ^: option in the #+OPTIONS line.
677 Convert CHAR depending on STRING-BEFORE and STRING-AFTER."
678 (cond ((equal string-before
"\\")
679 (concat string-before char string-after
))
680 ;; this is part of a math formula
681 ((and (string-match "\\S-+" string-before
)
682 (string-match "\\S-+" string-after
))
683 (cond ((eq 'org-link
(get-text-property 0 'face char
))
684 ;; (cond ((get-text-property 0 'org-protected char)
685 (concat string-before
"\\" char string-after
))
686 ((save-match-data (org-inside-LaTeX-fragment-p))
688 (cond ((eq 1 (length string-after
))
689 (concat string-before char string-after
))
690 ((string-match "[({]?\\([^)}]+\\)[)}]?" string-after
)
691 (format "%s%s{%s}" string-before char
692 (match-string 1 string-after
))))))
694 (> (length string-after
) 1)
695 (string-match "[({]?\\([^)}]+\\)[)}]?" string-after
))
696 (format "$%s%s{%s}$" string-before char
697 (match-string 1 string-after
)))
698 (subsup (concat "$" string-before char string-after
"$"))
699 (t (concat string-before char string-after
))))
700 (t (concat string-before
"\\" char string-after
))))
702 (defun org-export-latex-treat-backslash-char (string-before string-after
)
703 "Convert the \"$\" special character to LaTeX.
704 The conversion is made depending of STRING-BEFORE and STRING-AFTER."
705 (cond ((member (list string-after
) org-html-entities
)
706 ;; backslash is part of a special entity (like "\alpha")
707 (concat string-before
"$\\"
708 (or (cdar (member (list string-after
) org-html-entities
))
710 ((and (not (string-match "^[ \n\t]" string-after
))
711 (not (string-match "[ \n\t]\\'" string-before
)))
712 ;; backslash is inside a word
713 (concat string-before
"$\\backslash$" string-after
))
714 ((not (or (equal string-after
"")
715 (string-match "^[ \t\n]" string-after
)))
716 ;; backslash might escape a character (like \#) or a user TeX
717 ;; macro (like \setcounter)
718 (concat string-before
"\\" string-after
))
719 ((and (string-match "^[ \t\n]" string-after
)
720 (string-match "[ \t\n]\\'" string-before
))
721 ;; backslash is alone, convert it to $\backslash$
722 (concat string-before
"$\\backslash$" string-after
))
723 (t (concat string-before
"$\\backslash$" string-after
))))
725 (defun org-export-latex-fixed-width (opt)
726 "When OPT is non-nil convert fixed-width sections to LaTeX."
727 (goto-char (point-min))
728 (while (re-search-forward "^[ \t]*:" nil t
)
730 (progn (goto-char (match-beginning 0))
731 (insert "\\begin{verbatim}\n")
732 (while (looking-at "^\\([ \t]*\\):\\(.*\\)$")
733 (replace-match (concat (match-string 1)
734 (match-string 2)) t t
)
736 (insert "\\end{verbatim}\n\n"))
737 (progn (goto-char (match-beginning 0))
738 (while (looking-at "^\\([ \t]*\\):\\(.*\\)$")
739 (replace-match (concat "%" (match-string 1)
740 (match-string 2)) t t
)
743 (defun org-export-latex-tables (opt)
744 "When OPT is non-nil convert tables to LaTeX."
745 (goto-char (point-min))
746 (while (re-search-forward "^\\([ \t]*\\)|" nil t
)
747 ;; Re-align the table to update org-table-last-alignment
748 (save-excursion (save-match-data (org-table-align)))
750 (beg (match-beginning 0))
753 (concat "^" (regexp-quote (match-string 1))
754 "[^|]\\|\\'") nil t
) (match-beginning 0))))
756 (while (not (eq end
(point)))
757 (if (looking-at "[ \t]*|\\([^-|].+\\)|[ \t]*$")
758 (push (split-string (org-trim (match-string 1)) "|") tbl-list
)
759 (push 'hline tbl-list
))
761 ;; comment region out instead of deleting it ?
762 (apply 'delete-region
(list beg end
))
763 (when opt
(insert (orgtbl-to-latex (nreverse tbl-list
)
766 (defun org-export-latex-special-keywords ()
767 "Convert special keywords to LaTeX.
768 Regexps are those from `org-latex-special-string-regexps'."
769 (let ((rg org-latex-special-string-regexps
) r
)
770 (while (setq r
(pop rg
))
771 (goto-char (point-min))
772 (while (re-search-forward (eval r
) nil t
)
773 (replace-match (format "\\\\texttt{%s}" (match-string 0)) t
)))))
775 ;; FIXME - we need better implementation for nested lists
776 (defun org-export-latex-list (srch0 srch1 srch2 rpl0 rpl1
)
777 "Convert lists to LaTeX."
778 (goto-char (point-min))
779 (while (re-search-forward srch0 nil t
)
780 (let* ((beg (match-beginning 0))
781 (prefix (regexp-quote (match-string 1)))
782 (end-string (when (re-search-forward srch1 nil t
)
784 (goto-char beg
) (insert rpl0
)
785 (while (re-search-forward
786 (concat "^" prefix srch2
)
792 (regexp-quote end-string
) nil t
)))) t
)
796 (format "\\texttt{%s}" (match-string 1))))
798 (goto-char (if end-string
799 (progn (re-search-forward
800 (regexp-quote end-string
) nil t
)
803 (skip-chars-backward "\n") (forward-line 2)
806 (defun org-export-latex-itemize ()
807 "Convert item list to LaTeX."
808 (org-export-latex-list
815 (defun org-export-latex-enumerate ()
816 "Convert numeric list to LaTeX."
817 (org-export-latex-list
818 "^\\([ \t]*\\)[0-9]+[\.)] \\(\\[.+\\]\\)? ?"
820 "[0-9]+[\.)] ?\\(\\[.+\\]\\)?"
821 "\\begin{enumerate}\n"
822 "\\end{enumerate}\n"))
824 (defun org-export-latex-fontify ()
825 "Convert fontification to LaTeX."
826 (goto-char (point-min))
827 (while (re-search-forward org-emph-re nil t
)
828 ;; The match goes one char after the *string*
829 (unless (get-text-property (1- (point)) 'org-protected
)
831 (concat (match-string 1)
833 (org-export-latex-protect-char-in-string
835 (cadr (assoc (match-string 3)
836 org-export-latex-emphasis-alist
)))
838 (match-string 5)) t t
)
841 (defun org-export-latex-protect-char-in-string (char-list string
)
842 "Add org-protected text-property to char from CHAR-LIST in STRING."
846 (goto-char (point-min))
847 (while (re-search-forward (regexp-opt char-list
) nil t
)
848 (add-text-properties (match-beginning 0)
849 (match-end 0) '(org-protected t
)))
852 (defun org-export-latex-links ()
853 ;; Make sure to use the LaTeX hyperref and graphicx package
854 ;; or send some warnings.
855 "Convert links to LaTeX."
856 (goto-char (point-min))
857 (while (re-search-forward org-bracket-link-analytic-regexp nil t
)
859 (goto-char (match-beginning 0))
860 (let* ((re-radio org-latex-all-targets-regexp
)
861 (remove (list (match-beginning 0) (match-end 0)))
862 (type (match-string 2))
863 (raw-path (match-string 3))
864 (full-raw-path (concat (match-string 1) raw-path
))
865 (desc (match-string 5))
867 ;; define the path of the link
869 ((member type
'("http" "https" "ftp"))
870 (concat type
":" raw-path
))
871 ((and re-radio
(string-match re-radio raw-path
))
873 ((equal type
"mailto")
874 (concat type
":" raw-path
))
876 (if (and (or (org-file-image-p (expand-file-name raw-path
))
877 (string-match "\\.eps$" raw-path
))
878 (equal desc full-raw-path
))
880 (progn (when (string-match "\\(.+\\)::.+" raw-path
)
881 (setq raw-path
(match-string 1 raw-path
)))
882 (if (file-exists-p raw-path
)
883 (concat type
"://" (expand-file-name raw-path
))
884 (concat type
"://" (org-export-directory
885 :LaTeX org-latex-options-plist
)
887 ;; process with link inserting
888 (apply 'delete-region remove
)
889 (cond ((and imgp
(plist-get org-latex-options-plist
:inline-images
))
890 (insert (format "\\includegraphics[%s]{%s}"
891 ;; image option should be set be a comment line
892 org-export-latex-image-default-option
893 (expand-file-name raw-path
))))
894 ;; FIXME: what about caption? image properties?
895 (radiop (insert (format "\\hyperref[%s]{%s}" raw-path desc
)))
896 (path (insert (format "\\href{%s}{%s}" path desc
)))
897 (t (insert "\\texttt{" desc
"}")))))))
900 (defun org-latex-cleaned-string-for-export (string &rest parameters
)
901 "Cleanup a buffer STRING so that links can be created safely."
903 (let* ((re-radio (and org-target-link-regexp
904 (concat "\\([^<]\\)\\(" org-target-link-regexp
"\\)")))
905 (re-plain-link (concat "\\([^[<]\\)" org-plain-link-re
))
906 (re-angle-link (concat "\\([^[]\\)" org-angle-link-re
))
907 (re-archive (concat ":" org-archive-tag
":"))
908 (re-quote (concat "^\\*+[ \t]+" org-quote-string
"\\>"))
909 (htmlp (plist-get parameters
:for-html
))
910 (latexp (plist-get parameters
:for-LaTeX
))
911 (commentsp (plist-get parameters
:comments
))
912 (inhibit-read-only t
)
913 (outline-regexp "\\*+ ")
917 (set-buffer (get-buffer-create " org-mode-tmp"))
920 ;; Remove license-to-kill stuff
921 (while (setq p
(text-property-any (point-min) (point-max)
922 :org-license-to-kill t
))
923 (delete-region p
(next-single-property-change p
:org-license-to-kill
)))
925 (let ((org-inhibit-startup t
)) (org-mode))
926 (untabify (point-min) (point-max))
928 ;; Get the correct stuff before the first headline
929 (when (plist-get parameters
:skip-before-1st-heading
)
930 (goto-char (point-min))
931 (when (re-search-forward "^\\*+[ \t]" nil t
)
932 (delete-region (point-min) (match-beginning 0))
933 (goto-char (point-min))
935 (when (plist-get parameters
:add-text
)
936 (goto-char (point-min))
937 (insert (plist-get parameters
:add-text
) "\n"))
939 ;; Get rid of archived trees
940 (when (not (eq org-export-with-archived-trees t
))
941 (goto-char (point-min))
942 (while (re-search-forward re-archive nil t
)
943 (if (not (org-on-heading-p t
))
944 (org-end-of-subtree t
)
945 (beginning-of-line 1)
946 (setq a
(if org-export-with-archived-trees
947 (1+ (point-at-eol)) (point))
948 b
(org-end-of-subtree t
))
949 (if (> b a
) (delete-region a b
)))))
951 ;; Get rid of property drawers
952 (unless org-export-with-property-drawer
953 (goto-char (point-min))
954 (while (re-search-forward "^[ \t]*:PROPERTIES:[ \t]*\n\\([^@]*?\n\\)?[ \t]*:END:[ \t]*\n" nil t
)
957 ;; Find targets in comments and move them out of comments,
958 ;; but mark them as targets that should be invisible
959 (goto-char (point-min))
960 (while (re-search-forward "^#.*?\\(<<<?[^>\r\n]+>>>?\\).*" nil t
)
961 (replace-match "\\1(INVISIBLE)"))
963 ;; Specific LaTeX cleaning
965 (require 'org-export-latex nil t
)
966 (org-export-latex-cleaned-string))
968 ;; Protect stuff from HTML processing
969 (goto-char (point-min))
970 (let ((formatters `((,htmlp
"HTML" "BEGIN_HTML" "END_HTML"))) fmt
)
971 (while (re-search-forward "^[ \t]*:.*\\(\n[ \t]*:.*\\)*" nil t
)
972 (add-text-properties (match-beginning 0) (match-end 0)
975 (setq fmt
(pop formatters
))
977 (goto-char (point-min))
978 (while (re-search-forward (concat "^#\\+" (cadr fmt
)
979 ":[ \t]*\\(.*\\)") nil t
)
980 (replace-match "\\1" t
)
982 (point-at-bol) (min (1+ (point-at-eol)) (point-max))
983 '(org-protected t
))))
984 (goto-char (point-min))
985 (while (re-search-forward
987 (caddr fmt
) "\\>.*\\(\\(\n.*\\)*?\n\\)#\\+"
988 (cadddr fmt
) "\\>.*\n?") nil t
)
990 (add-text-properties (match-beginning 1) (1+ (match-end 1))
992 (delete-region (match-beginning 0) (match-end 0))))
993 (goto-char (point-min))
994 (while (re-search-forward re-quote nil t
)
995 (goto-char (match-beginning 0))
997 (add-text-properties (point) (org-end-of-subtree t
)
998 '(org-protected t
)))))
1000 ;; Remove or replace comments
1001 ;; If :comments is set, use this char for commenting out comments and
1002 ;; protect them. otherwise delete them
1003 (goto-char (point-min))
1004 (while (re-search-forward "^#\\(.*\n?\\)" nil t
)
1006 (progn (add-text-properties
1007 (match-beginning 0) (match-end 0) '(org-protected t
))
1008 (replace-match (format commentsp
(match-string 1)) t t
))
1009 (replace-match "")))
1011 ;; Find matches for radio targets and turn them into internal links
1012 (goto-char (point-min))
1014 (while (re-search-forward re-radio nil t
)
1016 (replace-match "\\1[[\\2]]"))))
1018 ;; Find all links that contain a newline and put them into a single line
1019 (goto-char (point-min))
1020 (while (re-search-forward "\\(\\(\\[\\|\\]\\)\\[[^]]*?\\)[ \t]*\n[ \t]*\\([^]]*\\]\\(\\[\\|\\]\\)\\)" nil t
)
1022 (replace-match "\\1 \\3")
1023 (goto-char (match-beginning 0))))
1025 ;; Convert LaTeX fragments to images
1026 (when (plist-get parameters
:LaTeX-fragments
)
1028 (concat "ltxpng/" (file-name-sans-extension
1029 (file-name-nondirectory
1030 org-current-export-file
)))
1031 org-current-export-dir nil
"Creating LaTeX image %s"))
1032 (message "Exporting...")
1034 ;; Normalize links: Convert angle and plain links into bracket links
1035 ;; Expand link abbreviations
1036 (goto-char (point-min))
1037 (while (re-search-forward re-plain-link nil t
)
1038 (goto-char (1- (match-end 0)))
1040 (let* ((s (concat (match-string 1) "[[" (match-string 2)
1041 ":" (match-string 3) "]]")))
1042 ;; added 'org-protected property to links
1043 (put-text-property 0 (length s
) 'face
'org-link s
)
1044 ;; (add-text-properties 0 (length s) '(org-protected t) s)
1045 (replace-match s t t
))))
1046 (goto-char (point-min))
1047 (while (re-search-forward re-angle-link nil t
)
1048 (goto-char (1- (match-end 0)))
1050 (let* ((s (concat (match-string 1) "[[" (match-string 2)
1051 ":" (match-string 3) "]]")))
1052 (put-text-property 0 (length s
) 'face
'org-link s
)
1053 ;; (add-text-properties 0 (length s) '(org-protected t) s)
1054 (replace-match s t t
))))
1055 (goto-char (point-min))
1056 (while (re-search-forward org-bracket-link-regexp nil t
)
1058 (let* ((s (concat "[[" (setq xx
(save-match-data
1059 (org-link-expand-abbrev (match-string 1))))
1063 (concat "[" xx
"]"))
1065 (put-text-property 0 (length s
) 'face
'org-link s
)
1066 ;; (add-text-properties 0 (length s) '(org-protected t) s)
1067 (replace-match s t t
))))
1069 ;; Find multiline emphasis and put them into single line
1070 (when (plist-get parameters
:emph-multiline
)
1071 (goto-char (point-min))
1072 (while (re-search-forward org-emph-re nil t
)
1073 (if (not (= (char-after (match-beginning 3))
1074 (char-after (match-beginning 4))))
1076 (subst-char-in-region (match-beginning 0) (match-end 0)
1078 (goto-char (1- (match-end 0))))
1079 (goto-char (1+ (match-beginning 0))))))
1081 (setq rtn
(buffer-string)))
1082 (kill-buffer " org-mode-tmp")
1085 (defsubst org-latex-protect
(string)
1086 (add-text-properties 0 (length string
) '(org-protected t
) string
)
1089 (defun org-export-latex-cleaned-string ()
1090 "Clean stuff in the LaTeX export."
1092 ;; preserve line breaks
1093 (goto-char (point-min))
1094 (while (re-search-forward "\\\\\\\\" nil t
)
1095 (add-text-properties (match-beginning 0) (match-end 0)
1096 '(org-protected t
)))
1098 ;; convert LaTeX to @LaTeX{}
1099 (goto-char (point-min))
1100 (let ((case-fold-search nil
) rpl
)
1101 (while (re-search-forward "\\([^+_]\\)LaTeX" nil t
)
1102 (replace-match (org-latex-protect
1103 (concat (match-string 1) "\\LaTeX{}")) t t
)))
1105 ;; convert horizontal rules
1106 (goto-char (point-min))
1107 (while (re-search-forward "^----+.$" nil t
)
1108 (replace-match (org-latex-protect "\\hrule") t t
))
1110 ;; Remove COMMENT subtrees
1111 ;; What about QUOTE subtrees?
1112 (goto-char (point-min))
1113 (while (re-search-forward
1114 (concat "^\\*+ \\(" org-comment-string
"\\)")
1119 ;; protect LaTeX \commands{...}
1120 (goto-char (point-min))
1121 (while (re-search-forward "\\\\[a-z]+{.+}" nil t
)
1122 (add-text-properties (match-beginning 0) (match-end 0)
1123 '(org-protected t
)))
1125 ;; Replace radio links
1126 (goto-char (point-min))
1127 (let ((search (concat "<<<?" org-latex-all-targets-regexp
">?>>")))
1128 (while (re-search-forward search nil t
)
1130 (org-latex-protect (format "\\label{%s}" (match-string 1))) t t
)))
1132 ;; delete @<br /> cookies
1133 (goto-char (point-min))
1134 (while (re-search-forward "@<[^<>\n]*>" nil t
)
1137 ;; add #+BEGIN_LaTeX before any \begin{...}
1138 (goto-char (point-min))
1139 (while (re-search-forward "^ *\\\\begin{" nil t
)
1140 (replace-match "#+BEGIN_LaTeX:\n\\&" t
))
1142 ;; add #+END_LaTeX after any \end{...}
1143 (goto-char (point-min))
1144 (while (re-search-forward "^ *\\\\end{.+}.*$" nil t
)
1145 (replace-match "\\&\n#+END_LaTeX" t
))
1147 ;; When converting to LaTeX, replace footnotes
1148 ;; FIXME: don't protect footnotes from conversion
1149 (when (plist-get org-latex-options-plist
:footnotes
)
1150 (goto-char (point-min))
1151 (while (re-search-forward "\\[[0-9]+\\]" nil t
)
1152 (when (save-match-data
1153 (save-excursion (beginning-of-line)
1154 (looking-at "[^:|]")))
1155 (let ((foot-beg (match-beginning 0))
1156 (foot-end (match-end 0))
1157 (foot-prefix (match-string 0))
1158 footnote footnote-rpl
)
1159 (when (and (re-search-forward (regexp-quote foot-prefix
) nil t
))
1161 (let ((end (save-excursion
1162 (if (re-search-forward "^$\\|\\[[0-9]+\\]" nil t
)
1163 (match-beginning 0) (point-max)))))
1164 (setq footnote
(concat
1165 (org-trim (buffer-substring (point) end
))
1166 ;; FIXME stupid workaround for cases where
1167 ;; `org-bracket-link-analytic-regexp' matches
1168 ;; }. as part of the link.
1170 (delete-region (point) end
)))
1171 (goto-char foot-beg
)
1172 (delete-region foot-beg foot-end
)
1173 (setq footnote-rpl
(format "\\footnote{%s}" footnote
))
1175 ;; (add-text-properties 0 1 '(org-protected t) footnote-rpl)
1176 (add-text-properties 0 10 '(org-protected t
) footnote-rpl
)
1177 ;; FIXME: why protecting the content of a footnote?
1178 (add-text-properties (1- (length footnote-rpl
))
1179 (length footnote-rpl
)
1180 '(org-protected t
) footnote-rpl
)
1181 (insert footnote-rpl
))))
1183 ;; Replace footnote section tag for LaTeX
1184 (goto-char (point-min))
1185 (while (re-search-forward
1186 (concat "^" footnote-section-tag-regexp
) nil t
)
1187 (replace-match "")))
1189 ;; Protect stuff from LaTeX processing.
1190 ;; We will get rid on this once org.el integrate org-export-latex.el
1191 ;; FIXME: #+LaTeX should be aware of the preceeding indentation in lists
1192 (goto-char (point-min))
1193 (let ((formatters `((,latexp
"LaTeX" "BEGIN_LaTeX" "END_LaTeX"))) fmt
)
1194 (while (re-search-forward "^[ \t]*:.*\\(\n[ \t]*:.*\\)*" nil t
)
1195 (add-text-properties (match-beginning 0) (match-end 0)
1196 '(org-protected t
)))
1198 (setq fmt
(pop formatters
))
1200 (goto-char (point-min))
1201 (while (re-search-forward (concat "^#\\+" (cadr fmt
)
1202 ":[ \t]*\\(.*\\)") nil t
)
1203 (replace-match "\\1" t
)
1204 (add-text-properties
1205 (point-at-bol) (min (1+ (point-at-eol)) (point-max))
1206 '(org-protected t
))))
1207 (goto-char (point-min))
1208 (while (re-search-forward
1210 (caddr fmt
) "\\>.*\\(\\(\n.*\\)*?\n\\)#\\+"
1211 (cadddr fmt
) "\\>.*\n?") nil t
)
1213 (add-text-properties (match-beginning 1) (1+ (match-end 1))
1215 (delete-region (match-beginning 0) (match-end 0))))
1216 (goto-char (point-min))
1217 (while (re-search-forward re-quote nil t
)
1218 (goto-char (match-beginning 0))
1220 (add-text-properties (point) (org-end-of-subtree t
)
1221 '(org-protected t
))))))
1223 (provide 'org-export-latex
)
1225 ;;; org-export-latex.el ends here