Mercurial > emacs
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 |