comparison lisp/textmodes/org-export-latex.el @ 82752:b0315a14fb58

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