Revert "in emails, wrap html and images in a multipart/mixed structure"
[org-mode.git] / EXPERIMENTAL / org-e-odt.el
blobbf5bb8f4edd51cf2b0fb733cd915e70bf851e413
1 ;;; org-e-odt.el --- OpenDocument Text exporter for Org-mode
3 ;; Copyright (C) 2010-2012 Free Software Foundation, Inc.
5 ;; Author: Jambunathan K <kjambunathan at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp
7 ;; Homepage: http://orgmode.org
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;;; Code:
27 (eval-when-compile
28 (require 'cl))
30 ;; FIXMES
31 ;; org-e-odt-preprocess-latex-fragments
32 ;; org-export-as-e-odt-and-open
33 ;; org-export-as-e-odt-batch
34 ;; org-export-as-e-odt
36 (defun org-e-odt-get-style-name-for-entity (category &optional entity)
37 (let ((entity (or entity 'default)))
38 (or
39 (cdr (assoc entity (cdr (assoc category
40 org-e-odt-org-styles-alist))))
41 (cdr (assoc entity (cdr (assoc category
42 org-e-odt-default-org-styles-alist))))
43 (error "Cannot determine style name for entity %s of type %s"
44 entity category))))
46 ;; Following variable is let bound when `org-do-lparse' is in
47 ;; progress. See org-html.el.
49 (defun org-e-odt-format-preamble (info)
50 (let* ((title (org-export-secondary-string
51 (plist-get info :title) 'e-odt info))
52 (author (and (plist-get info :with-author)
53 (let ((auth (plist-get info :author)))
54 (and auth (org-export-secondary-string
55 auth 'e-odt info)))))
56 (date (plist-get info :date))
57 (iso-date (org-e-odt-format-date date))
58 (date (org-e-odt-format-date date "%d %b %Y"))
59 (email (plist-get info :email))
60 ;; switch on or off above vars based on user settings
61 (author (and (plist-get info :with-author) (or author email)))
62 ;; (date (and (plist-get info :time-stamp-file) date))
63 (email (and (plist-get info :with-email) email)))
64 (concat
65 ;; title
66 (when title
67 (concat
68 (org-e-odt-format-stylized-paragraph
69 'title (format "\n<text:title>%s</text:title>" title))
70 ;; separator
71 "\n<text:p text:style-name=\"OrgTitle\"/>"))
72 (cond
73 ((and author (not email))
74 ;; author only
75 (concat
76 (org-e-odt-format-stylized-paragraph
77 'subtitle
78 (format "<text:initial-creator>%s</text:initial-creator>" author))
79 ;; separator
80 "\n<text:p text:style-name=\"OrgSubtitle\"/>"))
81 ((and author email)
82 ;; author and email
83 (concat
84 (org-e-odt-format-stylized-paragraph
85 'subtitle
86 (org-e-odt-format-link
87 (format "<text:initial-creator>%s</text:initial-creator>" author)
88 (concat "mailto:" email)))
89 ;; separator
90 "\n<text:p text:style-name=\"OrgSubtitle\"/>")))
91 ;; date
92 (when date
93 (concat
94 (org-e-odt-format-stylized-paragraph
95 'subtitle
96 (org-e-odt-format-tags
97 '("<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">"
98 . "</text:date>")
99 date "N75" iso-date))
100 ;; separator
101 "<text:p text:style-name=\"OrgSubtitle\"/>")))))
103 (defun org-e-odt-begin-section (style &optional name)
104 (let ((default-name (car (org-e-odt-add-automatic-style "Section"))))
105 (format "<text:section text:style-name=\"%s\" text:name=\"%s\">"
106 style (or name default-name))))
108 (defun org-e-odt-end-section ()
109 "</text:section>")
111 (defun org-e-odt-begin-paragraph (&optional style)
112 (format "<text:p%s>" (org-e-odt-get-extra-attrs-for-paragraph-style style)))
114 (defun org-e-odt-end-paragraph ()
115 "</text:p>")
117 (defun org-e-odt-get-extra-attrs-for-paragraph-style (style)
118 (let (style-name)
119 (setq style-name
120 (cond
121 ((stringp style) style)
122 ((symbolp style) (org-e-odt-get-style-name-for-entity
123 'paragraph style))))
124 (unless style-name
125 (error "Don't know how to handle paragraph style %s" style))
126 (format " text:style-name=\"%s\"" style-name)))
128 (defun org-e-odt-format-stylized-paragraph (style text)
129 (format "\n<text:p%s>%s</text:p>"
130 (org-e-odt-get-extra-attrs-for-paragraph-style style)
131 text))
133 (defun org-e-odt-format-author (&optional author )
134 (when (setq author (or author (plist-get org-lparse-opt-plist :author)))
135 (format "<dc:creator>%s</dc:creator>" author)))
137 (defun org-e-odt-format-date (&optional org-ts fmt)
138 (save-match-data
139 (let* ((time
140 (and (stringp org-ts)
141 (string-match org-ts-regexp0 org-ts)
142 (apply 'encode-time
143 (org-fix-decoded-time
144 (org-parse-time-string (match-string 0 org-ts) t)))))
145 date)
146 (cond
147 (fmt (format-time-string fmt time))
148 (t (setq date (format-time-string "%Y-%m-%dT%H:%M:%S%z" time))
149 (format "%s:%s" (substring date 0 -2) (substring date -2)))))))
151 (defun org-e-odt-begin-annotation (&optional author date)
152 (concat
153 "<office:annotation>\n"
154 (and author (org-e-odt-format-author author))
155 (org-e-odt-format-tags
156 '("<dc:date>" . "</dc:date>")
157 (org-e-odt-format-date
158 (or date (plist-get org-lparse-opt-plist :date))))
159 (org-e-odt-begin-paragraph)))
161 (defun org-e-odt-end-annotation ()
162 "</office:annotation>")
164 (defun org-e-odt-begin-plain-list (ltype)
165 (let* ((style-name (org-e-odt-get-style-name-for-entity 'list ltype))
166 (extra (concat
167 ;; (if (or org-lparse-list-table-p
168 ;; (and (= 1 (length org-lparse-list-stack))
169 ;; (null org-e-odt-list-stack-stashed)))
170 ;; " text:continue-numbering=\"false\""
171 ;; " text:continue-numbering=\"true\"")
173 " text:continue-numbering=\"true\""
175 (when style-name
176 (format " text:style-name=\"%s\"" style-name)))))
177 (case ltype
178 ((ordered unordered descriptive)
179 (concat
180 ;; (org-e-odt-end-paragraph)
181 (format "<text:list%s>" extra)))
182 (t (error "Unknown list type: %s" ltype)))))
184 (defun org-e-odt-end-plain-list (ltype)
185 (if ltype "</text:list>"
186 (error "Unknown list type: %s" ltype)))
188 (defun org-e-odt-begin-list-item (ltype &optional arg headline)
189 (case ltype
190 (ordered
191 (assert (not headline) t)
192 (let* ((counter arg) (extra ""))
193 (concat "<text:list-item>" ;; (org-e-odt-begin-paragraph)
195 ;; (if (= (length org-lparse-list-stack)
196 ;; (length org-e-odt-list-stack-stashed))
197 ;; "<text:list-header>" "<text:list-item>")
199 (unordered
200 (let* ((id arg) (extra ""))
201 (concat
202 "<text:list-item>"
203 ;; (org-e-odt-begin-paragraph)
204 (if headline (org-e-odt-format-target headline id)
205 (org-e-odt-format-bookmark "" id)))
206 ;; (if (= (length org-lparse-list-stack)
207 ;; (length org-e-odt-list-stack-stashed))
208 ;; "<text:list-header>" "<text:list-item>")
210 (descriptive
211 (assert (not headline) t)
212 (let ((term (or arg "(no term)")))
213 (concat
214 (org-e-odt-format-tags
215 '("<text:list-item>" . "</text:list-item>")
216 (org-e-odt-format-stylized-paragraph 'definition-term term))
217 (org-e-odt-begin-list-item 'unordered)
218 (org-e-odt-begin-plain-list 'descriptive)
219 (org-e-odt-begin-list-item 'unordered))))
220 (t (error "Unknown list type"))))
222 (defun org-e-odt-end-list-item (ltype)
223 (case ltype
224 ((ordered unordered)
225 ;; (org-lparse-insert-tag
226 ;; (if (= (length org-lparse-list-stack)
227 ;; (length org-e-odt-list-stack-stashed))
228 ;; (prog1 "</text:list-header>"
229 ;; (setq org-e-odt-list-stack-stashed nil))
230 ;; "</text:list-item>")
231 "</text:list-item>"
232 ;; )
234 (descriptive
235 (concat
236 (org-e-odt-end-list-item 'unordered)
237 (org-e-odt-end-plain-list 'descriptive)
238 (org-e-odt-end-list-item 'unordered)
240 (t (error "Unknown list type"))))
242 (defun org-e-odt-discontinue-list ()
243 (let ((stashed-stack org-lparse-list-stack))
244 (loop for list-type in stashed-stack
245 do (org-lparse-end-list-item-1 list-type)
246 (org-lparse-end-list list-type))
247 (setq org-e-odt-list-stack-stashed stashed-stack)))
249 (defun org-e-odt-continue-list ()
250 (setq org-e-odt-list-stack-stashed (nreverse org-e-odt-list-stack-stashed))
251 (loop for list-type in org-e-odt-list-stack-stashed
252 do (org-lparse-begin-list list-type)
253 (org-lparse-begin-list-item list-type)))
255 (defun org-e-odt-write-automatic-styles ()
256 "Write automatic styles to \"content.xml\"."
257 (with-current-buffer
258 (find-file-noselect (expand-file-name "content.xml") t)
259 ;; position the cursor
260 (goto-char (point-min))
261 (re-search-forward " </office:automatic-styles>" nil t)
262 (goto-char (match-beginning 0))
263 ;; write automatic table styles
264 (loop for (style-name props) in
265 (plist-get org-e-odt-automatic-styles 'Table) do
266 (when (setq props (or (plist-get props :rel-width) 96))
267 (insert (format org-e-odt-table-style-format style-name props))))))
269 (defun org-e-odt-add-automatic-style (object-type &optional object-props)
270 "Create an automatic style of type OBJECT-TYPE with param OBJECT-PROPS.
271 OBJECT-PROPS is (typically) a plist created by passing
272 \"#+ATTR_ODT: \" option of the object in question to
273 `org-e-odt-parse-block-attributes'.
275 Use `org-e-odt-object-counters' to generate an automatic
276 OBJECT-NAME and STYLE-NAME. If OBJECT-PROPS is non-nil, add a
277 new entry in `org-e-odt-automatic-styles'. Return (OBJECT-NAME
278 . STYLE-NAME)."
279 (assert (stringp object-type))
280 (let* ((object (intern object-type))
281 (seqvar object)
282 (seqno (1+ (or (plist-get org-e-odt-object-counters seqvar) 0)))
283 (object-name (format "%s%d" object-type seqno)) style-name)
284 (setq org-e-odt-object-counters
285 (plist-put org-e-odt-object-counters seqvar seqno))
286 (when object-props
287 (setq style-name (format "Org%s" object-name))
288 (setq org-e-odt-automatic-styles
289 (plist-put org-e-odt-automatic-styles object
290 (append (list (list style-name object-props))
291 (plist-get org-e-odt-automatic-styles object)))))
292 (cons object-name style-name)))
294 (defun org-e-odt-format-table-columns ()
295 (let* ((num-cols (length (plist-get table-info :alignment)))
296 (col-nos (loop for i from 0 below num-cols collect i))
297 (levels )
298 (col-widths (plist-get table-info :width))
299 (style (or (nth 1 org-e-odt-table-style-spec) "OrgTable")))
300 (mapconcat
301 (lambda (c)
302 (let* ((width (or (and org-lparse-table-is-styled (aref col-widths c))
303 0)))
304 (org-e-odt-make-string
305 (1+ width)
306 (org-e-odt-format-tags
307 "<table:table-column table:style-name=\"%sColumn\"/>" "" style))))
308 col-nos "\n")))
311 (defun org-e-odt-begin-table (caption label attributes short-caption)
312 ;; (setq org-e-odt-table-indentedp (not (null org-lparse-list-stack)))
313 (setq org-e-odt-table-indentedp nil) ; FIXME
314 (when org-e-odt-table-indentedp
315 ;; Within the Org file, the table is appearing within a list item.
316 ;; OpenDocument doesn't allow table to appear within list items.
317 ;; Temporarily terminate the list, emit the table and then
318 ;; re-continue the list.
319 (org-e-odt-discontinue-list)
320 ;; Put the Table in an indented section.
321 (let ((level (length org-e-odt-list-stack-stashed)))
322 (org-e-odt-begin-section (format "OrgIndentedSection-Level-%d" level))))
323 (setq attributes (org-e-odt-parse-block-attributes attributes))
324 (setq org-e-odt-table-style (plist-get attributes :style))
325 (setq org-e-odt-table-style-spec
326 (assoc org-e-odt-table-style org-e-odt-table-styles))
327 (concat
328 (org-e-odt-format-stylized-paragraph
329 'table (org-e-odt-format-entity-caption label caption "__Table__"))
330 (let ((automatic-name (org-e-odt-add-automatic-style "Table" attributes)))
331 (format
332 "\n<table:table table:name=\"%s\" table:style-name=\"%s\">\n"
333 (or short-caption (car automatic-name))
334 (or (nth 1 org-e-odt-table-style-spec) (cdr automatic-name) "OrgTable")))
335 (org-e-odt-format-table-columns) "\n")
337 ;; (org-e-html-pp table-info)
341 (defun org-e-odt-end-table ()
342 (concat
343 "</table:table>"
344 ;; (when org-e-odt-table-indentedp
345 ;; (org-e-odt-end-section)
346 ;; (org-e-odt-continue-list))
349 (defun org-e-odt-begin-table-rowgroup (&optional is-header-row)
350 (prog1
351 (concat (when org-e-odt-table-rowgrp-open
352 (org-e-odt-end-table-rowgroup))
353 (if is-header-row "<table:table-header-rows>"
354 "<table:table-rows>"))
355 (setq org-e-odt-table-rowgrp-open t)
356 (setq org-e-odt-table-cur-rowgrp-is-hdr is-header-row)))
358 (defun org-e-odt-end-table-rowgroup ()
359 (when org-e-odt-table-rowgrp-open
360 (setq org-e-odt-table-rowgrp-open nil)
361 (if org-e-odt-table-cur-rowgrp-is-hdr
362 "</table:table-header-rows>" "</table:table-rows>")))
364 (defun org-e-odt-format-table-row (row)
365 (org-e-odt-format-tags
366 '("<table:table-row>" . "</table:table-row>") row))
368 (defun org-e-odt-get-column-alignment (c)
369 (let ((colalign-vector (plist-get table-info :alignment)))
370 ;; FIXME
371 (assoc-default (aref colalign-vector c)
372 '(("l" . "left")
373 ("r" . "right")
374 ("c" . "center")))))
376 (defun org-e-odt-get-table-cell-styles (r c &optional style-spec)
377 "Retrieve styles applicable to a table cell.
378 R and C are (zero-based) row and column numbers of the table
379 cell. STYLE-SPEC is an entry in `org-e-odt-table-styles'
380 applicable to the current table. It is `nil' if the table is not
381 associated with any style attributes.
383 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
385 When STYLE-SPEC is nil, style the table cell the conventional way
386 - choose cell borders based on row and column groupings and
387 choose paragraph alignment based on `org-col-cookies' text
388 property. See also
389 `org-e-odt-get-paragraph-style-cookie-for-table-cell'.
391 When STYLE-SPEC is non-nil, ignore the above cookie and return
392 styles congruent with the ODF-1.2 specification."
393 (cond
394 (style-spec
396 ;; LibreOffice - particularly the Writer - honors neither table
397 ;; templates nor custom table-cell styles. Inorder to retain
398 ;; inter-operability with LibreOffice, only automatic styles are
399 ;; used for styling of table-cells. The current implementation is
400 ;; congruent with ODF-1.2 specification and hence is
401 ;; future-compatible.
403 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
404 ;; which recognizes as many as 16 different cell types - is much
405 ;; richer. Unfortunately it is NOT amenable to easy configuration
406 ;; by hand.
408 (let* ((template-name (nth 1 style-spec))
409 (cell-style-selectors (nth 2 style-spec))
410 (cell-type
411 (cond
412 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors))
413 (= c 0)) "FirstColumn")
414 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors))
415 (= c (1- org-lparse-table-ncols))) "LastColumn")
416 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors))
417 (= r 0)) "FirstRow")
418 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors))
419 (= r org-e-odt-table-rownum))
420 "LastRow")
421 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
422 (= (% r 2) 1)) "EvenRow")
423 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
424 (= (% r 2) 0)) "OddRow")
425 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
426 (= (% c 2) 1)) "EvenColumn")
427 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
428 (= (% c 2) 0)) "OddColumn")
429 (t ""))))
430 (cons
431 (concat template-name cell-type "TableCell")
432 (concat template-name cell-type "TableParagraph"))))
434 (cons
435 (concat
436 "OrgTblCell"
437 (cond
438 ((= r 0) "T")
439 ((eq (cdr (assoc r nil ;; org-lparse-table-rowgrp-info FIXME
440 )) :start) "T")
441 (t ""))
442 (when (= r org-e-odt-table-rownum) "B")
443 (cond
444 ((= c 0) "")
445 ((or (memq (nth c org-table-colgroup-info) '(:start :startend))
446 (memq (nth (1- c) org-table-colgroup-info) '(:end :startend))) "L")
447 (t "")))
448 (capitalize (org-e-odt-get-column-alignment c))))))
450 (defun org-e-odt-get-paragraph-style-cookie-for-table-cell (r c)
451 (concat
452 (and (not org-e-odt-table-style-spec)
453 (cond
454 (org-e-odt-table-cur-rowgrp-is-hdr "OrgTableHeading")
455 ((and (= c 0) nil
456 ;; (org-lparse-get 'TABLE-FIRST-COLUMN-AS-LABELS)
458 "OrgTableHeading")
459 (t "OrgTableContents")))
460 (and org-lparse-table-is-styled
461 (cdr (org-e-odt-get-table-cell-styles
462 r c org-e-odt-table-style-spec)))))
464 (defun org-e-odt-get-style-name-cookie-for-table-cell (r c)
465 (when org-lparse-table-is-styled
466 (let* ((cell-styles (org-e-odt-get-table-cell-styles
467 r c org-e-odt-table-style-spec))
468 (table-cell-style (car cell-styles)))
469 table-cell-style)))
471 (defun org-e-odt-format-table-cell (data r c horiz-span)
472 (concat
473 (let* ((paragraph-style-cookie
474 (org-e-odt-get-paragraph-style-cookie-for-table-cell r c))
475 (style-name-cookie
476 (org-e-odt-get-style-name-cookie-for-table-cell r c))
477 (extra (and style-name-cookie
478 (format " table:style-name=\"%s\"" style-name-cookie)))
479 (extra (concat extra
480 (and (> horiz-span 0)
481 (format " table:number-columns-spanned=\"%d\""
482 (1+ horiz-span))))))
483 (org-e-odt-format-tags
484 '("<table:table-cell%s>" . "</table:table-cell>")
485 (if org-lparse-list-table-p data
486 (org-e-odt-format-stylized-paragraph paragraph-style-cookie data)) extra))
487 (let (s)
488 (dotimes (i horiz-span)
489 (setq s (concat s "\n<table:covered-table-cell/>"))) s)
490 "\n"))
492 (defun org-e-odt-begin-toc (lang-specific-heading max-level)
493 (concat
494 (format "
495 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
496 <text:table-of-content-source text:outline-level=\"%d\">
497 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
498 " max-level lang-specific-heading)
500 (let ((entry-templates ""))
501 (loop for level from 1 upto 10
502 do (setq entry-templates
503 (concat entry-templates
504 (format
506 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
507 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
508 <text:index-entry-chapter/>
509 <text:index-entry-text/>
510 <text:index-entry-link-end/>
511 </text:table-of-content-entry-template>
512 " level level))))
513 entry-templates)
515 (format "
516 </text:table-of-content-source>
518 <text:index-body>
519 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
520 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
521 </text:index-title>
522 " lang-specific-heading)))
524 (defun org-e-odt-end-toc ()
525 (format "
526 </text:index-body>
527 </text:table-of-content>
530 (defun org-e-odt-format-toc-entry (snumber todo headline tags href)
532 ;; FIXME
533 (setq headline (concat
534 (and org-export-with-section-numbers
535 (concat snumber ". "))
536 headline
537 (and tags
538 (concat
539 (org-e-odt-format-spaces 3)
540 (org-e-odt-format-fontify tags "tag")))))
541 (when todo
542 (setq headline (org-e-odt-format-fontify headline "todo")))
544 (let ((org-e-odt-suppress-xref t))
545 (org-e-odt-format-link headline (concat "#" href))))
547 (defun org-e-odt-format-toc-item (toc-entry level org-last-level)
548 (let ((style (format "Contents_20_%d" level)))
549 (concat "\n" (org-e-odt-format-stylized-paragraph style toc-entry) "\n")))
551 ;; Following variable is let bound during 'ORG-LINK callback. See
552 ;; org-html.el
554 (defun org-e-odt-format-link (desc href &optional attr)
555 (cond
556 ((and (= (string-to-char href) ?#) (not org-e-odt-suppress-xref))
557 (setq href (substring href 1))
558 (let ((xref-format "text"))
559 (when (numberp desc)
560 (setq desc (format "%d" desc) xref-format "number"))
561 (when (listp desc)
562 (setq desc (mapconcat 'identity desc ".") xref-format "chapter"))
563 (setq href (concat org-e-odt-bookmark-prefix href))
564 (org-e-odt-format-tags-simple
565 '("<text:bookmark-ref text:reference-format=\"%s\" text:ref-name=\"%s\">" .
566 "</text:bookmark-ref>")
567 desc xref-format href)))
568 (org-lparse-link-description-is-image
569 (org-e-odt-format-tags
570 '("<draw:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</draw:a>")
571 desc href (or attr "")))
573 (org-e-odt-format-tags-simple
574 '("<text:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</text:a>")
575 desc href (or attr "")))))
577 (defun org-e-odt-format-spaces (n)
578 (cond
579 ((= n 1) " ")
580 ((> n 1) (concat
581 " " (org-e-odt-format-tags "<text:s text:c=\"%d\"/>" "" (1- n))))
582 (t "")))
584 (defun org-e-odt-format-tabs (&optional n)
585 (let ((tab "<text:tab/>")
586 (n (or n 1)))
587 (insert tab)))
589 (defun org-e-odt-format-line-break ()
590 (org-e-odt-format-tags "<text:line-break/>" ""))
592 (defun org-e-odt-format-horizontal-line ()
593 (org-e-odt-format-stylized-paragraph 'horizontal-line ""))
595 (defun org-e-odt-encode-plain-text (line &optional no-whitespace-filling)
596 (setq line (org-e-html-encode-plain-text line))
597 (if no-whitespace-filling line
598 (org-e-odt-fill-tabs-and-spaces line)))
600 (defun org-e-odt-format-line (line)
601 (case org-lparse-dyn-current-environment
602 (fixedwidth (concat
603 (org-e-odt-format-stylized-paragraph
604 'fixedwidth (org-e-odt-encode-plain-text line)) "\n"))
605 (t (concat line "\n"))))
607 (defun org-e-odt-format-comment (fmt &rest args)
608 (let ((comment (apply 'format fmt args)))
609 (format "\n<!-- %s -->\n" comment)))
611 (defun org-e-odt-format-org-entity (wd)
612 (org-entity-get-representation wd 'utf8))
614 (defun org-e-odt-fill-tabs-and-spaces (line)
615 (replace-regexp-in-string
616 "\\([\t]\\|\\([ ]+\\)\\)" (lambda (s)
617 (cond
618 ((string= s "\t") (org-e-odt-format-tabs))
619 (t (org-e-odt-format-spaces (length s))))) line))
621 (defun org-e-odt-hfy-face-to-css (fn)
622 "Create custom style for face FN.
623 When FN is the default face, use it's foreground and background
624 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
625 use it's color attribute to create a character style whose name
626 is obtained from FN. Currently all attributes of FN other than
627 color are ignored.
629 The style name for a face FN is derived using the following
630 operations on the face name in that order - de-dash, CamelCase
631 and prefix with \"OrgSrc\". For example,
632 `font-lock-function-name-face' is associated with
633 \"OrgSrcFontLockFunctionNameFace\"."
634 (let* ((css-list (hfy-face-to-style fn))
635 (style-name ((lambda (fn)
636 (concat "OrgSrc"
637 (mapconcat
638 'capitalize (split-string
639 (hfy-face-or-def-to-name fn) "-")
640 ""))) fn))
641 (color-val (cdr (assoc "color" css-list)))
642 (background-color-val (cdr (assoc "background" css-list)))
643 (style (and org-e-odt-create-custom-styles-for-srcblocks
644 (cond
645 ((eq fn 'default)
646 (format org-src-block-paragraph-format
647 background-color-val color-val))
649 (format
651 <style:style style:name=\"%s\" style:family=\"text\">
652 <style:text-properties fo:color=\"%s\"/>
653 </style:style>" style-name color-val))))))
654 (cons style-name style)))
656 (defun org-e-odt-insert-custom-styles-for-srcblocks (styles)
657 "Save STYLES used for colorizing of source blocks.
658 Update styles.xml with styles that were collected as part of
659 `org-e-odt-hfy-face-to-css' callbacks."
660 (when styles
661 (with-current-buffer
662 (find-file-noselect (expand-file-name "styles.xml") t)
663 (goto-char (point-min))
664 (when (re-search-forward "</office:styles>" nil t)
665 (goto-char (match-beginning 0))
666 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n")))))
668 (defun org-e-odt-remap-stylenames (style-name)
670 (cdr (assoc style-name '(("timestamp-wrapper" . "OrgTimestampWrapper")
671 ("timestamp" . "OrgTimestamp")
672 ("timestamp-kwd" . "OrgTimestampKeyword")
673 ("tag" . "OrgTag")
674 ("todo" . "OrgTodo")
675 ("done" . "OrgDone")
676 ("target" . "OrgTarget"))))
677 style-name))
679 (defun org-e-odt-format-fontify (text style &optional id)
680 (let* ((style-name
681 (cond
682 ((stringp style)
683 (org-e-odt-remap-stylenames style))
684 ((symbolp style)
685 (org-e-odt-get-style-name-for-entity 'character style))
686 ((listp style)
687 (assert (< 1 (length style)))
688 (let ((parent-style (pop style)))
689 (mapconcat (lambda (s)
690 ;; (assert (stringp s) t)
691 (org-e-odt-remap-stylenames s)) style "")
692 (org-e-odt-remap-stylenames parent-style)))
693 (t (error "Don't how to handle style %s" style)))))
694 (org-e-odt-format-tags-simple
695 '("<text:span text:style-name=\"%s\">" . "</text:span>")
696 text style-name)))
698 (defun org-e-odt-relocate-relative-path (path dir)
699 (if (file-name-absolute-p path) path
700 (file-relative-name (expand-file-name path dir)
701 (expand-file-name "eyecandy" dir))))
703 (defun org-e-odt-format-formula (src &optional caption label attr)
704 (let* ((href
705 (org-e-odt-format-tags
706 "<draw:object xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
707 (file-name-directory (org-e-odt-copy-formula-file src))))
708 ;; FIXME
709 ;; (caption (org-find-text-property-in-string 'org-caption src))
710 ;; (short-caption
711 ;; (or (org-find-text-property-in-string 'org-caption-shortn src)
712 ;; caption))
713 ;; (caption (and caption (org-xml-format-desc caption)))
714 ;; (short-caption (and short-caption
715 ;; (org-xml-encode-plain-text short-caption)))
716 ;; (label (org-find-text-property-in-string 'org-label src))
717 ;; (latex-frag (org-find-text-property-in-string 'org-latex-src src))
718 (embed-as (or
719 ;; FIXME
720 ;; (and latex-frag
721 ;; (org-find-text-property-in-string
722 ;; 'org-latex-src-embed-type src))
723 (if (or caption label) 'paragraph 'character)))
724 width height)
725 ;; FIXME
726 ;; (when latex-frag
727 ;; (setq href (org-propertize href :title "LaTeX Fragment"
728 ;; :description latex-frag)))
729 (cond
730 ((eq embed-as 'character)
731 (org-e-odt-format-entity "InlineFormula" href width height))
733 ;; (org-lparse-end-paragraph)
735 (let ((table-info nil)
736 (table-info
737 '(:alignment ["c" "c"]
738 :column-groups [nil nil]
739 :row-groups (0)
740 :special-column-p nil :width [8 1]))
741 (org-lparse-table-ncols 2)
742 ) ; FIXME
743 (org-e-odt-list-table
744 `((,(org-e-odt-format-entity
745 (if (not (or caption label)) "DisplayFormula"
746 "CaptionedDisplayFormula")
747 href width height :caption caption :label label
748 :short-caption short-caption)
749 ,(if (not (or caption label)) ""
750 (let* ((label-props (car org-e-odt-entity-labels-alist)))
751 (setcar (last label-props) "math-label")
752 (apply 'org-e-odt-format-label-definition
753 caption label-props)))))
754 nil nil ":style \"OrgEquation\"" ;; nil '((1 "c" 8) (2 "c" 1)) FIXME
757 ;; (throw 'nextline nil)
758 ))))
760 (defun org-e-odt-copy-formula-file (path)
761 "Returns the internal name of the file"
762 (let* ((src-file (expand-file-name
763 path (file-name-directory org-current-export-file)))
764 (target-dir (format "Formula-%04d/"
765 (incf org-e-odt-embedded-formulas-count)))
766 (target-file (concat target-dir "content.xml")))
767 (message "Embedding %s as %s ..."
768 (substring-no-properties path) target-file)
770 (make-directory target-dir)
771 (org-e-odt-create-manifest-file-entry
772 "application/vnd.oasis.opendocument.formula" target-dir "1.2")
774 (case (org-e-odt-is-formula-link-p src-file)
775 (mathml
776 (copy-file src-file target-file 'overwrite))
777 (odf
778 (org-e-odt-zip-extract-one src-file "content.xml" target-dir))
780 (error "%s is not a formula file" src-file)))
782 (org-e-odt-create-manifest-file-entry "text/xml" target-file)
783 target-file))
785 (defun org-e-odt-is-formula-link-p (file)
786 (let ((case-fold-search nil))
787 (cond
788 ((string-match "\\.\\(mathml\\|mml\\)\\'" file)
789 'mathml)
790 ((string-match "\\.odf\\'" file)
791 'odf))))
793 (defun org-e-odt-format-org-link (opt-plist type-1 path fragment desc attr
794 descp)
795 "Make a OpenDocument link.
796 OPT-PLIST is an options list.
797 TYPE-1 is the device-type of the link (THIS://foo.html).
798 PATH is the path of the link (http://THIS#location).
799 FRAGMENT is the fragment part of the link, if any (foo.html#THIS).
800 DESC is the link description, if any.
801 ATTR is a string of other attributes of the a element."
802 (declare (special org-lparse-par-open))
803 (save-match-data
804 (let* ((may-inline-p
805 (and (member type-1 '("http" "https" "file"))
806 (org-lparse-should-inline-p path descp)
807 (not fragment)))
808 (type (if (equal type-1 "id") "file" type-1))
809 (filename path)
810 (thefile path))
811 (cond
812 ;; check for inlined images
813 ((and (member type '("file"))
814 (not fragment)
815 (org-file-image-p
816 filename org-e-odt-inline-image-extensions)
817 (not descp))
818 (org-e-odt-format-inline-image thefile))
819 ;; check for embedded formulas
820 ((and (member type '("file"))
821 (not fragment)
822 (org-e-odt-is-formula-link-p filename)
823 (or (not descp)))
824 (org-e-odt-format-formula thefile))
825 ((string= type "coderef")
826 (let* ((ref fragment)
827 (lineno-or-ref (cdr (assoc ref org-export-code-refs)))
828 (desc (and descp desc))
829 (org-e-odt-suppress-xref nil)
830 (href (org-xml-format-href (concat "#coderef-" ref))))
831 (cond
832 ((and (numberp lineno-or-ref) (not desc))
833 (org-e-odt-format-link lineno-or-ref href))
834 ((and (numberp lineno-or-ref) desc
835 (string-match (regexp-quote (concat "(" ref ")")) desc))
836 (format (replace-match "%s" t t desc)
837 (org-e-odt-format-link lineno-or-ref href)))
839 (setq desc (format
840 (if (and desc (string-match
841 (regexp-quote (concat "(" ref ")"))
842 desc))
843 (replace-match "%s" t t desc)
844 (or desc "%s"))
845 lineno-or-ref))
846 (org-e-odt-format-link (org-xml-format-desc desc) href)))))
848 (when (string= type "file")
849 (setq thefile
850 (cond
851 ((file-name-absolute-p path)
852 (concat "file://" (expand-file-name path)))
853 (t (org-e-odt-relocate-relative-path
854 thefile org-current-export-file)))))
856 (when (and (member type '("" "http" "https" "file")) fragment)
857 (setq thefile (concat thefile "#" fragment)))
859 (setq thefile (org-xml-format-href thefile))
861 (when (not (member type '("" "file")))
862 (setq thefile (concat type ":" thefile)))
864 (let ((org-e-odt-suppress-xref nil))
865 (org-e-odt-format-link
866 (org-xml-format-desc desc) thefile attr)))))))
868 (defun org-e-odt-format-anchor (text name &optional class)
869 (org-e-odt-format-target text name))
871 (defun org-e-odt-format-bookmark (text id)
872 (if id
873 (org-e-odt-format-tags "<text:bookmark text:name=\"%s\"/>" text id)
874 text))
876 (defun org-e-odt-format-target (text id)
877 (let ((name (concat org-e-odt-bookmark-prefix id)))
878 (concat
879 (and id (org-e-odt-format-tags
880 "<text:bookmark-start text:name=\"%s\"/>" "" name))
881 (org-e-odt-format-bookmark text id)
882 (and id (org-e-odt-format-tags
883 "<text:bookmark-end text:name=\"%s\"/>" "" name)))))
885 (defun org-e-odt-format-footnote (n def)
886 (setq n (format "%d" n))
887 (let ((id (concat "fn" n))
888 (note-class "footnote")
889 (par-style "Footnote"))
890 (org-e-odt-format-tags-simple
891 '("<text:note text:id=\"%s\" text:note-class=\"%s\">" . "</text:note>")
892 (concat
893 (org-e-odt-format-tags-simple
894 '("<text:note-citation>" . "</text:note-citation>") n)
895 (org-e-odt-format-tags-simple
896 '("<text:note-body>" . "</text:note-body>") def))
897 id note-class)))
899 (defun org-e-odt-format-footnote-reference (n def refcnt)
900 (if (= refcnt 1)
901 (org-e-odt-format-footnote n def)
902 (org-e-odt-format-footnote-ref n)))
904 (defun org-e-odt-format-footnote-ref (n)
905 (setq n (format "%d" n))
906 (let ((note-class "footnote")
907 (ref-format "text")
908 (ref-name (concat "fn" n)))
909 (org-e-odt-format-tags-simple
910 '("<text:span text:style-name=\"%s\">" . "</text:span>")
911 (org-e-odt-format-tags-simple
912 '("<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">" . "</text:note-ref>")
913 n note-class ref-format ref-name)
914 "OrgSuperscript")))
916 (defun org-e-odt-parse-block-attributes (params)
917 (save-match-data
918 (when params
919 (setq params (org-trim params))
920 (unless (string-match "\\`(.*)\\'" params)
921 (setq params (format "(%s)" params)))
922 (ignore-errors (read params)))))
924 (defun org-e-odt-format-image (src &optional
925 caption label attr
926 embed-as ; FIXME
927 category ; FIXME
929 "Create image tag with source and attributes."
930 (let* ((href (org-e-odt-format-tags
931 "<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
932 (org-e-odt-copy-image-file src)))
933 ;; (caption (org-find-text-property-in-string 'org-caption src))
934 ;; (short-caption
935 ;; (or (org-find-text-property-in-string 'org-caption-shortn src)
936 ;; caption))
937 ;; (caption (and caption (org-xml-format-desc caption)))
938 ;; (short-caption (and short-caption
939 ;; (org-xml-encode-plain-text short-caption)))
940 ;; (attr (org-find-text-property-in-string 'org-attributes src))
941 ;; (label (org-find-text-property-in-string 'org-label src))
942 ;; (latex-frag (org-find-text-property-in-string
943 ;; 'org-latex-src src))
944 ;; (category (and latex-frag "__DvipngImage__")) ; FIXME
945 (attr-plist (org-e-odt-parse-block-attributes attr))
946 (user-frame-anchor
947 (car (assoc-string (plist-get attr-plist :anchor)
948 '(("as-char") ("paragraph") ("page")) t)))
949 (user-frame-style
950 (and user-frame-anchor (plist-get attr-plist :style)))
951 (user-frame-attrs
952 (and user-frame-anchor (plist-get attr-plist :attributes)))
953 (user-frame-params
954 (list user-frame-style user-frame-attrs user-frame-anchor))
955 (embed-as (or embed-as user-frame-anchor "paragraph"))
956 ;; (embed-as (cond
957 ;; (latex-frag
958 ;; (symbol-name
959 ;; (case (org-find-text-property-in-string ; FIXME
960 ;; 'org-latex-src-embed-type src)
961 ;; (paragraph 'paragraph)
962 ;; (t 'as-char))))
963 ;; (user-frame-anchor)
964 ;; (t "paragraph")))
965 (size (org-e-odt-image-size-from-file
966 src (plist-get attr-plist :width)
967 (plist-get attr-plist :height)
968 (plist-get attr-plist :scale) nil embed-as))
969 (width (car size)) (height (cdr size)))
970 ;; (when latex-frag ; FIXME
971 ;; (setq href (org-propertize href :title "LaTeX Fragment"
972 ;; :description latex-frag)))
973 (let ((frame-style-handle (concat (and (or caption label) "Captioned")
974 embed-as "Image")))
975 (org-e-odt-format-entity
976 frame-style-handle href width height
977 :caption caption :label label :category category
978 :short-caption short-caption
979 :user-frame-params user-frame-params))))
981 (defun org-e-odt-format-object-description (title description)
982 (concat (and title (org-e-odt-format-tags
983 '("<svg:title>" . "</svg:title>")
984 (org-e-odt-encode-plain-text title t)))
985 (and description (org-e-odt-format-tags
986 '("<svg:desc>" . "</svg:desc>")
987 (org-e-odt-encode-plain-text description t)))))
989 (defun org-e-odt-format-frame (text width height style &optional
990 extra anchor-type)
991 (let ((frame-attrs
992 (concat
993 (if width (format " svg:width=\"%0.2fcm\"" width) "")
994 (if height (format " svg:height=\"%0.2fcm\"" height) "")
995 extra
996 (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph")))))
997 (org-e-odt-format-tags
998 '("<draw:frame draw:style-name=\"%s\"%s>" . "</draw:frame>")
999 (concat text (org-e-odt-format-object-description
1000 (get-text-property 0 :title text)
1001 (get-text-property 0 :description text)))
1002 style frame-attrs)))
1004 (defun org-e-odt-format-textbox (text width height style &optional
1005 extra anchor-type)
1006 (org-e-odt-format-frame
1007 (org-e-odt-format-tags
1008 '("<draw:text-box %s>" . "</draw:text-box>")
1009 text (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
1010 (unless width
1011 (format " fo:min-width=\"%0.2fcm\"" (or width .2)))))
1012 width nil style extra anchor-type))
1014 (defun org-e-odt-merge-frame-params(default-frame-params user-frame-params)
1015 (if (not user-frame-params) default-frame-params
1016 (assert (= (length default-frame-params) 3))
1017 (assert (= (length user-frame-params) 3))
1018 (loop for user-frame-param in user-frame-params
1019 for default-frame-param in default-frame-params
1020 collect (or user-frame-param default-frame-param))))
1022 (defun* org-e-odt-format-entity (entity href width height
1023 &key caption label category
1024 user-frame-params short-caption)
1025 (let* ((entity-style (assoc-string entity org-e-odt-entity-frame-styles t))
1026 default-frame-params frame-params)
1027 (cond
1028 ((not (or caption label))
1029 (setq default-frame-params (nth 2 entity-style))
1030 (setq frame-params (org-e-odt-merge-frame-params
1031 default-frame-params user-frame-params))
1032 (apply 'org-e-odt-format-frame href width height frame-params))
1034 (setq default-frame-params (nth 3 entity-style))
1035 (setq frame-params (org-e-odt-merge-frame-params
1036 default-frame-params user-frame-params))
1037 (apply 'org-e-odt-format-textbox
1038 (org-e-odt-format-stylized-paragraph
1039 'illustration
1040 (concat
1041 (apply 'org-e-odt-format-frame href width height
1042 (let ((entity-style-1 (copy-sequence
1043 (nth 2 entity-style))))
1044 (setcar (cdr entity-style-1)
1045 (concat
1046 (cadr entity-style-1)
1047 (and short-caption
1048 (format " draw:name=\"%s\" "
1049 short-caption))))
1050 entity-style-1))
1051 (org-e-odt-format-entity-caption
1052 label caption (or category (nth 1 entity-style)))))
1053 width height frame-params)))))
1055 (defun org-e-odt-copy-image-file (path)
1056 "Returns the internal name of the file"
1057 (let* ((image-type (file-name-extension path))
1058 (media-type (format "image/%s" image-type))
1059 (src-file (expand-file-name
1060 path (file-name-directory org-current-export-file)))
1061 (target-dir "Images/")
1062 (target-file
1063 (format "%s%04d.%s" target-dir
1064 (incf org-e-odt-embedded-images-count) image-type)))
1065 (message "Embedding %s as %s ..."
1066 (substring-no-properties path) target-file)
1068 (when (= 1 org-e-odt-embedded-images-count)
1069 (make-directory target-dir)
1070 (org-e-odt-create-manifest-file-entry "" target-dir))
1072 (copy-file src-file target-file 'overwrite)
1073 (org-e-odt-create-manifest-file-entry media-type target-file)
1074 target-file))
1076 (defun org-e-odt-do-image-size (probe-method file &optional dpi anchor-type)
1077 (setq dpi (or dpi org-e-odt-pixels-per-inch))
1078 (setq anchor-type (or anchor-type "paragraph"))
1079 (flet ((size-in-cms (size-in-pixels)
1080 (flet ((pixels-to-cms (pixels)
1081 (let* ((cms-per-inch 2.54)
1082 (inches (/ pixels dpi)))
1083 (* cms-per-inch inches))))
1084 (and size-in-pixels
1085 (cons (pixels-to-cms (car size-in-pixels))
1086 (pixels-to-cms (cdr size-in-pixels)))))))
1087 (case probe-method
1088 (emacs
1089 (size-in-cms (ignore-errors ; Emacs could be in batch mode
1090 (clear-image-cache)
1091 (image-size (create-image file) 'pixels))))
1092 (imagemagick
1093 (size-in-cms
1094 (let ((dim (shell-command-to-string
1095 (format "identify -format \"%%w:%%h\" \"%s\"" file))))
1096 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
1097 (cons (string-to-number (match-string 1 dim))
1098 (string-to-number (match-string 2 dim)))))))
1100 (cdr (assoc-string anchor-type
1101 org-e-odt-default-image-sizes-alist))))))
1103 (defun org-e-odt-image-size-from-file (file &optional user-width
1104 user-height scale dpi embed-as)
1105 (unless (file-name-absolute-p file)
1106 (setq file (expand-file-name
1107 file (file-name-directory org-current-export-file))))
1108 (let* (size width height)
1109 (unless (and user-height user-width)
1110 (loop for probe-method in org-e-odt-image-size-probe-method
1111 until size
1112 do (setq size (org-e-odt-do-image-size
1113 probe-method file dpi embed-as)))
1114 (or size (error "Cannot determine Image size. Aborting ..."))
1115 (setq width (car size) height (cdr size)))
1116 (cond
1117 (scale
1118 (setq width (* width scale) height (* height scale)))
1119 ((and user-height user-width)
1120 (setq width user-width height user-height))
1121 (user-height
1122 (setq width (* user-height (/ width height)) height user-height))
1123 (user-width
1124 (setq height (* user-width (/ height width)) width user-width))
1125 (t (ignore)))
1126 ;; ensure that an embedded image fits comfortably within a page
1127 (let ((max-width (car org-e-odt-max-image-size))
1128 (max-height (cdr org-e-odt-max-image-size)))
1129 (when (or (> width max-width) (> height max-height))
1130 (let* ((scale1 (/ max-width width))
1131 (scale2 (/ max-height height))
1132 (scale (min scale1 scale2)))
1133 (setq width (* scale width) height (* scale height)))))
1134 (cons width height)))
1136 (defun org-e-odt-add-label-definition (label default-category)
1137 "Create an entry in `org-e-odt-entity-labels-alist' and return it."
1138 (let* ((label-props (assoc default-category org-e-odt-category-map-alist))
1139 ;; identify the sequence number
1140 (counter (nth 1 label-props))
1141 (sequence-var (intern counter))
1142 (seqno (1+ (or (plist-get org-e-odt-entity-counts-plist sequence-var)
1143 0)))
1144 ;; assign an internal label, if user has not provided one
1145 (label (if label (substring-no-properties label)
1146 (format "%s-%s" default-category seqno)))
1147 ;; identify label style
1148 (label-style (nth 2 label-props))
1149 ;; grok language setting
1150 (en-strings (assoc-default "en" org-e-odt-category-strings))
1151 (lang (plist-get info :language)) ; FIXME
1152 (lang-strings (assoc-default lang org-e-odt-category-strings))
1153 ;; retrieve localized category sting
1154 (pos (- (length org-e-odt-category-map-alist)
1155 (length (memq label-props org-e-odt-category-map-alist))))
1156 (category (or (nth pos lang-strings) (nth pos en-strings)))
1157 (label-props (list label category counter seqno label-style)))
1158 (setq org-e-odt-entity-counts-plist
1159 (plist-put org-e-odt-entity-counts-plist sequence-var seqno))
1160 ;; stash label properties for later retrieval
1161 (push label-props org-e-odt-entity-labels-alist)
1162 label-props))
1164 (defun org-e-odt-format-label-reference (label default-category
1165 seqno) ; FIXME
1166 (let* ((label-props (assoc default-category org-e-odt-category-map-alist))
1167 (category (nth 1 label-props))
1168 (counter category) ; FIXME
1169 (label-style (nth 2 label-props)))
1170 (unless label-props
1171 (error "Unknown category: %S" default-category))
1172 (org-e-odt-do-format-label-reference
1173 label category counter seqno label-style)))
1175 (defun org-e-odt-format-label-definition (caption label category counter
1176 seqno label-style)
1177 (assert label)
1178 (setq label (org-solidify-link-text label))
1179 (format-spec
1180 (cadr (assoc-string label-style org-e-odt-label-styles t))
1181 `((?e . ,category)
1182 (?n . ,(org-e-odt-format-tags-simple
1183 '("<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">" . "</text:sequence>")
1184 (format "%d" seqno) label counter counter))
1185 (?c . ,(or caption "")))))
1187 (defun org-e-odt-do-format-label-reference (label category counter
1188 seqno label-style)
1189 (assert label)
1190 (save-match-data
1191 (let* ((fmt (cddr (assoc-string label-style org-e-odt-label-styles t)))
1192 (fmt1 (car fmt))
1193 (fmt2 (cadr fmt)))
1194 (org-e-odt-format-tags-simple
1195 '("<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">"
1196 . "</text:sequence-ref>")
1197 (format-spec fmt2 `((?e . ,category)
1198 (?n . ,(format "%d" seqno)))) fmt1 label))))
1200 (defun org-e-odt-format-entity-caption (label caption category)
1201 (if (not (or label caption)) ""
1202 (apply 'org-e-odt-format-label-definition caption
1203 (org-e-odt-add-label-definition label category))))
1205 (defun org-e-odt-format-tags-1 (tag text prefix suffix &rest args)
1206 (cond
1207 ((consp tag)
1208 (concat prefix (apply 'format (car tag) args) text suffix
1209 (format (cdr tag))))
1210 ((stringp tag) ; singleton tag
1211 (concat prefix (apply 'format tag args) text))))
1213 (defun org-e-odt-format-tags (tag text &rest args)
1214 (apply 'org-e-odt-format-tags-1 tag text "\n" "\n" args))
1216 (defun org-e-odt-format-tags-simple (tag text &rest args)
1217 (apply 'org-e-odt-format-tags-1 tag text nil nil args))
1219 (defun org-e-odt-init-outfile ()
1220 (unless (executable-find "zip")
1221 ;; Not at all OSes ship with zip by default
1222 (error "Executable \"zip\" needed for creating OpenDocument files"))
1224 (let* ((outdir (make-temp-file
1225 (format org-e-odt-tmpdir-prefix 'odt) t)) ; FIXME
1226 (content-file (expand-file-name "content.xml" outdir)))
1228 ;; reset variables
1229 (setq org-e-odt-manifest-file-entries nil
1230 org-e-odt-embedded-images-count 0
1231 org-e-odt-embedded-formulas-count 0
1232 org-e-odt-section-count 0
1233 org-e-odt-entity-labels-alist nil
1234 org-e-odt-list-stack-stashed nil
1235 org-e-odt-automatic-styles nil
1236 org-e-odt-object-counters nil
1237 org-e-odt-entity-counts-plist nil)
1239 ;; let `htmlfontify' know that we are interested in collecting
1240 ;; styles - FIXME
1242 (setq hfy-user-sheet-assoc nil)
1244 ;; init conten.xml
1245 (with-current-buffer
1246 (find-file-noselect content-file t)
1247 (current-buffer))))
1251 (defun org-e-odt-save-as-outfile (target opt-plist)
1252 ;; write automatic styles
1253 (org-e-odt-write-automatic-styles)
1255 ;; write styles file
1256 ;; (when (equal org-lparse-backend 'odt) FIXME
1257 ;; )
1259 ;; (org-e-odt-update-styles-file opt-plist)
1261 ;; create mimetype file
1262 (let ((mimetype (org-e-odt-write-mimetype-file ;; org-lparse-backend FIXME
1263 'odt)))
1264 (org-e-odt-create-manifest-file-entry mimetype "/" "1.2"))
1266 ;; create a manifest entry for content.xml
1267 (org-e-odt-create-manifest-file-entry "text/xml" "content.xml")
1269 ;; write out the manifest entries before zipping
1270 (org-e-odt-write-manifest-file)
1272 (let ((xml-files '("mimetype" "META-INF/manifest.xml" "content.xml"
1273 "meta.xml"))
1274 (zipdir default-directory))
1275 (when (or t (equal org-lparse-backend 'odt)) ; FIXME
1276 (push "styles.xml" xml-files))
1277 (message "Switching to directory %s" (expand-file-name zipdir))
1279 ;; save all xml files
1280 (mapc (lambda (file)
1281 (with-current-buffer
1282 (find-file-noselect (expand-file-name file) t)
1283 ;; prettify output if needed
1284 (when org-e-odt-prettify-xml
1285 (indent-region (point-min) (point-max)))
1286 (save-buffer 0)))
1287 xml-files)
1289 (let* ((target-name (file-name-nondirectory target))
1290 (target-dir (file-name-directory target))
1291 (cmds `(("zip" "-mX0" ,target-name "mimetype")
1292 ("zip" "-rmTq" ,target-name "."))))
1293 (when (file-exists-p target)
1294 ;; FIXME: If the file is locked this throws a cryptic error
1295 (delete-file target))
1297 (let ((coding-system-for-write 'no-conversion) exitcode err-string)
1298 (message "Creating odt file...")
1299 (mapc
1300 (lambda (cmd)
1301 (message "Running %s" (mapconcat 'identity cmd " "))
1302 (setq err-string
1303 (with-output-to-string
1304 (setq exitcode
1305 (apply 'call-process (car cmd)
1306 nil standard-output nil (cdr cmd)))))
1307 (or (zerop exitcode)
1308 (ignore (message "%s" err-string))
1309 (error "Unable to create odt file (%S)" exitcode)))
1310 cmds))
1312 ;; move the file from outdir to target-dir
1313 (rename-file target-name target-dir)
1315 ;; kill all xml buffers
1316 (mapc (lambda (file)
1317 (kill-buffer
1318 (find-file-noselect (expand-file-name file zipdir) t)))
1319 xml-files)
1321 (delete-directory zipdir)))
1322 (message "Created %s" target)
1323 (set-buffer (find-file-noselect target t)))
1326 (defun org-e-odt-create-manifest-file-entry (&rest args)
1327 (push args org-e-odt-manifest-file-entries))
1329 (defun org-e-odt-write-manifest-file ()
1330 (make-directory "META-INF")
1331 (let ((manifest-file (expand-file-name "META-INF/manifest.xml")))
1332 (with-current-buffer
1333 (find-file-noselect manifest-file t)
1334 (insert
1335 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1336 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
1337 (mapc
1338 (lambda (file-entry)
1339 (let* ((version (nth 2 file-entry))
1340 (extra (if version
1341 (format " manifest:version=\"%s\"" version)
1342 "")))
1343 (insert
1344 (format org-e-odt-manifest-file-entry-tag
1345 (nth 0 file-entry) (nth 1 file-entry) extra))))
1346 org-e-odt-manifest-file-entries)
1347 (insert "\n</manifest:manifest>"))))
1349 (defun org-e-odt-update-meta-file (info) ; FIXME opt-plist
1350 (let ((title (org-export-secondary-string
1351 (plist-get info :title) 'e-odt info))
1352 (author (or (let ((auth (plist-get info :author)))
1353 (and auth (org-export-secondary-string
1354 auth 'e-odt info))) ""))
1355 (date (org-e-odt-format-date (plist-get info :date)))
1356 (email (plist-get info :email))
1357 (keywords (plist-get info :keywords))
1358 (description (plist-get info :description)))
1359 (write-region
1360 (concat
1361 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1362 <office:document-meta
1363 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
1364 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
1365 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
1366 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
1367 xmlns:ooo=\"http://openoffice.org/2004/office\"
1368 office:version=\"1.2\">
1369 <office:meta>\n"
1370 (org-e-odt-format-author author) "\n"
1371 (format "<meta:initial-creator>%s</meta:initial-creator>\n" author)
1372 (format "<dc:date>%s</dc:date>\n" date)
1373 (format "<meta:creation-date>%s</meta:creation-date>\n" date)
1374 (format "<meta:generator>%s</meta:generator>\n"
1375 (when org-export-creator-info
1376 (format "Org-%s/Emacs-%s"
1377 org-version emacs-version)))
1378 (format "<meta:keyword>%s</meta:keyword>\n" keywords)
1379 (format "<dc:subject>%s</dc:subject>\n" description)
1380 (format "<dc:title>%s</dc:title>\n" title)
1381 "\n"
1382 " </office:meta>\n" "</office:document-meta>")
1383 nil (expand-file-name "meta.xml")))
1385 ;; create a manifest entry for meta.xml
1386 (org-e-odt-create-manifest-file-entry "text/xml" "meta.xml"))
1388 (defun org-e-odt-update-styles-file (info)
1389 ;; write styles file
1390 (let ((styles-file (plist-get info :odt-styles-file)))
1391 (org-e-odt-copy-styles-file (and styles-file
1392 (read (org-trim styles-file))))
1394 ;; FIXME: Who is opening an empty styles.xml before this point?
1395 (with-current-buffer
1396 (find-file-noselect (expand-file-name "styles.xml") t)
1397 (revert-buffer t t)))
1399 ;; Write custom styles for source blocks
1400 (org-e-odt-insert-custom-styles-for-srcblocks
1401 (mapconcat
1402 (lambda (style)
1403 (format " %s\n" (cddr style)))
1404 hfy-user-sheet-assoc "")))
1406 (defun org-e-odt-write-mimetype-file (format)
1407 ;; create mimetype file
1408 (let ((mimetype
1409 (case format
1410 (odt "application/vnd.oasis.opendocument.text")
1411 (odf "application/vnd.oasis.opendocument.formula")
1412 (t (error "Unknown OpenDocument backend %S" org-lparse-backend)))))
1413 (write-region mimetype nil (expand-file-name "mimetype"))
1414 mimetype))
1416 (declare-function org-create-math-formula "org"
1417 (latex-frag &optional mathml-file))
1419 (defun org-e-odt-get (what &optional opt-plist)
1420 (case what
1421 (EXPORT-DIR (org-export-directory :html opt-plist))
1422 (TABLE-FIRST-COLUMN-AS-LABELS nil)
1423 (CODING-SYSTEM-FOR-WRITE 'utf-8)
1424 (CODING-SYSTEM-FOR-SAVE 'utf-8)
1425 (t (error "Unknown property: %s" what))))
1427 (defun org-e-odt-do-preprocess-latex-fragments ()
1428 "Convert LaTeX fragments to images."
1429 (let* ((latex-frag-opt (plist-get org-lparse-opt-plist :LaTeX-fragments))
1430 (latex-frag-opt ; massage the options
1431 (or (and (member latex-frag-opt '(mathjax t))
1432 (not (and (fboundp 'org-format-latex-mathml-available-p)
1433 (org-format-latex-mathml-available-p)))
1434 (prog1 org-lparse-latex-fragment-fallback
1435 (org-lparse-warn
1436 (concat
1437 "LaTeX to MathML converter not available. "
1438 (format "Using %S instead."
1439 org-lparse-latex-fragment-fallback)))))
1440 latex-frag-opt))
1441 cache-dir display-msg)
1442 (cond
1443 ((eq latex-frag-opt 'dvipng)
1444 (setq cache-dir "ltxpng/")
1445 (setq display-msg "Creating LaTeX image %s"))
1446 ((member latex-frag-opt '(mathjax t))
1447 (setq latex-frag-opt 'mathml)
1448 (setq cache-dir "ltxmathml/")
1449 (setq display-msg "Creating MathML formula %s")))
1450 (when (and org-current-export-file)
1451 (org-format-latex
1452 (concat cache-dir (file-name-sans-extension
1453 (file-name-nondirectory org-current-export-file)))
1454 org-current-export-dir nil display-msg
1455 nil nil latex-frag-opt))))
1457 (eval-after-load 'org-odt
1458 '(ad-deactivate 'org-format-latex-as-mathml))
1460 ; FIXME
1462 ;; (defadvice org-format-latex-as-mathml ; FIXME
1463 ;; (after org-e-odt-protect-latex-fragment activate)
1464 ;; "Encode LaTeX fragment as XML.
1465 ;; Do this when translation to MathML fails."
1466 ;; (when (or (not (> (length ad-return-value) 0))
1467 ;; (get-text-property 0 'org-protected ad-return-value))
1468 ;; (setq ad-return-value
1469 ;; (org-propertize (org-e-odt-encode-plain-text (ad-get-arg 0))
1470 ;; 'org-protected t))))
1472 (defun org-e-odt-zip-extract-one (archive member &optional target)
1473 (require 'arc-mode)
1474 (let* ((target (or target default-directory))
1475 (archive (expand-file-name archive))
1476 (archive-zip-extract
1477 (list "unzip" "-qq" "-o" "-d" target))
1478 exit-code command-output)
1479 (setq command-output
1480 (with-temp-buffer
1481 (setq exit-code (archive-zip-extract archive member))
1482 (buffer-string)))
1483 (unless (zerop exit-code)
1484 (message command-output)
1485 (error "Extraction failed"))))
1487 (defun org-e-odt-zip-extract (archive members &optional target)
1488 (when (atom members) (setq members (list members)))
1489 (mapc (lambda (member)
1490 (org-e-odt-zip-extract-one archive member target))
1491 members))
1493 (defun org-e-odt-copy-styles-file (&optional styles-file)
1494 ;; Non-availability of styles.xml is not a critical error. For now
1495 ;; throw an error purely for aesthetic reasons.
1496 (setq styles-file (or styles-file
1497 org-e-odt-styles-file
1498 (expand-file-name "OrgOdtStyles.xml"
1499 org-e-odt-styles-dir)
1500 (error "org-e-odt: Missing styles file?")))
1501 (cond
1502 ((listp styles-file)
1503 (let ((archive (nth 0 styles-file))
1504 (members (nth 1 styles-file)))
1505 (org-e-odt-zip-extract archive members)
1506 (mapc
1507 (lambda (member)
1508 (when (org-file-image-p member)
1509 (let* ((image-type (file-name-extension member))
1510 (media-type (format "image/%s" image-type)))
1511 (org-e-odt-create-manifest-file-entry media-type member))))
1512 members)))
1513 ((and (stringp styles-file) (file-exists-p styles-file))
1514 (let ((styles-file-type (file-name-extension styles-file)))
1515 (cond
1516 ((string= styles-file-type "xml")
1517 (copy-file styles-file (expand-file-name "styles.xml") t))
1518 ((member styles-file-type '("odt" "ott"))
1519 (org-e-odt-zip-extract styles-file "styles.xml")))))
1521 (error (format "Invalid specification of styles.xml file: %S"
1522 org-e-odt-styles-file))))
1524 ;; create a manifest entry for styles.xml
1525 (org-e-odt-create-manifest-file-entry "text/xml" "styles.xml"))
1527 (defun org-e-odt-configure-outline-numbering ()
1528 "Outline numbering is retained only upto LEVEL.
1529 To disable outline numbering pass a LEVEL of 0."
1530 (goto-char (point-min))
1531 (let ((regex
1532 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
1533 (replacement
1534 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
1535 (while (re-search-forward regex nil t)
1536 (unless (let ((sec-num (plist-get info :section-numbers))
1537 (level (string-to-number (match-string 2))))
1538 (if (wholenump sec-num) (<= level sec-num) sec-num))
1539 (replace-match replacement t nil))))
1540 (save-buffer 0))
1542 ;;;###autoload
1543 (defun org-export-as-odf (latex-frag &optional odf-file)
1544 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
1545 Use `org-create-math-formula' to convert LATEX-FRAG first to
1546 MathML. When invoked as an interactive command, use
1547 `org-latex-regexps' to infer LATEX-FRAG from currently active
1548 region. If no LaTeX fragments are found, prompt for it. Push
1549 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
1550 non-nil."
1551 (interactive
1552 `(,(let (frag)
1553 (setq frag (and (setq frag (and (region-active-p)
1554 (buffer-substring (region-beginning)
1555 (region-end))))
1556 (loop for e in org-latex-regexps
1557 thereis (when (string-match (nth 1 e) frag)
1558 (match-string (nth 2 e) frag)))))
1559 (read-string "LaTeX Fragment: " frag nil frag))
1560 ,(let ((odf-filename (expand-file-name
1561 (concat
1562 (file-name-sans-extension
1563 (or (file-name-nondirectory buffer-file-name)))
1564 "." "odf")
1565 (file-name-directory buffer-file-name))))
1566 (read-file-name "ODF filename: " nil odf-filename nil
1567 (file-name-nondirectory odf-filename)))))
1568 (let* ((org-lparse-backend 'odf)
1569 org-lparse-opt-plist
1570 (filename (or odf-file
1571 (expand-file-name
1572 (concat
1573 (file-name-sans-extension
1574 (or (file-name-nondirectory buffer-file-name)))
1575 "." "odf")
1576 (file-name-directory buffer-file-name))))
1577 (buffer (find-file-noselect (org-e-odt-init-outfile filename)))
1578 (coding-system-for-write 'utf-8)
1579 (save-buffer-coding-system 'utf-8))
1580 (set-buffer buffer)
1581 (set-buffer-file-coding-system coding-system-for-write)
1582 (let ((mathml (org-create-math-formula latex-frag)))
1583 (unless mathml (error "No Math formula created"))
1584 (insert mathml)
1585 (or (org-export-push-to-kill-ring
1586 (upcase (symbol-name org-lparse-backend)))
1587 (message "Exporting... done")))
1588 (org-e-odt-save-as-outfile filename nil ; FIXME
1591 ;;;###autoload
1592 (defun org-export-as-odf-and-open ()
1593 "Export LaTeX fragment as OpenDocument formula and immediately open it.
1594 Use `org-export-as-odf' to read LaTeX fragment and OpenDocument
1595 formula file."
1596 (interactive)
1597 (org-lparse-and-open
1598 nil nil nil (call-interactively 'org-export-as-odf)))
1603 ;;; Driver Starts here
1604 ;;; Dependencies
1606 (require 'format-spec)
1607 (eval-when-compile (require 'cl) (require 'table))
1611 ;;; Hooks
1613 ;; FIXME: it already exists in org-e-odt.el
1614 ;;; Function Declarations
1616 (declare-function org-element-property "org-element" (property element))
1617 (declare-function org-element-normalize-string "org-element" (s))
1618 (declare-function org-element-parse-secondary-string
1619 "org-element" (string restriction &optional buffer))
1620 (defvar org-element-string-restrictions)
1621 (defvar org-element-object-restrictions)
1623 (declare-function org-export-clean-table "org-export" (table specialp))
1624 (declare-function org-export-data "org-export" (data backend info))
1625 (declare-function org-export-directory "org-export" (type plist))
1626 (declare-function org-export-expand-macro "org-export" (macro info))
1627 (declare-function org-export-first-sibling-p "org-export" (headline info))
1628 (declare-function org-export-footnote-first-reference-p "org-export"
1629 (footnote-reference info))
1630 (declare-function org-export-get-coderef-format "org-export" (path desc))
1631 (declare-function org-export-get-footnote-definition "org-export"
1632 (footnote-reference info))
1633 (declare-function org-export-get-footnote-number "org-export" (footnote info))
1634 (declare-function org-export-get-previous-element "org-export" (blob info))
1635 (declare-function org-export-get-relative-level "org-export" (headline info))
1636 (declare-function org-export-handle-code
1637 "org-export" (element info &optional num-fmt ref-fmt delayed))
1638 (declare-function org-export-included-file "org-export" (keyword backend info))
1639 (declare-function org-export-inline-image-p "org-export"
1640 (link &optional extensions))
1641 (declare-function org-export-last-sibling-p "org-export" (headline info))
1642 (declare-function org-export-low-level-p "org-export" (headline info))
1643 (declare-function org-export-output-file-name
1644 "org-export" (extension &optional subtreep pub-dir))
1645 (declare-function org-export-resolve-coderef "org-export" (ref info))
1646 (declare-function org-export-resolve-fuzzy-link "org-export" (link info))
1647 (declare-function org-export-secondary-string "org-export"
1648 (secondary backend info))
1649 (declare-function org-export-solidify-link-text "org-export" (s))
1650 (declare-function org-export-table-format-info "org-export" (table))
1651 (declare-function
1652 org-export-to-buffer "org-export"
1653 (backend buffer &optional subtreep visible-only body-only ext-plist))
1654 (declare-function
1655 org-export-to-file "org-export"
1656 (backend file &optional subtreep visible-only body-only ext-plist))
1658 (declare-function org-id-find-id-file "org-id" (id))
1659 (declare-function htmlize-region "ext:htmlize" (beg end))
1660 (declare-function org-pop-to-buffer-same-window
1661 "org-compat" (&optional buffer-or-name norecord label))
1667 (declare-function hfy-face-to-style "htmlfontify" (fn))
1668 (declare-function hfy-face-or-def-to-name "htmlfontify" (fn))
1669 (declare-function archive-zip-extract "arc-mode.el" (archive name))
1671 ;;; Internal Variables
1673 ;;;; ODT Internal Variables
1675 (defconst org-e-odt-lib-dir
1676 (file-name-directory load-file-name)
1677 "Location of ODT exporter.
1678 Use this to infer values of `org-e-odt-styles-dir' and
1679 `org-e-odt-schema-dir'.")
1681 (defvar org-e-odt-data-dir
1682 (expand-file-name "../etc/" org-e-odt-lib-dir)
1683 "Data directory for ODT exporter.
1684 Use this to infer values of `org-e-odt-styles-dir' and
1685 `org-e-odt-schema-dir'.")
1687 (defconst org-e-odt-special-string-regexps
1688 '(("\\\\-" . "&#x00ad;\\1") ; shy
1689 ("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
1690 ("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
1691 ("\\.\\.\\." . "&#x2026;")) ; hellip
1692 "Regular expressions for special string conversion.")
1694 (defconst org-e-odt-schema-dir-list
1695 (list
1696 (and org-e-odt-data-dir
1697 (expand-file-name "./schema/" org-e-odt-data-dir)) ; bail out
1698 (eval-when-compile
1699 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
1700 (expand-file-name "./schema/" org-e-odt-data-dir)))
1701 (expand-file-name "../contrib/odt/etc/schema/" org-e-odt-lib-dir) ; git
1703 "List of directories to search for OpenDocument schema files.
1704 Use this list to set the default value of
1705 `org-e-odt-schema-dir'. The entries in this list are
1706 populated heuristically based on the values of `org-e-odt-lib-dir'
1707 and `org-e-odt-data-dir'.")
1710 (defconst org-e-odt-styles-dir-list
1711 (list
1712 (and org-e-odt-data-dir
1713 (expand-file-name "./styles/" org-e-odt-data-dir)) ; bail out
1714 (eval-when-compile
1715 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
1716 (expand-file-name "./styles/" org-e-odt-data-dir)))
1717 (expand-file-name "../etc/styles/" org-e-odt-lib-dir) ; git
1718 (expand-file-name "./etc/styles/" org-e-odt-lib-dir) ; elpa
1719 (expand-file-name "./org/" data-directory) ; system
1721 "List of directories to search for OpenDocument styles files.
1722 See `org-e-odt-styles-dir'. The entries in this list are populated
1723 heuristically based on the values of `org-e-odt-lib-dir' and
1724 `org-e-odt-data-dir'.")
1726 (defconst org-e-odt-styles-dir
1727 (let* ((styles-dir
1728 (catch 'styles-dir
1729 (message "Debug (org-e-odt): Searching for OpenDocument styles files...")
1730 (mapc (lambda (styles-dir)
1731 (when styles-dir
1732 (message "Debug (org-e-odt): Trying %s..." styles-dir)
1733 (when (and (file-readable-p
1734 (expand-file-name
1735 "OrgOdtContentTemplate.xml" styles-dir))
1736 (file-readable-p
1737 (expand-file-name
1738 "OrgOdtStyles.xml" styles-dir)))
1739 (message "Debug (org-e-odt): Using styles under %s"
1740 styles-dir)
1741 (throw 'styles-dir styles-dir))))
1742 org-e-odt-styles-dir-list)
1743 nil)))
1744 (unless styles-dir
1745 (error "Error (org-e-odt): Cannot find factory styles files. Aborting."))
1746 styles-dir)
1747 "Directory that holds auxiliary XML files used by the ODT exporter.
1749 This directory contains the following XML files -
1750 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
1751 XML files are used as the default values of
1752 `org-e-odt-styles-file' and
1753 `org-e-odt-content-template-file'.
1755 The default value of this variable varies depending on the
1756 version of org in use and is initialized from
1757 `org-e-odt-styles-dir-list'. Note that the user could be using org
1758 from one of: org's own private git repository, GNU ELPA tar or
1759 standard Emacs.")
1761 (defconst org-e-odt-tmpdir-prefix "%s-")
1762 (defconst org-e-odt-bookmark-prefix "OrgXref.")
1764 (defconst org-e-odt-manifest-file-entry-tag
1766 <manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
1770 (defvar org-lparse-dyn-first-heading-pos) ; let bound during org-do-lparse
1772 (defvar org-e-odt-suppress-xref nil)
1773 (defvar org-e-odt-file-extensions
1774 '(("odt" . "OpenDocument Text")
1775 ("ott" . "OpenDocument Text Template")
1776 ("odm" . "OpenDocument Master Document")
1777 ("ods" . "OpenDocument Spreadsheet")
1778 ("ots" . "OpenDocument Spreadsheet Template")
1779 ("odg" . "OpenDocument Drawing (Graphics)")
1780 ("otg" . "OpenDocument Drawing Template")
1781 ("odp" . "OpenDocument Presentation")
1782 ("otp" . "OpenDocument Presentation Template")
1783 ("odi" . "OpenDocument Image")
1784 ("odf" . "OpenDocument Formula")
1785 ("odc" . "OpenDocument Chart")))
1787 (defvar org-e-odt-default-org-styles-alist
1788 '((paragraph . ((default . "Text_20_body")
1789 (fixedwidth . "OrgFixedWidthBlock")
1790 (verse . "OrgVerse")
1791 (quote . "Quotations")
1792 (blockquote . "Quotations")
1793 (center . "OrgCenter")
1794 (left . "OrgLeft")
1795 (right . "OrgRight")
1796 (title . "OrgTitle")
1797 (subtitle . "OrgSubtitle")
1798 (footnote . "Footnote")
1799 (src . "OrgSrcBlock")
1800 (illustration . "Illustration")
1801 (table . "Table")
1802 (listing . "Listing")
1803 (definition-term . "Text_20_body_20_bold")
1804 (horizontal-line . "Horizontal_20_Line")))
1805 (character . ((bold . "Bold")
1806 (emphasis . "Emphasis")
1807 (code . "OrgCode")
1808 (verbatim . "OrgCode")
1809 (strike . "Strikethrough")
1810 (underline . "Underline")
1811 (subscript . "OrgSubscript")
1812 (superscript . "OrgSuperscript")))
1813 (list . ((ordered . "OrgNumberedList")
1814 (unordered . "OrgBulletedList")
1815 (descriptive . "OrgDescriptionList"))))
1816 "Default styles for various entities.")
1818 (defvar org-e-odt-org-styles-alist org-e-odt-default-org-styles-alist)
1820 ;;;_. callbacks
1821 ;;;_. control callbacks
1822 ;;;_ , document body
1824 (defvar org-lparse-body-only) ; let bound during org-do-lparse
1825 (defvar org-lparse-opt-plist) ; bound during org-do-lparse
1826 (defvar org-lparse-list-stack) ; dynamically bound in org-do-lparse
1827 (defvar org-e-odt-list-stack-stashed)
1828 (defvar org-lparse-table-ncols)
1829 (defvar org-e-odt-table-rowgrp-open)
1830 (defvar org-e-odt-table-rownum)
1831 (defvar org-e-odt-table-cur-rowgrp-is-hdr)
1832 (defvar org-lparse-table-is-styled)
1833 (defvar org-lparse-table-rowgrp-info)
1834 (defvar org-lparse-table-colalign-vector)
1836 (defvar org-e-odt-table-style nil
1837 "Table style specified by \"#+ATTR_ODT: <style-name>\" line.
1838 This is set during `org-e-odt-begin-table'.")
1840 (defvar org-e-odt-table-style-spec nil
1841 "Entry for `org-e-odt-table-style' in `org-e-odt-table-styles'.")
1844 (defvar org-e-odt-table-style-format
1846 <style:style style:name=\"%s\" style:family=\"table\">
1847 <style:table-properties style:rel-width=\"%d%%\" fo:margin-top=\"0cm\" fo:margin-bottom=\"0.20cm\" table:align=\"center\"/>
1848 </style:style>
1850 "Template for auto-generated Table styles.")
1852 (defvar org-e-odt-automatic-styles '()
1853 "Registry of automatic styles for various OBJECT-TYPEs.
1854 The variable has the following form:
1855 \(\(OBJECT-TYPE-A
1856 \(\(OBJECT-NAME-A.1 OBJECT-PROPS-A.1\)
1857 \(OBJECT-NAME-A.2 OBJECT-PROPS-A.2\) ...\)\)
1858 \(OBJECT-TYPE-B
1859 \(\(OBJECT-NAME-B.1 OBJECT-PROPS-B.1\)
1860 \(OBJECT-NAME-B.2 OBJECT-PROPS-B.2\) ...\)\)
1861 ...\).
1863 OBJECT-TYPEs could be \"Section\", \"Table\", \"Figure\" etc.
1864 OBJECT-PROPS is (typically) a plist created by passing
1865 \"#+ATTR_ODT: \" option to `org-e-odt-parse-block-attributes'.
1867 Use `org-e-odt-add-automatic-style' to add update this variable.'")
1869 (defvar org-e-odt-object-counters nil
1870 "Running counters for various OBJECT-TYPEs.
1871 Use this to generate automatic names and style-names. See
1872 `org-e-odt-add-automatic-style'.")
1874 (defvar org-e-odt-table-indentedp nil)
1875 (defvar org-lparse-table-colalign-info)
1876 (defvar org-lparse-link-description-is-image nil)
1879 (defvar org-src-block-paragraph-format
1880 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
1881 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
1882 <style:background-image/>
1883 </style:paragraph-properties>
1884 <style:text-properties fo:color=\"%s\"/>
1885 </style:style>"
1886 "Custom paragraph style for colorized source and example blocks.
1887 This style is much the same as that of \"OrgFixedWidthBlock\"
1888 except that the foreground and background colors are set
1889 according to the default face identified by the `htmlfontify'.")
1891 (defvar hfy-optimisations)
1892 (defvar org-e-odt-embedded-formulas-count 0)
1893 (defvar org-e-odt-entity-frame-styles
1894 '(("As-CharImage" "__Figure__" ("OrgInlineImage" nil "as-char"))
1895 ("ParagraphImage" "__Figure__" ("OrgDisplayImage" nil "paragraph"))
1896 ("PageImage" "__Figure__" ("OrgPageImage" nil "page"))
1897 ("CaptionedAs-CharImage" "__Figure__"
1898 ("OrgCaptionedImage"
1899 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1900 ("OrgInlineImage" nil "as-char"))
1901 ("CaptionedParagraphImage" "__Figure__"
1902 ("OrgCaptionedImage"
1903 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1904 ("OrgImageCaptionFrame" nil "paragraph"))
1905 ("CaptionedPageImage" "__Figure__"
1906 ("OrgCaptionedImage"
1907 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1908 ("OrgPageImageCaptionFrame" nil "page"))
1909 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil "as-char"))
1910 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil "as-char"))
1911 ("CaptionedDisplayFormula" "__MathFormula__"
1912 ("OrgCaptionedFormula" nil "paragraph")
1913 ("OrgFormulaCaptionFrame" nil "as-char"))))
1915 (defvar org-e-odt-embedded-images-count 0)
1917 (defvar org-e-odt-image-size-probe-method
1918 (append (and (executable-find "identify") '(imagemagick)) ; See Bug#10675
1919 '(emacs fixed))
1920 "Ordered list of methods for determining image sizes.")
1922 (defvar org-e-odt-default-image-sizes-alist
1923 '(("as-char" . (5 . 0.4))
1924 ("paragraph" . (5 . 5)))
1925 "Hardcoded image dimensions one for each of the anchor
1926 methods.")
1928 ;; A4 page size is 21.0 by 29.7 cms
1929 ;; The default page settings has 2cm margin on each of the sides. So
1930 ;; the effective text area is 17.0 by 25.7 cm
1931 (defvar org-e-odt-max-image-size '(17.0 . 20.0)
1932 "Limiting dimensions for an embedded image.")
1934 (defvar org-e-odt-entity-labels-alist nil
1935 "Associate Labels with the Labeled entities.
1936 Each element of the alist is of the form (LABEL-NAME
1937 CATEGORY-NAME SEQNO LABEL-STYLE-NAME). LABEL-NAME is same as
1938 that specified by \"#+LABEL: ...\" line. CATEGORY-NAME is the
1939 type of the entity that LABEL-NAME is attached to. CATEGORY-NAME
1940 can be one of \"Table\", \"Figure\" or \"Equation\". SEQNO is
1941 the unique number assigned to the referenced entity on a
1942 per-CATEGORY basis. It is generated sequentially and is 1-based.
1943 LABEL-STYLE-NAME is a key `org-e-odt-label-styles'.
1945 See `org-e-odt-add-label-definition' and
1946 `org-e-odt-fixup-label-references'.")
1948 (defvar org-e-odt-entity-counts-plist nil
1949 "Plist of running counters of SEQNOs for each of the CATEGORY-NAMEs.
1950 See `org-e-odt-entity-labels-alist' for known CATEGORY-NAMEs.")
1952 (defvar org-e-odt-label-styles
1953 '(("math-formula" "%c" "text" "(%n)")
1954 ("math-label" "(%n)" "text" "(%n)")
1955 ("category-and-value" "%e %n: %c" "category-and-value" "%e %n")
1956 ("value" "%e %n: %c" "value" "%n"))
1957 "Specify how labels are applied and referenced.
1958 This is an alist where each element is of the
1959 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
1960 LABEL-REF-FMT).
1962 LABEL-ATTACH-FMT controls how labels and captions are attached to
1963 an entity. It may contain following specifiers - %e, %n and %c.
1964 %e is replaced with the CATEGORY-NAME. %n is replaced with
1965 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
1966 with CAPTION. See `org-e-odt-format-label-definition'.
1968 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
1969 are generated. The following XML is generated for a label
1970 reference - \"<text:sequence-ref
1971 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
1972 </text:sequence-ref>\". LABEL-REF-FMT may contain following
1973 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
1974 %n is replaced with SEQNO. See
1975 `org-e-odt-format-label-reference'.")
1977 (defcustom org-e-odt-category-strings
1978 '(("en" "Table" "Figure" "Equation" "Equation" "Listing"))
1979 "Specify category strings for various captionable entities.
1980 Captionable entity can be one of a Table, an Embedded Image, a
1981 LaTeX fragment (generated with dvipng) or a Math Formula.
1983 For example, when `org-export-default-language' is \"en\", an
1984 embedded image will be captioned as \"Figure 1: Orgmode Logo\".
1985 If you want the images to be captioned instead as \"Illustration
1986 1: Orgmode Logo\", then modify the entry for \"en\" as shown
1987 below.
1989 \(setq org-e-odt-category-strings
1990 '\(\(\"en\" \"Table\" \"Illustration\"
1991 \"Equation\" \"Equation\"\)\)\)"
1992 :group 'org-export-e-odt
1993 :version "24.1"
1994 :type '(repeat (list (string :tag "Language tag")
1995 (choice :tag "Table"
1996 (const :tag "Use Default" nil)
1997 (string :tag "Category string"))
1998 (choice :tag "Figure"
1999 (const :tag "Use Default" nil)
2000 (string :tag "Category string"))
2001 (choice :tag "Math Formula"
2002 (const :tag "Use Default" nil)
2003 (string :tag "Category string"))
2004 (choice :tag "Dvipng Image"
2005 (const :tag "Use Default" nil)
2006 (string :tag "Category string"))
2007 (choice :tag "Listing"
2008 (const :tag "Use Default" nil)
2009 (string :tag "Category string")))))
2011 (defvar org-e-odt-category-map-alist
2012 '(("__Table__" "Table" "value")
2013 ("__Figure__" "Illustration" "value")
2014 ("__MathFormula__" "Text" "math-formula")
2015 ("__DvipngImage__" "Equation" "value")
2016 ("__Listing__" "Listing" "value")
2017 ;; ("__Table__" "Table" "category-and-value")
2018 ;; ("__Figure__" "Figure" "category-and-value")
2019 ;; ("__DvipngImage__" "Equation" "category-and-value")
2021 "Map a CATEGORY-HANDLE to OD-VARIABLE and LABEL-STYLE.
2022 This is a list where each entry is of the form \\(CATEGORY-HANDLE
2023 OD-VARIABLE LABEL-STYLE\\). CATEGORY_HANDLE identifies the
2024 captionable entity in question. OD-VARIABLE is the OpenDocument
2025 sequence counter associated with the entity. These counters are
2026 declared within
2027 \"<text:sequence-decls>...</text:sequence-decls>\" block of
2028 `org-e-odt-content-template-file'. LABEL-STYLE is a key
2029 into `org-e-odt-label-styles' and specifies how a given entity
2030 should be captioned and referenced.
2032 The position of a CATEGORY-HANDLE in this list is used as an
2033 index in to per-language entry for
2034 `org-e-odt-category-strings' to retrieve a CATEGORY-NAME.
2035 This CATEGORY-NAME is then used for qualifying the user-specified
2036 captions on export.")
2038 (defvar org-e-odt-manifest-file-entries nil)
2039 (defvar hfy-user-sheet-assoc) ; bound during org-do-lparse
2040 (defvar org-lparse-latex-fragment-fallback) ; set by org-do-lparse
2043 ;;;; HTML Internal Variables
2045 (defvar org-e-odt-option-alist
2047 ;; (:agenda-style nil nil org-agenda-export-html-style)
2048 ;; (:convert-org-links nil nil org-e-odt-link-org-files-as-html)
2049 ;; ;; FIXME Use (org-xml-encode-org-text-skip-links s) ??
2050 ;; ;; (:expand-quoted-html nil "@" org-e-odt-expand)
2051 ;; (:inline-images nil nil org-e-odt-inline-images)
2052 ;; ;; (:link-home nil nil org-e-odt-link-home) FIXME
2053 ;; ;; (:link-up nil nil org-e-odt-link-up) FIXME
2054 ;; (:style nil nil org-e-odt-style)
2055 ;; (:style-extra nil nil org-e-odt-style-extra)
2056 ;; (:style-include-default nil nil org-e-odt-style-include-default)
2057 ;; (:style-include-scripts nil nil org-e-odt-style-include-scripts)
2058 ;; ;; (:timestamp nil nil org-e-odt-with-timestamp)
2059 ;; (:html-extension nil nil org-e-odt-extension)
2060 ;; (:html-postamble nil nil org-e-odt-postamble)
2061 ;; (:html-preamble nil nil org-e-odt-preamble)
2062 ;; (:html-table-tag nil nil org-e-odt-table-tag)
2063 ;; (:xml-declaration nil nil org-e-odt-xml-declaration)
2064 (:odt-styles-file "ODT_STYLES_FILE" nil nil t)
2065 (:LaTeX-fragments nil "LaTeX" org-export-with-LaTeX-fragments))
2066 "Alist between export properties and ways to set them.
2068 The car of the alist is the property name, and the cdr is a list
2069 like \(KEYWORD OPTION DEFAULT BEHAVIOUR\) where:
2071 KEYWORD is a string representing a buffer keyword, or nil.
2072 OPTION is a string that could be found in an #+OPTIONS: line.
2073 DEFAULT is the default value for the property.
2074 BEHAVIOUR determine how Org should handle multiple keywords for
2075 the same property. It is a symbol among:
2076 nil Keep old value and discard the new one.
2077 t Replace old value with the new one.
2078 `space' Concatenate the values, separating them with a space.
2079 `newline' Concatenate the values, separating them with
2080 a newline.
2081 `split' Split values at white spaces, and cons them to the
2082 previous list.
2084 KEYWORD and OPTION have precedence over DEFAULT.
2086 All these properties should be back-end agnostic. For back-end
2087 specific properties, define a similar variable named
2088 `org-BACKEND-option-alist', replacing BACKEND with the name of
2089 the appropriate back-end. You can also redefine properties
2090 there, as they have precedence over these.")
2092 (defvar html-table-tag nil) ; dynamically scoped into this.
2094 ;; FIXME: it already exists in org-e-odt.el
2095 (defconst org-e-odt-cvt-link-fn
2097 "Function to convert link URLs to exportable URLs.
2098 Takes two arguments, TYPE and PATH.
2099 Returns exportable url as (TYPE PATH), or nil to signal that it
2100 didn't handle this case.
2101 Intended to be locally bound around a call to `org-export-as-html'." )
2106 (defvar org-e-odt-format-table-no-css)
2107 (defvar htmlize-buffer-places) ; from htmlize.el
2108 (defvar body-only) ; dynamically scoped into this.
2110 (defvar org-e-odt-table-rowgrp-open)
2111 (defvar org-e-odt-table-rownum)
2112 (defvar org-e-odt-table-cur-rowgrp-is-hdr)
2113 (defvar org-lparse-table-is-styled)
2116 (defvar org-e-odt-headline-formatter
2117 (lambda (level snumber todo todo-type priority
2118 title tags target extra-targets extra-class)
2119 (concat snumber " " title)))
2123 ;;; User Configuration Variables
2125 (defgroup org-export-e-odt nil
2126 "Options for exporting Org mode files to ODT."
2127 :tag "Org Export ODT"
2128 :group 'org-export)
2130 (defcustom org-e-odt-protect-char-alist
2131 '(("&" . "&amp;")
2132 ("<" . "&lt;")
2133 (">" . "&gt;"))
2134 "Alist of characters to be converted by `org-e-html-protect'."
2135 :group 'org-export-e-html
2136 :type '(repeat (cons (string :tag "Character")
2137 (string :tag "ODT equivalent"))))
2138 (defcustom org-e-odt-schema-dir
2139 (let* ((schema-dir
2140 (catch 'schema-dir
2141 (message "Debug (org-e-odt): Searching for OpenDocument schema files...")
2142 (mapc
2143 (lambda (schema-dir)
2144 (when schema-dir
2145 (message "Debug (org-e-odt): Trying %s..." schema-dir)
2146 (when (and (file-readable-p
2147 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc"
2148 schema-dir))
2149 (file-readable-p
2150 (expand-file-name "od-schema-v1.2-cs01.rnc"
2151 schema-dir))
2152 (file-readable-p
2153 (expand-file-name "schemas.xml" schema-dir)))
2154 (message "Debug (org-e-odt): Using schema files under %s"
2155 schema-dir)
2156 (throw 'schema-dir schema-dir))))
2157 org-e-odt-schema-dir-list)
2158 (message "Debug (org-e-odt): No OpenDocument schema files installed")
2159 nil)))
2160 schema-dir)
2161 "Directory that contains OpenDocument schema files.
2163 This directory contains:
2164 1. rnc files for OpenDocument schema
2165 2. a \"schemas.xml\" file that specifies locating rules needed
2166 for auto validation of OpenDocument XML files.
2168 Use the customize interface to set this variable. This ensures
2169 that `rng-schema-locating-files' is updated and auto-validation
2170 of OpenDocument XML takes place based on the value
2171 `rng-nxml-auto-validate-flag'.
2173 The default value of this variable varies depending on the
2174 version of org in use and is initialized from
2175 `org-e-odt-schema-dir-list'. The OASIS schema files are available
2176 only in the org's private git repository. It is *not* bundled
2177 with GNU ELPA tar or standard Emacs distribution."
2178 :type '(choice
2179 (const :tag "Not set" nil)
2180 (directory :tag "Schema directory"))
2181 :group 'org-export-e-odt
2182 :version "24.1"
2183 :set
2184 (lambda (var value)
2185 "Set `org-e-odt-schema-dir'.
2186 Also add it to `rng-schema-locating-files'."
2187 (let ((schema-dir value))
2188 (set var
2189 (if (and
2190 (file-readable-p
2191 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir))
2192 (file-readable-p
2193 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir))
2194 (file-readable-p
2195 (expand-file-name "schemas.xml" schema-dir)))
2196 schema-dir
2197 (when value
2198 (message "Error (org-e-odt): %s has no OpenDocument schema files"
2199 value))
2200 nil)))
2201 (when org-e-odt-schema-dir
2202 (eval-after-load 'rng-loc
2203 '(add-to-list 'rng-schema-locating-files
2204 (expand-file-name "schemas.xml"
2205 org-e-odt-schema-dir))))))
2207 (defcustom org-e-odt-content-template-file nil
2208 "Template file for \"content.xml\".
2209 The exporter embeds the exported content just before
2210 \"</office:text>\" element.
2212 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
2213 under `org-e-odt-styles-dir' is used."
2214 :type 'file
2215 :group 'org-export-e-odt
2216 :version "24.1")
2218 (defcustom org-e-odt-styles-file nil
2219 "Default styles file for use with ODT export.
2220 Valid values are one of:
2221 1. nil
2222 2. path to a styles.xml file
2223 3. path to a *.odt or a *.ott file
2224 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
2225 ...))
2227 In case of option 1, an in-built styles.xml is used. See
2228 `org-e-odt-styles-dir' for more information.
2230 In case of option 3, the specified file is unzipped and the
2231 styles.xml embedded therein is used.
2233 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
2234 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
2235 generated odt file. Use relative path for specifying the
2236 FILE-MEMBERS. styles.xml must be specified as one of the
2237 FILE-MEMBERS.
2239 Use options 1, 2 or 3 only if styles.xml alone suffices for
2240 achieving the desired formatting. Use option 4, if the styles.xml
2241 references additional files like header and footer images for
2242 achieving the desired formatting.
2244 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
2245 a per-file basis. For example,
2247 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
2248 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
2249 :group 'org-export-e-odt
2250 :version "24.1"
2251 :type
2252 '(choice
2253 (const :tag "Factory settings" nil)
2254 (file :must-match t :tag "styles.xml")
2255 (file :must-match t :tag "ODT or OTT file")
2256 (list :tag "ODT or OTT file + Members"
2257 (file :must-match t :tag "ODF Text or Text Template file")
2258 (cons :tag "Members"
2259 (file :tag " Member" "styles.xml")
2260 (repeat (file :tag "Member"))))))
2263 (defcustom org-e-odt-inline-image-extensions
2264 '("png" "jpeg" "jpg" "gif")
2265 "Extensions of image files that can be inlined into HTML."
2266 :type '(repeat (string :tag "Extension"))
2267 :group 'org-export-e-odt
2268 :version "24.1")
2270 (defcustom org-e-odt-pixels-per-inch display-pixels-per-inch
2271 "Scaling factor for converting images pixels to inches.
2272 Use this for sizing of embedded images. See Info node `(org)
2273 Images in ODT export' for more information."
2274 :type 'float
2275 :group 'org-export-e-odt
2276 :version "24.1")
2278 (defcustom org-e-odt-create-custom-styles-for-srcblocks t
2279 "Whether custom styles for colorized source blocks be automatically created.
2280 When this option is turned on, the exporter creates custom styles
2281 for source blocks based on the advice of `htmlfontify'. Creation
2282 of custom styles happen as part of `org-e-odt-hfy-face-to-css'.
2284 When this option is turned off exporter does not create such
2285 styles.
2287 Use the latter option if you do not want the custom styles to be
2288 based on your current display settings. It is necessary that the
2289 styles.xml already contains needed styles for colorizing to work.
2291 This variable is effective only if
2292 `org-e-odt-fontify-srcblocks' is turned on."
2293 :group 'org-export-e-odt
2294 :version "24.1"
2295 :type 'boolean)
2297 (defcustom org-e-odt-preferred-output-format nil
2298 "Automatically post-process to this format after exporting to \"odt\".
2299 Interactive commands `org-export-as-e-odt' and
2300 `org-export-as-e-odt-and-open' export first to \"odt\" format and
2301 then use `org-e-odt-convert-process' to convert the
2302 resulting document to this format. During customization of this
2303 variable, the list of valid values are populated based on
2304 `org-e-odt-convert-capabilities'."
2305 :group 'org-export-e-odt
2306 :version "24.1"
2307 :type '(choice :convert-widget
2308 (lambda (w)
2309 (apply 'widget-convert (widget-type w)
2310 (eval (car (widget-get w :args)))))
2311 `((const :tag "None" nil)
2312 ,@(mapcar (lambda (c)
2313 `(const :tag ,c ,c))
2314 (org-e-odt-reachable-formats "odt")))))
2316 (defcustom org-e-odt-table-styles
2317 '(("OrgEquation" "OrgEquation"
2318 ((use-first-column-styles . t)
2319 (use-last-column-styles . t))))
2320 "Specify how Table Styles should be derived from a Table Template.
2321 This is a list where each element is of the
2322 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
2324 TABLE-STYLE-NAME is the style associated with the table through
2325 `org-e-odt-table-style'.
2327 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
2328 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
2329 below) that is included in
2330 `org-e-odt-content-template-file'.
2332 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
2333 \"TableCell\"
2334 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
2335 \"TableParagraph\"
2336 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
2337 \"FirstRow\" | \"LastRow\" |
2338 \"EvenRow\" | \"OddRow\" |
2339 \"EvenColumn\" | \"OddColumn\" | \"\"
2340 where \"+\" above denotes string concatenation.
2342 TABLE-CELL-OPTIONS is an alist where each element is of the
2343 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
2344 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
2345 `use-last-row-styles' |
2346 `use-first-column-styles' |
2347 `use-last-column-styles' |
2348 `use-banding-rows-styles' |
2349 `use-banding-columns-styles' |
2350 `use-first-row-styles'
2351 ON-OR-OFF := `t' | `nil'
2353 For example, with the following configuration
2355 \(setq org-e-odt-table-styles
2356 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
2357 \(\(use-first-row-styles . t\)
2358 \(use-first-column-styles . t\)\)\)
2359 \(\"TableWithHeaderColumns\" \"Custom\"
2360 \(\(use-first-column-styles . t\)\)\)\)\)
2362 1. A table associated with \"TableWithHeaderRowsAndColumns\"
2363 style will use the following table-cell styles -
2364 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
2365 \"CustomTableCell\" and the following paragraph styles
2366 \"CustomFirstRowTableParagraph\",
2367 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
2368 as appropriate.
2370 2. A table associated with \"TableWithHeaderColumns\" style will
2371 use the following table-cell styles -
2372 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
2373 following paragraph styles
2374 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
2375 as appropriate..
2377 Note that TABLE-TEMPLATE-NAME corresponds to the
2378 \"<table:table-template>\" elements contained within
2379 \"<office:styles>\". The entries (TABLE-STYLE-NAME
2380 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
2381 \"table:template-name\" and \"table:use-first-row-styles\" etc
2382 attributes of \"<table:table>\" element. Refer ODF-1.2
2383 specification for more information. Also consult the
2384 implementation filed under `org-e-odt-get-table-cell-styles'.
2386 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
2387 formatting of numbered display equations. Do not delete this
2388 style from the list."
2389 :group 'org-export-e-odt
2390 :version "24.1"
2391 :type '(choice
2392 (const :tag "None" nil)
2393 (repeat :tag "Table Styles"
2394 (list :tag "Table Style Specification"
2395 (string :tag "Table Style Name")
2396 (string :tag "Table Template Name")
2397 (alist :options (use-first-row-styles
2398 use-last-row-styles
2399 use-first-column-styles
2400 use-last-column-styles
2401 use-banding-rows-styles
2402 use-banding-columns-styles)
2403 :key-type symbol
2404 :value-type (const :tag "True" t))))))
2405 (defcustom org-e-odt-fontify-srcblocks t
2406 "Specify whether or not source blocks need to be fontified.
2407 Turn this option on if you want to colorize the source code
2408 blocks in the exported file. For colorization to work, you need
2409 to make available an enhanced version of `htmlfontify' library."
2410 :type 'boolean
2411 :group 'org-export-e-odt
2412 :version "24.1")
2414 (defcustom org-e-odt-prettify-xml t ; FIXME
2415 "Specify whether or not the xml output should be prettified.
2416 When this option is turned on, `indent-region' is run on all
2417 component xml buffers before they are saved. Turn this off for
2418 regular use. Turn this on if you need to examine the xml
2419 visually."
2420 :group 'org-export-e-odt
2421 :version "24.1"
2422 :type 'boolean)
2424 (defcustom org-e-odt-convert-processes
2425 '(("LibreOffice"
2426 "soffice --headless --convert-to %f%x --outdir %d %i")
2427 ("unoconv"
2428 "unoconv -f %f -o %d %i"))
2429 "Specify a list of document converters and their usage.
2430 The converters in this list are offered as choices while
2431 customizing `org-e-odt-convert-process'.
2433 This variable is a list where each element is of the
2434 form (CONVERTER-NAME CONVERTER-CMD). CONVERTER-NAME is the name
2435 of the converter. CONVERTER-CMD is the shell command for the
2436 converter and can contain format specifiers. These format
2437 specifiers are interpreted as below:
2439 %i input file name in full
2440 %I input file name as a URL
2441 %f format of the output file
2442 %o output file name in full
2443 %O output file name as a URL
2444 %d output dir in full
2445 %D output dir as a URL.
2446 %x extra options as set in `org-e-odt-convert-capabilities'."
2447 :group 'org-export-e-odt
2448 :version "24.1"
2449 :type
2450 '(choice
2451 (const :tag "None" nil)
2452 (alist :tag "Converters"
2453 :key-type (string :tag "Converter Name")
2454 :value-type (group (string :tag "Command line")))))
2456 (defcustom org-e-odt-convert-process "LibreOffice"
2457 "Use this converter to convert from \"odt\" format to other formats.
2458 During customization, the list of converter names are populated
2459 from `org-e-odt-convert-processes'."
2460 :group 'org-export-e-odt
2461 :version "24.1"
2462 :type '(choice :convert-widget
2463 (lambda (w)
2464 (apply 'widget-convert (widget-type w)
2465 (eval (car (widget-get w :args)))))
2466 `((const :tag "None" nil)
2467 ,@(mapcar (lambda (c)
2468 `(const :tag ,(car c) ,(car c)))
2469 org-e-odt-convert-processes))))
2471 (defcustom org-e-odt-convert-capabilities
2472 '(("Text"
2473 ("odt" "ott" "doc" "rtf" "docx")
2474 (("pdf" "pdf") ("odt" "odt") ("rtf" "rtf") ("ott" "ott")
2475 ("doc" "doc" ":\"MS Word 97\"") ("docx" "docx") ("html" "html")))
2476 ("Web"
2477 ("html")
2478 (("pdf" "pdf") ("odt" "odt") ("html" "html")))
2479 ("Spreadsheet"
2480 ("ods" "ots" "xls" "csv" "xlsx")
2481 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
2482 ("xls" "xls") ("xlsx" "xlsx")))
2483 ("Presentation"
2484 ("odp" "otp" "ppt" "pptx")
2485 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("otp" "otp") ("ppt" "ppt")
2486 ("pptx" "pptx") ("odg" "odg"))))
2487 "Specify input and output formats of `org-e-odt-convert-process'.
2488 More correctly, specify the set of input and output formats that
2489 the user is actually interested in.
2491 This variable is an alist where each element is of the
2492 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
2493 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
2494 alist where each element is of the form (OUTPUT-FMT
2495 OUTPUT-FILE-EXTENSION EXTRA-OPTIONS).
2497 The variable is interpreted as follows:
2498 `org-e-odt-convert-process' can take any document that is in
2499 INPUT-FMT-LIST and produce any document that is in the
2500 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
2501 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
2502 serves dual purposes:
2503 - It is used for populating completion candidates during
2504 `org-e-odt-convert' commands.
2505 - It is used as the value of \"%f\" specifier in
2506 `org-e-odt-convert-process'.
2508 EXTRA-OPTIONS is used as the value of \"%x\" specifier in
2509 `org-e-odt-convert-process'.
2511 DOCUMENT-CLASS is used to group a set of file formats in
2512 INPUT-FMT-LIST in to a single class.
2514 Note that this variable inherently captures how LibreOffice based
2515 converters work. LibreOffice maps documents of various formats
2516 to classes like Text, Web, Spreadsheet, Presentation etc and
2517 allow document of a given class (irrespective of it's source
2518 format) to be converted to any of the export formats associated
2519 with that class.
2521 See default setting of this variable for an typical
2522 configuration."
2523 :group 'org-export-e-odt
2524 :version "24.1"
2525 :type
2526 '(choice
2527 (const :tag "None" nil)
2528 (alist :tag "Capabilities"
2529 :key-type (string :tag "Document Class")
2530 :value-type
2531 (group (repeat :tag "Input formats" (string :tag "Input format"))
2532 (alist :tag "Output formats"
2533 :key-type (string :tag "Output format")
2534 :value-type
2535 (group (string :tag "Output file extension")
2536 (choice
2537 (const :tag "None" nil)
2538 (string :tag "Extra options"))))))))
2540 ;;;; Debugging
2543 ;;;; Document
2545 ;;;; Document Header (Styles)
2547 ;;;; Document Header (Scripts)
2549 ;;;; Document Header (Mathjax)
2551 ;;;; Preamble
2553 ;;;; Postamble
2555 ;;;; Emphasis
2557 ;;;; Todos
2559 ;;;; Tags
2561 ;;;; Time-stamps
2562 ;;;; Statistics Cookie
2563 ;;;; Subscript
2564 ;;;; Superscript
2566 ;;;; Inline images
2568 ;;;; Block
2569 ;;;; Comment
2570 ;;;; Comment Block
2571 ;;;; Drawer
2572 ;;;; Dynamic Block
2573 ;;;; Emphasis
2574 ;;;; Entity
2575 ;;;; Example Block
2576 ;;;; Export Snippet
2577 ;;;; Export Block
2578 ;;;; Fixed Width
2579 ;;;; Footnotes
2581 ;;;; Headline
2582 ;;;; Horizontal Rule
2583 ;;;; Inline Babel Call
2584 ;;;; Inline Src Block
2585 ;;;; Inlinetask
2586 ;;;; Item
2587 ;;;; Keyword
2588 ;;;; Latex Environment
2589 ;;;; Latex Fragment
2590 ;;;; Line Break
2591 ;;;; Link
2592 ;;;; Babel Call
2593 ;;;; Macro
2594 ;;;; Paragraph
2595 ;;;; Plain List
2596 ;;;; Plain Text
2597 ;;;; Property Drawer
2598 ;;;; Quote Block
2599 ;;;; Quote Section
2600 ;;;; Section
2601 ;;;; Radio Target
2602 ;;;; Special Block
2603 ;;;; Src Block
2605 ;;;; Table
2607 ;;;; Target
2608 ;;;; Time-stamp
2610 ;;;; Verbatim
2611 ;;;; Verse Block
2612 ;;;; Headline
2614 ;;;; Links
2615 ;;;; Drawers
2616 ;;;; Inlinetasks
2617 ;;;; Publishing
2619 ;;;; Compilation
2623 ;;; User Configurable Variables (MAYBE)
2625 ;;;; Preamble
2627 ;;;; Headline
2629 ;;;; Emphasis
2631 (defcustom org-e-odt-format-headline-function nil
2632 "Function to format headline text.
2634 This function will be called with 5 arguments:
2635 TODO the todo keyword \(string or nil\).
2636 TODO-TYPE the type of todo \(symbol: `todo', `done', nil\)
2637 PRIORITY the priority of the headline \(integer or nil\)
2638 TEXT the main headline text \(string\).
2639 TAGS the tags string, separated with colons \(string or nil\).
2641 The function result will be used in the section format string.
2643 As an example, one could set the variable to the following, in
2644 order to reproduce the default set-up:
2646 \(defun org-e-odt-format-headline \(todo todo-type priority text tags\)
2647 \"Default format function for an headline.\"
2648 \(concat \(when todo
2649 \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo\)\)
2650 \(when priority
2651 \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
2652 text
2653 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)"
2654 :group 'org-export-e-odt
2655 :type 'function)
2657 ;;;; Footnotes
2659 ;;;; Time-stamps
2661 (defcustom org-e-odt-active-timestamp-format "\\textit{%s}"
2662 "A printf format string to be applied to active time-stamps."
2663 :group 'org-export-e-odt
2664 :type 'string)
2666 (defcustom org-e-odt-inactive-timestamp-format "\\textit{%s}"
2667 "A printf format string to be applied to inactive time-stamps."
2668 :group 'org-export-e-odt
2669 :type 'string)
2671 (defcustom org-e-odt-diary-timestamp-format "\\textit{%s}"
2672 "A printf format string to be applied to diary time-stamps."
2673 :group 'org-export-e-odt
2674 :type 'string)
2677 ;;;; Links
2679 (defcustom org-e-odt-inline-image-rules
2680 '(("file" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\)\\'"))
2681 "Rules characterizing image files that can be inlined into HTML.
2683 A rule consists in an association whose key is the type of link
2684 to consider, and value is a regexp that will be matched against
2685 link's path.
2687 Note that, by default, the image extension *actually* allowed
2688 depend on the way the HTML file is processed. When used with
2689 pdflatex, pdf, jpg and png images are OK. When processing
2690 through dvi to Postscript, only ps and eps are allowed. The
2691 default we use here encompasses both."
2692 :group 'org-export-e-odt
2693 :type '(alist :key-type (string :tag "Type")
2694 :value-type (regexp :tag "Path")))
2696 ;;;; Tables
2698 (defcustom org-e-odt-table-caption-above t
2699 "When non-nil, place caption string at the beginning of the table.
2700 Otherwise, place it near the end."
2701 :group 'org-export-e-odt
2702 :type 'boolean)
2704 ;;;; Drawers
2706 (defcustom org-e-odt-format-drawer-function nil
2707 "Function called to format a drawer in HTML code.
2709 The function must accept two parameters:
2710 NAME the drawer name, like \"LOGBOOK\"
2711 CONTENTS the contents of the drawer.
2713 The function should return the string to be exported.
2715 For example, the variable could be set to the following function
2716 in order to mimic default behaviour:
2718 \(defun org-e-odt-format-drawer-default \(name contents\)
2719 \"Format a drawer element for HTML export.\"
2720 contents\)"
2721 :group 'org-export-e-odt
2722 :type 'function)
2725 ;;;; Inlinetasks
2727 (defcustom org-e-odt-format-inlinetask-function nil
2728 "Function called to format an inlinetask in HTML code.
2730 The function must accept six parameters:
2731 TODO the todo keyword, as a string
2732 TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
2733 PRIORITY the inlinetask priority, as a string
2734 NAME the inlinetask name, as a string.
2735 TAGS the inlinetask tags, as a string.
2736 CONTENTS the contents of the inlinetask, as a string.
2738 The function should return the string to be exported.
2740 For example, the variable could be set to the following function
2741 in order to mimic default behaviour:
2743 \(defun org-e-odt-format-inlinetask \(todo type priority name tags contents\)
2744 \"Format an inline task element for HTML export.\"
2745 \(let \(\(full-title
2746 \(concat
2747 \(when todo
2748 \(format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo\)\)
2749 \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
2750 title
2751 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)\)
2752 \(format \(concat \"\\\\begin{center}\\n\"
2753 \"\\\\fbox{\\n\"
2754 \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\"
2755 \"%s\\n\\n\"
2756 \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\"
2757 \"%s\"
2758 \"\\\\end{minipage}}\"
2759 \"\\\\end{center}\"\)
2760 full-title contents\)\)"
2761 :group 'org-export-e-odt
2762 :type 'function)
2765 ;; Src blocks
2767 ;;;; Plain text
2769 (defcustom org-e-odt-quotes
2770 '(("fr" ("\\(\\s-\\|[[(]\\)\"" . "«~") ("\\(\\S-\\)\"" . "~»") ("\\(\\s-\\|(\\)'" . "'"))
2771 ("en" ("\\(\\s-\\|[[(]\\)\"" . "``") ("\\(\\S-\\)\"" . "''") ("\\(\\s-\\|(\\)'" . "`")))
2772 "Alist for quotes to use when converting english double-quotes.
2774 The CAR of each item in this alist is the language code.
2775 The CDR of each item in this alist is a list of three CONS:
2776 - the first CONS defines the opening quote;
2777 - the second CONS defines the closing quote;
2778 - the last CONS defines single quotes.
2780 For each item in a CONS, the first string is a regexp
2781 for allowed characters before/after the quote, the second
2782 string defines the replacement string for this quote."
2783 :group 'org-export-e-odt
2784 :type '(list
2785 (cons :tag "Opening quote"
2786 (string :tag "Regexp for char before")
2787 (string :tag "Replacement quote "))
2788 (cons :tag "Closing quote"
2789 (string :tag "Regexp for char after ")
2790 (string :tag "Replacement quote "))
2791 (cons :tag "Single quote"
2792 (string :tag "Regexp for char before")
2793 (string :tag "Replacement quote "))))
2796 ;;;; Compilation
2800 ;;; Internal Functions (HTML)
2802 ;; (defun org-e-odt-format-inline-image (path &optional caption label attr)
2803 ;; ;; FIXME: alt text missing here?
2804 ;; (let ((inline-image (format "<img src=\"%s\" alt=\"%s\"/>"
2805 ;; path (file-name-nondirectory path))))
2806 ;; (if (not label) inline-image
2807 ;; (org-e-odt-format-section inline-image "figure" label))))
2809 ;;;; Bibliography
2811 (defun org-e-odt-bibliography ()
2812 "Find bibliography, cut it out and return it."
2813 (catch 'exit
2814 (let (beg end (cnt 1) bib)
2815 (save-excursion
2816 (goto-char (point-min))
2817 (when (re-search-forward
2818 "^[ \t]*<div \\(id\\|class\\)=\"bibliography\"" nil t)
2819 (setq beg (match-beginning 0))
2820 (while (re-search-forward "</?div\\>" nil t)
2821 (setq cnt (+ cnt (if (string= (match-string 0) "<div") +1 -1)))
2822 (when (= cnt 0)
2823 (and (looking-at ">") (forward-char 1))
2824 (setq bib (buffer-substring beg (point)))
2825 (delete-region beg (point))
2826 (throw 'exit bib))))
2827 nil))))
2829 ;;;; Table
2831 (defun org-e-odt-format-table (lines olines)
2832 (let ((org-e-odt-format-table-no-css nil))
2833 (org-lparse-format-table lines olines)))
2835 (defun org-e-odt-splice-attributes (tag attributes)
2836 "Read attributes in string ATTRIBUTES, add and replace in HTML tag TAG."
2837 (if (not attributes)
2839 (let (oldatt newatt)
2840 (setq oldatt (org-extract-attributes-from-string tag)
2841 tag (pop oldatt)
2842 newatt (cdr (org-extract-attributes-from-string attributes)))
2843 (while newatt
2844 (setq oldatt (plist-put oldatt (pop newatt) (pop newatt))))
2845 (if (string-match ">" tag)
2846 (setq tag
2847 (replace-match (concat (org-attributes-to-string oldatt) ">")
2848 t t tag)))
2849 tag)))
2851 (defun org-export-splice-style (style extra)
2852 "Splice EXTRA into STYLE, just before \"</style>\"."
2853 (if (and (stringp extra)
2854 (string-match "\\S-" extra)
2855 (string-match "</style>" style))
2856 (concat (substring style 0 (match-beginning 0))
2857 "\n" extra "\n"
2858 (substring style (match-beginning 0)))
2859 style))
2861 (defun org-e-odt-toc-entry-formatter
2862 (level snumber todo todo-type priority
2863 headline tags target extra-targets extra-class)
2864 (org-e-odt-format-toc-entry snumber todo headline tags target))
2866 (defun org-e-odt-make-string (n string)
2867 (let (out) (dotimes (i n out) (setq out (concat string out)))))
2869 (defun org-e-odt-toc-text (toc-entries)
2870 (let* ((prev-level (1- (nth 1 (car toc-entries))))
2871 (start-level prev-level))
2872 (mapconcat
2873 (lambda (entry)
2874 (let ((headline (nth 0 entry))
2875 (level (nth 1 entry)))
2876 (prog1 (org-e-odt-format-toc-item headline level prev-level)
2877 (setq prev-level level))))
2878 toc-entries "")))
2880 (defun* org-e-odt-format-toc-headline
2881 (todo todo-type priority text tags
2882 &key level section-number headline-label &allow-other-keys)
2883 ;; FIXME
2884 (setq text (concat
2885 (and org-export-with-section-numbers
2886 (concat section-number ". "))
2887 text
2888 (and tags
2889 (concat
2890 (org-e-odt-format-spaces 3)
2891 (org-e-odt-format-fontify tags "tag")))))
2892 (when todo
2893 (setq text (org-e-odt-format-fontify text "todo")))
2895 (let ((org-e-odt-suppress-xref t))
2896 (org-e-odt-format-link text (concat "#" headline-label))))
2898 (defun org-e-odt-toc (depth info)
2899 (assert (wholenump depth))
2900 (let* ((headlines (org-export-collect-headlines info depth))
2901 (toc-entries
2902 (loop for headline in headlines collect
2903 (list (org-e-odt-format-headline--wrap
2904 headline info 'org-e-odt-format-toc-headline)
2905 (org-export-get-relative-level headline info)))))
2906 (when toc-entries
2907 (let* ((lang-specific-heading "Table of Contents")) ; FIXME
2908 (concat
2909 (org-e-odt-begin-toc lang-specific-heading depth)
2910 (org-e-odt-toc-text toc-entries)
2911 (org-e-odt-end-toc))))))
2913 (defun org-e-odt-begin-outline (level1 snumber title tags
2914 target extra-targets extra-class)
2915 (let* ((class (format "outline-%d" level1))
2916 (class (if extra-class (concat class " " extra-class) class))
2917 (id (format "outline-container-%s"
2918 (org-lparse-suffix-from-snumber snumber)))
2919 (extra (concat (when id (format " id=\"%s\"" id))
2920 (when class (format " class=\"%s\"" class)))))
2921 (org-lparse-insert-tag "<div%s>" extra)
2922 (insert
2923 (org-lparse-format 'HEADING
2924 (org-lparse-format
2925 'HEADLINE title extra-targets tags snumber level1)
2926 level1 target))))
2928 (defun org-e-odt-end-outline ()
2929 (org-lparse-insert-tag "</div>"))
2931 (defun org-e-odt-suffix-from-snumber (snumber)
2932 (let* ((snu (replace-regexp-in-string "\\." "-" snumber))
2933 (href (cdr (assoc (concat "sec-" snu)
2934 org-export-preferred-target-alist))))
2935 (org-solidify-link-text (or href snu))))
2937 (defun org-e-odt-format-outline (contents level1 snumber title
2938 tags target extra-targets extra-class)
2941 ;; (defun org-e-odt-format-line (line)
2942 ;; (case org-lparse-dyn-current-environment
2943 ;; ((quote fixedwidth) (concat (org-e-odt-encode-plain-text line) "\n"))
2944 ;; (t (concat line "\n"))))
2946 (defun org-e-odt-fix-class-name (kwd) ; audit callers of this function
2947 "Turn todo keyword into a valid class name.
2948 Replaces invalid characters with \"_\"."
2949 (save-match-data
2950 (while (string-match "[^a-zA-Z0-9_]" kwd)
2951 (setq kwd (replace-match "_" t t kwd))))
2952 kwd)
2954 (defun org-e-odt-format-internal-link (text href &optional extra)
2955 (org-e-odt-format-link text (concat "#" href) extra))
2957 (defun org-e-odt-format-extra-targets (extra-targets)
2958 (if (not extra-targets) ""
2959 (mapconcat (lambda (x)
2960 (when x
2961 (setq x (org-solidify-link-text
2962 (if (org-uuidgen-p x) (concat "ID-" x) x)))
2963 (org-e-odt-format-anchor "" x))) extra-targets "")))
2965 (defun org-e-odt-format-org-tags (tags)
2966 (if (not tags) ""
2967 (org-e-odt-format-fontify
2968 (mapconcat
2969 (lambda (x)
2970 (org-e-odt-format-fontify
2971 x (concat "" ;; org-e-odt-tag-class-prefix
2972 (org-e-odt-fix-class-name x))))
2973 (org-split-string tags ":")
2974 (org-e-odt-format-spaces 1)) "tag")))
2976 (defun org-e-odt-format-section-number (&optional snumber level)
2977 ;; FIXME
2978 (and nil org-export-with-section-numbers
2979 ;; (not org-lparse-body-only)
2980 snumber level
2981 (org-e-odt-format-fontify snumber (format "section-number-%d" level))))
2983 ;; (defun org-e-odt-format-headline (title extra-targets tags
2984 ;; &optional snumber level)
2985 ;; (concat
2986 ;; (org-e-odt-format-extra-targets extra-targets)
2987 ;; (concat (org-e-odt-format-section-number snumber level) " ")
2988 ;; title
2989 ;; (and tags (concat (org-e-odt-format-spaces 3)
2990 ;; (org-e-odt-format-org-tags tags)))))
2992 (defun org-e-odt-get-coding-system-for-write ()
2993 (or org-e-odt-coding-system
2994 (and (boundp 'buffer-file-coding-system) buffer-file-coding-system)))
2996 (defun org-e-odt-get-coding-system-for-save ()
2997 (or org-e-odt-coding-system
2998 (and (boundp 'buffer-file-coding-system) buffer-file-coding-system)))
3000 ;; (defun org-e-odt-format-date (info)
3001 ;; (let ((date (plist-get info :date)))
3002 ;; (cond
3003 ;; ((and date (string-match "%" date))
3004 ;; (format-time-string date))
3005 ;; (date date)
3006 ;; (t (format-time-string "%Y-%m-%d %T %Z")))))
3010 ;;; Internal Functions (Ngz)
3012 (defun org-e-odt--caption/label-string (caption label info)
3013 "Return caption and label HTML string for floats.
3015 CAPTION is a cons cell of secondary strings, the car being the
3016 standard caption and the cdr its short form. LABEL is a string
3017 representing the label. INFO is a plist holding contextual
3018 information.
3020 If there's no caption nor label, return the empty string.
3022 For non-floats, see `org-e-odt--wrap-label'."
3023 (setq label nil) ;; FIXME
3025 (let ((label-str (if label (format "\\label{%s}" label) "")))
3026 (cond
3027 ((and (not caption) (not label)) "")
3028 ((not caption) (format "\\label{%s}\n" label))
3029 ;; Option caption format with short name.
3030 ((cdr caption)
3031 (format "\\caption[%s]{%s%s}\n"
3032 (org-export-secondary-string (cdr caption) 'e-odt info)
3033 label-str
3034 (org-export-secondary-string (car caption) 'e-odt info)))
3035 ;; Standard caption format.
3036 ;; (t (format "\\caption{%s%s}\n"
3037 ;; label-str
3038 ;; (org-export-secondary-string (car caption) 'e-odt info)))
3040 (t (org-export-secondary-string (car caption) 'e-odt info)))))
3042 (defun org-e-odt--find-verb-separator (s)
3043 "Return a character not used in string S.
3044 This is used to choose a separator for constructs like \\verb."
3045 (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
3046 (loop for c across ll
3047 when (not (string-match (regexp-quote (char-to-string c)) s))
3048 return (char-to-string c))))
3050 (defun org-e-odt--quotation-marks (text info)
3051 "Export quotation marks depending on language conventions.
3052 TEXT is a string containing quotation marks to be replaced. INFO
3053 is a plist used as a communication channel."
3054 (mapc (lambda(l)
3055 (let ((start 0))
3056 (while (setq start (string-match (car l) text start))
3057 (let ((new-quote (concat (match-string 1 text) (cdr l))))
3058 (setq text (replace-match new-quote t t text))))))
3059 (cdr (or (assoc (plist-get info :language) org-e-odt-quotes)
3060 ;; Falls back on English.
3061 (assoc "en" org-e-odt-quotes))))
3062 text)
3064 (defun org-e-odt--wrap-label (element output)
3065 "Wrap label associated to ELEMENT around OUTPUT, if appropriate.
3066 This function shouldn't be used for floats. See
3067 `org-e-odt--caption/label-string'."
3068 ;; (let ((label (org-element-property :name element)))
3069 ;; (if (or (not output) (not label) (string= output "") (string= label ""))
3070 ;; output
3071 ;; (concat (format "\\label{%s}\n" label) output)))
3072 output)
3076 ;;; Transcode Helpers
3078 (defun* org-e-odt-format-headline
3079 (todo todo-type priority text tags
3080 &key level section-number headline-label &allow-other-keys)
3081 (concat (org-e-odt-todo todo) (and todo " ") text
3082 (and tags (org-e-odt-format-spaces 3))
3083 (and tags (org-e-odt-format-org-tags tags))))
3085 ;;;; Src Code
3087 (defun org-e-odt-htmlfontify-string (line)
3088 (let* ((hfy-html-quote-regex "\\([<\"&> ]\\)")
3089 (hfy-html-quote-map '(("\"" "&quot;")
3090 ("<" "&lt;")
3091 ("&" "&amp;")
3092 (">" "&gt;")
3093 (" " "<text:s/>")
3094 (" " "<text:tab/>")))
3095 (hfy-face-to-css 'org-e-odt-hfy-face-to-css)
3096 (hfy-optimisations-1 (copy-seq hfy-optimisations))
3097 (hfy-optimisations (add-to-list 'hfy-optimisations-1
3098 'body-text-only))
3099 (hfy-begin-span-handler
3100 (lambda (style text-block text-id text-begins-block-p)
3101 (insert (format "<text:span text:style-name=\"%s\">" style))))
3102 (hfy-end-span-handler (lambda nil (insert "</text:span>"))))
3103 (htmlfontify-string line)))
3105 (defun org-e-odt-do-format-code
3106 (code &optional lang refs retain-labels num-start)
3107 (let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
3108 (lang-mode (and lang (intern (format "%s-mode" lang))))
3109 (code-lines (org-split-string code "\n"))
3110 (code-length (length code-lines))
3111 (use-htmlfontify-p (and (functionp lang-mode)
3112 org-e-odt-fontify-srcblocks
3113 (require 'htmlfontify nil t)
3114 (fboundp 'htmlfontify-string)))
3115 (code (if (not use-htmlfontify-p) code
3116 (with-temp-buffer
3117 (insert code)
3118 (funcall lang-mode)
3119 (font-lock-fontify-buffer)
3120 (buffer-string))))
3121 (fontifier (if use-htmlfontify-p 'org-e-odt-htmlfontify-string
3122 'org-e-odt-encode-plain-text))
3123 (par-style (if use-htmlfontify-p "OrgSrcBlock"
3124 "OrgFixedWidthBlock"))
3125 (i 0))
3126 (assert (= code-length (length (org-split-string code "\n"))))
3127 (setq code
3128 (org-export-format-code
3129 code
3130 (lambda (loc line-num ref)
3131 (setq par-style
3132 (concat par-style (and (= (incf i) code-length) "LastLine")))
3134 (setq loc (concat loc (and ref retain-labels (format " (%s)" ref))))
3135 (setq loc (funcall fontifier loc))
3136 (when ref
3137 (setq loc (org-e-odt-format-target loc (concat "coderef-" ref))))
3138 (setq loc (org-e-odt-format-stylized-paragraph par-style loc))
3139 (if (not line-num) loc
3140 (org-e-odt-format-tags
3141 '("<text:list-item>" . "</text:list-item>") loc)))
3142 num-start refs))
3143 (cond
3144 ((not num-start) code)
3145 ((equal num-start 0)
3146 (org-e-odt-format-tags
3147 '("<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>"
3148 . "</text:list>") code " text:continue-numbering=\"false\""))
3149 (t (org-e-odt-format-tags
3150 '("<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>"
3151 . "</text:list>") code " text:continue-numbering=\"true\"")))))
3153 (defun org-e-odt-format-code (element info)
3154 (let* ((lang (org-element-property :language element))
3155 ;; Extract code and references.
3156 (code-info (org-export-unravel-code element))
3157 (code (car code-info))
3158 (refs (cdr code-info))
3159 ;; Does the src block contain labels?
3160 (retain-labels (org-element-property :retain-labels element))
3161 ;; Does it have line numbers?
3162 (num-start (case (org-element-property :number-lines element)
3163 (continued (org-export-get-loc element info))
3164 (new 0))))
3165 (org-e-odt-do-format-code code lang refs retain-labels num-start)))
3169 ;;; Template
3171 (defun org-e-odt-template (contents info)
3172 "Return complete document string after HTML conversion.
3173 CONTENTS is the transcoded contents string. RAW-DATA is the
3174 original parsed data. INFO is a plist holding export options."
3175 ;; write meta file
3176 (org-e-odt-update-meta-file info)
3177 (with-temp-buffer
3178 (insert-file-contents
3179 (or org-e-odt-content-template-file
3180 (expand-file-name "OrgOdtContentTemplate.xml"
3181 org-e-odt-styles-dir)))
3182 (goto-char (point-min))
3183 (re-search-forward "</office:text>" nil nil)
3184 (goto-char (match-beginning 0))
3186 ;; Title
3187 (insert (org-e-odt-format-preamble info))
3188 ;; Table of Contents
3189 (let ((depth (plist-get info :with-toc)))
3190 (when (wholenump depth) (insert (org-e-odt-toc depth info))))
3192 ;; Copy styles.xml. Also dump htmlfontify styles, if there is any.
3193 (org-e-odt-update-styles-file info)
3195 ;; Update styles.xml - take care of outline numbering
3196 (with-current-buffer
3197 (find-file-noselect (expand-file-name "styles.xml") t)
3198 ;; Don't make automatic backup of styles.xml file. This setting
3199 ;; prevents the backed-up styles.xml file from being zipped in to
3200 ;; odt file. This is more of a hackish fix. Better alternative
3201 ;; would be to fix the zip command so that the output odt file
3202 ;; includes only the needed files and excludes any auto-generated
3203 ;; extra files like backups and auto-saves etc etc. Note that
3204 ;; currently the zip command zips up the entire temp directory so
3205 ;; that any auto-generated files created under the hood ends up in
3206 ;; the resulting odt file.
3207 (set (make-local-variable 'backup-inhibited) t)
3208 (org-e-odt-configure-outline-numbering))
3210 ;; Contents
3211 (insert contents)
3212 (buffer-substring-no-properties (point-min) (point-max))))
3216 ;;; Transcode Functions
3218 ;;;; Block
3220 (defun org-e-odt-center-block (center-block contents info)
3221 "Transcode a CENTER-BLOCK element from Org to HTML.
3222 CONTENTS holds the contents of the block. INFO is a plist
3223 holding contextual information."
3224 (org-e-odt--wrap-label center-block contents))
3227 ;;;; Comment
3229 ;; Comments are ignored.
3232 ;;;; Comment Block
3234 ;; Comment Blocks are ignored.
3237 ;;;; Drawer
3239 (defun org-e-odt-drawer (drawer contents info)
3240 "Transcode a DRAWER element from Org to HTML.
3241 CONTENTS holds the contents of the block. INFO is a plist
3242 holding contextual information."
3243 (let* ((name (org-element-property :drawer-name drawer))
3244 (output (if (functionp org-e-odt-format-drawer-function)
3245 (funcall org-e-odt-format-drawer-function
3246 name contents)
3247 ;; If there's no user defined function: simply
3248 ;; display contents of the drawer.
3249 contents)))
3250 (org-e-odt--wrap-label drawer output)))
3253 ;;;; Dynamic Block
3255 (defun org-e-odt-dynamic-block (dynamic-block contents info)
3256 "Transcode a DYNAMIC-BLOCK element from Org to HTML.
3257 CONTENTS holds the contents of the block. INFO is a plist
3258 holding contextual information. See
3259 `org-export-data'."
3260 (org-e-odt--wrap-label dynamic-block contents))
3263 ;;;; Emphasis
3265 (defun org-e-odt-emphasis (emphasis contents info)
3266 "Transcode EMPHASIS from Org to HTML.
3267 CONTENTS is the contents of the emphasized text. INFO is a plist
3268 holding contextual information.."
3269 ;; (format (cdr (assoc (org-element-property :marker emphasis)
3270 ;; org-e-odt-emphasis-alist))
3271 ;; contents)
3272 (org-e-odt-format-fontify
3273 contents (cadr (assoc
3274 (org-element-property :marker emphasis)
3275 '(("*" bold)
3276 ("/" emphasis)
3277 ("_" underline)
3278 ("=" code)
3279 ("~" verbatim)
3280 ("+" strike))))))
3283 ;;;; Entity
3285 (defun org-e-odt-entity (entity contents info)
3286 "Transcode an ENTITY object from Org to HTML.
3287 CONTENTS are the definition itself. INFO is a plist holding
3288 contextual information."
3289 ;; (let ((ent (org-element-property :latex entity)))
3290 ;; (if (org-element-property :latex-math-p entity)
3291 ;; (format "$%s$" ent)
3292 ;; ent))
3293 (org-element-property :utf-8 entity))
3296 ;;;; Example Block
3298 (defun org-e-odt-example-block (example-block contents info)
3299 "Transcode a EXAMPLE-BLOCK element from Org to HTML.
3300 CONTENTS is nil. INFO is a plist holding contextual information."
3301 (let* ((options (or (org-element-property :options example-block) ""))
3302 (value (org-export-handle-code example-block info nil nil t)))
3303 (org-e-odt--wrap-label
3304 example-block (org-e-odt-format-source-code-or-example value nil))))
3307 ;;;; Export Snippet
3309 (defun org-e-odt-export-snippet (export-snippet contents info)
3310 "Transcode a EXPORT-SNIPPET object from Org to HTML.
3311 CONTENTS is nil. INFO is a plist holding contextual information."
3312 (when (eq (org-export-snippet-backend export-snippet) 'e-odt)
3313 (org-element-property :value export-snippet)))
3316 ;;;; Export Block
3318 (defun org-e-odt-export-block (export-block contents info)
3319 "Transcode a EXPORT-BLOCK element from Org to HTML.
3320 CONTENTS is nil. INFO is a plist holding contextual information."
3321 (when (string= (org-element-property :type export-block) "latex")
3322 (org-remove-indentation (org-element-property :value export-block))))
3325 ;;;; Fixed Width
3327 (defun org-e-odt-fixed-width (fixed-width contents info)
3328 "Transcode a FIXED-WIDTH element from Org to HTML.
3329 CONTENTS is nil. INFO is a plist holding contextual information."
3330 (let* ((value (org-element-normalize-string
3331 (replace-regexp-in-string
3332 "^[ \t]*: ?" ""
3333 (org-element-property :value fixed-width)))))
3334 (org-e-odt--wrap-label
3335 fixed-width (org-e-odt-format-source-code-or-example value nil))))
3338 ;;;; Footnote Definition
3340 ;; Footnote Definitions are ignored.
3343 ;;;; Footnote Reference
3345 (defun org-e-odt-footnote-def (raw info) ; FIXME
3346 (if (equal (org-element-type raw) 'org-data)
3347 (org-trim (org-export-data raw 'e-odt info)) ; fix paragraph
3348 ; style
3349 (org-e-odt-format-stylized-paragraph
3350 'footnote (org-trim (org-export-secondary-string raw 'e-odt info)))))
3352 (defvar org-e-odt-footnote-separator
3353 (org-e-odt-format-fontify "," 'superscript))
3355 (defun org-e-odt-footnote-reference (footnote-reference contents info)
3356 "Transcode a FOOTNOTE-REFERENCE element from Org to HTML.
3357 CONTENTS is nil. INFO is a plist holding contextual information."
3358 (concat
3359 ;; Insert separator between two footnotes in a row.
3360 (let ((prev (org-export-get-previous-element footnote-reference info)))
3361 (when (eq (org-element-type prev) 'footnote-reference)
3362 org-e-odt-footnote-separator))
3363 (cond
3364 ((not (org-export-footnote-first-reference-p footnote-reference info))
3365 (let* ((n (org-export-get-footnote-number footnote-reference info)))
3366 (org-e-odt-format-footnote-reference n "IGNORED" 100)))
3367 ;; Inline definitions are secondary strings.
3368 ((eq (org-element-property :type footnote-reference) 'inline)
3369 (let* ((raw (org-export-get-footnote-definition footnote-reference info))
3370 (n (org-export-get-footnote-number footnote-reference info))
3371 (def (org-e-odt-footnote-def raw info)))
3372 (org-e-odt-format-footnote-reference n def 1)))
3373 ;; Non-inline footnotes definitions are full Org data.
3375 (let* ((raw (org-export-get-footnote-definition footnote-reference info))
3376 (n (org-export-get-footnote-number footnote-reference info))
3377 (def (org-e-odt-footnote-def raw info)))
3378 (org-e-odt-format-footnote-reference n def 1))))))
3381 ;;;; Headline
3383 (defun org-e-odt-todo (todo)
3384 (when todo
3385 (org-e-odt-format-fontify
3386 (concat
3387 "" ; org-e-odt-todo-kwd-class-prefix
3388 (org-e-odt-fix-class-name todo))
3389 (list (if (member todo org-done-keywords) "done" "todo")
3390 todo))))
3392 (defun org-e-odt-format-headline--wrap (headline info
3393 &optional format-function
3394 &rest extra-keys)
3395 "Transcode an HEADLINE element from Org to ODT.
3396 CONTENTS holds the contents of the headline. INFO is a plist
3397 holding contextual information."
3398 (let* ((level (+ (org-export-get-relative-level headline info)))
3399 (headline-number (org-export-get-headline-number headline info))
3400 (section-number (and (org-export-numbered-headline-p headline info)
3401 (mapconcat 'number-to-string
3402 headline-number ".")))
3403 (todo (and (plist-get info :with-todo-keywords)
3404 (let ((todo (org-element-property
3405 :todo-keyword headline)))
3406 (and todo
3407 (org-export-secondary-string todo 'e-odt info)))))
3408 (todo-type (and todo (org-element-property :todo-type headline)))
3409 (priority (and (plist-get info :with-priority)
3410 (org-element-property :priority headline)))
3411 (text (org-export-secondary-string
3412 (org-element-property :title headline) 'e-odt info))
3413 (tags (and (plist-get info :with-tags)
3414 (org-element-property :tags headline)))
3415 (headline-label (concat "sec-" (mapconcat 'number-to-string
3416 headline-number "-")))
3417 (format-function (cond
3418 ((functionp format-function) format-function)
3419 ((functionp org-e-odt-format-headline-function)
3420 (function*
3421 (lambda (todo todo-type priority text tags
3422 &allow-other-keys)
3423 (funcall org-e-odt-format-headline-function
3424 todo todo-type priority text tags))))
3425 (t 'org-e-odt-format-headline))))
3426 (apply format-function
3427 todo todo-type priority text tags
3428 :headline-label headline-label :level level
3429 :section-number section-number extra-keys)))
3431 (defun org-e-odt-headline (headline contents info)
3432 "Transcode an HEADLINE element from Org to HTML.
3433 CONTENTS holds the contents of the headline. INFO is a plist
3434 holding contextual information."
3435 (let* ((numberedp (org-export-numbered-headline-p headline info))
3436 ;; Get level relative to current parsed data.
3437 (level (org-export-get-relative-level headline info))
3438 (text (org-export-secondary-string
3439 (org-element-property :title headline) 'e-odt info))
3440 ;; Create the headline text.
3441 (full-text (org-e-odt-format-headline--wrap headline info)))
3442 (cond
3443 ;; Case 1: This is a footnote section: ignore it.
3444 ((org-element-property :footnote-section-p headline) nil)
3445 ;; Case 2. This is a deep sub-tree: export it as a list item.
3446 ;; Also export as items headlines for which no section
3447 ;; format has been found.
3448 ((org-export-low-level-p headline info) ; FIXME (or (not section-fmt))
3449 ;; Build the real contents of the sub-tree.
3450 (let* ((type (if numberedp 'unordered 'unordered)) ; FIXME
3451 (itemized-body (org-e-odt-format-list-item
3452 contents type nil nil full-text)))
3453 (concat
3454 (and (org-export-first-sibling-p headline info)
3455 (org-e-odt-begin-plain-list type))
3456 itemized-body
3457 (and (org-export-last-sibling-p headline info)
3458 (org-e-odt-end-plain-list type)))))
3459 ;; Case 3. Standard headline. Export it as a section.
3461 (let* ((extra-ids (list (org-element-property :custom-id headline)
3462 (org-element-property :id headline)))
3463 (extra-ids nil) ; FIXME
3464 (id (concat "sec-" (mapconcat 'number-to-string
3465 (org-export-get-headline-number
3466 headline info) "-"))))
3467 (concat
3468 (org-e-odt-format-tags
3469 '("<text:h text:style-name=\"Heading_20_%s\" text:outline-level=\"%s\">" .
3470 "</text:h>")
3471 (concat (org-e-odt-format-extra-targets extra-ids)
3472 (if (not id) full-text (org-e-odt-format-target full-text id) ))
3473 level level)
3474 contents))))))
3477 ;;;; Horizontal Rule
3479 (defun org-e-odt-horizontal-rule (horizontal-rule contents info)
3480 "Transcode an HORIZONTAL-RULE object from Org to HTML.
3481 CONTENTS is nil. INFO is a plist holding contextual information."
3482 (let ((attr (mapconcat #'identity
3483 (org-element-property :attr_odt horizontal-rule)
3484 " ")))
3485 (org-e-odt--wrap-label horizontal-rule
3486 (org-e-odt-format-horizontal-line))))
3489 ;;;; Inline Babel Call
3491 ;; Inline Babel Calls are ignored.
3494 ;;;; Inline Src Block
3496 (defun org-e-odt-inline-src-block (inline-src-block contents info)
3497 "Transcode an INLINE-SRC-BLOCK element from Org to HTML.
3498 CONTENTS holds the contents of the item. INFO is a plist holding
3499 contextual information."
3500 (let* ((org-lang (org-element-property :language inline-src-block))
3501 (code (org-element-property :value inline-src-block))
3502 (separator (org-e-odt--find-verb-separator code)))
3503 (error "FIXME")))
3506 ;;;; Inlinetask
3508 (defun org-e-odt-format-section (text class &optional id)
3509 (let ((extra (concat (when id (format " id=\"%s\"" id)))))
3510 (concat (format "<div class=\"%s\"%s>\n" class extra) text "</div>\n")))
3512 (defun org-e-odt-inlinetask (inlinetask contents info)
3513 "Transcode an INLINETASK element from Org to ODT.
3514 CONTENTS holds the contents of the block. INFO is a plist
3515 holding contextual information."
3516 (cond
3517 ;; If `org-e-odt-format-inlinetask-function' is provided, call it
3518 ;; with appropriate arguments.
3519 ((functionp org-e-odt-format-inlinetask-function)
3520 (let ((format-function
3521 (function*
3522 (lambda (todo todo-type priority text tags
3523 &key contents &allow-other-keys)
3524 (funcall org-e-odt-format-inlinetask-function
3525 todo todo-type priority text tags contents)))))
3526 (org-e-odt-format-headline--wrap
3527 inlinetask info format-function :contents contents)))
3528 ;; Otherwise, use a default template.
3529 (t (org-e-odt--wrap-label
3530 inlinetask
3531 (org-e-odt-format-stylized-paragraph
3532 nil (org-e-odt-format-textbox
3533 (concat (org-e-odt-format-stylized-paragraph
3534 "OrgInlineTaskHeading" (org-e-odt-format-headline--wrap
3535 inlinetask info))
3536 contents)
3537 nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\""))))))
3539 ;;;; Item
3541 (defun org-e-odt-format-list-item (contents type checkbox
3542 &optional term-counter-id
3543 headline)
3544 (when checkbox
3545 (setq checkbox
3546 (org-e-odt-format-fontify (case checkbox
3547 (on "[X]")
3548 (off "[&nbsp;]")
3549 (trans "[-]")) 'code)))
3550 (concat
3551 (org-e-odt-begin-list-item type term-counter-id headline)
3552 ;; FIXME checkbox (and checkbox " ")
3553 contents
3554 (org-e-odt-end-list-item type)))
3556 (defun org-e-odt-item (item contents info)
3557 "Transcode an ITEM element from Org to HTML.
3558 CONTENTS holds the contents of the item. INFO is a plist holding
3559 contextual information."
3560 ;; Grab `:level' from plain-list properties, which is always the
3561 ;; first element above current item.
3562 (let* ((plain-list (org-export-get-parent item info))
3563 (type (org-element-property :type plain-list))
3564 (level (org-element-property :level plain-list))
3565 (counter (org-element-property :counter item))
3566 (checkbox (org-element-property :checkbox item))
3567 (tag (let ((tag (org-element-property :tag item)))
3568 (and tag (org-export-secondary-string tag 'e-odt info)))))
3569 (org-e-odt-format-list-item
3570 contents type checkbox (or tag counter))))
3573 ;;;; Keyword
3575 (defun org-e-odt-keyword (keyword contents info)
3576 "Transcode a KEYWORD element from Org to HTML.
3577 CONTENTS is nil. INFO is a plist holding contextual information."
3578 (let ((key (org-element-property :key keyword))
3579 (value (org-element-property :value keyword)))
3580 (cond
3581 ((string= key "LATEX") value)
3582 ((string= key "INDEX") (format "\\index{%s}" value))
3583 ((string= key "TARGET") nil ; FIXME
3584 ;; (format "\\label{%s}" (org-export-solidify-link-text value))
3586 ((string= key "toc")
3587 (let ((value (downcase value)))
3588 (cond
3589 ((string-match "\\<headlines\\>" value)
3590 (let ((depth (or (and (string-match "[0-9]+" value)
3591 (string-to-number (match-string 0 value)))
3592 (plist-get info :with-toc))))
3593 (when (wholenump depth) (org-e-odt-toc depth info))))
3594 ((string= "tables" value) "FIXME")
3595 ((string= "figures" value) "FIXME")
3596 ((string= "listings" value)
3597 (cond
3598 ;; At the moment, src blocks with a caption are wrapped
3599 ;; into a figure environment.
3600 (t "FIXME")))))))))
3603 ;;;; Latex Environment
3605 (defun org-e-odt-format-latex (latex-frag processing-type)
3606 (let* ((prefix (case processing-type
3607 (dvipng "ltxpng/")
3608 (mathml "ltxmathml/")))
3609 (cache-relpath
3610 (concat prefix (file-name-sans-extension
3611 (file-name-nondirectory (buffer-file-name)))))
3612 (cache-dir (file-name-directory (buffer-file-name )))
3613 (display-msg (case processing-type
3614 (dvipng "Creating LaTeX Image...")
3615 (mathml "Creating MathML snippet..."))))
3616 (with-temp-buffer
3617 (insert latex-frag)
3618 (org-format-latex cache-relpath cache-dir nil display-msg
3619 nil nil processing-type)
3620 (buffer-string))))
3622 (defun org-e-odt-latex-environment (latex-environment contents info)
3623 "Transcode a LATEX-ENVIRONMENT element from Org to HTML.
3624 CONTENTS is nil. INFO is a plist holding contextual information."
3625 (org-e-odt--wrap-label
3626 latex-environment
3627 (let* ((latex-frag
3628 (org-remove-indentation
3629 (org-element-property :value latex-environment)))
3630 (processing-type (plist-get info :LaTeX-fragments))
3631 (caption (org-element-property :caption latex-environment))
3632 (short-caption (and (cdr caption) (org-export-secondary-string
3633 (cdr caption) 'e-odt info)))
3634 (caption (and (car caption) (org-export-secondary-string
3635 (car caption) 'e-odt info)))
3636 (label (org-element-property :name latex-environment))
3637 (attr nil) ; FIXME
3638 (label (org-element-property :name latex-environment)))
3639 (cond
3640 ((member processing-type '(t mathjax))
3641 (let* ((formula-link (org-e-odt-format-latex latex-frag 'mathml)))
3642 (when (and formula-link
3643 (string-match "file:\\([^]]*\\)" formula-link))
3644 (org-e-odt-format-formula
3645 (match-string 1 formula-link) caption label attr))))
3646 ((equal processing-type 'dvipng)
3647 (let* ((formula-link (org-e-odt-format-latex
3648 latex-frag processing-type)))
3649 (when (and formula-link
3650 (string-match "file:\\([^]]*\\)" formula-link))
3651 (org-e-odt-format-image
3652 (match-string 1 formula-link) caption label attr "paragraph"
3653 "__DvipngImage__"))))
3654 (t latex-frag)))))
3657 ;;;; Latex Fragment
3659 (defun org-e-odt-latex-fragment (latex-fragment contents info)
3660 "Transcode a LATEX-FRAGMENT object from Org to HTML.
3661 CONTENTS is nil. INFO is a plist holding contextual information."
3662 (let* ((latex-frag (org-element-property :value latex-fragment))
3663 (processing-type (plist-get info :LaTeX-fragments)))
3664 (cond
3665 ((member processing-type '(t mathjax))
3666 (let* ((formula-link (org-e-odt-format-latex latex-frag 'mathml))
3667 (src (and formula-link
3668 (string-match "file:\\([^]]*\\)" formula-link)
3669 (match-string 1 formula-link))))
3670 (assert src)
3671 (org-e-odt-format-formula src)))
3672 ((equal processing-type 'dvipng)
3673 (let* ((formula-link (org-e-odt-format-latex latex-frag processing-type))
3674 (src (and formula-link
3675 (string-match "file:\\([^]]*\\)" formula-link)
3676 (match-string 1 formula-link))))
3677 (assert src)
3678 (org-e-odt-format-image src)))
3679 (t latex-frag))))
3682 ;;;; Line Break
3684 (defun org-e-odt-line-break (line-break contents info)
3685 "Transcode a LINE-BREAK object from Org to HTML.
3686 CONTENTS is nil. INFO is a plist holding contextual information."
3687 "<text:line-break/>\n")
3690 ;;;; Link
3692 (defun org-e-odt-link--inline-image (link desc info)
3693 "Return HTML code for an inline image.
3694 LINK is the link pointing to the inline image. INFO is a plist
3695 used as a communication channel."
3696 (let* ((type (org-element-property :type link))
3697 (raw-path (org-element-property :path link))
3698 (path (cond ((member type '("http" "https"))
3699 (concat type ":" raw-path))
3700 ((file-name-absolute-p raw-path)
3701 (expand-file-name raw-path))
3702 (t raw-path)))
3703 (parent (org-export-get-parent-paragraph link info))
3704 (caption (org-element-property :caption parent))
3705 (short-caption (and (cdr caption) (org-export-secondary-string
3706 (cdr caption) 'e-odt info)))
3707 (caption (and (car caption) (org-export-secondary-string
3708 (car caption) 'e-odt info)))
3709 (label (org-element-property :name parent))
3710 ;; Retrieve latex attributes from the element around.
3711 (attr (let ((raw-attr
3712 (mapconcat #'identity
3713 (org-element-property :attr_odt parent)
3714 " ")))
3715 (unless (string= raw-attr "") raw-attr))))
3716 ;; Now clear ATTR from any special keyword and set a default
3717 ;; value if nothing is left.
3718 (setq attr (if (not attr) "" (org-trim attr)))
3719 ;; Return proper string, depending on DISPOSITION.
3720 (org-e-odt-format-image
3721 path caption label attr (if (org-e-html-standalone-image-p link info)
3722 "paragraph" "as-char"))))
3724 (defvar org-e-odt-standalone-image-predicate
3725 (function (lambda (paragraph)
3726 (or (org-element-property :caption paragraph)
3727 (org-element-property :name paragraph)))))
3729 (defun org-e-odt-standalone-image-p (element info &optional predicate)
3730 "Test if ELEMENT is a standalone image for the purpose ODT export.
3731 INFO is a plist holding contextual information.
3733 Return non-nil, if ELEMENT is of type paragraph and it's sole
3734 content, save for whitespaces, is a link that qualifies as an
3735 inline image.
3737 Return non-nil, if ELEMENT is of type link and it's containing
3738 paragraph has no other content save for leading and trailing
3739 whitespaces.
3741 Return nil, otherwise.
3743 Bind `org-e-odt-standalone-image-predicate' to constrain
3744 paragraph further. For example, to check for only captioned
3745 standalone images, do the following.
3747 \(setq org-e-odt-standalone-image-predicate
3748 \(lambda \(paragraph\)
3749 \(org-element-property :caption paragraph\)\)\)
3751 (let ((paragraph (case (org-element-type element)
3752 (paragraph element)
3753 (link (and (org-export-inline-image-p
3754 element org-e-odt-inline-image-rules)
3755 (org-export-get-parent element info)))
3756 (t nil))))
3757 (when paragraph
3758 (assert (eq (org-element-type paragraph) 'paragraph))
3759 (when (or (not (and (boundp 'org-e-odt-standalone-image-predicate)
3760 (functionp org-e-odt-standalone-image-predicate)))
3761 (funcall org-e-odt-standalone-image-predicate paragraph))
3762 (let ((contents (org-element-contents paragraph)))
3763 (loop for x in contents
3764 with inline-image-count = 0
3765 always (cond
3766 ((eq (org-element-type x) 'plain-text)
3767 (not (org-string-nw-p x)))
3768 ((eq (org-element-type x) 'link)
3769 (when (org-export-inline-image-p
3770 x org-e-odt-inline-image-rules)
3771 (= (incf inline-image-count) 1)))
3772 (t nil))))))))
3774 (defun org-e-odt-link (link desc info)
3775 "Transcode a LINK object from Org to HTML.
3777 DESC is the description part of the link, or the empty string.
3778 INFO is a plist holding contextual information. See
3779 `org-export-data'."
3780 (let* ((type (org-element-property :type link))
3781 (raw-path (org-element-property :path link))
3782 ;; Ensure DESC really exists, or set it to nil.
3783 (desc (and (not (string= desc "")) desc))
3784 (imagep (org-export-inline-image-p
3785 link org-e-odt-inline-image-rules))
3786 (path (cond
3787 ((member type '("http" "https" "ftp" "mailto"))
3788 (concat type ":" raw-path))
3789 ((string= type "file")
3790 (when (string-match "\\(.+\\)::.+" raw-path)
3791 (setq raw-path (match-string 1 raw-path)))
3792 (if (file-name-absolute-p raw-path)
3793 (concat "file://" (expand-file-name raw-path))
3794 ;; TODO: Not implemented yet. Concat also:
3795 ;; (org-export-directory :HTML info)
3796 (concat "file://" raw-path)))
3797 (t raw-path)))
3798 protocol)
3799 (cond
3800 ;; Image file.
3801 ((and (not desc) (org-export-inline-image-p
3802 link org-e-odt-inline-image-rules))
3803 (org-e-odt-link--inline-image link desc info))
3804 ;; Radioed target: Target's name is obtained from original raw
3805 ;; link. Path is parsed and transcoded in order to have a proper
3806 ;; display of the contents.
3807 ((string= type "radio")
3808 (org-e-odt-format-internal-link
3809 (org-export-secondary-string
3810 (org-element-parse-secondary-string
3811 path (cdr (assq 'radio-target org-element-object-restrictions)))
3812 'e-odt info)
3813 (org-export-solidify-link-text path)))
3814 ;; Links pointing to an headline: Find destination and build
3815 ;; appropriate referencing command.
3816 ((member type '("custom-id" "fuzzy" "id"))
3817 (let ((destination (if (string= type "fuzzy")
3818 (org-export-resolve-fuzzy-link link info)
3819 (org-export-resolve-id-link link info))))
3820 (case (org-element-type destination)
3821 ;; Fuzzy link points nowhere.
3822 ('nil
3823 (org-e-odt-format-fontify
3824 (or desc (org-export-secondary-string
3825 (org-element-property :raw-link link)
3826 'e-odt info)) 'emphasis))
3827 ;; Fuzzy link points to an invisible target.
3828 (keyword nil)
3829 ;; LINK points to an headline. If headlines are numbered
3830 ;; and the link has no description, display headline's
3831 ;; number. Otherwise, display description or headline's
3832 ;; title.
3833 (headline
3834 (let* ((headline-no (org-export-get-headline-number destination info))
3835 (label (format "sec-%s" (mapconcat 'number-to-string
3836 headline-no "-")))
3837 (section-no (mapconcat 'number-to-string headline-no ".")))
3838 (setq desc
3839 (cond
3840 (desc desc)
3841 ((plist-get info :section-numbers) section-no)
3842 (t (org-export-secondary-string
3843 (org-element-property :title destination)
3844 'e-odt info))))
3845 (org-e-odt-format-internal-link desc label)))
3846 ;; Fuzzy link points to a target. Do as above.
3847 (otherwise
3848 ;; "__Table__"
3849 ;; "__Figure__"
3850 ;; "__MathFormula__"
3851 ;; "__DvipngImage__"
3852 (let ((path (org-export-solidify-link-text path)))
3853 (unless desc
3854 (setq number (cond
3855 ((org-e-odt-standalone-image-p destination info)
3856 (org-export-get-ordinal
3857 (assoc 'link (org-element-contents destination))
3858 info 'link 'org-e-odt-standalone-image-p))
3859 (t (org-export-get-ordinal destination info))))
3860 (setq desc (when number
3861 (if (atom number) (number-to-string number)
3862 (mapconcat 'number-to-string number ".")))))
3863 (let ((label path)
3864 (default-category
3865 (cond
3866 ((eq (org-element-type destination) 'table)
3867 "__Table__")
3868 ((org-e-odt-standalone-image-p destination info)
3869 "__Figure__")
3870 ((eq (org-element-type destination) 'latex-environment)
3871 ; FIXME: Check if it is
3872 ; acutally latex eqn.
3873 "__MathFormula__")
3874 ((eq (org-element-type destination) 'src-block)
3875 "__Listing__")
3876 (t (error "Handle enumeration of %S" destination)))))
3877 (org-e-odt-format-label-reference label default-category number)))))))
3878 ;; Coderef: replace link with the reference name or the
3879 ;; equivalent line number.
3880 ((string= type "coderef")
3881 (let* ((fmt (org-export-get-coderef-format path (or desc "%s")))
3882 (res (org-export-resolve-coderef path info))
3883 (org-e-odt-suppress-xref nil)
3884 (href (org-xml-format-href (concat "#coderef-" path))))
3885 (format fmt (org-e-odt-format-link res href))))
3886 ;; Link type is handled by a special function.
3887 ((functionp (setq protocol (nth 2 (assoc type org-link-protocols))))
3888 (funcall protocol (org-link-unescape path) desc 'html))
3889 ;; External link with a description part.
3890 ((and path desc) (org-e-odt-format-link desc path))
3891 ;; External link without a description part.
3892 (path (org-e-odt-format-link path path))
3893 ;; No path, only description. Try to do something useful.
3894 (t (org-e-odt-format-fontify desc 'emphasis)))))
3897 ;;;; Babel Call
3899 ;; Babel Calls are ignored.
3902 ;;;; Macro
3904 (defun org-e-odt-macro (macro contents info)
3905 "Transcode a MACRO element from Org to HTML.
3906 CONTENTS is nil. INFO is a plist holding contextual information."
3907 ;; Use available tools.
3908 (org-export-expand-macro macro info))
3911 ;;;; Paragraph
3913 (defun org-e-odt-paragraph (paragraph contents info)
3914 "Transcode a PARAGRAPH element from Org to HTML.
3915 CONTENTS is the contents of the paragraph, as a string. INFO is
3916 the plist used as a communication channel."
3917 (let* ((style nil) ; FIXME
3918 (class (cdr (assoc style '((footnote . "footnote")
3919 (verse . nil)))))
3920 (extra (if class (format " class=\"%s\"" class) ""))
3921 (parent (org-export-get-parent paragraph info))
3922 (parent-type (org-element-type parent))
3923 (style (case parent-type
3924 (quote-block 'quote)
3925 (center-block 'center)
3926 (footnote-definition 'footnote)
3927 (t nil))))
3928 (org-e-odt-format-stylized-paragraph style contents)))
3931 ;;;; Plain List
3933 (defun org-e-odt-plain-list (plain-list contents info)
3934 "Transcode a PLAIN-LIST element from Org to HTML.
3935 CONTENTS is the contents of the list. INFO is a plist holding
3936 contextual information."
3937 (let* (arg1 ;; FIXME
3938 (type (org-element-property :type plain-list))
3939 (attr (mapconcat #'identity
3940 (org-element-property :attr_odt plain-list)
3941 " ")))
3942 (org-e-odt--wrap-label
3943 plain-list (format "%s\n%s%s"
3944 (org-e-odt-begin-plain-list type)
3945 contents (org-e-odt-end-plain-list type)))))
3947 ;;;; Plain Text
3949 (defun org-e-odt-convert-special-strings (string)
3950 "Convert special characters in STRING to ODT."
3951 (let ((all org-e-odt-special-string-regexps)
3952 e a re rpl start)
3953 (while (setq a (pop all))
3954 (setq re (car a) rpl (cdr a) start 0)
3955 (while (string-match re string start)
3956 (setq string (replace-match rpl t nil string))))
3957 string))
3959 ;; (defun org-e-odt-encode-plain-text (s)
3960 ;; "Convert plain text characters to HTML equivalent.
3961 ;; Possible conversions are set in `org-export-html-protect-char-alist'."
3962 ;; (let ((cl org-e-odt-protect-char-alist) c)
3963 ;; (while (setq c (pop cl))
3964 ;; (let ((start 0))
3965 ;; (while (string-match (car c) s start)
3966 ;; (setq s (replace-match (cdr c) t t s)
3967 ;; start (1+ (match-beginning 0))))))
3968 ;; s))
3970 (defun org-e-odt-plain-text (text info)
3971 "Transcode a TEXT string from Org to HTML.
3972 TEXT is the string to transcode. INFO is a plist holding
3973 contextual information."
3974 (setq text (org-e-odt-encode-plain-text text t))
3975 ;; Protect %, #, &, $, ~, ^, _, { and }.
3976 ;; (while (string-match "\\([^\\]\\|^\\)\\([%$#&{}~^_]\\)" text)
3977 ;; (setq text
3978 ;; (replace-match (format "\\%s" (match-string 2 text)) nil t text 2)))
3979 ;; Protect \
3980 ;; (setq text (replace-regexp-in-string
3981 ;; "\\(?:[^\\]\\|^\\)\\(\\\\\\)\\(?:[^%$#&{}~^_\\]\\|$\\)"
3982 ;; "$\\backslash$" text nil t 1))
3983 ;; HTML into \HTML{} and TeX into \TeX{}.
3984 ;; (let ((case-fold-search nil)
3985 ;; (start 0))
3986 ;; (while (string-match "\\<\\(\\(?:La\\)?TeX\\)\\>" text start)
3987 ;; (setq text (replace-match
3988 ;; (format "\\%s{}" (match-string 1 text)) nil t text)
3989 ;; start (match-end 0))))
3990 ;; Handle quotation marks
3991 ;; (setq text (org-e-odt--quotation-marks text info))
3992 ;; Convert special strings.
3993 ;; (when (plist-get info :with-special-strings)
3994 ;; (while (string-match (regexp-quote "...") text)
3995 ;; (setq text (replace-match "\\ldots{}" nil t text))))
3996 (when (plist-get info :with-special-strings)
3997 (setq text (org-e-odt-convert-special-strings text)))
3998 ;; Handle break preservation if required.
3999 (when (plist-get info :preserve-breaks)
4000 (setq text (replace-regexp-in-string "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n"
4001 text)))
4002 ;; Return value.
4003 text)
4006 ;;;; Property Drawer
4008 (defun org-e-odt-property-drawer (property-drawer contents info)
4009 "Transcode a PROPERTY-DRAWER element from Org to HTML.
4010 CONTENTS is nil. INFO is a plist holding contextual
4011 information."
4012 ;; The property drawer isn't exported but we want separating blank
4013 ;; lines nonetheless.
4017 ;;;; Quote Block
4019 (defun org-e-odt-quote-block (quote-block contents info)
4020 "Transcode a QUOTE-BLOCK element from Org to HTML.
4021 CONTENTS holds the contents of the block. INFO is a plist
4022 holding contextual information."
4023 (org-e-odt--wrap-label quote-block contents))
4026 ;;;; Quote Section
4028 (defun org-e-odt-quote-section (quote-section contents info)
4029 "Transcode a QUOTE-SECTION element from Org to HTML.
4030 CONTENTS is nil. INFO is a plist holding contextual information."
4031 (let ((value (org-remove-indentation
4032 (org-element-property :value quote-section))))
4033 (when value (org-e-odt-format-source-code-or-example value nil))))
4036 ;;;; Section
4038 (defun org-e-odt-section (section contents info) ; FIXME
4039 "Transcode a SECTION element from Org to HTML.
4040 CONTENTS holds the contents of the section. INFO is a plist
4041 holding contextual information."
4042 contents)
4044 ;;;; Radio Target
4046 (defun org-e-odt-radio-target (radio-target text info)
4047 "Transcode a RADIO-TARGET object from Org to HTML.
4048 TEXT is the text of the target. INFO is a plist holding
4049 contextual information."
4050 (org-e-odt-format-anchor
4051 text (org-export-solidify-link-text
4052 (org-element-property :value radio-target))))
4055 ;;;; Special Block
4057 (defun org-e-odt-special-block (special-block contents info)
4058 "Transcode a SPECIAL-BLOCK element from Org to HTML.
4059 CONTENTS holds the contents of the block. INFO is a plist
4060 holding contextual information."
4061 (let ((type (downcase (org-element-property :type special-block))))
4062 (org-e-odt--wrap-label
4063 special-block
4064 (format "\\begin{%s}\n%s\\end{%s}" type contents type))))
4067 ;;;; Src Block
4069 (defun org-e-odt-src-block (src-block contents info)
4070 "Transcode a SRC-BLOCK element from Org to HTML.
4071 CONTENTS holds the contents of the item. INFO is a plist holding
4072 contextual information."
4073 (let* ((lang (org-element-property :language src-block))
4074 (caption (org-element-property :caption src-block))
4075 (short-caption (and (cdr caption) (org-export-secondary-string
4076 (cdr caption) 'e-odt info)))
4077 (caption (and (car caption) (org-export-secondary-string
4078 (car caption) 'e-odt info)))
4079 (label (org-element-property :name src-block)))
4080 ;; FIXME: Handle caption
4081 ;; caption-str (when caption)
4082 ;; (main (org-export-secondary-string (car caption) 'e-odt info))
4083 ;; (secondary (org-export-secondary-string (cdr caption) 'e-odt info))
4084 ;; (caption-str (org-e-odt--caption/label-string caption label info))
4085 (concat
4086 (org-e-odt-format-stylized-paragraph
4087 'listing (org-e-odt-format-entity-caption label caption "__Listing__"))
4088 (org-e-odt-format-code src-block info))))
4091 ;;;; Statistics Cookie
4093 (defun org-e-odt-statistics-cookie (statistics-cookie contents info)
4094 "Transcode a STATISTICS-COOKIE object from Org to HTML.
4095 CONTENTS is nil. INFO is a plist holding contextual information."
4096 (let ((cookie-value (org-element-property :value statistics-cookie)))
4097 (org-e-odt-format-fontify cookie-value 'code)))
4100 ;;;; Subscript
4102 (defun org-e-odt-subscript (subscript contents info)
4103 "Transcode a SUBSCRIPT object from Org to HTML.
4104 CONTENTS is the contents of the object. INFO is a plist holding
4105 contextual information."
4106 ;; (format (if (= (length contents) 1) "$_%s$" "$_{\\mathrm{%s}}$") contents)
4107 (org-e-odt-format-fontify contents 'subscript))
4110 ;;;; Superscript
4112 (defun org-e-odt-superscript (superscript contents info)
4113 "Transcode a SUPERSCRIPT object from Org to HTML.
4114 CONTENTS is the contents of the object. INFO is a plist holding
4115 contextual information."
4116 ;; (format (if (= (length contents) 1) "$^%s$" "$^{\\mathrm{%s}}$") contents)
4117 (org-e-odt-format-fontify contents 'superscript))
4120 ;;;; Table
4122 (defun org-e-odt-get-colwidth (c)
4123 (let ((col-widths (plist-get table-info :width)))
4124 (or (and org-lparse-table-is-styled (aref col-widths c)) 0)))
4126 (defun org-e-odt-table-row (fields &optional text-for-empty-fields)
4127 (incf org-e-odt-table-rownum)
4128 (let ((i -1))
4129 (org-e-odt-format-table-row
4130 (mapconcat
4131 (lambda (x)
4132 (when (and (string= x "") text-for-empty-fields)
4133 (setq x text-for-empty-fields))
4134 (incf i)
4135 (let ((horiz-span (org-e-odt-get-colwidth i)))
4136 (org-e-odt-format-table-cell
4137 x org-e-odt-table-rownum i horiz-span)))
4138 fields "\n"))))
4140 (defun org-e-odt-table-preamble ()
4141 (let ((colgroup-vector (plist-get table-info :column-groups)) ;; FIXME
4142 c gr colgropen preamble)
4143 (unless (aref colgroup-vector 0)
4144 (setf (aref colgroup-vector 0) 'start))
4145 (dotimes (c columns-number preamble)
4146 (setq gr (aref colgroup-vector c))
4147 (setq preamble
4148 (concat
4149 preamble
4150 (when (memq gr '(start start-end))
4151 (prog1 (if colgropen "</colgroup>\n<colgroup>" "\n<colgroup>")
4152 (setq colgropen t)))
4153 (let* ((colalign-vector (plist-get table-info :alignment)) ;; FIXME
4154 (align (cdr (assoc (aref colalign-vector c)
4155 '(("l" . "left")
4156 ("r" . "right")
4157 ("c" . "center")))))
4158 (alignspec (if (and (boundp 'org-e-odt-format-table-no-css)
4159 org-e-odt-format-table-no-css)
4160 " align=\"%s\"" " class=\"%s\""))
4161 (extra (format alignspec align)))
4162 (format "<col%s />" extra))
4163 (when (memq gr '(end start-end))
4164 (setq colgropen nil)
4165 "</colgroup>"))))
4166 (concat preamble (if colgropen "</colgroup>"))))
4168 (defun org-e-odt-list-table (lines caption label attributes
4169 &optional short-caption)
4170 (let* ((splice nil) head
4171 (org-e-odt-table-rownum -1)
4172 i (cnt 0)
4173 fields line
4174 org-e-odt-table-cur-rowgrp-is-hdr
4175 org-e-odt-table-rowgrp-open
4177 (org-lparse-table-style 'org-table)
4178 org-lparse-table-is-styled)
4179 (cond
4180 (splice
4181 (setq org-lparse-table-is-styled nil)
4182 (mapconcat 'org-e-odt-table-row lines "\n"))
4184 (setq org-lparse-table-is-styled t)
4186 (concat
4187 (org-e-odt-begin-table caption label attributes short-caption)
4188 ;; FIXME (org-e-odt-table-preamble)
4189 (org-e-odt-begin-table-rowgroup head)
4191 (mapconcat
4192 (lambda (line)
4193 (cond
4194 ((equal line 'hline) (org-e-odt-begin-table-rowgroup))
4195 (t (org-e-odt-table-row line))))
4196 lines "\n")
4198 (org-e-odt-end-table-rowgroup)
4199 (org-e-odt-end-table))))))
4201 (defun org-e-odt-transcode-table-row (row)
4202 (if (string-match org-table-hline-regexp row) 'hline
4203 (mapcar
4204 (lambda (cell)
4205 (org-export-secondary-string
4206 (let ((cell (org-element-parse-secondary-string
4207 cell
4208 (cdr (assq 'table org-element-string-restrictions)))))
4209 cell)
4210 'e-odt info))
4211 (org-split-string row "[ \t]*|[ \t]*"))))
4213 (defun org-e-odt-org-table-to-list-table (lines &optional splice)
4214 "Convert org-table to list-table.
4215 LINES is a list of the form (ROW1 ROW2 ROW3 ...) where each
4216 element is a `string' representing a single row of org-table.
4217 Thus each ROW has vertical separators \"|\" separating the table
4218 fields. A ROW could also be a row-group separator of the form
4219 \"|---...|\". Return a list of the form (ROW1 ROW2 ROW3
4220 ...). ROW could either be symbol `'hline' or a list of the
4221 form (FIELD1 FIELD2 FIELD3 ...) as appropriate."
4222 (let (line lines-1)
4223 (cond
4224 (splice
4225 (while (setq line (pop lines))
4226 (unless (string-match "^[ \t]*|-" line)
4227 (push (org-e-odt-transcode-table-row line) lines-1))))
4228 (t (while (setq line (pop lines))
4229 (cond
4230 ((string-match "^[ \t]*|-" line)
4231 (when lines (push 'hline lines-1)))
4232 (t (push (org-e-odt-transcode-table-row line) lines-1))))))
4233 (nreverse lines-1)))
4235 (defun org-e-odt-table-table (raw-table)
4236 (require 'table)
4237 (with-current-buffer (get-buffer-create "*org-export-table*")
4238 (erase-buffer))
4239 (let ((output (with-temp-buffer
4240 (insert raw-table)
4241 (goto-char 1)
4242 (re-search-forward "^[ \t]*|[^|]" nil t)
4243 (table-generate-source 'html "*org-export-table*")
4244 (with-current-buffer "*org-export-table*"
4245 (org-trim (buffer-string))))))
4246 (kill-buffer (get-buffer "*org-export-table*"))
4247 output))
4249 (defun org-e-odt-table (table contents info)
4250 "Transcode a TABLE element from Org to HTML.
4251 CONTENTS is nil. INFO is a plist holding contextual information."
4252 (let* ((caption (org-element-property :caption table))
4253 (short-caption (and (cdr caption) (org-export-secondary-string
4254 (cdr caption) 'e-odt info)))
4255 (caption (and (car caption) (org-export-secondary-string
4256 (car caption) 'e-odt info)))
4257 (label (org-element-property :name table))
4258 (attr (mapconcat #'identity
4259 (org-element-property :attr_odt table)
4260 " "))
4261 (raw-table (org-element-property :raw-table table))
4262 (table-type (org-element-property :type table)))
4263 (case table-type
4264 (table.el
4265 ;; (org-e-odt-table-table raw-table)
4268 (let* ((table-info (org-export-table-format-info raw-table))
4269 (columns-number (length (plist-get table-info :alignment)))
4270 (lines (org-split-string
4271 (org-export-clean-table
4272 raw-table (plist-get table-info :special-column-p)) "\n"))
4274 (genealogy (org-export-get-genealogy table info))
4275 (parent (car genealogy))
4276 (parent-type (org-element-type parent)))
4277 (org-e-odt-list-table
4278 (org-e-odt-org-table-to-list-table lines)
4279 caption label attr short-caption))))))
4282 ;;;; Target
4284 (defun org-e-odt-target (target contents info)
4285 "Transcode a TARGET object from Org to HTML.
4286 CONTENTS is nil. INFO is a plist holding contextual
4287 information."
4288 (org-e-odt-format-anchor
4289 "" (org-export-solidify-link-text (org-element-property :value target))))
4292 ;;;; Time-stamp
4294 (defun org-e-odt-time-stamp (time-stamp contents info)
4295 "Transcode a TIME-STAMP object from Org to HTML.
4296 CONTENTS is nil. INFO is a plist holding contextual
4297 information."
4298 ;; (let ((value (org-element-property :value time-stamp))
4299 ;; (type (org-element-property :type time-stamp))
4300 ;; (appt-type (org-element-property :appt-type time-stamp)))
4301 ;; (concat (cond ((eq appt-type 'scheduled)
4302 ;; (format "\\textbf{\\textsc{%s}} " org-scheduled-string))
4303 ;; ((eq appt-type 'deadline)
4304 ;; (format "\\textbf{\\textsc{%s}} " org-deadline-string))
4305 ;; ((eq appt-type 'closed)
4306 ;; (format "\\textbf{\\textsc{%s}} " org-closed-string)))
4307 ;; (cond ((memq type '(active active-range))
4308 ;; (format org-e-odt-active-timestamp-format value))
4309 ;; ((memq type '(inactive inactive-range))
4310 ;; (format org-e-odt-inactive-timestamp-format value))
4311 ;; (t
4312 ;; (format org-e-odt-diary-timestamp-format value)))))
4313 (let ((value (org-element-property :value time-stamp))
4314 (type (org-element-property :type time-stamp))
4315 (appt-type (org-element-property :appt-type time-stamp)))
4316 (setq value (org-export-secondary-string value 'e-odt info))
4317 (org-e-odt-format-fontify
4318 (concat
4319 (org-e-odt-format-fontify
4320 (cond ((eq appt-type 'scheduled) org-scheduled-string)
4321 ((eq appt-type 'deadline) org-deadline-string)
4322 ((eq appt-type 'closed) org-closed-string)) "timestamp-kwd")
4323 ;; FIXME: (org-translate-time value)
4324 (org-e-odt-format-fontify value "timestamp"))
4325 "timestamp-wrapper")))
4328 ;;;; Verbatim
4330 (defun org-e-odt-verbatim (verbatim contents info)
4331 "Transcode a VERBATIM object from Org to HTML.
4332 CONTENTS is nil. INFO is a plist used as a communication
4333 channel."
4334 (org-e-odt-emphasis
4335 verbatim (org-element-property :value verbatim) info))
4338 ;;;; Verse Block
4340 (defun org-e-odt-verse-block (verse-block contents info)
4341 "Transcode a VERSE-BLOCK element from Org to HTML.
4342 CONTENTS is nil. INFO is a plist holding contextual information."
4343 ;; Replace each newline character with line break. Also replace
4344 ;; each blank line with a line break.
4345 (setq contents (replace-regexp-in-string
4346 "^ *\\\\\\\\$" "<br/>\n"
4347 (replace-regexp-in-string
4348 "\\(\\\\\\\\\\)?[ \t]*\n" " <br/>\n"
4349 (org-remove-indentation
4350 (org-export-secondary-string
4351 (org-element-property :value verse-block)
4352 'e-odt info)))))
4354 ;; Replace each white space at beginning of a line with a
4355 ;; non-breaking space.
4356 (while (string-match "^[ \t]+" contents)
4357 (let ((new-str (org-e-odt-format-spaces
4358 (length (match-string 0 contents)))))
4359 (setq contents (replace-match new-str nil t contents))))
4361 (org-e-odt--wrap-label
4362 verse-block (format "<p class=\"verse\">\n%s</p>" contents)))
4367 ;;; Filter Functions
4369 ;;;; Filter Settings
4370 ;;;; Filters
4372 ;;; Interactive functions
4374 (defun org-e-odt-export-to-odt
4375 (&optional subtreep visible-only body-only ext-plist pub-dir)
4376 "Export current buffer to a HTML file.
4378 If narrowing is active in the current buffer, only export its
4379 narrowed part.
4381 If a region is active, export that region.
4383 When optional argument SUBTREEP is non-nil, export the sub-tree
4384 at point, extracting information from the headline properties
4385 first.
4387 When optional argument VISIBLE-ONLY is non-nil, don't export
4388 contents of hidden elements.
4390 When optional argument BODY-ONLY is non-nil, only write code
4391 between \"\\begin{document}\" and \"\\end{document}\".
4393 EXT-PLIST, when provided, is a property list with external
4394 parameters overriding Org default settings, but still inferior to
4395 file-local settings.
4397 When optional argument PUB-DIR is set, use it as the publishing
4398 directory.
4400 Return output file's name."
4401 (interactive)
4402 (setq debug-on-error t)
4404 ;; (let* ((outfile (org-export-output-file-name ".html" subtreep pub-dir))
4405 ;; (outfile "content.xml"))
4406 ;; (org-export-to-file
4407 ;; 'e-odt outfile subtreep visible-only body-only ext-plist))
4409 (let* ((outbuf (org-e-odt-init-outfile))
4410 (target (org-export-output-file-name ".odt" subtreep pub-dir))
4411 (outdir (file-name-directory (buffer-file-name outbuf)))
4412 (default-directory outdir))
4414 ;; FIXME: for copying embedded images
4415 (setq org-current-export-file
4416 (file-name-directory
4417 (org-export-output-file-name ".odt" subtreep nil)))
4419 (org-export-to-buffer
4420 'e-odt outbuf
4421 (memq 'subtree optns) (memq 'visible optns) (memq 'body optns))
4423 (setq org-lparse-opt-plist nil) ; FIXME
4424 (org-e-odt-save-as-outfile target ;; info
4428 ;; return outfile
4429 (if (not org-e-odt-preferred-output-format) target
4430 (or (org-e-odt-convert target org-e-odt-preferred-output-format)
4431 target))))
4437 (defun org-e-odt-reachable-p (in-fmt out-fmt)
4438 "Return non-nil if IN-FMT can be converted to OUT-FMT."
4439 (catch 'done
4440 (let ((reachable-formats (org-e-odt-do-reachable-formats in-fmt)))
4441 (dolist (e reachable-formats)
4442 (let ((out-fmt-spec (assoc out-fmt (cdr e))))
4443 (when out-fmt-spec
4444 (throw 'done (cons (car e) out-fmt-spec))))))))
4446 (defun org-e-odt-do-convert (in-file out-fmt &optional prefix-arg)
4447 "Workhorse routine for `org-e-odt-convert'."
4448 (require 'browse-url)
4449 (let* ((in-file (expand-file-name (or in-file buffer-file-name)))
4450 (dummy (or (file-readable-p in-file)
4451 (error "Cannot read %s" in-file)))
4452 (in-fmt (file-name-extension in-file))
4453 (out-fmt (or out-fmt (error "Output format unspecified")))
4454 (how (or (org-e-odt-reachable-p in-fmt out-fmt)
4455 (error "Cannot convert from %s format to %s format?"
4456 in-fmt out-fmt)))
4457 (convert-process (car how))
4458 (out-file (concat (file-name-sans-extension in-file) "."
4459 (nth 1 (or (cdr how) out-fmt))))
4460 (extra-options (or (nth 2 (cdr how)) ""))
4461 (out-dir (file-name-directory in-file))
4462 (cmd (format-spec convert-process
4463 `((?i . ,(shell-quote-argument in-file))
4464 (?I . ,(browse-url-file-url in-file))
4465 (?f . ,out-fmt)
4466 (?o . ,out-file)
4467 (?O . ,(browse-url-file-url out-file))
4468 (?d . , (shell-quote-argument out-dir))
4469 (?D . ,(browse-url-file-url out-dir))
4470 (?x . ,extra-options)))))
4471 (when (file-exists-p out-file)
4472 (delete-file out-file))
4474 (message "Executing %s" cmd)
4475 (let ((cmd-output (shell-command-to-string cmd)))
4476 (message "%s" cmd-output))
4478 (cond
4479 ((file-exists-p out-file)
4480 (message "Exported to %s" out-file)
4481 (when prefix-arg
4482 (message "Opening %s..." out-file)
4483 (org-open-file out-file))
4484 out-file)
4486 (message "Export to %s failed" out-file)
4487 nil))))
4489 (defun org-e-odt-do-reachable-formats (in-fmt)
4490 "Return verbose info about formats to which IN-FMT can be converted.
4491 Return a list where each element is of the
4492 form (CONVERTER-PROCESS . OUTPUT-FMT-ALIST). See
4493 `org-e-odt-convert-processes' for CONVERTER-PROCESS and see
4494 `org-e-odt-convert-capabilities' for OUTPUT-FMT-ALIST."
4495 (let* ((converter
4496 (and org-e-odt-convert-process
4497 (cadr (assoc-string org-e-odt-convert-process
4498 org-e-odt-convert-processes t))))
4499 (capabilities
4500 (and org-e-odt-convert-process
4501 (cadr (assoc-string org-e-odt-convert-process
4502 org-e-odt-convert-processes t))
4503 org-e-odt-convert-capabilities))
4504 reachable-formats)
4505 (when converter
4506 (dolist (c capabilities)
4507 (when (member in-fmt (nth 1 c))
4508 (push (cons converter (nth 2 c)) reachable-formats))))
4509 reachable-formats))
4511 (defun org-e-odt-reachable-formats (in-fmt)
4512 "Return list of formats to which IN-FMT can be converted.
4513 The list of the form (OUTPUT-FMT-1 OUTPUT-FMT-2 ...)."
4514 (let (l)
4515 (mapc (lambda (e) (add-to-list 'l e))
4516 (apply 'append (mapcar
4517 (lambda (e) (mapcar 'car (cdr e)))
4518 (org-e-odt-do-reachable-formats in-fmt))))
4521 (defun org-e-odt-convert-read-params ()
4522 "Return IN-FILE and OUT-FMT params for `org-e-odt-do-convert'.
4523 This is a helper routine for interactive use."
4524 (let* ((input (if (featurep 'ido) 'ido-completing-read 'completing-read))
4525 (in-file (read-file-name "File to be converted: "
4526 nil buffer-file-name t))
4527 (in-fmt (file-name-extension in-file))
4528 (out-fmt-choices (org-e-odt-reachable-formats in-fmt))
4529 (out-fmt
4530 (or (and out-fmt-choices
4531 (funcall input "Output format: "
4532 out-fmt-choices nil nil nil))
4533 (error
4534 "No known converter or no known output formats for %s files"
4535 in-fmt))))
4536 (list in-file out-fmt)))
4538 ;;;###autoload
4539 (defun org-e-odt-convert (&optional in-file out-fmt prefix-arg)
4540 "Convert IN-FILE to format OUT-FMT using a command line converter.
4541 IN-FILE is the file to be converted. If unspecified, it defaults
4542 to variable `buffer-file-name'. OUT-FMT is the desired output
4543 format. Use `org-e-odt-convert-process' as the converter.
4544 If PREFIX-ARG is non-nil then the newly converted file is opened
4545 using `org-open-file'."
4546 (interactive
4547 (append (org-e-odt-convert-read-params) current-prefix-arg))
4548 (org-e-odt-do-convert in-file out-fmt prefix-arg))
4550 ;;; FIXMES, TODOS, FOR REVIEW etc
4552 ;;;; org-format-table-html
4553 ;;;; org-format-org-table-html
4554 ;;;; org-format-table-table-html
4555 ;;;; org-table-number-fraction
4556 ;;;; org-table-number-regexp
4557 ;;;; org-e-odt-table-caption-above
4559 ;;;; org-whitespace
4560 ;;;; "<span style=\"visibility:hidden;\">%s</span>"
4561 ;;;; Remove display properties
4563 ;;;; org-e-odt-with-timestamp
4564 ;;;; org-e-odt-html-helper-timestamp
4566 ;;;; org-export-as-html-and-open
4567 ;;;; org-export-as-html-batch
4568 ;;;; org-export-as-html-to-buffer
4569 ;;;; org-replace-region-by-html
4570 ;;;; org-export-region-as-html
4571 ;;;; org-export-as-html
4573 ;;;; (org-export-directory :html opt-plist)
4574 ;;;; (plist-get opt-plist :html-extension)
4575 ;;;; org-e-odt-toplevel-hlevel
4576 ;;;; org-e-odt-coding-system
4577 ;;;; org-e-odt-coding-system
4578 ;;;; org-e-odt-inline-image-extensions
4579 ;;;; org-e-odt-protect-char-alist
4580 ;;;; org-e-odt-table-use-header-tags-for-first-column
4581 ;;;; org-e-odt-todo-kwd-class-prefix
4582 ;;;; org-e-odt-tag-class-prefix
4583 ;;;; org-e-odt-footnote-separator
4586 ;;; Library Initializations
4588 (mapc
4589 (lambda (desc)
4590 ;; Let Org open all OpenDocument files using system-registered app
4591 (add-to-list 'org-file-apps
4592 (cons (concat "\\." (car desc) "\\'") 'system))
4593 ;; Let Emacs open all OpenDocument files in archive mode
4594 (add-to-list 'auto-mode-alist
4595 (cons (concat "\\." (car desc) "\\'") 'archive-mode)))
4596 org-e-odt-file-extensions)
4598 (provide 'org-e-odt)
4600 ;;; org-e-odt.el ends here