Release 5.07
[org-mode.git] / org-export-latex.el
blob52e502a10ded0ed89c802ad0e2cede4699fc34cf
1 ;;; org-export-latex.el --- LaTeX exporter for Org-mode
2 ;; Copyright (C) 2007 Free Software Foundation, Inc.
3 ;;
4 ;; Author: Bastien Guerry <bzg AT altern DOT org>
5 ;; Keywords: org organizer latex export convert
6 ;; Version: $Id: org-export-latex.el,v 0.27b 2007/08/25 14:26:06 guerry Exp guerry $
7 ;; X-URL: <http://www.cognition.ens.fr/~guerry/u/org-export-latex.el>
8 ;;
9 ;; This file is part of GNU Emacs.
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 2, or (at your option)
14 ;; any later version.
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; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
26 ;;; Commentary:
28 ;; This library implements a LaTeX exporter for org-mode.
29 ;;
30 ;; Put this file into your load-path and the following into your ~/.emacs:
31 ;; (require 'org-export-latex)
32 ;;
33 ;; The interactive functions are similar to those of the HTML exporter:
34 ;;
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'
41 ;;; Code:
43 (eval-when-compile
44 (require 'cl)
45 (require 'footnote))
47 (require 'org)
49 (defvar org-latex-options-plist nil)
50 (defvar org-latex-todo-keywords-1 nil)
51 (defvar org-latex-all-targets-regexp nil)
52 (defvar org-latex-add-level 0)
53 (defvar org-latex-sectioning-depth 0)
55 (defvar org-latex-special-string-regexps
56 '(org-ts-regexp
57 org-scheduled-string
58 org-deadline-string
59 org-clock-string)
60 "A list of regexps to convert as special keywords.")
62 (defcustom org-export-latex-sectioning-alist
63 '((1 "\\section{%s}" "\\section*{%s}")
64 (2 "\\subsection{%s}" "\\subsection*{%s}")
65 (3 "\\subsubsection{%s}" "\\subsubsection*{%s}")
66 (4 "\\paragraph{%s}" "\\paragraph*{%s}")
67 (5 "\\subparagraph{%s}" "\\subparagraph*{%s}"))
68 "Alist of LaTeX commands for inserting sections.
69 Here is the structure of each cell:
71 \(level unnumbered-section numbered-section\)
73 The %s formatter will be replaced by the title of the section."
74 :group 'org-export-latex
75 :type 'alist)
77 (defcustom org-export-latex-emphasis-alist
78 '(("*" "\\textbf{%s}")
79 ("/" "\\emph{%s}")
80 ("_" "\\underline{%s}")
81 ("+" "\\texttt{%s}")
82 ("=" "\\texttt{%s}"))
83 "Alist of LaTeX expressions to convert emphasis fontifiers."
84 :group 'org-export-latex
85 :type 'alist)
87 (defcustom org-export-latex-preamble
88 "\\documentclass[11pt,a4paper]{article}
89 \\usepackage[utf8]{inputenc}
90 \\usepackage[T1]{fontenc}
91 \\usepackage{hyperref}"
92 "Preamble to be inserted at the very beginning of the LaTeX export."
93 :group 'org-export-latex
94 :type 'string)
96 (defcustom org-export-latex-date-format nil
97 "Format string for \\date{...}."
98 :group 'org-export-latex
99 :type 'string)
101 (defcustom org-export-latex-packages-alist nil
102 "Alist of packages to be inserted in the preamble.
103 Each cell is of the forma \( option . package \).
105 For example:
107 \(setq org-export-latex-packages-alist
108 '((\"french\" \"babel\"))"
109 :group 'org-export-latex
110 :type 'alist)
112 (defcustom org-export-latex-low-levels 'description
113 "Choice for converting sections that are below the current
114 admitted level of sectioning. This can be either nil (ignore the
115 sections), 'description (convert them as description lists) or a
116 string to be used instead of \\section{%s} (a %s for inserted the
117 headline is mandatory)."
118 :group 'org-export-latex
119 :type '(choice (const :tag "Ignore" nil)
120 (symbol :tag "Convert as descriptive list" description)
121 (string :tag "Use a section string" :value "\\subparagraph{%s}")))
123 (defcustom org-export-latex-remove-from-headines
124 '(:todo t :priority t :tags t)
125 "A plist of keywords to remove from headlines.
126 Non-nil means remove this keyword type from the headline.
128 Don't remove the keys, just change their values."
129 :type 'plist
130 :group 'org-export-latex)
132 (defcustom org-export-latex-quotation-marks-convention "en"
133 "Convention for conversion of the quotation marks.
134 This value is overriden by any infile language setup."
135 :group 'org-export-latex
136 :type '(choice (string :tag "english" "en")
137 (string :tag "french" "fr")))
139 (defcustom org-export-latex-image-default-option "width=10em"
140 "Default option for images."
141 :group 'org-export-latex
142 :type '(string))
144 (defcustom org-export-latex-coding-system nil
145 "Coding system for the exported LaTex file."
146 :group 'org-export-latex
147 :type 'coding-system)
149 ;; FIXME Do we want this one?
150 ;; (defun org-export-as-latex-and-open (arg) ...)
152 ;;;###autoload
153 (defun org-export-as-latex-batch ()
154 "Call `org-export-as-latex', may be used in batch processing as
155 emacs --batch
156 --load=$HOME/lib/emacs/org.el
157 --eval \"(setq org-export-headline-levels 2)\"
158 --visit=MyFile --funcall org-export-as-latex-batch"
159 (org-export-as-latex org-export-headline-levels 'hidden))
161 ;;;###autoload
162 (defun org-export-as-latex-to-buffer (arg)
163 "Call `org-exort-as-latex` with output to a temporary buffer.
164 No file is created. The prefix ARG is passed through to `org-export-as-latex'."
165 (interactive "P")
166 (org-export-as-latex arg nil nil "*Org LaTeX Export*")
167 (switch-to-buffer-other-window "*Org LaTeX Export*"))
169 ;;;###autoload
170 (defun org-replace-region-by-latex (beg end)
171 "Replace the region from BEG to END with its LaTeX export.
172 It assumes the region has `org-mode' syntax, and then convert it to
173 LaTeX. This can be used in any buffer. For example, you could
174 write an itemized list in `org-mode' syntax in an LaTeX buffer and
175 then use this command to convert it."
176 (interactive "r")
177 (let (reg latex buf)
178 (save-window-excursion
179 (if (org-mode-p)
180 (setq latex (org-export-region-as-latex
181 beg end t 'string))
182 (setq reg (buffer-substring beg end)
183 buf (get-buffer-create "*Org tmp*"))
184 (save-excursion
185 (set-buffer buf)
186 (erase-buffer)
187 (insert reg)
188 (org-mode)
189 (setq latex (org-export-region-as-latex
190 (point-min) (point-max) t 'string)))
191 (kill-buffer buf)))
192 (delete-region beg end)
193 (insert latex)))
195 ;;;###autoload
196 (defun org-export-region-as-latex (beg end &optional body-only buffer)
197 "Convert region from BEG to END in `org-mode' buffer to LaTeX.
198 If prefix arg BODY-ONLY is set, omit file header, footer, and table of
199 contents, and only produce the region of converted text, useful for
200 cut-and-paste operations.
201 If BUFFER is a buffer or a string, use/create that buffer as a target
202 of the converted LaTeX. If BUFFER is the symbol `string', return the
203 produced LaTeX as a string and leave not buffer behind. For example,
204 a Lisp program could call this function in the following way:
206 (setq latex (org-export-region-as-latex beg end t 'string))
208 When called interactively, the output buffer is selected, and shown
209 in a window. A non-interactive call will only retunr the buffer."
210 (interactive "r\nP")
211 (when (interactive-p)
212 (setq buffer "*Org LaTeX Export*"))
213 (let ((transient-mark-mode t) (zmacs-regions t)
214 rtn)
215 (goto-char end)
216 (set-mark (point)) ;; to activate the region
217 (goto-char beg)
218 (setq rtn (org-export-as-latex
219 nil nil nil
220 buffer body-only))
221 (if (fboundp 'deactivate-mark) (deactivate-mark))
222 (if (and (interactive-p) (bufferp rtn))
223 (switch-to-buffer-other-window rtn)
224 rtn)))
226 ;;;###autoload
227 (defun org-export-as-latex (arg &optional hidden ext-plist
228 to-buffer body-only)
229 "Export current buffer to a LaTeX file."
230 (interactive "P")
231 ;; Make sure we have a file name when we need it.
232 (when (and (not (or to-buffer body-only))
233 (not buffer-file-name))
234 (if (buffer-base-buffer)
235 (org-set-local 'buffer-file-name
236 (with-current-buffer (buffer-base-buffer)
237 buffer-file-name))
238 (error "Need a file name to be able to export")))
240 (message "Exporting to LaTeX...")
241 (org-update-radio-target-regexp)
242 (org-export-latex-set-initial-vars ext-plist)
243 (let* ((opt-plist org-latex-options-plist)
244 (filename (concat (file-name-as-directory
245 (org-export-directory :LaTeX ext-plist))
246 (file-name-sans-extension
247 (file-name-nondirectory ;sans-extension
248 buffer-file-name)) ".tex"))
249 (filename (if (equal (file-truename filename)
250 (file-truename buffer-file-name))
251 (concat filename ".tex")
252 filename))
253 (buffer (if to-buffer
254 (cond
255 ((eq to-buffer 'string) (get-buffer-create
256 "*Org LaTeX Export*"))
257 (t (get-buffer-create to-buffer)))
258 (find-file-noselect filename)))
259 (region-p (org-region-active-p))
260 (odd org-odd-levels-only)
261 (preamble (org-export-latex-make-preamble opt-plist))
262 (skip (plist-get opt-plist :skip-before-1st-heading))
263 (text (plist-get opt-plist :text))
264 (first-lines (if skip "" (org-export-latex-first-lines)))
265 (coding-system (and (boundp 'buffer-file-coding-system)
266 buffer-file-coding-system))
267 (coding-system-for-write (or org-export-latex-coding-system
268 coding-system))
269 (save-buffer-coding-system (or org-export-latex-coding-system
270 coding-system))
271 (region (buffer-substring
272 (if region-p (region-beginning) (point-min))
273 (if region-p (region-end) (point-max))))
274 (string-for-export
275 ;; FIXME Use org-cleaned-string-for-export instead, only when
276 ;; everyone uses Org >5.04
277 (org-latex-cleaned-string-for-export
278 region :for-html nil
279 :comments nil
280 :for-LaTeX t
281 :skip-before-1st-heading nil
282 :LaTeX-fragments nil)))
283 (set-buffer buffer)
284 (erase-buffer)
285 (unless body-only (insert preamble))
286 (when text (insert (org-export-latex-content text) "\n\n"))
287 (unless skip (insert first-lines))
289 ;; handle the case where the region does not begin with a section
290 (when region-p
291 (insert (with-temp-buffer
292 (insert string-for-export)
293 (org-export-latex-first-lines))))
295 (org-export-latex-global
296 (with-temp-buffer
297 (insert string-for-export)
298 (goto-char (point-min))
299 (re-search-forward "^\\(\\*+\\) " nil t)
300 (let* ((asters (length (match-string 1)))
301 (level (if odd (- asters 2) (- asters 1))))
302 (setq org-latex-add-level
303 (if odd (1- (/ (1+ asters) 2)) (1- asters)))
304 (org-export-latex-parse-global level odd))))
306 (unless body-only (insert "\n\\end{document}"))
307 (or to-buffer (write-file filename))
308 (goto-char (point-min))
309 (message "Exporting to LaTeX...done")
310 (if (eq to-buffer 'string)
311 (prog1 (buffer-substring (point-min) (point-max))
312 (kill-buffer (current-buffer)))
313 (current-buffer))))
315 (defun org-export-latex-set-initial-vars (ext-plist)
316 "Store org local variables required for LaTeX export.
317 EXT-PLIST is an optional additional plist."
318 (setq org-latex-todo-keywords-1 org-todo-keywords-1
319 org-latex-all-targets-regexp
320 (org-make-target-link-regexp (org-all-targets))
321 org-latex-options-plist
322 (org-combine-plists (org-default-export-plist) ext-plist
323 (org-infile-export-plist))
324 org-latex-sectioning-depth
325 (let ((hl-levels (plist-get org-latex-options-plist :headline-levels))
326 (sec-depth (length org-export-latex-sectioning-alist)))
327 ;; Fall back on org-export-latex-sectioning-alist length if
328 ;; headline-levels goes beyond it
329 (if (> hl-levels sec-depth) sec-depth hl-levels))))
331 (defun org-export-latex-make-preamble (opt-plist)
332 "Make the LaTeX preamble and return it as a string.
333 Argument OPT-PLIST is the options plist for current buffer."
334 (let ((toc (plist-get opt-plist :table-of-contents)))
335 (format (concat org-export-latex-preamble
339 \\begin{document}
341 \\title{%s}
344 \\maketitle
348 (if org-export-latex-packages-alist
349 (mapconcat (lambda(p)
350 (if (equal "" (car p))
351 (format "\\usepackage{%s}" (cadr p))
352 (format "\\usepackage[%s]{%s}"
353 (car p) (cadr p))))
354 org-export-latex-packages-alist "\n") "")
355 (or (plist-get opt-plist :title)
356 (and (not
357 (plist-get opt-plist :skip-before-1st-heading))
358 (org-export-grab-title-from-buffer))
359 (and buffer-file-name
360 (file-name-sans-extension
361 (file-name-nondirectory buffer-file-name)))
362 "UNTITLED")
363 (if (plist-get opt-plist :author-info)
364 (format "\\author{%s}"
365 (or (plist-get opt-plist :author) user-full-name))
366 (format "%%\\author{%s}"
367 (or (plist-get opt-plist :author) user-full-name)))
368 (if (plist-get opt-plist :timestamps)
369 (format "\\date{%s}"
370 (format-time-string (or org-export-latex-date-format
371 (car org-time-stamp-formats))))
372 "%\\date{}")
373 (if (and (plist-get opt-plist :section-numbers) toc)
374 (format "\\setcounter{tocdepth}{%s}"
375 (plist-get opt-plist :headline-levels)) "")
376 (if (and (plist-get opt-plist :section-numbers) toc)
377 "\\tableofcontents" ""))))
379 (defun org-export-latex-first-lines (&optional comments)
380 "Export the first lines before first headline.
381 COMMENTS is either nil to replace them with the empty string or a
382 formatting string like %%%%s if we want to comment them out."
383 (save-excursion
384 (goto-char (point-min))
385 (let* ((end (if (re-search-forward "^\\*" nil t)
386 (goto-char (match-beginning 0))
387 (goto-char (point-max)))))
388 (org-export-latex-content
389 (org-latex-cleaned-string-for-export
390 (buffer-substring (point-min) end)
391 :for-html nil
392 :for-LaTeX t
393 :comments nil
394 :skip-before-1st-heading nil
395 :LaTeX-fragments nil)))))
397 (defun org-export-latex-parse-global (level odd)
398 "Parse the current buffer recursively, starting at LEVEL.
399 If ODD is non-nil, assume the buffer only contains odd sections.
400 Return A list reflecting the document structure."
401 (save-excursion
402 (goto-char (point-min))
403 (let* ((cnt 0) output
404 (depth org-latex-sectioning-depth))
405 (while (re-search-forward
406 (concat "^\\(\\(?:\\*\\)\\{"
407 (number-to-string (+ (if odd 2 1) level))
408 "\\}\\) \\(.*\\)$")
409 ;; make sure that there is no upper heading
410 (when (> level 0)
411 (save-excursion
412 (save-match-data
413 (re-search-forward
414 (concat "^\\(\\(?:\\*\\)\\{"
415 (number-to-string level)
416 "\\}\\) \\(.*\\)$") nil t)))) t)
417 (setq cnt (1+ cnt))
418 (let* ((pos (match-beginning 0))
419 (heading (match-string 2))
420 (nlevel (if odd (/ (+ 3 level) 2) (1+ level))))
421 (save-excursion
422 (narrow-to-region
423 (point)
424 (save-match-data
425 (if (re-search-forward
426 (concat "^\\(\\(?:\\*\\)\\{"
427 (number-to-string (+ (if odd 2 1) level))
428 "\\}\\) \\(.*\\)$") nil t)
429 (match-beginning 0)
430 (point-max))))
431 (goto-char (point-min))
432 (setq output
433 (append output
434 (list
435 (list
436 `(pos . ,pos)
437 `(level . ,nlevel)
438 `(occur . ,cnt)
439 `(heading . ,heading)
440 `(content . ,(org-export-latex-parse-content))
441 `(subcontent . ,(org-export-latex-parse-subcontent
442 level odd)))))))
443 (widen)))
444 (list output))))
446 (defun org-export-latex-parse-content ()
447 "Extract the content of a section."
448 (let ((beg (point))
449 (end (if (re-search-forward "^\\(\\*\\)+ .*$" nil t)
450 (progn (beginning-of-line) (point))
451 (point-max))))
452 (buffer-substring beg end)))
454 (defun org-export-latex-parse-subcontent (level odd)
455 "Extract the subcontent of a section at LEVEL.
456 If ODD Is non-nil, assume subcontent only contains odd sections."
457 (if (not (re-search-forward
458 (concat "^\\(\\(?:\\*\\)\\{"
459 (number-to-string (+ (if odd 4 2) level))
460 "\\}\\) \\(.*\\)$")
461 nil t))
462 nil ; subcontent is nil
463 (org-export-latex-parse-global (+ (if odd 2 1) level) odd)))
465 (defun org-export-latex-global (content)
466 "Export CONTENT to LaTeX.
467 CONTENT is an element of the list produced by
468 `org-export-latex-parse-global'."
469 (if (eq (car content) 'subcontent)
470 (mapc 'org-export-latex-sub (cdr content))
471 (org-export-latex-sub (car content))))
473 (defun org-export-latex-sub (subcontent)
474 "Export the list SUBCONTENT to LaTeX.
475 SUBCONTENT is an alist containing information about the headline
476 and its content."
477 (mapc (lambda(x) (org-export-latex-subcontent x)) subcontent))
479 (defun org-export-latex-subcontent (subcontent)
480 "Export each cell of SUBCONTENT to LaTeX."
481 (let ((heading (org-export-latex-fontify-headline
482 (cdr (assoc 'heading subcontent))))
483 (level (- (cdr (assoc 'level subcontent))
484 org-latex-add-level))
485 (occur (number-to-string (cdr (assoc 'occur subcontent))))
486 (content (cdr (assoc 'content subcontent)))
487 (subcontent (cadr (assoc 'subcontent subcontent)))
488 (num (plist-get org-latex-options-plist :section-numbers)))
489 (cond
490 ;; Normal conversion
491 ((<= level org-latex-sectioning-depth)
492 (let ((sec (assoc level org-export-latex-sectioning-alist)))
493 (insert (format (if num (cadr sec) (caddr sec)) heading) "\n"))
494 (insert (org-export-latex-content content))
495 (cond ((stringp subcontent) (insert subcontent))
496 ((listp subcontent) (org-export-latex-sub subcontent))))
497 ;; At a level under the hl option: we can drop this subsection
498 ((> level org-latex-sectioning-depth)
499 (cond ((eq org-export-latex-low-levels 'description)
500 (insert (format "\\begin{description}\n\n\\item[%s]\n\n" heading))
501 (insert (org-export-latex-content content))
502 (cond ((stringp subcontent) (insert subcontent))
503 ((listp subcontent) (org-export-latex-sub subcontent)))
504 (insert "\\end{description}\n"))
505 ((stringp org-export-latex-low-levels)
506 (insert (format org-export-latex-low-levels heading) "\n")
507 (insert (org-export-latex-content content))
508 (cond ((stringp subcontent) (insert subcontent))
509 ((listp subcontent) (org-export-latex-sub subcontent)))))))))
511 (defun org-export-latex-keywords-maybe (remove-list)
512 "Maybe remove keywords depending on rules in REMOVE-LIST."
513 (goto-char (point-min))
514 (let ((re-todo (mapconcat 'identity org-latex-todo-keywords-1 "\\|")))
515 ;; convert TODO keywords
516 (when (re-search-forward (concat "^\\(" re-todo "\\)") nil t)
517 (if (plist-get remove-list :todo)
518 (replace-match "")
519 (replace-match (format "\\texttt{%s}" (match-string 1)) t t)))
520 ;; convert priority string
521 (when (re-search-forward "\\[\\\\#.\\]" nil t)
522 (if (plist-get remove-list :priority)
523 (replace-match "")
524 (replace-match (format "\\texttt{%s}" (match-string 0)) t t)))
525 ;; convert tags
526 (when (re-search-forward "\\(:[a-zA-Z0-9]+\\)+:" nil t)
527 (if (or (not org-export-with-tags)
528 (plist-get remove-list :tags))
529 (replace-match "")
530 (replace-match (format "\\texttt{%s}" (match-string 0)) t t)))))
532 (defun org-export-latex-fontify-headline (headline)
533 "Fontify special words in a HEADLINE."
534 (with-temp-buffer
535 ;; FIXME: org-inside-LaTeX-fragment-p doesn't work when the $...$ is at
536 ;; the beginning of the buffer - inserting "\n" is safe here though.
537 (insert "\n" headline)
538 (goto-char (point-min))
539 (org-export-latex-special-chars
540 (plist-get org-latex-options-plist :sub-superscript))
541 (when (plist-get org-latex-options-plist :emphasize)
542 (org-export-latex-fontify))
543 (org-export-latex-keywords-maybe
544 org-export-latex-remove-from-headines)
545 (org-export-latex-links)
546 (org-trim (buffer-substring-no-properties (point-min) (point-max)))))
548 (defun org-export-latex-content (content)
549 "Convert CONTENT string to LaTeX."
550 (with-temp-buffer
551 (insert content)
552 (org-export-latex-quotation-marks)
553 (org-export-latex-special-chars
554 (plist-get org-latex-options-plist :sub-superscript))
555 (when (plist-get org-latex-options-plist :emphasize)
556 (org-export-latex-fontify))
557 (org-export-latex-links)
558 (org-export-latex-keywords)
559 (org-export-latex-itemize)
560 (org-export-latex-enumerate)
561 (org-export-latex-tables
562 (plist-get org-latex-options-plist :tables))
563 (org-export-latex-fixed-width
564 (plist-get org-latex-options-plist :fixed-width))
565 (org-export-fix-invisible-strings)
566 (buffer-substring (point-min) (point-max))))
568 (defun org-export-fix-invisible-strings ()
569 "Comment out (INVISIBLE) warnings."
570 (goto-char (point-min))
571 (while (re-search-forward "(INVISIBLE)" nil t)
572 (replace-match "%\\&")))
574 (defun org-export-latex-quotation-marks ()
575 "Export question marks depending on language conventions.
576 Local definition of the language overrides
577 `org-export-latex-quotation-marks-convention' which overrides
578 `org-export-default-language'."
579 (let* ((lang (or (plist-get org-latex-options-plist :language)
580 org-export-latex-quotation-marks-convention))
581 (quote-rpl (if (equal lang "fr")
582 '(("\\(\\s-\\)\"" "«~")
583 ("\\(\\S-\\)\"" "~»")
584 ("\\(\\s-\\)'" "`"))
585 '(("\\(\\s-\\)\"" "``")
586 ("\\(\\S-\\)\"" "''")
587 ("\\(\\s-\\)'" "`")))))
588 (mapc (lambda(l) (goto-char (point-min))
589 (while (re-search-forward (car l) nil t)
590 (let ((rpl (concat (match-string 1) (cadr l))))
591 (org-latex-protect rpl)
592 (org-if-unprotected
593 (replace-match rpl t t))))) quote-rpl)))
595 ;; | chars/string in Org | normal environment | math environment |
596 ;; |-----------------------+-----------------------+-----------------------|
597 ;; | & # % $ | \& \# \% \$ | \& \# \% \$ |
598 ;; | { } _ ^ \ | \ { \ } \_ \^ \\ | { } _ ^ \ |
599 ;; |-----------------------+-----------------------+-----------------------|
600 ;; | a_b and a^b | $a_b$ and $a^b$ | a_b and a^b |
601 ;; | a_abc and a_{abc} | $a_a$bc and $a_{abc}$ | a_abc and a_{abc} |
602 ;; | \tau and \mu | $\tau$ and $\mu$ | \tau and \mu |
603 ;; |-----------------------+-----------------------+-----------------------|
604 ;; | \_ \^ | \_ \^ | \_ \^ |
605 ;; | \(a=\mu\mbox{m}\) | \(a=\mu\mbox{m}\) | \(a=\mu\mbox{m}\) |
606 ;; | \[\beta^2-a=0\] | \[\beta^2-a=0\] | \[\beta^2-a=0\] |
607 ;; | $x=22\tau$ | $x=22\tau$ | $x=22\tau$ |
608 ;; | $$\alpha=\sqrt{a^3}$$ | $$\alpha=\sqrt{a^3}$$ | $$\alpha=\sqrt{a^3}$$ |
610 (defun org-export-latex-special-chars (sub-superscript)
611 "Export special characters to LaTeX.
612 If SUB-SUPERSCRIPT is non-nil, convert \\ and ^.
613 See the `org-export-latex.el' code for a complete conversion table."
614 (goto-char (point-min))
615 (mapc (lambda(c)
616 (goto-char (point-min))
617 (while (re-search-forward c nil t)
618 ;; Put the point where to check for org-protected
619 (unless (get-text-property (match-beginning 2) 'org-protected)
620 (cond ((member (match-string 2) '("\\$" "$"))
621 (if (equal (match-string 2) "\\$")
622 (replace-match (concat (match-string 1) "$"
623 (match-string 3)) t t)
624 (replace-match (concat (match-string 1) "\\$"
625 (match-string 3)) t t)))
626 ((member (match-string 2) '("&" "#" "%"))
627 (if (equal (match-string 1) "\\")
628 (replace-match (match-string 2) t t)
629 (replace-match (concat (match-string 1) "\\"
630 (match-string 2)) t t)))
631 ((equal (match-string 2) "~")
632 (cond ((equal (match-string 1) "\\") nil)
633 ((eq 'org-link (get-text-property 0 'face (match-string 2)))
634 (replace-match (concat (match-string 1) "\\~") t t))
635 (t (replace-match
636 (org-latex-protect
637 (concat (match-string 1) "\\~{}")) t t))))
638 ((member (match-string 2) '("{" "}"))
639 (unless (save-match-data (org-inside-LaTeX-fragment-p))
640 (if (equal (match-string 1) "\\")
641 (replace-match (match-string 2) t t)
642 (replace-match (concat (match-string 1) "\\"
643 (match-string 2)) t t)))))
644 (unless (save-match-data (org-inside-LaTeX-fragment-p))
645 (cond ((equal (match-string 2) "\\")
646 (replace-match (or (save-match-data
647 (org-export-latex-treat-backslash-char
648 (match-string 1)
649 (match-string 3))) "") t t))
650 ((member (match-string 2) '("_" "^"))
651 (replace-match (or (save-match-data
652 (org-export-latex-treat-sub-super-char
653 sub-superscript
654 (match-string 1)
655 (match-string 2)
656 (match-string 3))) "") t t)))))))
657 '("^\\([^\n$]*?\\|^\\)\\(\\\\?\\$\\)\\([^\n$]*\\)$"
658 "\\([a-za-z0-9]+\\|[ \t\n]\\|\\\\\\)\\(_\\|\\^\\)\\([a-za-z0-9]+\\|[ \t\n]\\|[:punct:]\\|{[a-za-z0-9]+}\\|([a-za-z0-9]+)\\)"
659 "\\(.\\|^\\)\\(\\\\\\)\\([ \t\n]\\|[a-za-z&#%{}]+\\)"
660 "\\(.\\|^\\)\\(&\\)"
661 "\\(.\\|^\\)\\(#\\)"
662 "\\(.\\|^\\)\\(%\\)"
663 "\\(.\\|^\\)\\({\\)"
664 "\\(.\\|^\\)\\(}\\)"
665 "\\(.\\|^\\)\\(~\\)")))
667 (defun org-export-latex-treat-sub-super-char
668 (subsup string-before char string-after)
669 "Convert the \"_\" and \"^\" characters to LaTeX.
670 SUBSUP corresponds to the ^: option in the #+OPTIONS line.
671 Convert CHAR depending on STRING-BEFORE and STRING-AFTER."
672 (cond ((equal string-before "\\")
673 (concat string-before char string-after))
674 ;; this is part of a math formula
675 ((and (string-match "\\S-+" string-before)
676 (string-match "\\S-+" string-after))
677 (cond ((eq 'org-link (get-text-property 0 'face char))
678 (concat string-before "\\" char string-after))
679 ((save-match-data (org-inside-LaTeX-fragment-p))
680 (if subsup
681 (cond ((eq 1 (length string-after))
682 (concat string-before char string-after))
683 ((string-match "[({]?\\([^)}]+\\)[)}]?" string-after)
684 (format "%s%s{%s}" string-before char
685 (match-string 1 string-after))))))
686 ((and subsup
687 (> (length string-after) 1)
688 (string-match "[({]?\\([^)}]+\\)[)}]?" string-after))
689 (format "$%s%s{%s}$" string-before char
690 (match-string 1 string-after)))
691 (subsup (concat "$" string-before char string-after "$"))
692 (t (concat string-before "\\" char string-after))))
693 (t (concat string-before "\\" char string-after))))
695 (defun org-export-latex-treat-backslash-char (string-before string-after)
696 "Convert the \"$\" special character to LaTeX.
697 The conversion is made depending of STRING-BEFORE and STRING-AFTER."
698 (cond ((member (list string-after) org-html-entities)
699 ;; backslash is part of a special entity (like "\alpha")
700 (concat string-before "$\\"
701 (or (cdar (member (list string-after) org-html-entities))
702 string-after) "$"))
703 ((and (not (string-match "^[ \n\t]" string-after))
704 (not (string-match "[ \t]\\'" string-before)))
705 ;; backslash is inside a word
706 (concat string-before "$\\backslash$" string-after))
707 ((not (or (equal string-after "")
708 (string-match "^[ \t\n]" string-after)))
709 ;; backslash might escape a character (like \#) or a user TeX
710 ;; macro (like \setcounter)
711 (concat string-before "\\" string-after))
712 ((and (string-match "^[ \t\n]" string-after)
713 (string-match "[ \t\n]\\'" string-before))
714 ;; backslash is alone, convert it to $\backslash$
715 (concat string-before "$\\backslash$" string-after))
716 (t (concat string-before "$\\backslash$" string-after))))
718 (defun org-export-latex-fixed-width (opt)
719 "When OPT is non-nil convert fixed-width sections to LaTeX."
720 (goto-char (point-min))
721 (while (re-search-forward "^[ \t]*:" nil t)
722 (if opt
723 (progn (goto-char (match-beginning 0))
724 (insert "\\begin{verbatim}\n")
725 (while (looking-at "^\\([ \t]*\\):\\(.*\\)$")
726 (replace-match (concat (match-string 1)
727 (match-string 2)) t t)
728 (forward-line))
729 (insert "\\end{verbatim}\n\n"))
730 (progn (goto-char (match-beginning 0))
731 (while (looking-at "^\\([ \t]*\\):\\(.*\\)$")
732 (replace-match (concat "%" (match-string 1)
733 (match-string 2)) t t)
734 (forward-line))))))
736 ;; FIXME Use org-export-highlight-first-table-line ?
737 (defun org-export-latex-tables (opt)
738 "When OPT is non-nil convert tables to LaTeX."
739 (goto-char (point-min))
740 (while (re-search-forward "^\\([ \t]*\\)|" nil t)
741 ;; Re-align the table to update org-table-last-alignment
742 (save-excursion (save-match-data (org-table-align)))
743 (let (tbl-list
744 (beg (match-beginning 0))
745 (end (save-excursion
746 (re-search-forward
747 (concat "^" (regexp-quote (match-string 1))
748 "[^|]\\|\\'") nil t) (match-beginning 0))))
749 (beginning-of-line)
750 (while (not (eq end (point)))
751 (if (looking-at "[ \t]*|\\([^-|].+\\)|[ \t]*$")
752 (push (split-string (org-trim (match-string 1)) "|") tbl-list)
753 (push 'hline tbl-list))
754 (forward-line))
755 ;; comment region out instead of deleting it ?
756 (apply 'delete-region (list beg end))
757 (when opt (insert (orgtbl-to-latex (nreverse tbl-list)
758 nil) "\n\n")))))
760 (defun org-export-latex-keywords ()
761 "Convert special keywords to LaTeX.
762 Regexps are those from `org-latex-special-string-regexps'."
763 (let ((rg org-latex-special-string-regexps) r)
764 (while (setq r (pop rg))
765 (goto-char (point-min))
766 (while (re-search-forward (eval r) nil t)
767 (replace-match (format "\\\\texttt{%s}" (match-string 0)) t)))))
769 ;; FIXME - we need better implementation for nested lists
770 (defun org-export-latex-list (srch0 srch1 srch2 rpl0 rpl1)
771 "Convert lists to LaTeX."
772 (goto-char (point-min))
773 (while (re-search-forward srch0 nil t)
774 (let* ((beg (match-beginning 0))
775 (prefix (regexp-quote (match-string 1)))
776 (end-string (when (re-search-forward srch1 nil t)
777 (match-string 0))))
778 (goto-char beg) (insert rpl0)
779 (while (re-search-forward
780 (concat "^" prefix srch2)
781 (if (not end-string)
782 (point-max)
783 (save-match-data
784 (save-excursion
785 (re-search-forward
786 (regexp-quote end-string) nil t)))) t)
787 (replace-match
788 (concat "\\item "
789 (if (match-string 1)
790 (format "\\texttt{%s}" (match-string 1))))
791 t t))
792 (goto-char (if end-string
793 (progn (re-search-forward
794 (regexp-quote end-string) nil t)
795 (match-beginning 0))
796 (point-max)))
797 (skip-chars-backward "\n") (forward-line 2)
798 (insert rpl1))))
800 (defun org-export-latex-itemize ()
801 "Convert item list to LaTeX."
802 (org-export-latex-list
803 "^\\([ \t]*\\)-"
804 "^[^ \n\t-]+.*$"
805 "- ?\\(\\[.+\\]\\)?"
806 "\\begin{itemize}\n"
807 "\\end{itemize}\n"))
809 (defun org-export-latex-enumerate ()
810 "Convert numeric list to LaTeX."
811 (org-export-latex-list
812 "^\\([ \t]*\\)[0-9]+[\.)] \\(\\[.+\\]\\)? ?"
813 "^[^ \n\t0-9]+.*$"
814 "[0-9]+[\.)] ?\\(\\[.+\\]\\)?"
815 "\\begin{enumerate}\n"
816 "\\end{enumerate}\n"))
818 (defun org-export-latex-fontify ()
819 "Convert fontification to LaTeX."
820 (goto-char (point-min))
821 (while (re-search-forward org-emph-re nil t)
822 ;; The match goes one char after the *string*
823 (unless (get-text-property (1- (point)) 'org-protected)
824 (replace-match
825 (concat (match-string 1)
826 (format
827 (org-export-latex-protect-char-in-string
828 '("\\" "{" "}")
829 (cadr (assoc (match-string 3)
830 org-export-latex-emphasis-alist)))
831 (match-string 4))
832 (match-string 5)) t t)
833 (backward-char))))
835 (defun org-export-latex-protect-char-in-string (char-list string)
836 "Add org-protected text-property to char from CHAR-LIST in STRING."
837 (with-temp-buffer
838 (save-match-data
839 (insert string)
840 (goto-char (point-min))
841 (while (re-search-forward (regexp-opt char-list) nil t)
842 (add-text-properties (match-beginning 0)
843 (match-end 0) '(org-protected t)))
844 (buffer-string))))
846 (defun org-export-latex-links ()
847 ;; Make sure to use the LaTeX hyperref and graphicx package
848 ;; or send some warnings.
849 "Convert links to LaTeX."
850 (goto-char (point-min))
851 (while (re-search-forward org-bracket-link-analytic-regexp nil t)
852 (org-if-unprotected
853 (goto-char (match-beginning 0))
854 (let* ((re-radio org-latex-all-targets-regexp)
855 (remove (list (match-beginning 0) (match-end 0)))
856 (type (match-string 2))
857 (raw-path (match-string 3))
858 (full-raw-path (concat (match-string 1) raw-path))
859 (desc (match-string 5))
860 imgp radiop
861 ;; define the path of the link
862 (path (cond
863 ((member type '("http" "https" "ftp"))
864 (concat type ":" raw-path))
865 ((and re-radio (string-match re-radio raw-path))
866 (setq radiop t))
867 ((equal type "mailto")
868 (concat type ":" raw-path))
869 ((equal type "file")
870 (if (and (or (org-file-image-p (expand-file-name raw-path))
871 (string-match "\\.eps$" raw-path))
872 (equal desc full-raw-path))
873 (setq imgp t)
874 (progn (when (string-match "\\(.+\\)::.+" raw-path)
875 (setq raw-path (match-string 1 raw-path)))
876 (if (file-exists-p raw-path)
877 (concat type "://" (expand-file-name raw-path))
878 (concat type "://" (org-export-directory
879 :LaTeX org-latex-options-plist)
880 raw-path))))))))
881 ;; process with link inserting
882 (apply 'delete-region remove)
883 (cond ((and imgp (plist-get org-latex-options-plist :inline-images))
884 (insert (format "\\includegraphics[%s]{%s}"
885 ;; image option should be set be a comment line
886 org-export-latex-image-default-option
887 (expand-file-name raw-path))))
888 ;; FIXME: what about caption? image properties?
889 (radiop (insert (format "\\hyperref[%s]{%s}" raw-path desc)))
890 (path (insert (format "\\href{%s}{%s}" path desc)))
891 (t (insert "\\texttt{" desc "}")))))))
894 (defun org-latex-cleaned-string-for-export (string &rest parameters)
895 "Cleanup a buffer STRING so that links can be created safely."
896 (interactive)
897 (let* ((re-radio (and org-target-link-regexp
898 (concat "\\([^<]\\)\\(" org-target-link-regexp "\\)")))
899 (re-plain-link (concat "\\([^[<]\\)" org-plain-link-re))
900 (re-angle-link (concat "\\([^[]\\)" org-angle-link-re))
901 (re-archive (concat ":" org-archive-tag ":"))
902 (re-quote (concat "^\\*+[ \t]+" org-quote-string "\\>"))
903 (htmlp (plist-get parameters :for-html))
904 (latexp (plist-get parameters :for-LaTeX))
905 (commentsp (plist-get parameters :comments))
906 (inhibit-read-only t)
907 (outline-regexp "\\*+ ")
908 a b xx
909 rtn p)
910 (save-excursion
911 (set-buffer (get-buffer-create " org-mode-tmp"))
912 (erase-buffer)
913 (insert string)
914 ;; Remove license-to-kill stuff
915 (while (setq p (text-property-any (point-min) (point-max)
916 :org-license-to-kill t))
917 (delete-region p (next-single-property-change p :org-license-to-kill)))
919 (let ((org-inhibit-startup t)) (org-mode))
920 (untabify (point-min) (point-max))
922 ;; Get the correct stuff before the first headline
923 (when (plist-get parameters :skip-before-1st-heading)
924 (goto-char (point-min))
925 (when (re-search-forward "^\\*+[ \t]" nil t)
926 (delete-region (point-min) (match-beginning 0))
927 (goto-char (point-min))
928 (insert "\n")))
929 (when (plist-get parameters :add-text)
930 (goto-char (point-min))
931 (insert (plist-get parameters :add-text) "\n"))
933 ;; Get rid of archived trees
934 (when (not (eq org-export-with-archived-trees t))
935 (goto-char (point-min))
936 (while (re-search-forward re-archive nil t)
937 (if (not (org-on-heading-p t))
938 (org-end-of-subtree t)
939 (beginning-of-line 1)
940 (setq a (if org-export-with-archived-trees
941 (1+ (point-at-eol)) (point))
942 b (org-end-of-subtree t))
943 (if (> b a) (delete-region a b)))))
945 ;; Get rid of property drawers
946 (unless org-export-with-property-drawer
947 (goto-char (point-min))
948 (while (re-search-forward "^[ \t]*:PROPERTIES:[ \t]*\n\\([^@]*?\n\\)?[ \t]*:END:[ \t]*\n" nil t)
949 (replace-match "")))
951 ;; Find targets in comments and move them out of comments,
952 ;; but mark them as targets that should be invisible
953 (goto-char (point-min))
954 (while (re-search-forward "^#.*?\\(<<<?[^>\r\n]+>>>?\\).*" nil t)
955 (replace-match "\\1(INVISIBLE)"))
957 ;; Specific LaTeX cleaning
958 (when latexp
959 (require 'org-export-latex nil t)
960 (org-export-latex-cleaned-string))
962 ;; Protect stuff from HTML processing
963 (goto-char (point-min))
964 (let ((formatters `((,htmlp "HTML" "BEGIN_HTML" "END_HTML"))) fmt)
965 (while (re-search-forward "^[ \t]*:.*\\(\n[ \t]*:.*\\)*" nil t)
966 (add-text-properties (match-beginning 0) (match-end 0)
967 '(org-protected t)))
968 (while formatters
969 (setq fmt (pop formatters))
970 (when (car fmt)
971 (goto-char (point-min))
972 (while (re-search-forward (concat "^#\\+" (cadr fmt)
973 ":[ \t]*\\(.*\\)") nil t)
974 (replace-match "\\1" t)
975 (add-text-properties
976 (point-at-bol) (min (1+ (point-at-eol)) (point-max))
977 '(org-protected t))))
978 (goto-char (point-min))
979 (while (re-search-forward
980 (concat "^#\\+"
981 (caddr fmt) "\\>.*\\(\\(\n.*\\)*?\n\\)#\\+"
982 (cadddr fmt) "\\>.*\n?") nil t)
983 (if (car fmt)
984 (add-text-properties (match-beginning 1) (1+ (match-end 1))
985 '(org-protected t))
986 (delete-region (match-beginning 0) (match-end 0))))
987 (goto-char (point-min))
988 (while (re-search-forward re-quote nil t)
989 (goto-char (match-beginning 0))
990 (end-of-line 1)
991 (add-text-properties (point) (org-end-of-subtree t)
992 '(org-protected t)))))
994 ;; Remove or replace comments
995 ;; If :comments is set, use this char for commenting out comments and
996 ;; protect them. otherwise delete them
997 (goto-char (point-min))
998 (while (re-search-forward "^#\\(.*\n?\\)" nil t)
999 (if commentsp
1000 (progn (add-text-properties
1001 (match-beginning 0) (match-end 0) '(org-protected t))
1002 (replace-match (format commentsp (match-string 1)) t t))
1003 (replace-match "")))
1005 ;; Find matches for radio targets and turn them into internal links
1006 (goto-char (point-min))
1007 (when re-radio
1008 (while (re-search-forward re-radio nil t)
1009 (org-if-unprotected
1010 (replace-match "\\1[[\\2]]"))))
1012 ;; Find all links that contain a newline and put them into a single line
1013 (goto-char (point-min))
1014 (while (re-search-forward "\\(\\(\\[\\|\\]\\)\\[[^]]*?\\)[ \t]*\n[ \t]*\\([^]]*\\]\\(\\[\\|\\]\\)\\)" nil t)
1015 (org-if-unprotected
1016 (replace-match "\\1 \\3")
1017 (goto-char (match-beginning 0))))
1019 ;; Convert LaTeX fragments to images
1020 (when (plist-get parameters :LaTeX-fragments)
1021 (org-format-latex
1022 (concat "ltxpng/" (file-name-sans-extension
1023 (file-name-nondirectory
1024 org-current-export-file)))
1025 org-current-export-dir nil "Creating LaTeX image %s"))
1026 (message "Exporting...")
1028 ;; Normalize links: Convert angle and plain links into bracket links
1029 ;; Expand link abbreviations
1030 (goto-char (point-min))
1031 (while (re-search-forward re-plain-link nil t)
1032 (goto-char (1- (match-end 0)))
1033 (org-if-unprotected
1034 (let* ((s (concat (match-string 1) "[[" (match-string 2)
1035 ":" (match-string 3) "]]")))
1036 ;; added 'org-protected property to links
1037 (put-text-property 0 (length s) 'face 'org-link s)
1038 (replace-match s t t))))
1039 (goto-char (point-min))
1040 (while (re-search-forward re-angle-link nil t)
1041 (goto-char (1- (match-end 0)))
1042 (org-if-unprotected
1043 (let* ((s (concat (match-string 1) "[[" (match-string 2)
1044 ":" (match-string 3) "]]")))
1045 (put-text-property 0 (length s) 'face 'org-link s)
1046 (replace-match s t t))))
1047 (goto-char (point-min))
1048 (while (re-search-forward org-bracket-link-regexp nil t)
1049 (org-if-unprotected
1050 (let* ((s (concat "[[" (setq xx (save-match-data
1051 (org-link-expand-abbrev (match-string 1))))
1053 (if (match-end 3)
1054 (match-string 2)
1055 (concat "[" xx "]"))
1056 "]")))
1057 (put-text-property 0 (length s) 'face 'org-link s)
1058 (replace-match s t t))))
1060 ;; Find multiline emphasis and put them into single line
1061 (when (plist-get parameters :emph-multiline)
1062 (goto-char (point-min))
1063 (while (re-search-forward org-emph-re nil t)
1064 (if (not (= (char-after (match-beginning 3))
1065 (char-after (match-beginning 4))))
1066 (org-if-unprotected
1067 (subst-char-in-region (match-beginning 0) (match-end 0)
1068 ?\n ?\ t)
1069 (goto-char (1- (match-end 0))))
1070 (goto-char (1+ (match-beginning 0))))))
1072 (setq rtn (buffer-string)))
1073 (kill-buffer " org-mode-tmp")
1074 rtn))
1076 (defsubst org-latex-protect (string)
1077 (add-text-properties 0 (length string) '(org-protected t) string) string)
1079 (defun org-export-latex-cleaned-string ()
1080 "Clean stuff in the LaTeX export."
1082 ;; Preserve line breaks
1083 (goto-char (point-min))
1084 (while (re-search-forward "\\\\\\\\" nil t)
1085 (add-text-properties (match-beginning 0) (match-end 0)
1086 '(org-protected t)))
1088 ;; Convert LaTeX to @LaTeX{}
1089 (goto-char (point-min))
1090 (let ((case-fold-search nil) rpl)
1091 (while (re-search-forward "\\([^+_]\\)LaTeX" nil t)
1092 (replace-match (org-latex-protect
1093 (concat (match-string 1) "\\LaTeX{}")) t t)))
1095 ;; Convert horizontal rules
1096 (goto-char (point-min))
1097 (while (re-search-forward "^----+.$" nil t)
1098 (replace-match (org-latex-protect "\\hrule") t t))
1100 ;; Remove COMMENT subtrees
1101 ;; What about QUOTE subtrees?
1102 (goto-char (point-min))
1103 (while (re-search-forward
1104 (concat "^\\*+ \\(" org-comment-string "\\)")
1105 nil t)
1106 (beginning-of-line)
1107 (org-cut-subtree))
1109 ;; Protect LaTeX \commands{...}
1110 (goto-char (point-min))
1111 (while (re-search-forward "\\\\[a-z]+\\(?:\\[.*\\]\\)?\\(?:{.*}\\)?" nil t)
1112 (add-text-properties (match-beginning 0) (match-end 0)
1113 '(org-protected t)))
1115 ;; Replace radio links
1116 (goto-char (point-min))
1117 (let ((search (concat "<<<?" org-latex-all-targets-regexp ">?>>")))
1118 (while (re-search-forward search nil t)
1119 (replace-match
1120 (org-latex-protect (format "\\label{%s}" (match-string 1))) t t)))
1122 ;; Delete @<...> constructs
1123 (goto-char (point-min))
1124 ;; Thanks to Daniel Clemente for this regexp
1125 (while (re-search-forward "@<\\(?:[^\"\n]\\|\".*\"\\)*?>" nil t)
1126 (replace-match ""))
1128 ;; Add #+BEGIN_LaTeX before any \begin{...}
1129 (goto-char (point-min))
1130 (while (re-search-forward "^ *\\\\begin{" nil t)
1131 (replace-match "#+BEGIN_LaTeX:\n\\&" t))
1133 ;; Add #+END_LaTeX after any \end{...}
1134 (goto-char (point-min))
1135 (while (re-search-forward "^ *\\\\end{.+}.*$" nil t)
1136 (replace-match "\\&\n#+END_LaTeX" t))
1138 ;; When converting to LaTeX, replace footnotes
1139 ;; FIXME: don't protect footnotes from conversion
1140 (when (plist-get org-latex-options-plist :footnotes)
1141 (goto-char (point-min))
1142 (while (re-search-forward "\\[[0-9]+\\]" nil t)
1143 (when (save-match-data
1144 (save-excursion (beginning-of-line)
1145 (looking-at "[^:|]")))
1146 (let ((foot-beg (match-beginning 0))
1147 (foot-end (match-end 0))
1148 (foot-prefix (match-string 0))
1149 footnote footnote-rpl)
1150 (when (and (re-search-forward (regexp-quote foot-prefix) nil t))
1151 (replace-match "")
1152 (let ((end (save-excursion
1153 (if (re-search-forward "^$\\|\\[[0-9]+\\]" nil t)
1154 (match-beginning 0) (point-max)))))
1155 (setq footnote (concat
1156 (org-trim (buffer-substring (point) end))
1157 ;; FIXME stupid workaround for cases where
1158 ;; `org-bracket-link-analytic-regexp' matches
1159 ;; }. as part of the link.
1160 " "))
1161 (delete-region (point) end)))
1162 (goto-char foot-beg)
1163 (delete-region foot-beg foot-end)
1164 (setq footnote-rpl (format "\\footnote{%s}" footnote))
1165 (add-text-properties 0 10 '(org-protected t) footnote-rpl)
1166 (add-text-properties (1- (length footnote-rpl))
1167 (length footnote-rpl)
1168 '(org-protected t) footnote-rpl)
1169 (insert footnote-rpl))))
1171 ;; Replace footnote section tag for LaTeX
1172 (goto-char (point-min))
1173 (while (re-search-forward
1174 (concat "^" footnote-section-tag-regexp) nil t)
1175 (replace-match "")))
1177 ;; Protect stuff from LaTeX processing.
1178 ;; We will get rid on this once org.el integrate org-export-latex.el
1179 (goto-char (point-min))
1180 (let ((formatters `((,latexp "LaTeX" "BEGIN_LaTeX" "END_LaTeX"))) fmt)
1181 (while (re-search-forward "^[ \t]*:.*\\(\n[ \t]*:.*\\)*" nil t)
1182 (add-text-properties (match-beginning 0) (match-end 0)
1183 '(org-protected t)))
1184 (while formatters
1185 (setq fmt (pop formatters))
1186 (when (car fmt)
1187 (goto-char (point-min))
1188 (while (re-search-forward (concat "^#\\+" (cadr fmt)
1189 ;; ":[ \t]*\\(.*\\)") nil t)
1190 ;; FIXME: authorize spaces after #+LaTeX:
1191 ;; to get list correctly exported
1192 ":\\(.*\\)") nil t)
1193 (replace-match "\\1" t)
1194 (add-text-properties
1195 (point-at-bol) (min (1+ (point-at-eol)) (point-max))
1196 '(org-protected t))))
1197 (goto-char (point-min))
1198 (while (re-search-forward
1199 (concat "^#\\+"
1200 (caddr fmt) "\\>.*\\(\\(\n.*\\)*?\n\\)#\\+"
1201 (cadddr fmt) "\\>.*\n?") nil t)
1202 (if (car fmt)
1203 (add-text-properties (match-beginning 1) (1+ (match-end 1))
1204 '(org-protected t))
1205 (delete-region (match-beginning 0) (match-end 0))))
1206 (goto-char (point-min))
1207 (while (re-search-forward re-quote nil t)
1208 (goto-char (match-beginning 0))
1209 (end-of-line 1)
1210 (add-text-properties (point) (org-end-of-subtree t)
1211 '(org-protected t))))))
1213 (provide 'org-export-latex)
1215 ;;; org-export-latex.el ends here