Require htmlfontify.el only if emacs-version is greater than 23.2.
[org-mode.git] / lisp / org-odt.el
blob558445bab7ab2d0d2a10865910a38990755ff288
1 ;;; org-odt.el --- OpenDocumentText export for Org-mode
3 ;; Copyright (C) 2010-2011 Free Software Foundation, Inc.
5 ;; Author: Jambunathan K <kjambunathan at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp
7 ;; Homepage: http://orgmode.org
8 ;;
9 ;; This file is not (yet) part of GNU Emacs.
10 ;; However, it is distributed under the same license.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;;; Commentary:
28 ;;; Code:
29 (eval-when-compile
30 (require 'cl)
31 ;; htmlfontify.el was introduce in Emacs 23.2
32 (when (>= (string-to-number emacs-version) 23.2)
33 (require 'htmlfontify)))
34 (require 'org-lparse)
36 (defgroup org-export-odt nil
37 "Options specific for ODT export of Org-mode files."
38 :tag "Org Export ODT"
39 :group 'org-export)
41 (defun org-odt-end-export ()
42 (org-odt-fixup-label-references)
44 ;; remove empty paragraphs
45 (goto-char (point-min))
46 (while (re-search-forward
47 "<text:p\\( text:style-name=\"Text_20_body\"\\)?>[ \r\n\t]*</text:p>"
48 nil t)
49 (replace-match ""))
50 (goto-char (point-min))
52 ;; Convert whitespace place holders
53 (goto-char (point-min))
54 (let (beg end n)
55 (while (setq beg (next-single-property-change (point) 'org-whitespace))
56 (setq n (get-text-property beg 'org-whitespace)
57 end (next-single-property-change beg 'org-whitespace))
58 (goto-char beg)
59 (delete-region beg end)
60 (insert (format "<span style=\"visibility:hidden;\">%s</span>"
61 (make-string n ?x)))))
63 ;; Remove empty lines at the beginning of the file.
64 (goto-char (point-min))
65 (when (looking-at "\\s-+\n") (replace-match ""))
67 ;; Remove display properties
68 (remove-text-properties (point-min) (point-max) '(display t)))
70 (defvar org-odt-suppress-xref nil)
71 (defconst org-export-odt-special-string-regexps
72 '(("\\\\-" . "&#x00ad;\\1") ; shy
73 ("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
74 ("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
75 ("\\.\\.\\." . "&#x2026;")) ; hellip
76 "Regular expressions for special string conversion.")
78 (defconst org-odt-lib-dir (file-name-directory load-file-name))
79 (defconst org-odt-styles-dir
80 (let* ((styles-dir1 (expand-file-name "../etc/styles/" org-odt-lib-dir))
81 (styles-dir2 (expand-file-name "./etc/styles/" org-odt-lib-dir))
82 (styles-dir
83 (catch 'styles-dir
84 (mapc (lambda (styles-dir)
85 (when (and (file-readable-p
86 (expand-file-name
87 "OrgOdtContentTemplate.xml" styles-dir))
88 (file-readable-p
89 (expand-file-name
90 "OrgOdtStyles.xml" styles-dir)))
91 (throw 'styles-dir styles-dir)))
92 (list styles-dir1 styles-dir2))
93 nil)))
94 (unless styles-dir
95 (error "Cannot find factory styles file. Check package dir layout"))
96 styles-dir)
97 "Directory that holds auxiliary XML files used by the ODT exporter.
99 This directory contains the following XML files -
100 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
101 XML files are used as the default values of
102 `org-export-odt-styles-file' and
103 `org-export-odt-content-template-file'.")
105 (defcustom org-export-odt-schema-dir
106 (let ((schema-dir (expand-file-name
107 "../contrib/odt/etc/schema/" org-odt-lib-dir)))
108 (if (and (file-readable-p
109 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir))
110 (file-readable-p
111 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir))
112 (file-readable-p
113 (expand-file-name "schemas.xml" schema-dir)))
114 schema-dir
115 (prog1 nil (message "Unable to locate OpenDocument schema files."))))
116 "Directory that contains OpenDocument schema files.
118 This directory contains rnc files for OpenDocument schema. It
119 also contains a \"schemas.xml\" that can be added to
120 `rng-schema-locating-files' for auto validation of OpenDocument
121 XML files. See also `rng-nxml-auto-validate-flag'."
122 :type '(choice
123 (const :tag "Not set" nil)
124 (directory :tag "Schema directory"))
125 :group 'org-export-odt
126 :set
127 (lambda (var value)
128 "Set `org-export-odt-schema-dir'.
129 Also add it to `rng-schema-locating-files'."
130 (let ((schema-dir value))
131 (set var
132 (if (and
133 (file-readable-p
134 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir))
135 (file-readable-p
136 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir))
137 (file-readable-p
138 (expand-file-name "schemas.xml" schema-dir)))
139 schema-dir
140 (prog1 nil
141 (message "Warning (org-odt): Unable to locate OpenDocument schema files.")))))
142 (when org-export-odt-schema-dir
143 (eval-after-load 'rng-loc
144 '(add-to-list 'rng-schema-locating-files
145 (expand-file-name "schemas.xml"
146 org-export-odt-schema-dir))))))
148 (defvar org-odt-file-extensions
149 '(("odt" . "OpenDocument Text")
150 ("ott" . "OpenDocument Text Template")
151 ("odm" . "OpenDocument Master Document")
152 ("ods" . "OpenDocument Spreadsheet")
153 ("ots" . "OpenDocument Spreadsheet Template")
154 ("odg" . "OpenDocument Drawing (Graphics)")
155 ("otg" . "OpenDocument Drawing Template")
156 ("odp" . "OpenDocument Presentation")
157 ("otp" . "OpenDocument Presentation Template")
158 ("odi" . "OpenDocument Image")
159 ("odf" . "OpenDocument Formula")
160 ("odc" . "OpenDocument Chart")))
162 (mapc
163 (lambda (desc)
164 ;; Let Org open all OpenDocument files using system-registered app
165 (add-to-list 'org-file-apps
166 (cons (concat "\\." (car desc) "\\'") 'system))
167 ;; Let Emacs open all OpenDocument files in archive mode
168 (add-to-list 'auto-mode-alist
169 (cons (concat "\\." (car desc) "\\'") 'archive-mode)))
170 org-odt-file-extensions)
172 ;; register the odt exporter with the pre-processor
173 (add-to-list 'org-export-backends 'odt)
175 ;; register the odt exporter with org-lparse library
176 (org-lparse-register-backend 'odt)
178 (defun org-odt-unload-function ()
179 (org-lparse-unregister-backend 'odt)
180 (remove-hook 'org-export-preprocess-after-blockquote-hook
181 'org-export-odt-preprocess-latex-fragments)
182 nil)
184 (defcustom org-export-odt-content-template-file nil
185 "Template file for \"content.xml\".
186 The exporter embeds the exported content just before
187 \"</office:text>\" element.
189 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
190 under `org-odt-styles-dir' is used."
191 :type 'file
192 :group 'org-export-odt)
194 (defcustom org-export-odt-styles-file nil
195 "Default styles file for use with ODT export.
196 Valid values are one of:
197 1. nil
198 2. path to a styles.xml file
199 3. path to a *.odt or a *.ott file
200 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
201 ...))
203 In case of option 1, an in-built styles.xml is used. See
204 `org-odt-styles-dir' for more information.
206 In case of option 3, the specified file is unzipped and the
207 styles.xml embedded therein is used.
209 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
210 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
211 generated odt file. Use relative path for specifying the
212 FILE-MEMBERS. styles.xml must be specified as one of the
213 FILE-MEMBERS.
215 Use options 1, 2 or 3 only if styles.xml alone suffices for
216 achieving the desired formatting. Use option 4, if the styles.xml
217 references additional files like header and footer images for
218 achieving the desired formattting.
220 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
221 a per-file basis. For example,
223 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
224 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
225 :group 'org-export-odt
226 :type
227 '(choice
228 (const :tag "Factory settings" nil)
229 (file :must-match t :tag "styles.xml")
230 (file :must-match t :tag "ODT or OTT file")
231 (list :tag "ODT or OTT file + Members"
232 (file :must-match t :tag "ODF Text or Text Template file")
233 (cons :tag "Members"
234 (file :tag " Member" "styles.xml")
235 (repeat (file :tag "Member"))))))
237 (eval-after-load 'org-exp
238 '(add-to-list 'org-export-inbuffer-options-extra
239 '("ODT_STYLES_FILE" :odt-styles-file)))
241 (defconst org-export-odt-tmpdir-prefix "%s-")
242 (defconst org-export-odt-bookmark-prefix "OrgXref.")
244 (defvar org-export-odt-embed-images t
245 "Should the images be copied in to the odt file or just linked?")
247 (defvar org-export-odt-inline-images 'maybe) ; counterpart of
248 ; `org-export-html-inline-images'
250 (defcustom org-export-odt-inline-image-extensions
251 '("png" "jpeg" "jpg" "gif")
252 "Extensions of image files that can be inlined into HTML."
253 :type '(repeat (string :tag "Extension"))
254 :group 'org-export-odt)
256 (defcustom org-export-odt-pixels-per-inch display-pixels-per-inch
257 ;; FIXME add docstring
259 :type 'float
260 :group 'org-export-odt)
262 (defcustom org-export-odt-create-custom-styles-for-srcblocks t
263 "Whether custom styles for colorized source blocks be automatically created.
264 When this option is turned on, the exporter creates custom styles
265 for source blocks based on the advice of `htmlfontify'. Creation
266 of custom styles happen as part of `org-odt-hfy-face-to-css'.
268 When this option is turned off exporter does not create such
269 styles.
271 Use the latter option if you do not want the custom styles to be
272 based on your current display settings. It is necessary that the
273 styles.xml already contains needed styles for colorizing to work.
275 This variable is effective only if
276 `org-export-odt-fontify-srcblocks' is turned on."
277 :group 'org-export-odt
278 :type 'boolean)
280 (defvar org-export-odt-default-org-styles-alist
281 '((paragraph . ((default . "Text_20_body")
282 (fixedwidth . "OrgFixedWidthBlock")
283 (verse . "OrgVerse")
284 (quote . "Quotations")
285 (blockquote . "Quotations")
286 (center . "OrgCenter")
287 (left . "OrgLeft")
288 (right . "OrgRight")
289 (title . "Heading_20_1.title")
290 (footnote . "Footnote")
291 (src . "OrgSrcBlock")
292 (illustration . "Illustration")
293 (table . "Table")
294 (definition-term . "Text_20_body_20_bold")
295 (horizontal-line . "Horizontal_20_Line")))
296 (character . ((bold . "Bold")
297 (emphasis . "Emphasis")
298 (code . "OrgCode")
299 (verbatim . "OrgCode")
300 (strike . "Strikethrough")
301 (underline . "Underline")
302 (subscript . "OrgSubscript")
303 (superscript . "OrgSuperscript")))
304 (list . ((ordered . "OrgNumberedList")
305 (unordered . "OrgBulletedList")
306 (description . "OrgDescriptionList"))))
307 "Default styles for various entities.")
309 (defvar org-export-odt-org-styles-alist org-export-odt-default-org-styles-alist)
310 (defun org-odt-get-style-name-for-entity (category &optional entity)
311 (let ((entity (or entity 'default)))
313 (cdr (assoc entity (cdr (assoc category
314 org-export-odt-org-styles-alist))))
315 (cdr (assoc entity (cdr (assoc category
316 org-export-odt-default-org-styles-alist))))
317 (error "Cannot determine style name for entity %s of type %s"
318 entity category))))
320 (defcustom org-export-odt-preferred-output-format nil
321 "Automatically post-process to this format after exporting to \"odt\".
322 Interactive commands `org-export-as-odt' and
323 `org-export-as-odt-and-open' export first to \"odt\" format and
324 then use `org-export-odt-convert-process' to convert the
325 resulting document to this format. During customization of this
326 variable, the list of valid values are populated based on
327 `org-export-odt-convert-capabilities'."
328 :group 'org-export-odt
329 :type '(choice :convert-widget
330 (lambda (w)
331 (apply 'widget-convert (widget-type w)
332 (eval (car (widget-get w :args)))))
333 `((const :tag "None" nil)
334 ,@(mapcar (lambda (c)
335 `(const :tag ,c ,c))
336 (org-lparse-reachable-formats "odt")))))
338 ;;;###autoload
339 (defun org-export-as-odt-and-open (arg)
340 "Export the outline as ODT and immediately open it with a browser.
341 If there is an active region, export only the region.
342 The prefix ARG specifies how many levels of the outline should become
343 headlines. The default is 3. Lower levels will become bulleted lists."
344 (interactive "P")
345 (org-lparse-and-open
346 (or org-export-odt-preferred-output-format "odt") "odt" arg))
348 ;;;###autoload
349 (defun org-export-as-odt-batch ()
350 "Call the function `org-lparse-batch'.
351 This function can be used in batch processing as:
352 emacs --batch
353 --load=$HOME/lib/emacs/org.el
354 --eval \"(setq org-export-headline-levels 2)\"
355 --visit=MyFile --funcall org-export-as-odt-batch"
356 (org-lparse-batch "odt"))
358 ;;;###autoload
359 (defun org-export-as-odt-to-buffer (arg)
360 "Call `org-lparse-odt` with output to a temporary buffer.
361 No file is created. The prefix ARG is passed through to `org-lparse-to-buffer'."
362 (interactive "P")
363 (org-lparse-to-buffer "odt" arg))
365 ;;;###autoload
366 (defun org-replace-region-by-odt (beg end)
367 "Assume the current region has org-mode syntax, and convert it to ODT.
368 This can be used in any buffer. For example, you could write an
369 itemized list in org-mode syntax in an ODT buffer and then use this
370 command to convert it."
371 (interactive "r")
372 (org-replace-region-by "odt" beg end))
374 ;;;###autoload
375 (defun org-export-region-as-odt (beg end &optional body-only buffer)
376 "Convert region from BEG to END in org-mode buffer to ODT.
377 If prefix arg BODY-ONLY is set, omit file header, footer, and table of
378 contents, and only produce the region of converted text, useful for
379 cut-and-paste operations.
380 If BUFFER is a buffer or a string, use/create that buffer as a target
381 of the converted ODT. If BUFFER is the symbol `string', return the
382 produced ODT as a string and leave not buffer behind. For example,
383 a Lisp program could call this function in the following way:
385 (setq odt (org-export-region-as-odt beg end t 'string))
387 When called interactively, the output buffer is selected, and shown
388 in a window. A non-interactive call will only return the buffer."
389 (interactive "r\nP")
390 (org-lparse-region "odt" beg end body-only buffer))
392 ;;; org-export-as-odt
393 ;;;###autoload
394 (defun org-export-as-odt (arg &optional hidden ext-plist
395 to-buffer body-only pub-dir)
396 "Export the outline as a OpenDocumentText file.
397 If there is an active region, export only the region. The prefix
398 ARG specifies how many levels of the outline should become
399 headlines. The default is 3. Lower levels will become bulleted
400 lists. HIDDEN is obsolete and does nothing.
401 EXT-PLIST is a property list with external parameters overriding
402 org-mode's default settings, but still inferior to file-local
403 settings. When TO-BUFFER is non-nil, create a buffer with that
404 name and export to that buffer. If TO-BUFFER is the symbol
405 `string', don't leave any buffer behind but just return the
406 resulting XML as a string. When BODY-ONLY is set, don't produce
407 the file header and footer, simply return the content of
408 <body>...</body>, without even the body tags themselves. When
409 PUB-DIR is set, use this as the publishing directory."
410 (interactive "P")
411 (org-lparse (or org-export-odt-preferred-output-format "odt")
412 "odt" arg hidden ext-plist to-buffer body-only pub-dir))
414 (defvar org-odt-entity-control-callbacks-alist
415 `((EXPORT
416 . (org-odt-begin-export org-odt-end-export))
417 (DOCUMENT-CONTENT
418 . (org-odt-begin-document-content org-odt-end-document-content))
419 (DOCUMENT-BODY
420 . (org-odt-begin-document-body org-odt-end-document-body))
421 (TOC
422 . (org-odt-begin-toc org-odt-end-toc))
423 (ENVIRONMENT
424 . (org-odt-begin-environment org-odt-end-environment))
425 (FOOTNOTE-DEFINITION
426 . (org-odt-begin-footnote-definition org-odt-end-footnote-definition))
427 (TABLE
428 . (org-odt-begin-table org-odt-end-table))
429 (TABLE-ROWGROUP
430 . (org-odt-begin-table-rowgroup org-odt-end-table-rowgroup))
431 (LIST
432 . (org-odt-begin-list org-odt-end-list))
433 (LIST-ITEM
434 . (org-odt-begin-list-item org-odt-end-list-item))
435 (OUTLINE
436 . (org-odt-begin-outline org-odt-end-outline))
437 (OUTLINE-TEXT
438 . (org-odt-begin-outline-text org-odt-end-outline-text))
439 (PARAGRAPH
440 . (org-odt-begin-paragraph org-odt-end-paragraph)))
443 (defvar org-odt-entity-format-callbacks-alist
444 `((EXTRA-TARGETS . org-lparse-format-extra-targets)
445 (ORG-TAGS . org-lparse-format-org-tags)
446 (SECTION-NUMBER . org-lparse-format-section-number)
447 (HEADLINE . org-odt-format-headline)
448 (TOC-ENTRY . org-odt-format-toc-entry)
449 (TOC-ITEM . org-odt-format-toc-item)
450 (TAGS . org-odt-format-tags)
451 (SPACES . org-odt-format-spaces)
452 (TABS . org-odt-format-tabs)
453 (LINE-BREAK . org-odt-format-line-break)
454 (FONTIFY . org-odt-format-fontify)
455 (TODO . org-lparse-format-todo)
456 (LINK . org-odt-format-link)
457 (INLINE-IMAGE . org-odt-format-inline-image)
458 (ORG-LINK . org-odt-format-org-link)
459 (HEADING . org-odt-format-heading)
460 (ANCHOR . org-odt-format-anchor)
461 (TABLE . org-lparse-format-table)
462 (TABLE-ROW . org-odt-format-table-row)
463 (TABLE-CELL . org-odt-format-table-cell)
464 (FOOTNOTES-SECTION . ignore)
465 (FOOTNOTE-REFERENCE . org-odt-format-footnote-reference)
466 (HORIZONTAL-LINE . org-odt-format-horizontal-line)
467 (COMMENT . org-odt-format-comment)
468 (LINE . org-odt-format-line)
469 (ORG-ENTITY . org-odt-format-org-entity))
472 ;;;_. callbacks
473 ;;;_. control callbacks
474 ;;;_ , document body
475 (defun org-odt-begin-office-body ()
476 ;; automatic styles
477 (insert-file-contents
478 (or org-export-odt-content-template-file
479 (expand-file-name "OrgOdtContentTemplate.xml"
480 org-odt-styles-dir)))
481 (goto-char (point-min))
482 (re-search-forward "</office:text>" nil nil)
483 (delete-region (match-beginning 0) (point-max)))
485 ;; Following variable is let bound when `org-do-lparse' is in
486 ;; progress. See org-html.el.
487 (defvar org-lparse-toc)
488 (defun org-odt-begin-document-body (opt-plist)
489 (org-odt-begin-office-body)
490 (let ((title (plist-get opt-plist :title)))
491 (when title
492 (insert
493 (org-odt-format-stylized-paragraph 'title title))))
495 ;; insert toc
496 (when org-lparse-toc
497 (insert "\n" org-lparse-toc "\n")))
499 (defvar org-lparse-body-only) ; let bound during org-do-lparse
500 (defvar org-lparse-to-buffer) ; let bound during org-do-lparse
501 (defun org-odt-end-document-body (opt-plist)
502 (unless org-lparse-body-only
503 (org-lparse-insert-tag "</office:text>")
504 (org-lparse-insert-tag "</office:body>")))
506 (defun org-odt-begin-document-content (opt-plist)
507 (ignore))
509 (defun org-odt-end-document-content ()
510 (org-lparse-insert-tag "</office:document-content>"))
512 (defun org-odt-begin-outline (level1 snumber title tags
513 target extra-targets class)
514 (org-lparse-insert
515 'HEADING (org-lparse-format
516 'HEADLINE title extra-targets tags snumber level1)
517 level1 target))
519 (defun org-odt-end-outline ()
520 (ignore))
522 (defun org-odt-begin-outline-text (level1 snumber class)
523 (ignore))
525 (defun org-odt-end-outline-text ()
526 (ignore))
528 (defun org-odt-begin-paragraph (&optional style)
529 (org-lparse-insert-tag
530 "<text:p%s>" (org-odt-get-extra-attrs-for-paragraph-style style)))
532 (defun org-odt-end-paragraph ()
533 (org-lparse-insert-tag "</text:p>"))
535 (defun org-odt-get-extra-attrs-for-paragraph-style (style)
536 (let (style-name)
537 (setq style-name
538 (cond
539 ((stringp style) style)
540 ((symbolp style) (org-odt-get-style-name-for-entity
541 'paragraph style))))
542 (unless style-name
543 (error "Don't know how to handle paragraph style %s" style))
544 (format " text:style-name=\"%s\"" style-name)))
546 (defun org-odt-format-stylized-paragraph (style text)
547 (org-odt-format-tags
548 '("<text:p%s>" . "</text:p>") text
549 (org-odt-get-extra-attrs-for-paragraph-style style)))
551 (defvar org-lparse-opt-plist) ; bound during org-do-lparse
552 (defun org-odt-format-author (&optional author)
553 (when (setq author (or author (plist-get org-lparse-opt-plist :author)))
554 (org-odt-format-tags '("<dc:creator>" . "</dc:creator>") author)))
556 (defun org-odt-iso-date-from-org-timestamp (&optional org-ts)
557 (save-match-data
558 (let* ((time
559 (and (stringp org-ts)
560 (string-match org-ts-regexp0 org-ts)
561 (apply 'encode-time
562 (org-fix-decoded-time
563 (org-parse-time-string (match-string 0 org-ts) t)))))
564 (date (format-time-string "%Y-%m-%dT%H:%M:%S%z" time)))
565 (format "%s:%s" (substring date 0 -2) (substring date -2)))))
567 (defun org-odt-begin-annotation (&optional author date)
568 (org-lparse-insert-tag "<office:annotation>")
569 (when (setq author (org-odt-format-author author))
570 (insert author))
571 (insert (org-odt-format-tags
572 '("<dc:date>" . "</dc:date>")
573 (org-odt-iso-date-from-org-timestamp
574 (or date (plist-get org-lparse-opt-plist :date)))))
575 (org-lparse-begin-paragraph))
577 (defun org-odt-end-annotation ()
578 (org-lparse-insert-tag "</office:annotation>"))
580 (defun org-odt-begin-environment (style env-options-plist)
581 (case style
582 (annotation
583 (org-lparse-stash-save-paragraph-state)
584 (org-odt-begin-annotation (plist-get env-options-plist 'author)
585 (plist-get env-options-plist 'date)))
586 ((blockquote verse center quote)
587 (org-lparse-begin-paragraph style)
588 (list))
589 ((fixedwidth native)
590 (org-lparse-end-paragraph)
591 (list))
592 (t (error "Unknown environment %s" style))))
594 (defun org-odt-end-environment (style env-options-plist)
595 (case style
596 (annotation
597 (org-lparse-end-paragraph)
598 (org-odt-end-annotation)
599 (org-lparse-stash-pop-paragraph-state))
600 ((blockquote verse center quote)
601 (org-lparse-end-paragraph)
602 (list))
603 ((fixedwidth native)
604 (org-lparse-begin-paragraph)
605 (list))
606 (t (error "Unknown environment %s" style))))
608 (defvar org-lparse-list-level) ; dynamically bound in org-do-lparse
609 (defun org-odt-begin-list (ltype)
610 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
611 ltype))
612 (let* ((style-name (org-odt-get-style-name-for-entity 'list ltype))
613 (extra (concat (when (= org-lparse-list-level 1)
614 " text:continue-numbering=\"false\"")
615 (when style-name
616 (format " text:style-name=\"%s\"" style-name)))))
617 (case ltype
618 ((ordered unordered description)
619 (org-lparse-end-paragraph)
620 (org-lparse-insert-tag "<text:list%s>" extra))
621 (t (error "Unknown list type: %s" ltype)))))
623 (defun org-odt-end-list (ltype)
624 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
625 ltype))
626 (if ltype
627 (org-lparse-insert-tag "</text:list>")
628 (error "Unknown list type: %s" ltype)))
630 (defun org-odt-begin-list-item (ltype &optional arg headline)
631 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
632 ltype))
633 (case ltype
634 (ordered
635 (assert (not headline) t)
636 (let* ((counter arg) (extra ""))
637 (org-lparse-insert-tag "<text:list-item>")
638 (org-lparse-begin-paragraph)))
639 (unordered
640 (let* ((id arg) (extra ""))
641 (org-lparse-insert-tag "<text:list-item>")
642 (org-lparse-begin-paragraph)
643 (insert (if headline (org-odt-format-target headline id)
644 (org-odt-format-bookmark "" id)))))
645 (description
646 (assert (not headline) t)
647 (let ((term (or arg "(no term)")))
648 (insert
649 (org-odt-format-tags
650 '("<text:list-item>" . "</text:list-item>")
651 (org-odt-format-stylized-paragraph 'definition-term term)))
652 (org-lparse-begin-list-item 'unordered)
653 (org-lparse-begin-list 'description)
654 (org-lparse-begin-list-item 'unordered)))
655 (t (error "Unknown list type"))))
657 (defun org-odt-end-list-item (ltype)
658 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
659 ltype))
660 (case ltype
661 ((ordered unordered)
662 (org-lparse-insert-tag "</text:list-item>"))
663 (description
664 (org-lparse-end-list-item-1)
665 (org-lparse-end-list 'description)
666 (org-lparse-end-list-item-1))
667 (t (error "Unknown list type"))))
669 ;; Following variables are let bound when table emission is in
670 ;; progress. See org-lparse.el.
671 (defvar org-lparse-table-begin-marker)
672 (defvar org-lparse-table-ncols)
673 (defvar org-lparse-table-rowgrp-open)
674 (defvar org-lparse-table-rownum)
675 (defvar org-lparse-table-cur-rowgrp-is-hdr)
676 (defvar org-lparse-table-is-styled)
677 (defvar org-lparse-table-rowgrp-info)
678 (defvar org-lparse-table-colalign-vector)
680 (defvar org-odt-table-style nil
681 "Table style specified by \"#+ATTR_ODT: <style-name>\" line.
682 This is set during `org-odt-begin-table'.")
684 (defvar org-odt-table-style-spec nil
685 "Entry for `org-odt-table-style' in `org-export-odt-table-styles'.")
687 (defcustom org-export-odt-table-styles
688 '(("OrgEquation" "OrgEquation"
689 ((use-first-column-styles . t)
690 (use-last-column-styles . t))))
691 "Specify how Table Styles should be derived from a Table Template.
692 This is a list where each element is of the
693 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
695 TABLE-STYLE-NAME is the style associated with the table through
696 `org-odt-table-style'.
698 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
699 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
700 below) that is included in
701 `org-export-odt-content-template-file'.
703 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
704 \"TableCell\"
705 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
706 \"TableParagraph\"
707 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
708 \"FirstRow\" | \"LastRow\" |
709 \"EvenRow\" | \"OddRow\" |
710 \"EvenColumn\" | \"OddColumn\" | \"\"
711 where \"+\" above denotes string concatenation.
713 TABLE-CELL-OPTIONS is an alist where each element is of the
714 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
715 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
716 `use-last-row-styles' |
717 `use-first-column-styles' |
718 `use-last-column-styles' |
719 `use-banding-rows-styles' |
720 `use-banding-columns-styles' |
721 `use-first-row-styles'
722 ON-OR-OFF := `t' | `nil'
724 For example, with the following configuration
726 \(setq org-export-odt-table-styles
727 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
728 \(\(use-first-row-styles . t\)
729 \(use-first-column-styles . t\)\)\)
730 \(\"TableWithHeaderColumns\" \"Custom\"
731 \(\(use-first-column-styles . t\)\)\)\)\)
733 1. A table associated with \"TableWithHeaderRowsAndColumns\"
734 style will use the following table-cell styles -
735 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
736 \"CustomTableCell\" and the following paragraph styles
737 \"CustomFirstRowTableParagraph\",
738 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
739 as appropriate.
741 2. A table associated with \"TableWithHeaderColumns\" style will
742 use the following table-cell styles -
743 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
744 following paragraph styles
745 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
746 as appropriate..
748 Note that TABLE-TEMPLATE-NAME corresponds to the
749 \"<table:table-template>\" elements contained within
750 \"<office:styles>\". The entries (TABLE-STYLE-NAME
751 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
752 \"table:template-name\" and \"table:use-first-row-styles\" etc
753 attributes of \"<table:table>\" element. Refer ODF-1.2
754 specification for more information. Also consult the
755 implementation filed under `org-odt-get-table-cell-styles'.
757 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
758 formatting of numbered display equations. Do not delete this
759 style from the list."
760 :group 'org-export-odt
761 :type '(choice
762 (const :tag "None" nil)
763 (repeat :tag "Table Styles"
764 (list :tag "Table Style Specification"
765 (string :tag "Table Style Name")
766 (string :tag "Table Template Name")
767 (alist :options (use-first-row-styles
768 use-last-row-styles
769 use-first-column-styles
770 use-last-column-styles
771 use-banding-rows-styles
772 use-banding-columns-styles)
773 :key-type symbol
774 :value-type (const :tag "True" t))))))
776 (defun org-odt-begin-table (caption label attributes)
777 (setq org-odt-table-style attributes)
778 (setq org-odt-table-style-spec
779 (assoc org-odt-table-style org-export-odt-table-styles))
780 (when label
781 (insert
782 (org-odt-format-stylized-paragraph
783 'table (org-odt-format-entity-caption label caption "__Table__"))))
784 (org-lparse-insert-tag
785 "<table:table table:name=\"%s\" table:style-name=\"%s\">"
786 (or label "") (or (nth 1 org-odt-table-style-spec) "OrgTable"))
787 (setq org-lparse-table-begin-marker (point)))
789 (defvar org-lparse-table-colalign-info)
790 (defun org-odt-end-table ()
791 (goto-char org-lparse-table-begin-marker)
792 (loop for level from 0 below org-lparse-table-ncols
793 do (let* ((col-cookie (and org-lparse-table-is-styled
794 (cdr (assoc (1+ level)
795 org-lparse-table-colalign-info))))
796 (extra-columns (or (nth 1 col-cookie) 0)))
797 (dotimes (i (1+ extra-columns))
798 (insert
799 (org-odt-format-tags
800 "<table:table-column table:style-name=\"%sColumn\"/>"
801 "" (or (nth 1 org-odt-table-style-spec) "OrgTable"))))
802 (insert "\n")))
803 ;; fill style attributes for table cells
804 (when org-lparse-table-is-styled
805 (while (re-search-forward "@@\\(table-cell:p\\|table-cell:style-name\\)@@\\([0-9]+\\)@@\\([0-9]+\\)@@" nil t)
806 (let* ((spec (match-string 1))
807 (r (string-to-number (match-string 2)))
808 (c (string-to-number (match-string 3)))
809 (cell-styles (org-odt-get-table-cell-styles
810 r c org-odt-table-style-spec))
811 (table-cell-style (car cell-styles))
812 (table-cell-paragraph-style (cdr cell-styles)))
813 (cond
814 ((equal spec "table-cell:p")
815 (replace-match table-cell-paragraph-style t t))
816 ((equal spec "table-cell:style-name")
817 (replace-match table-cell-style t t))))))
818 (goto-char (point-max))
819 (org-lparse-insert-tag "</table:table>"))
821 (defun org-odt-begin-table-rowgroup (&optional is-header-row)
822 (when org-lparse-table-rowgrp-open
823 (org-lparse-end 'TABLE-ROWGROUP))
824 (org-lparse-insert-tag (if is-header-row
825 "<table:table-header-rows>"
826 "<table:table-rows>"))
827 (setq org-lparse-table-rowgrp-open t)
828 (setq org-lparse-table-cur-rowgrp-is-hdr is-header-row))
830 (defun org-odt-end-table-rowgroup ()
831 (when org-lparse-table-rowgrp-open
832 (setq org-lparse-table-rowgrp-open nil)
833 (org-lparse-insert-tag
834 (if org-lparse-table-cur-rowgrp-is-hdr
835 "</table:table-header-rows>" "</table:table-rows>"))))
837 (defun org-odt-format-table-row (row)
838 (org-odt-format-tags
839 '("<table:table-row>" . "</table:table-row>") row))
841 (defun org-odt-get-table-cell-styles (r c &optional style-spec)
842 "Retrieve styles applicable to a table cell.
843 R and C are (zero-based) row and column numbers of the table
844 cell. STYLE-SPEC is an entry in `org-export-odt-table-styles'
845 applicable to the current table. It is `nil' if the table is not
846 associated with any style attributes.
848 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
850 When STYLE-SPEC is nil, style the table cell the conventional way
851 - choose cell borders based on row and column groupings and
852 choose paragraph alignment based on `org-col-cookies' text
853 property. See also
854 `org-odt-get-paragraph-style-cookie-for-table-cell'.
856 When STYLE-SPEC is non-nil, ignore the above cookie and return
857 styles congruent with the ODF-1.2 specification."
858 (cond
859 (style-spec
861 ;; LibreOffice - particularly the Writer - honors neither table
862 ;; templates nor custom table-cell styles. Inorder to retain
863 ;; inter-operability with LibreOffice, only automatic styles are
864 ;; used for styling of table-cells. The current implementation is
865 ;; congruent with ODF-1.2 specification and hence is
866 ;; future-compatible.
868 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
869 ;; which recognizes as many as 16 different cell types - is much
870 ;; richer. Unfortunately it is NOT amenable to easy configuration
871 ;; by hand.
873 (let* ((template-name (nth 1 style-spec))
874 (cell-style-selectors (nth 2 style-spec))
875 (cell-type
876 (cond
877 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors))
878 (= c 0)) "FirstColumn")
879 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors))
880 (= c (1- org-lparse-table-ncols))) "LastColumn")
881 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors))
882 (= r 0)) "FirstRow")
883 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors))
884 (= r org-lparse-table-rownum))
885 "LastRow")
886 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
887 (= (% r 2) 1)) "EvenRow")
888 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
889 (= (% r 2) 0)) "OddRow")
890 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
891 (= (% c 2) 1)) "EvenColumn")
892 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
893 (= (% c 2) 0)) "OddColumn")
894 (t ""))))
895 (cons
896 (concat template-name cell-type "TableCell")
897 (concat template-name cell-type "TableParagraph"))))
899 (cons
900 (concat
901 "OrgTblCell"
902 (cond
903 ((= r 0) "T")
904 ((eq (cdr (assoc r org-lparse-table-rowgrp-info)) :start) "T")
905 (t ""))
906 (when (= r org-lparse-table-rownum) "B")
907 (cond
908 ((= c 0) "")
909 ((or (memq (nth c org-table-colgroup-info) '(:start :startend))
910 (memq (nth (1- c) org-table-colgroup-info) '(:end :startend))) "L")
911 (t "")))
912 (capitalize (aref org-lparse-table-colalign-vector c))))))
914 (defun org-odt-get-paragraph-style-cookie-for-table-cell (r c)
915 (concat
916 (and (not org-odt-table-style-spec)
917 (cond
918 (org-lparse-table-cur-rowgrp-is-hdr "OrgTableHeading")
919 ((and (= c 0) (org-lparse-get 'TABLE-FIRST-COLUMN-AS-LABELS))
920 "OrgTableHeading")
921 (t "OrgTableContents")))
922 (and org-lparse-table-is-styled
923 (format "@@table-cell:p@@%03d@@%03d@@" r c))))
925 (defun org-odt-get-style-name-cookie-for-table-cell (r c)
926 (when org-lparse-table-is-styled
927 (format "@@table-cell:style-name@@%03d@@%03d@@" r c)))
929 (defun org-odt-format-table-cell (data r c horiz-span)
930 (concat
931 (let* ((paragraph-style-cookie
932 (org-odt-get-paragraph-style-cookie-for-table-cell r c))
933 (style-name-cookie
934 (org-odt-get-style-name-cookie-for-table-cell r c))
935 (extra (and style-name-cookie
936 (format " table:style-name=\"%s\"" style-name-cookie)))
937 (extra (concat extra
938 (and (> horiz-span 0)
939 (format " table:number-columns-spanned=\"%d\""
940 (1+ horiz-span))))))
941 (org-odt-format-tags
942 '("<table:table-cell%s>" . "</table:table-cell>")
943 (if org-lparse-list-table-p data
944 (org-odt-format-stylized-paragraph paragraph-style-cookie data)) extra))
945 (let (s)
946 (dotimes (i horiz-span)
947 (setq s (concat s "\n<table:covered-table-cell/>"))) s)
948 "\n"))
950 (defun org-odt-begin-footnote-definition (n)
951 (org-lparse-begin-paragraph 'footnote))
953 (defun org-odt-end-footnote-definition (n)
954 (org-lparse-end-paragraph))
956 (defun org-odt-begin-toc (lang-specific-heading max-level)
957 (insert
958 (format "
959 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
960 <text:table-of-content-source text:outline-level=\"%d\">
961 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
962 " max-level lang-specific-heading))
963 (loop for level from 1 upto 10
964 do (insert (format
966 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
967 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
968 <text:index-entry-chapter/>
969 <text:index-entry-text/>
970 <text:index-entry-link-end/>
971 </text:table-of-content-entry-template>
972 " level level)))
974 (insert
975 (format "
976 </text:table-of-content-source>
978 <text:index-body>
979 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
980 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
981 </text:index-title>
982 " lang-specific-heading)))
984 (defun org-odt-end-toc ()
985 (insert "
986 </text:index-body>
987 </text:table-of-content>
990 (defun org-odt-format-toc-entry (snumber todo headline tags href)
991 (setq headline (concat
992 (and org-export-with-section-numbers
993 (concat snumber ". "))
994 headline
995 (and tags
996 (concat
997 (org-lparse-format 'SPACES 3)
998 (org-lparse-format 'FONTIFY tags "tag")))))
999 (when todo
1000 (setq headline (org-lparse-format 'FONTIFY headline "todo")))
1002 (let ((org-odt-suppress-xref t))
1003 (org-odt-format-link headline (concat "#" href))))
1005 (defun org-odt-format-toc-item (toc-entry level org-last-level)
1006 (let ((style (format "Contents_20_%d"
1007 (+ level (or (org-lparse-get 'TOPLEVEL-HLEVEL) 1) -1))))
1008 (insert "\n" (org-odt-format-stylized-paragraph style toc-entry) "\n")))
1010 ;; Following variable is let bound during 'ORG-LINK callback. See
1011 ;; org-html.el
1012 (defvar org-lparse-link-description-is-image nil)
1013 (defun org-odt-format-link (desc href &optional attr)
1014 (cond
1015 ((and (= (string-to-char href) ?#) (not org-odt-suppress-xref))
1016 (setq href (concat org-export-odt-bookmark-prefix (substring href 1)))
1017 (let ((xref-format "text"))
1018 (when (numberp desc)
1019 (setq desc (format "%d" desc) xref-format "number"))
1020 (org-odt-format-tags
1021 '("<text:bookmark-ref text:reference-format=\"%s\" text:ref-name=\"%s\">" .
1022 "</text:bookmark-ref>")
1023 desc xref-format href)))
1024 (org-lparse-link-description-is-image
1025 (org-odt-format-tags
1026 '("<draw:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</draw:a>")
1027 desc href (or attr "")))
1029 (org-odt-format-tags
1030 '("<text:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</text:a>")
1031 desc href (or attr "")))))
1033 (defun org-odt-format-spaces (n)
1034 (cond
1035 ((= n 1) " ")
1036 ((> n 1) (concat
1037 " " (org-odt-format-tags "<text:s text:c=\"%d\"/>" "" (1- n))))
1038 (t "")))
1040 (defun org-odt-format-tabs (&optional n)
1041 (let ((tab "<text:tab/>")
1042 (n (or n 1)))
1043 (insert tab)))
1045 (defun org-odt-format-line-break ()
1046 (org-odt-format-tags "<text:line-break/>" ""))
1048 (defun org-odt-format-horizontal-line ()
1049 (org-odt-format-stylized-paragraph 'horizontal-line ""))
1051 (defun org-odt-encode-plain-text (line &optional no-whitespace-filling)
1052 (setq line (org-xml-encode-plain-text line))
1053 (if no-whitespace-filling line
1054 (org-odt-fill-tabs-and-spaces line)))
1056 (defun org-odt-format-line (line)
1057 (case org-lparse-dyn-current-environment
1058 (fixedwidth (concat
1059 (org-odt-format-stylized-paragraph
1060 'fixedwidth (org-odt-encode-plain-text line)) "\n"))
1061 (t (concat line "\n"))))
1063 (defun org-odt-format-comment (fmt &rest args)
1064 (let ((comment (apply 'format fmt args)))
1065 (format "\n<!-- %s -->\n" comment)))
1067 (defun org-odt-format-org-entity (wd)
1068 (org-entity-get-representation wd 'utf8))
1070 (defun org-odt-fill-tabs-and-spaces (line)
1071 (replace-regexp-in-string
1072 "\\([\t]\\|\\([ ]+\\)\\)" (lambda (s)
1073 (cond
1074 ((string= s "\t") (org-odt-format-tabs))
1075 (t (org-odt-format-spaces (length s))))) line))
1077 (defcustom org-export-odt-fontify-srcblocks t
1078 "Specify whether or not source blocks need to be fontified.
1079 Turn this option on if you want to colorize the source code
1080 blocks in the exported file. For colorization to work, you need
1081 to make available an enhanced version of `htmlfontify' library."
1082 :type 'boolean
1083 :group 'org-export-odt)
1085 (defun org-odt-format-source-line-with-line-number-and-label
1086 (line rpllbl num fontifier par-style)
1088 (let ((keep-label (not (numberp rpllbl)))
1089 (ref (org-find-text-property-in-string 'org-coderef line)))
1090 (setq line (concat line (and keep-label ref (format "(%s)" ref))))
1091 (setq line (funcall fontifier line))
1092 (when ref
1093 (setq line (org-odt-format-target line (concat "coderef-" ref))))
1094 (setq line (org-odt-format-stylized-paragraph par-style line))
1095 (if (not num) line
1096 (org-odt-format-tags '("<text:list-item>" . "</text:list-item>") line))))
1098 (defun org-odt-format-source-code-or-example-plain
1099 (lines lang caption textareap cols rows num cont rpllbl fmt)
1100 "Format source or example blocks much like fixedwidth blocks.
1101 Use this when `org-export-odt-fontify-srcblocks' option is turned
1102 off."
1103 (let* ((lines (org-split-string lines "[\r\n]"))
1104 (line-count (length lines))
1105 (i 0))
1106 (mapconcat
1107 (lambda (line)
1108 (incf i)
1109 (org-odt-format-source-line-with-line-number-and-label
1110 line rpllbl num 'org-odt-encode-plain-text
1111 (if (= i line-count) "OrgFixedWidthBlockLastLine"
1112 "OrgFixedWidthBlock")))
1113 lines "\n")))
1115 (defvar org-src-block-paragraph-format
1116 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
1117 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
1118 <style:background-image/>
1119 </style:paragraph-properties>
1120 <style:text-properties fo:color=\"%s\"/>
1121 </style:style>"
1122 "Custom paragraph style for colorized source and example blocks.
1123 This style is much the same as that of \"OrgFixedWidthBlock\"
1124 except that the foreground and background colors are set
1125 according to the default face identified by the `htmlfontify'.")
1127 (defun org-odt-hfy-face-to-css (fn)
1128 "Create custom style for face FN.
1129 When FN is the default face, use it's foreground and background
1130 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
1131 use it's color attribute to create a character style whose name
1132 is obtained from FN. Currently all attributes of FN other than
1133 color are ignored.
1135 The style name for a face FN is derived using the following
1136 operations on the face name in that order - de-dash, CamelCase
1137 and prefix with \"OrgSrc\". For example,
1138 `font-lock-function-name-face' is associated with
1139 \"OrgSrcFontLockFunctionNameFace\"."
1140 (let* ((css-list (hfy-face-to-style fn))
1141 (style-name ((lambda (fn)
1142 (concat "OrgSrc"
1143 (mapconcat
1144 'capitalize (split-string
1145 (hfy-face-or-def-to-name fn) "-")
1146 ""))) fn))
1147 (color-val (cdr (assoc "color" css-list)))
1148 (background-color-val (cdr (assoc "background" css-list)))
1149 (style (and org-export-odt-create-custom-styles-for-srcblocks
1150 (cond
1151 ((eq fn 'default)
1152 (format org-src-block-paragraph-format
1153 background-color-val color-val))
1155 (format
1157 <style:style style:name=\"%s\" style:family=\"text\">
1158 <style:text-properties fo:color=\"%s\"/>
1159 </style:style>" style-name color-val))))))
1160 (cons style-name style)))
1162 (defun org-odt-insert-custom-styles-for-srcblocks (styles)
1163 "Save STYLES used for colorizing of source blocks.
1164 Update styles.xml with styles that were collected as part of
1165 `org-odt-hfy-face-to-css' callbacks."
1166 (when styles
1167 (with-current-buffer
1168 (find-file-noselect (expand-file-name "styles.xml") t)
1169 (goto-char (point-min))
1170 (when (re-search-forward "</office:styles>" nil t)
1171 (goto-char (match-beginning 0))
1172 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n")))))
1174 (defun org-odt-format-source-code-or-example-colored
1175 (lines lang caption textareap cols rows num cont rpllbl fmt)
1176 "Format source or example blocks using `htmlfontify-string'.
1177 Use this routine when `org-export-odt-fontify-srcblocks' option
1178 is turned on."
1179 (let* ((lang-m (and lang (or (cdr (assoc lang org-src-lang-modes)) lang)))
1180 (mode (and lang-m (intern (concat (if (symbolp lang-m)
1181 (symbol-name lang-m)
1182 lang-m) "-mode"))))
1183 (org-inhibit-startup t)
1184 (org-startup-folded nil)
1185 (lines (with-temp-buffer
1186 (insert lines)
1187 (if (functionp mode) (funcall mode) (fundamental-mode))
1188 (font-lock-fontify-buffer)
1189 (buffer-string)))
1190 (hfy-html-quote-regex "\\([<\"&> ]\\)")
1191 (hfy-html-quote-map '(("\"" "&quot;")
1192 ("<" "&lt;")
1193 ("&" "&amp;")
1194 (">" "&gt;")
1195 (" " "<text:s/>")
1196 (" " "<text:tab/>")))
1197 (hfy-face-to-css 'org-odt-hfy-face-to-css)
1198 (hfy-optimisations-1 (copy-seq hfy-optimisations))
1199 (hfy-optimisations (add-to-list 'hfy-optimisations-1
1200 'body-text-only))
1201 (hfy-begin-span-handler
1202 (lambda (style text-block text-id text-begins-block-p)
1203 (insert (format "<text:span text:style-name=\"%s\">" style))))
1204 (hfy-end-span-handler (lambda nil (insert "</text:span>"))))
1205 (when (fboundp 'htmlfontify-string)
1206 (let* ((lines (org-split-string lines "[\r\n]"))
1207 (line-count (length lines))
1208 (i 0))
1209 (mapconcat
1210 (lambda (line)
1211 (incf i)
1212 (org-odt-format-source-line-with-line-number-and-label
1213 line rpllbl num 'htmlfontify-string
1214 (if (= i line-count) "OrgSrcBlockLastLine" "OrgSrcBlock")))
1215 lines "\n")))))
1217 (defun org-odt-format-source-code-or-example (lines lang caption textareap
1218 cols rows num cont
1219 rpllbl fmt)
1220 "Format source or example blocks for export.
1221 Use `org-odt-format-source-code-or-example-plain' or
1222 `org-odt-format-source-code-or-example-colored' depending on the
1223 value of `org-export-odt-fontify-srcblocks."
1224 (setq lines (org-export-number-lines
1225 lines 0 0 num cont rpllbl fmt 'preprocess)
1226 lines (funcall
1227 (or (and org-export-odt-fontify-srcblocks
1228 (or (featurep 'htmlfontify)
1229 (require 'htmlfontify))
1230 (fboundp 'htmlfontify-string)
1231 'org-odt-format-source-code-or-example-colored)
1232 'org-odt-format-source-code-or-example-plain)
1233 lines lang caption textareap cols rows num cont rpllbl fmt))
1234 (if (not num) lines
1235 (let ((extra (format " text:continue-numbering=\"%s\""
1236 (if cont "true" "false"))))
1237 (org-odt-format-tags
1238 '("<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>"
1239 . "</text:list>") lines extra))))
1241 (defun org-odt-remap-stylenames (style-name)
1243 (cdr (assoc style-name '(("timestamp-wrapper" . "OrgTimestampWrapper")
1244 ("timestamp" . "OrgTimestamp")
1245 ("timestamp-kwd" . "OrgTimestampKeyword")
1246 ("tag" . "OrgTag")
1247 ("todo" . "OrgTodo")
1248 ("done" . "OrgDone")
1249 ("target" . "OrgTarget"))))
1250 style-name))
1252 (defun org-odt-format-fontify (text style &optional id)
1253 (let* ((style-name
1254 (cond
1255 ((stringp style)
1256 (org-odt-remap-stylenames style))
1257 ((symbolp style)
1258 (org-odt-get-style-name-for-entity 'character style))
1259 ((listp style)
1260 (assert (< 1 (length style)))
1261 (let ((parent-style (pop style)))
1262 (mapconcat (lambda (s)
1263 ;; (assert (stringp s) t)
1264 (org-odt-remap-stylenames s)) style "")
1265 (org-odt-remap-stylenames parent-style)))
1266 (t (error "Don't how to handle style %s" style)))))
1267 (org-odt-format-tags
1268 '("<text:span text:style-name=\"%s\">" . "</text:span>")
1269 text style-name)))
1271 (defun org-odt-relocate-relative-path (path dir)
1272 (if (file-name-absolute-p path) path
1273 (file-relative-name (expand-file-name path dir)
1274 (expand-file-name "eyecandy" dir))))
1276 (defun org-odt-format-inline-image (thefile)
1277 (let* ((thelink (if (file-name-absolute-p thefile) thefile
1278 (org-xml-format-href
1279 (org-odt-relocate-relative-path
1280 thefile org-current-export-file))))
1281 (href
1282 (org-odt-format-tags
1283 "<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1284 (if org-export-odt-embed-images
1285 (org-odt-copy-image-file thefile) thelink))))
1286 (org-export-odt-format-image thefile href)))
1288 (defun org-export-odt-format-formula (src href &optional embed-as)
1289 "Create image tag with source and attributes."
1290 (save-match-data
1291 (let* ((caption (org-find-text-property-in-string 'org-caption src))
1292 (caption (and caption (org-xml-format-desc caption)))
1293 (label (org-find-text-property-in-string 'org-label src))
1294 (latex-frag (org-find-text-property-in-string 'org-latex-src src))
1295 (embed-as (or embed-as
1296 (and latex-frag
1297 (org-find-text-property-in-string
1298 'org-latex-src-embed-type src))
1299 (if (or caption label) 'paragraph 'character)))
1300 width height)
1301 (when latex-frag
1302 (setq href (org-propertize href :title "LaTeX Fragment"
1303 :description latex-frag)))
1304 (cond
1305 ((eq embed-as 'character)
1306 (org-odt-format-entity "InlineFormula" href width height))
1308 (org-lparse-end-paragraph)
1309 (org-lparse-insert-list-table
1310 `((,(org-odt-format-entity
1311 (if caption "CaptionedDisplayFormula" "DisplayFormula")
1312 href width height caption nil)
1313 ,(if (not label) ""
1314 (org-odt-format-entity-caption label nil "__MathFormula__"))))
1315 nil nil nil "OrgEquation" nil '((1 "c" 8) (2 "c" 1)))
1316 (throw 'nextline nil))))))
1318 (defvar org-odt-embedded-formulas-count 0)
1319 (defun org-odt-copy-formula-file (path)
1320 "Returns the internal name of the file"
1321 (let* ((src-file (expand-file-name
1322 path (file-name-directory org-current-export-file)))
1323 (target-dir (format "Formula-%04d/"
1324 (incf org-odt-embedded-formulas-count)))
1325 (target-file (concat target-dir "content.xml")))
1326 (when (not org-lparse-to-buffer)
1327 (message "Embedding %s as %s ..."
1328 (substring-no-properties path) target-file)
1330 (make-directory target-dir)
1331 (org-odt-create-manifest-file-entry
1332 "application/vnd.oasis.opendocument.formula" target-dir "1.2")
1334 (case (org-odt-is-formula-link-p src-file)
1335 (mathml
1336 (copy-file src-file target-file 'overwrite))
1337 (odf
1338 (org-odt-zip-extract-one src-file "content.xml" target-dir))
1340 (error "%s is not a formula file" src-file)))
1342 (org-odt-create-manifest-file-entry "text/xml" target-file))
1343 target-file))
1345 (defun org-odt-format-inline-formula (thefile)
1346 (let* ((thelink (if (file-name-absolute-p thefile) thefile
1347 (org-xml-format-href
1348 (org-odt-relocate-relative-path
1349 thefile org-current-export-file))))
1350 (href
1351 (org-odt-format-tags
1352 "<draw:object xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1353 (file-name-directory (org-odt-copy-formula-file thefile)))))
1354 (org-export-odt-format-formula thefile href)))
1356 (defun org-odt-is-formula-link-p (file)
1357 (let ((case-fold-search nil))
1358 (cond
1359 ((string-match "\\.\\(mathml\\|mml\\)\\'" file)
1360 'mathml)
1361 ((string-match "\\.odf\\'" file)
1362 'odf))))
1364 (defun org-odt-format-org-link (opt-plist type-1 path fragment desc attr
1365 descp)
1366 "Make a OpenDocument link.
1367 OPT-PLIST is an options list.
1368 TYPE-1 is the device-type of the link (THIS://foo.html).
1369 PATH is the path of the link (http://THIS#location).
1370 FRAGMENT is the fragment part of the link, if any (foo.html#THIS).
1371 DESC is the link description, if any.
1372 ATTR is a string of other attributes of the a element."
1373 (declare (special org-lparse-par-open))
1374 (save-match-data
1375 (let* ((may-inline-p
1376 (and (member type-1 '("http" "https" "file"))
1377 (org-lparse-should-inline-p path descp)
1378 (not fragment)))
1379 (type (if (equal type-1 "id") "file" type-1))
1380 (filename path)
1381 (thefile path))
1382 (cond
1383 ;; check for inlined images
1384 ((and (member type '("file"))
1385 (not fragment)
1386 (org-file-image-p
1387 filename org-export-odt-inline-image-extensions)
1388 (or (eq t org-export-odt-inline-images)
1389 (and org-export-odt-inline-images (not descp))))
1390 (org-odt-format-inline-image thefile))
1391 ;; check for embedded formulas
1392 ((and (member type '("file"))
1393 (not fragment)
1394 (org-odt-is-formula-link-p filename)
1395 (or (not descp)))
1396 (org-odt-format-inline-formula thefile))
1397 ((string= type "coderef")
1398 (let* ((ref fragment)
1399 (lineno-or-ref (cdr (assoc ref org-export-code-refs)))
1400 (desc (and descp desc))
1401 (org-odt-suppress-xref nil)
1402 (href (org-xml-format-href (concat "#coderef-" ref))))
1403 (cond
1404 ((and (numberp lineno-or-ref) (not desc))
1405 (org-odt-format-link lineno-or-ref href))
1406 ((and (numberp lineno-or-ref) desc
1407 (string-match (regexp-quote (concat "(" ref ")")) desc))
1408 (format (replace-match "%s" t t desc)
1409 (org-odt-format-link lineno-or-ref href)))
1411 (setq desc (format
1412 (if (and desc (string-match
1413 (regexp-quote (concat "(" ref ")"))
1414 desc))
1415 (replace-match "%s" t t desc)
1416 (or desc "%s"))
1417 lineno-or-ref))
1418 (org-odt-format-link (org-xml-format-desc desc) href)))))
1420 (when (string= type "file")
1421 (setq thefile
1422 (cond
1423 ((file-name-absolute-p path)
1424 (concat "file://" (expand-file-name path)))
1425 (t (org-odt-relocate-relative-path
1426 thefile org-current-export-file)))))
1428 (when (and (member type '("" "http" "https" "file")) fragment)
1429 (setq thefile (concat thefile "#" fragment)))
1431 (setq thefile (org-xml-format-href thefile))
1433 (when (not (member type '("" "file")))
1434 (setq thefile (concat type ":" thefile)))
1436 (let ((org-odt-suppress-xref nil))
1437 (org-odt-format-link
1438 (org-xml-format-desc desc) thefile attr)))))))
1440 (defun org-odt-format-heading (text level &optional id)
1441 (let* ((text (if id (org-odt-format-target text id) text)))
1442 (org-odt-format-tags
1443 '("<text:h text:style-name=\"Heading_20_%s\" text:outline-level=\"%s\">" .
1444 "</text:h>") text level level)))
1446 (defun org-odt-format-headline (title extra-targets tags
1447 &optional snumber level)
1448 (concat
1449 (org-lparse-format 'EXTRA-TARGETS extra-targets)
1451 ;; No need to generate section numbers. They are auto-generated by
1452 ;; the application
1454 ;; (concat (org-lparse-format 'SECTION-NUMBER snumber level) " ")
1455 title
1456 (and tags (concat (org-lparse-format 'SPACES 3)
1457 (org-lparse-format 'ORG-TAGS tags)))))
1459 (defun org-odt-format-anchor (text name &optional class)
1460 (org-odt-format-target text name))
1462 (defun org-odt-format-bookmark (text id)
1463 (if id
1464 (org-odt-format-tags "<text:bookmark text:name=\"%s\"/>" text id)
1465 text))
1467 (defun org-odt-format-target (text id)
1468 (let ((name (concat org-export-odt-bookmark-prefix id)))
1469 (concat
1470 (and id (org-odt-format-tags
1471 "<text:bookmark-start text:name=\"%s\"/>" "" name))
1472 (org-odt-format-bookmark text id)
1473 (and id (org-odt-format-tags
1474 "<text:bookmark-end text:name=\"%s\"/>" "" name)))))
1476 (defun org-odt-format-footnote (n def)
1477 (let ((id (concat "fn" n))
1478 (note-class "footnote")
1479 (par-style "Footnote"))
1480 (org-odt-format-tags
1481 '("<text:note text:id=\"%s\" text:note-class=\"%s\">" .
1482 "</text:note>")
1483 (concat
1484 (org-odt-format-tags
1485 '("<text:note-citation>" . "</text:note-citation>")
1487 (org-odt-format-tags
1488 '("<text:note-body>" . "</text:note-body>")
1489 def))
1490 id note-class)))
1492 (defun org-odt-format-footnote-reference (n def refcnt)
1493 (if (= refcnt 1)
1494 (org-odt-format-footnote n def)
1495 (org-odt-format-footnote-ref n)))
1497 (defun org-odt-format-footnote-ref (n)
1498 (let ((note-class "footnote")
1499 (ref-format "text")
1500 (ref-name (concat "fn" n)))
1501 (org-odt-format-tags
1502 '("<text:span text:style-name=\"%s\">" . "</text:span>")
1503 (org-odt-format-tags
1504 '("<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">" . "</text:note-ref>")
1505 n note-class ref-format ref-name)
1506 "OrgSuperscript")))
1508 (defun org-odt-get-image-name (file-name)
1509 (require 'sha1)
1510 (file-relative-name
1511 (expand-file-name
1512 (concat (sha1 file-name) "." (file-name-extension file-name)) "Pictures")))
1514 (defun org-export-odt-format-image (src href &optional embed-as)
1515 "Create image tag with source and attributes."
1516 (save-match-data
1517 (let* ((caption (org-find-text-property-in-string 'org-caption src))
1518 (caption (and caption (org-xml-format-desc caption)))
1519 (attr (org-find-text-property-in-string 'org-attributes src))
1520 (label (org-find-text-property-in-string 'org-label src))
1521 (latex-frag (org-find-text-property-in-string
1522 'org-latex-src src))
1523 (category (and latex-frag "__DvipngImage__"))
1524 (embed-as (or embed-as
1525 (if latex-frag
1526 (or (org-find-text-property-in-string
1527 'org-latex-src-embed-type src) 'character)
1528 'paragraph)))
1529 (attr-plist (org-lparse-get-block-params attr))
1530 (size (org-odt-image-size-from-file
1531 src (plist-get attr-plist :width)
1532 (plist-get attr-plist :height)
1533 (plist-get attr-plist :scale) nil embed-as))
1534 (width (car size)) (height (cdr size)))
1535 (when latex-frag
1536 (setq href (org-propertize href :title "LaTeX Fragment"
1537 :description latex-frag)))
1538 (cond
1539 ((not (or caption label))
1540 (case embed-as
1541 (paragraph (org-odt-format-entity "DisplayImage" href width height))
1542 (character (org-odt-format-entity "InlineImage" href width height))
1543 (t (error "Unknown value for embed-as %S" embed-as))))
1545 (org-odt-format-entity
1546 "CaptionedDisplayImage" href width height caption label category))))))
1548 (defun org-odt-format-object-description (title description)
1549 (concat (and title (org-odt-format-tags
1550 '("<svg:title>" . "</svg:title>")
1551 (org-odt-encode-plain-text title t)))
1552 (and description (org-odt-format-tags
1553 '("<svg:desc>" . "</svg:desc>")
1554 (org-odt-encode-plain-text description t)))))
1556 (defun org-odt-format-frame (text width height style &optional
1557 extra anchor-type)
1558 (let ((frame-attrs
1559 (concat
1560 (if width (format " svg:width=\"%0.2fcm\"" width) "")
1561 (if height (format " svg:height=\"%0.2fcm\"" height) "")
1562 extra
1563 (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph")))))
1564 (org-odt-format-tags
1565 '("<draw:frame draw:style-name=\"%s\"%s>" . "</draw:frame>")
1566 (concat text (org-odt-format-object-description
1567 (get-text-property 0 :title text)
1568 (get-text-property 0 :description text)))
1569 style frame-attrs)))
1571 (defun org-odt-format-textbox (text width height style &optional
1572 extra anchor-type)
1573 (org-odt-format-frame
1574 (org-odt-format-tags
1575 '("<draw:text-box %s>" . "</draw:text-box>")
1576 text (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
1577 (format " fo:min-width=\"%0.2fcm\"" (or width .2))))
1578 width nil style extra anchor-type))
1580 (defun org-odt-format-inlinetask (heading content
1581 &optional todo priority tags)
1582 (org-odt-format-stylized-paragraph
1583 nil (org-odt-format-textbox
1584 (concat (org-odt-format-stylized-paragraph
1585 "OrgInlineTaskHeading"
1586 (org-lparse-format
1587 'HEADLINE (concat (org-lparse-format-todo todo) " " heading)
1588 nil tags))
1589 content) nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\"")))
1591 (defvar org-odt-entity-frame-styles
1592 '(("InlineImage" "__Figure__" ("OrgInlineImage" nil "as-char"))
1593 ("DisplayImage" "__Figure__" ("OrgDisplayImage" nil "paragraph"))
1594 ("CaptionedDisplayImage" "__Figure__"
1595 ("OrgCaptionedImage"
1596 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1597 ("OrgImageCaptionFrame"))
1598 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil "as-char"))
1599 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil "as-char"))
1600 ("CaptionedDisplayFormula" "__MathFormula__"
1601 ("OrgCaptionedFormula" nil "paragraph")
1602 ("OrgFormulaCaptionFrame" nil "as-char"))))
1604 (defun org-odt-format-entity (entity href width height
1605 &optional caption label category)
1606 (let* ((entity-style (assoc entity org-odt-entity-frame-styles))
1607 (entity-frame (apply 'org-odt-format-frame
1608 href width height (nth 2 entity-style))))
1609 (if (not (or caption label)) entity-frame
1610 (apply 'org-odt-format-textbox
1611 (org-odt-format-stylized-paragraph
1612 'illustration
1613 (concat entity-frame
1614 (org-odt-format-entity-caption
1615 label caption (or category (nth 1 entity-style)))))
1616 width height (nth 3 entity-style)))))
1618 (defvar org-odt-embedded-images-count 0)
1619 (defun org-odt-copy-image-file (path)
1620 "Returns the internal name of the file"
1621 (let* ((image-type (file-name-extension path))
1622 (media-type (format "image/%s" image-type))
1623 (src-file (expand-file-name
1624 path (file-name-directory org-current-export-file)))
1625 (target-dir "Images/")
1626 (target-file
1627 (format "%s%04d.%s" target-dir
1628 (incf org-odt-embedded-images-count) image-type)))
1629 (when (not org-lparse-to-buffer)
1630 (message "Embedding %s as %s ..."
1631 (substring-no-properties path) target-file)
1633 (when (= 1 org-odt-embedded-images-count)
1634 (make-directory target-dir)
1635 (org-odt-create-manifest-file-entry "" target-dir))
1637 (copy-file src-file target-file 'overwrite)
1638 (org-odt-create-manifest-file-entry media-type target-file))
1639 target-file))
1641 (defvar org-export-odt-image-size-probe-method
1642 '(emacs imagemagick force)
1643 "Ordered list of methods by for determining size of an embedded
1644 image.")
1646 (defvar org-export-odt-default-image-sizes-alist
1647 '(("character" . (5 . 0.4))
1648 ("paragraph" . (5 . 5)))
1649 "Hardcoded image dimensions one for each of the anchor
1650 methods.")
1652 ;; A4 page size is 21.0 by 29.7 cms
1653 ;; The default page settings has 2cm margin on each of the sides. So
1654 ;; the effective text area is 17.0 by 25.7 cm
1655 (defvar org-export-odt-max-image-size '(17.0 . 20.0)
1656 "Limiting dimensions for an embedded image.")
1658 (defun org-odt-do-image-size (probe-method file &optional dpi anchor-type)
1659 (setq dpi (or dpi org-export-odt-pixels-per-inch))
1660 (setq anchor-type (or anchor-type "paragraph"))
1661 (flet ((size-in-cms (size-in-pixels)
1662 (flet ((pixels-to-cms (pixels)
1663 (let* ((cms-per-inch 2.54)
1664 (inches (/ pixels dpi)))
1665 (* cms-per-inch inches))))
1666 (and size-in-pixels
1667 (cons (pixels-to-cms (car size-in-pixels))
1668 (pixels-to-cms (cdr size-in-pixels)))))))
1669 (case probe-method
1670 (emacs
1671 (size-in-cms (ignore-errors (image-size (create-image file) 'pixels))))
1672 (imagemagick
1673 (size-in-cms
1674 (let ((dim (shell-command-to-string
1675 (format "identify -format \"%%w:%%h\" \"%s\"" file))))
1676 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
1677 (cons (string-to-number (match-string 1 dim))
1678 (string-to-number (match-string 2 dim)))))))
1680 (cdr (assoc-string anchor-type
1681 org-export-odt-default-image-sizes-alist))))))
1683 (defun org-odt-image-size-from-file (file &optional user-width
1684 user-height scale dpi embed-as)
1685 (unless (file-name-absolute-p file)
1686 (setq file (expand-file-name
1687 file (file-name-directory org-current-export-file))))
1688 (let* (size width height)
1689 (unless (and user-height user-width)
1690 (loop for probe-method in org-export-odt-image-size-probe-method
1691 until size
1692 do (setq size (org-odt-do-image-size
1693 probe-method file dpi embed-as)))
1694 (or size (error "Cannot determine Image size. Aborting ..."))
1695 (setq width (car size) height (cdr size)))
1696 (cond
1697 (scale
1698 (setq width (* width scale) height (* height scale)))
1699 ((and user-height user-width)
1700 (setq width user-width height user-height))
1701 (user-height
1702 (setq width (* user-height (/ width height)) height user-height))
1703 (user-width
1704 (setq height (* user-width (/ height width)) width user-width))
1705 (t (ignore)))
1706 ;; ensure that an embedded image fits comfortably within a page
1707 (let ((max-width (car org-export-odt-max-image-size))
1708 (max-height (cdr org-export-odt-max-image-size)))
1709 (when (or (> width max-width) (> height max-height))
1710 (let* ((scale1 (/ max-width width))
1711 (scale2 (/ max-height height))
1712 (scale (min scale1 scale2)))
1713 (setq width (* scale width) height (* scale height)))))
1714 (cons width height)))
1716 (defvar org-odt-entity-labels-alist nil
1717 "Associate Labels with the Labelled entities.
1718 Each element of the alist is of the form (LABEL-NAME
1719 CATEGORY-NAME SEQNO LABEL-STYLE-NAME). LABEL-NAME is same as
1720 that specified by \"#+LABEL: ...\" line. CATEGORY-NAME is the
1721 type of the entity that LABEL-NAME is attached to. CATEGORY-NAME
1722 can be one of \"Table\", \"Figure\" or \"Equation\". SEQNO is
1723 the unique number assigned to the referenced entity on a
1724 per-CATEGORY basis. It is generated sequentially and is 1-based.
1725 LABEL-STYLE-NAME is a key `org-odt-label-styles'.
1727 See `org-odt-add-label-definition' and
1728 `org-odt-fixup-label-references'.")
1730 (defvar org-odt-entity-counts-plist nil
1731 "Plist of running counters of SEQNOs for each of the CATEGORY-NAMEs.
1732 See `org-odt-entity-labels-alist' for known CATEGORY-NAMEs.")
1734 (defvar org-odt-label-styles
1735 '(("text" "(%n)" "text" "(%n)")
1736 ("category-and-value" "%e %n%c" "category-and-value" "%e %n"))
1737 "Specify how labels are applied and referenced.
1738 This is an alist where each element is of the
1739 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
1740 LABEL-REF-FMT).
1742 LABEL-ATTACH-FMT controls how labels and captions are attached to
1743 an entity. It may contain following specifiers - %e, %n and %c.
1744 %e is replaced with the CATEGORY-NAME. %n is replaced with
1745 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
1746 with CAPTION. See `org-odt-format-label-definition'.
1748 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
1749 are generated. The following XML is generated for a label
1750 reference - \"<text:sequence-ref
1751 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
1752 </text:sequence-ref>\". LABEL-REF-FMT may contain following
1753 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
1754 %n is replaced with SEQNO. See
1755 `org-odt-format-label-reference'.")
1757 (defvar org-odt-category-map-alist
1758 '(("__Table__" "Table" "category-and-value")
1759 ("__Figure__" "Figure" "category-and-value")
1760 ("__MathFormula__" "Equation" "text")
1761 ("__DvipngImage__" "Equation" "category-and-value"))
1762 "Map a CATEGORY-HANDLE to CATEGORY-NAME and LABEL-STYLE.
1763 This is an alist where each element is of the form
1764 \\(CATEGORY-HANDLE CATEGORY-NAME LABEL-STYLE\\). CATEGORY_HANDLE
1765 could either be one of the internal handles (as seen above) or be
1766 derived from the \"#+LABEL:<label-name>\" specification. See
1767 `org-export-odt-get-category-from-label'. CATEGORY-NAME and
1768 LABEL-STYLE are used for generating ODT labels. See
1769 `org-odt-label-styles'.")
1771 (defvar org-export-odt-user-categories
1772 '("Illustration" "Table" "Text" "Drawing" "Equation" "Figure"))
1774 (defvar org-export-odt-get-category-from-label nil
1775 "Should category of label be inferred from label itself.
1776 When this option is non-nil, a label is parsed in to two
1777 component parts delimited by a \":\" (colon) as shown here -
1778 #+LABEL:[CATEGORY-HANDLE:]EXTRA. The CATEGORY-HANDLE is mapped
1779 to a CATEGORY-NAME and LABEL-STYLE using
1780 `org-odt-category-map-alist'. (If no such map is provided and
1781 CATEGORY-NAME is set to CATEGORY-HANDLE and LABEL-STYLE is set to
1782 \"category-and-value\"). If CATEGORY-NAME so obtained is listed
1783 under `org-export-odt-user-categories' then the user specified
1784 styles are used. Otherwise styles as determined by the internal
1785 CATEGORY-HANDLE is used. See
1786 `org-odt-get-label-category-and-style' for details.")
1788 (defun org-odt-get-label-category-and-style (label default-category)
1789 "See `org-export-odt-get-category-from-label'."
1790 (let ((default-category-map
1791 (assoc default-category org-odt-category-map-alist))
1792 user-category user-category-map category)
1793 (cond
1794 ((not org-export-odt-get-category-from-label)
1795 default-category-map)
1796 ((not (setq user-category
1797 (save-match-data
1798 (and (string-match "\\`\\(.*\\):.+" label)
1799 (match-string 1 label)))))
1800 default-category-map)
1802 (setq user-category-map
1803 (or (assoc user-category org-odt-category-map-alist)
1804 (list nil user-category "category-and-value"))
1805 category (nth 1 user-category-map))
1806 (if (member category org-export-odt-user-categories)
1807 user-category-map
1808 default-category-map)))))
1810 (defun org-odt-add-label-definition (label default-category)
1811 "Create an entry in `org-odt-entity-labels-alist' and return it."
1812 (setq label (substring-no-properties label))
1813 (let* ((label-props (org-odt-get-label-category-and-style
1814 label default-category))
1815 (category (nth 1 label-props))
1816 (counter category)
1817 (label-style (nth 2 label-props))
1818 (sequence-var (intern (mapconcat
1819 'downcase
1820 (org-split-string counter) "-")))
1821 (seqno (1+ (or (plist-get org-odt-entity-counts-plist sequence-var)
1822 0)))
1823 (label-props (list label category seqno label-style)))
1824 (setq org-odt-entity-counts-plist
1825 (plist-put org-odt-entity-counts-plist sequence-var seqno))
1826 (push label-props org-odt-entity-labels-alist)
1827 label-props))
1829 (defun org-odt-format-label-definition (caption label category seqno label-style)
1830 (assert label)
1831 (format-spec
1832 (cadr (assoc-string label-style org-odt-label-styles t))
1833 `((?e . ,category)
1834 (?n . ,(org-odt-format-tags
1835 '("<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">" . "</text:sequence>")
1836 (format "%d" seqno) label category category))
1837 (?c . ,(or (and caption (concat ": " caption)) "")))))
1839 (defun org-odt-format-label-reference (label category seqno label-style)
1840 (assert label)
1841 (save-match-data
1842 (let* ((fmt (cddr (assoc-string label-style org-odt-label-styles t)))
1843 (fmt1 (car fmt))
1844 (fmt2 (cadr fmt)))
1845 (org-odt-format-tags
1846 '("<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">"
1847 . "</text:sequence-ref>")
1848 (format-spec fmt2 `((?e . ,category)
1849 (?n . ,(format "%d" seqno)))) fmt1 label))))
1851 (defun org-odt-fixup-label-references ()
1852 (goto-char (point-min))
1853 (while (re-search-forward
1854 "<text:sequence-ref text:ref-name=\"\\([^\"]+\\)\">[ \t\n]*</text:sequence-ref>"
1855 nil t)
1856 (let* ((label (match-string 1))
1857 (label-def (assoc label org-odt-entity-labels-alist))
1858 (rpl (and label-def
1859 (apply 'org-odt-format-label-reference label-def))))
1860 (if rpl (replace-match rpl t t)
1861 (org-lparse-warn
1862 (format "Unable to resolve reference to label \"%s\"" label))))))
1864 (defun org-odt-format-entity-caption (label caption category)
1865 (or (and label
1866 (apply 'org-odt-format-label-definition
1867 caption (org-odt-add-label-definition label category)))
1868 caption ""))
1870 (defun org-odt-format-tags (tag text &rest args)
1871 (let ((prefix (when org-lparse-encode-pending "@"))
1872 (suffix (when org-lparse-encode-pending "@")))
1873 (apply 'org-lparse-format-tags tag text prefix suffix args)))
1875 (defvar org-odt-manifest-file-entries nil)
1876 (defun org-odt-init-outfile (filename)
1877 (unless (executable-find "zip")
1878 ;; Not at all OSes ship with zip by default
1879 (error "Executable \"zip\" needed for creating OpenDocument files"))
1881 (let* ((outdir (make-temp-file
1882 (format org-export-odt-tmpdir-prefix org-lparse-backend) t))
1883 (content-file (expand-file-name "content.xml" outdir)))
1885 ;; init conten.xml
1886 (with-current-buffer (find-file-noselect content-file t))
1888 ;; reset variables
1889 (setq org-odt-manifest-file-entries nil
1890 org-odt-embedded-images-count 0
1891 org-odt-embedded-formulas-count 0
1892 org-odt-entity-labels-alist nil
1893 org-odt-entity-counts-plist nil)
1894 content-file))
1896 (defcustom org-export-odt-prettify-xml nil
1897 "Specify whether or not the xml output should be prettified.
1898 When this option is turned on, `indent-region' is run on all
1899 component xml buffers before they are saved. Turn this off for
1900 regular use. Turn this on if you need to examine the xml
1901 visually."
1902 :group 'org-export-odt
1903 :type 'boolean)
1905 (defvar hfy-user-sheet-assoc) ; bound during org-do-lparse
1906 (defun org-odt-save-as-outfile (target opt-plist)
1907 ;; write meta file
1908 (org-odt-update-meta-file opt-plist)
1910 ;; write styles file
1911 (when (equal org-lparse-backend 'odt)
1912 (org-odt-update-styles-file opt-plist))
1914 ;; create mimetype file
1915 (let ((mimetype (org-odt-write-mimetype-file org-lparse-backend)))
1916 (org-odt-create-manifest-file-entry mimetype "/" "1.2"))
1918 ;; create a manifest entry for content.xml
1919 (org-odt-create-manifest-file-entry "text/xml" "content.xml")
1921 ;; write out the manifest entries before zipping
1922 (org-odt-write-manifest-file)
1924 (let ((xml-files '("mimetype" "META-INF/manifest.xml" "content.xml"
1925 "meta.xml"))
1926 (zipdir default-directory))
1927 (when (equal org-lparse-backend 'odt)
1928 (push "styles.xml" xml-files))
1929 (message "Switching to directory %s" (expand-file-name zipdir))
1931 ;; save all xml files
1932 (mapc (lambda (file)
1933 (with-current-buffer
1934 (find-file-noselect (expand-file-name file) t)
1935 ;; prettify output if needed
1936 (when org-export-odt-prettify-xml
1937 (indent-region (point-min) (point-max)))
1938 (save-buffer 0)))
1939 xml-files)
1941 (let* ((target-name (file-name-nondirectory target))
1942 (target-dir (file-name-directory target))
1943 (cmds `(("zip" "-mX0" ,target-name "mimetype")
1944 ("zip" "-rmTq" ,target-name "."))))
1945 (when (file-exists-p target)
1946 ;; FIXME: If the file is locked this throws a cryptic error
1947 (delete-file target))
1949 (let ((coding-system-for-write 'no-conversion) exitcode err-string)
1950 (message "Creating odt file...")
1951 (mapc
1952 (lambda (cmd)
1953 (message "Running %s" (mapconcat 'identity cmd " "))
1954 (setq err-string
1955 (with-output-to-string
1956 (setq exitcode
1957 (apply 'call-process (car cmd)
1958 nil standard-output nil (cdr cmd)))))
1959 (or (zerop exitcode)
1960 (ignore (message "%s" err-string))
1961 (error "Unable to create odt file (%S)" exitcode)))
1962 cmds))
1964 ;; move the file from outdir to target-dir
1965 (rename-file target-name target-dir)
1967 ;; kill all xml buffers
1968 (mapc (lambda (file)
1969 (kill-buffer
1970 (find-file-noselect (expand-file-name file zipdir) t)))
1971 xml-files)
1973 (delete-directory zipdir)))
1974 (message "Created %s" target)
1975 (set-buffer (find-file-noselect target t)))
1977 (defconst org-odt-manifest-file-entry-tag
1979 <manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
1981 (defun org-odt-create-manifest-file-entry (&rest args)
1982 (push args org-odt-manifest-file-entries))
1984 (defun org-odt-write-manifest-file ()
1985 (make-directory "META-INF")
1986 (let ((manifest-file (expand-file-name "META-INF/manifest.xml")))
1987 (with-current-buffer
1988 (find-file-noselect manifest-file t)
1989 (insert
1990 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1991 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
1992 (mapc
1993 (lambda (file-entry)
1994 (let* ((version (nth 2 file-entry))
1995 (extra (if version
1996 (format " manifest:version=\"%s\"" version)
1997 "")))
1998 (insert
1999 (format org-odt-manifest-file-entry-tag
2000 (nth 0 file-entry) (nth 1 file-entry) extra))))
2001 org-odt-manifest-file-entries)
2002 (insert "\n</manifest:manifest>"))))
2004 (defun org-odt-update-meta-file (opt-plist)
2005 (let ((date (org-odt-iso-date-from-org-timestamp
2006 (plist-get opt-plist :date)))
2007 (author (or (plist-get opt-plist :author) ""))
2008 (email (plist-get opt-plist :email))
2009 (keywords (plist-get opt-plist :keywords))
2010 (description (plist-get opt-plist :description))
2011 (title (plist-get opt-plist :title)))
2012 (write-region
2013 (concat
2014 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
2015 <office:document-meta
2016 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
2017 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2018 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
2019 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
2020 xmlns:ooo=\"http://openoffice.org/2004/office\"
2021 office:version=\"1.2\">
2022 <office:meta>" "\n"
2023 (org-odt-format-author)
2024 (org-odt-format-tags
2025 '("\n<meta:initial-creator>" . "</meta:initial-creator>") author)
2026 (org-odt-format-tags '("\n<dc:date>" . "</dc:date>") date)
2027 (org-odt-format-tags
2028 '("\n<meta:creation-date>" . "</meta:creation-date>") date)
2029 (org-odt-format-tags '("\n<meta:generator>" . "</meta:generator>")
2030 (when org-export-creator-info
2031 (format "Org-%s/Emacs-%s"
2032 org-version emacs-version)))
2033 (org-odt-format-tags '("\n<meta:keyword>" . "</meta:keyword>") keywords)
2034 (org-odt-format-tags '("\n<dc:subject>" . "</dc:subject>") description)
2035 (org-odt-format-tags '("\n<dc:title>" . "</dc:title>") title)
2036 "\n"
2037 " </office:meta>" "</office:document-meta>")
2038 nil (expand-file-name "meta.xml")))
2040 ;; create a manifest entry for meta.xml
2041 (org-odt-create-manifest-file-entry "text/xml" "meta.xml"))
2043 (defun org-odt-update-styles-file (opt-plist)
2044 ;; write styles file
2045 (let ((styles-file (plist-get opt-plist :odt-styles-file)))
2046 (org-odt-copy-styles-file (and styles-file
2047 (read (org-trim styles-file)))))
2049 ;; Update styles.xml - take care of outline numbering
2050 (with-current-buffer
2051 (find-file-noselect (expand-file-name "styles.xml") t)
2052 ;; Don't make automatic backup of styles.xml file. This setting
2053 ;; prevents the backedup styles.xml file from being zipped in to
2054 ;; odt file. This is more of a hackish fix. Better alternative
2055 ;; would be to fix the zip command so that the output odt file
2056 ;; includes only the needed files and excludes any auto-generated
2057 ;; extra files like backups and auto-saves etc etc. Note that
2058 ;; currently the zip command zips up the entire temp directory so
2059 ;; that any auto-generated files created under the hood ends up in
2060 ;; the resulting odt file.
2061 (set (make-local-variable 'backup-inhibited) t)
2063 ;; Import local setting of `org-export-with-section-numbers'
2064 (org-lparse-bind-local-variables opt-plist)
2065 (org-odt-configure-outline-numbering
2066 (if org-export-with-section-numbers org-export-headline-levels 0)))
2068 ;; Write custom stlyes for source blocks
2069 (org-odt-insert-custom-styles-for-srcblocks
2070 (mapconcat
2071 (lambda (style)
2072 (format " %s\n" (cddr style)))
2073 hfy-user-sheet-assoc "")))
2075 (defun org-odt-write-mimetype-file (format)
2076 ;; create mimetype file
2077 (let ((mimetype
2078 (case format
2079 (odt "application/vnd.oasis.opendocument.text")
2080 (odf "application/vnd.oasis.opendocument.formula")
2081 (t (error "Unknown OpenDocument backend %S" org-lparse-backend)))))
2082 (write-region mimetype nil (expand-file-name "mimetype"))
2083 mimetype))
2085 (defun org-odt-finalize-outfile ()
2086 (org-odt-delete-empty-paragraphs))
2088 (defun org-odt-delete-empty-paragraphs ()
2089 (goto-char (point-min))
2090 (let ((open "<text:p[^>]*>")
2091 (close "</text:p>"))
2092 (while (re-search-forward (format "%s[ \r\n\t]*%s" open close) nil t)
2093 (replace-match ""))))
2095 (defcustom org-export-odt-convert-processes
2096 '(("BasicODConverter"
2097 ("soffice" "-norestore" "-invisible" "-headless"
2098 "\"macro:///BasicODConverter.Main.Convert(%I,%f,%O)\""))
2099 ("unoconv"
2100 ("unoconv" "-f" "%f" "-o" "%d" "%i")))
2101 "Specify a list of document converters and their usage.
2102 The converters in this list are offered as choices while
2103 customizing `org-export-odt-convert-process'.
2105 This variable is an alist where each element is of the
2106 form (CONVERTER-NAME CONVERTER-PROCESS). CONVERTER-NAME is name
2107 of the converter. CONVERTER-PROCESS specifies the command-line
2108 syntax of the converter and is of the form (CONVERTER-PROGRAM
2109 ARG1 ARG2 ...). CONVERTER-PROGRAM is the name of the executable.
2110 ARG1, ARG2 etc are command line options that are passed to
2111 CONVERTER-PROGRAM. Format specifiers can be used in the ARGs and
2112 they are interpreted as below:
2114 %i input file name in full
2115 %I input file name as a URL
2116 %f format of the output file
2117 %o output file name in full
2118 %O output file name as a URL
2119 %d output dir in full
2120 %D output dir as a URL."
2121 :group 'org-export-odt
2122 :type
2123 '(choice
2124 (const :tag "None" nil)
2125 (alist :tag "Converters"
2126 :key-type (string :tag "Converter Name")
2127 :value-type (group (cons (string :tag "Executable")
2128 (repeat (string :tag "Command line args")))))))
2130 (defcustom org-export-odt-convert-process nil
2131 "Use this converter to convert from \"odt\" format to other formats.
2132 During customization, the list of converter names are populated
2133 from `org-export-odt-convert-processes'."
2134 :group 'org-export-odt
2135 :type '(choice :convert-widget
2136 (lambda (w)
2137 (apply 'widget-convert (widget-type w)
2138 (eval (car (widget-get w :args)))))
2139 `((const :tag "None" nil)
2140 ,@(mapcar (lambda (c)
2141 `(const :tag ,(car c) ,(car c)))
2142 org-export-odt-convert-processes))))
2144 (defcustom org-export-odt-convert-capabilities
2145 '(("Text"
2146 ("odt" "ott" "doc" "rtf")
2147 (("pdf" "pdf") ("odt" "odt") ("xhtml" "html") ("rtf" "rtf")
2148 ("ott" "ott") ("doc" "doc") ("ooxml" "xml") ("html" "html")))
2149 ("Web"
2150 ("html" "xhtml") (("pdf" "pdf") ("odt" "txt") ("html" "html")))
2151 ("Spreadsheet"
2152 ("ods" "ots" "xls" "csv")
2153 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv")
2154 ("ods" "ods") ("xls" "xls") ("xhtml" "xhtml") ("ooxml" "xml")))
2155 ("Presentation"
2156 ("odp" "otp" "ppt")
2157 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("xhtml" "xml")
2158 ("otp" "otp") ("ppt" "ppt") ("odg" "odg") ("html" "html"))))
2159 "Specify input and output formats of `org-export-odt-convert-process'.
2160 More correctly, specify the set of input and output formats that
2161 the user is actually interested in.
2163 This variable is an alist where each element is of the
2164 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
2165 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
2166 alist where each element is of the form (OUTPUT-FMT
2167 OUTPUT-FILE-EXTENSION).
2169 The variable is interpreted as follows:
2170 `org-export-odt-convert-process' can take any document that is in
2171 INPUT-FMT-LIST and produce any document that is in the
2172 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
2173 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
2174 serves dual purposes:
2175 - It is used for populating completion candidates during
2176 `org-export-odt-convert' commands.
2177 - It is used as the value of \"%f\" specifier in
2178 `org-export-odt-convert-process'.
2180 DOCUMENT-CLASS is used to group a set of file formats in
2181 INPUT-FMT-LIST in to a single class.
2183 Note that this variable inherently captures how LibreOffice based
2184 converters work. LibreOffice maps documents of various formats
2185 to classes like Text, Web, Spreadsheet, Presentation etc and
2186 allow document of a given class (irrespective of it's source
2187 format) to be converted to any of the export formats associated
2188 with that class.
2190 See default setting of this variable for an typical
2191 configuration."
2192 :group 'org-export-odt
2193 :type
2194 '(choice
2195 (const :tag "None" nil)
2196 (alist :key-type (string :tag "Document Class")
2197 :value-type
2198 (group (repeat :tag "Input formats" (string :tag "Input format"))
2199 (alist :tag "Output formats"
2200 :key-type (string :tag "Output format")
2201 :value-type
2202 (group (string :tag "Output file extension")))))))
2204 (declare-function org-create-math-formula "org"
2205 (latex-frag &optional mathml-file))
2207 ;;;###autoload
2208 (defun org-export-odt-convert (&optional in-file out-fmt prefix-arg)
2209 "Convert IN-FILE to format OUT-FMT using a command line converter.
2210 IN-FILE is the file to be converted. If unspecified, it defaults
2211 to variable `buffer-file-name'. OUT-FMT is the desired output
2212 format. Use `org-export-odt-convert-process' as the converter.
2213 If PREFIX-ARG is non-nil then the newly converted file is opened
2214 using `org-open-file'."
2215 (interactive
2216 (append (org-lparse-convert-read-params) current-prefix-arg))
2217 (org-lparse-do-convert in-file out-fmt prefix-arg))
2219 (defun org-odt-get (what &optional opt-plist)
2220 (case what
2221 (BACKEND 'odt)
2222 (EXPORT-DIR (org-export-directory :html opt-plist))
2223 (FILE-NAME-EXTENSION "odt")
2224 (EXPORT-BUFFER-NAME "*Org ODT Export*")
2225 (ENTITY-CONTROL org-odt-entity-control-callbacks-alist)
2226 (ENTITY-FORMAT org-odt-entity-format-callbacks-alist)
2227 (INIT-METHOD 'org-odt-init-outfile)
2228 (FINAL-METHOD 'org-odt-finalize-outfile)
2229 (SAVE-METHOD 'org-odt-save-as-outfile)
2230 (CONVERT-METHOD
2231 (and org-export-odt-convert-process
2232 (cadr (assoc-string org-export-odt-convert-process
2233 org-export-odt-convert-processes t))))
2234 (CONVERT-CAPABILITIES
2235 (and org-export-odt-convert-process
2236 (cadr (assoc-string org-export-odt-convert-process
2237 org-export-odt-convert-processes t))
2238 org-export-odt-convert-capabilities))
2239 (TOPLEVEL-HLEVEL 1)
2240 (SPECIAL-STRING-REGEXPS org-export-odt-special-string-regexps)
2241 (INLINE-IMAGES 'maybe)
2242 (INLINE-IMAGE-EXTENSIONS '("png" "jpeg" "jpg" "gif" "svg"))
2243 (PLAIN-TEXT-MAP '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
2244 (TABLE-FIRST-COLUMN-AS-LABELS nil)
2245 (FOOTNOTE-SEPARATOR (org-lparse-format 'FONTIFY "," 'superscript))
2246 (CODING-SYSTEM-FOR-WRITE 'utf-8)
2247 (CODING-SYSTEM-FOR-SAVE 'utf-8)
2248 (t (error "Unknown property: %s" what))))
2250 (defvar org-lparse-latex-fragment-fallback) ; set by org-do-lparse
2251 (defun org-export-odt-do-preprocess-latex-fragments ()
2252 "Convert LaTeX fragments to images."
2253 (let* ((latex-frag-opt (plist-get org-lparse-opt-plist :LaTeX-fragments))
2254 (latex-frag-opt ; massage the options
2255 (or (and (member latex-frag-opt '(mathjax t))
2256 (not (and (fboundp 'org-format-latex-mathml-available-p)
2257 (org-format-latex-mathml-available-p)))
2258 (prog1 org-lparse-latex-fragment-fallback
2259 (org-lparse-warn
2260 (concat
2261 "LaTeX to MathML converter not available. "
2262 (format "Using %S instead."
2263 org-lparse-latex-fragment-fallback)))))
2264 latex-frag-opt))
2265 cache-dir display-msg)
2266 (cond
2267 ((eq latex-frag-opt 'dvipng)
2268 (setq cache-dir "ltxpng/")
2269 (setq display-msg "Creating LaTeX image %s"))
2270 ((member latex-frag-opt '(mathjax t))
2271 (setq latex-frag-opt 'mathml)
2272 (setq cache-dir "ltxmathml/")
2273 (setq display-msg "Creating MathML formula %s")))
2274 (when (and org-current-export-file)
2275 (org-format-latex
2276 (concat cache-dir (file-name-sans-extension
2277 (file-name-nondirectory org-current-export-file)))
2278 org-current-export-dir nil display-msg
2279 nil nil latex-frag-opt))))
2281 (defadvice org-format-latex-as-mathml
2282 (after org-odt-protect-latex-fragment activate)
2283 "Encode LaTeX fragment as XML.
2284 Do this when translation to MathML fails."
2285 (when (or (not (> (length ad-return-value) 0))
2286 (get-text-property 0 'org-protected ad-return-value))
2287 (setq ad-return-value
2288 (org-propertize (org-odt-encode-plain-text (ad-get-arg 0))
2289 'org-protected t))))
2291 (defun org-export-odt-preprocess-latex-fragments ()
2292 (when (equal org-export-current-backend 'odt)
2293 (org-export-odt-do-preprocess-latex-fragments)))
2295 (defun org-export-odt-preprocess-label-references ()
2296 (goto-char (point-min))
2297 (let (label label-components category value pretty-label)
2298 (while (re-search-forward "\\\\ref{\\([^{}\n]+\\)}" nil t)
2299 (org-if-unprotected-at (match-beginning 1)
2300 (replace-match
2301 (let ((org-lparse-encode-pending t)
2302 (label (match-string 1)))
2303 ;; markup generated below is mostly an eye-candy. At
2304 ;; pre-processing stage, there is no information on which
2305 ;; entity a label reference points to. The actual markup
2306 ;; is generated as part of `org-odt-fixup-label-references'
2307 ;; which gets called at the fag end of export. By this
2308 ;; time we would have seen and collected all the label
2309 ;; definitions in `org-odt-entity-labels-alist'.
2310 (org-odt-format-tags
2311 '("<text:sequence-ref text:ref-name=\"%s\">" .
2312 "</text:sequence-ref>")
2313 "" (org-add-props label '(org-protected t)))) t t)))))
2315 ;; process latex fragments as part of
2316 ;; `org-export-preprocess-after-blockquote-hook'. Note that this hook
2317 ;; is the one that is closest and well before the call to
2318 ;; `org-export-attach-captions-and-attributes' in
2319 ;; `org-export-preprocess-stirng'. The above arrangement permits
2320 ;; captions, labels and attributes to be attached to png images
2321 ;; generated out of latex equations.
2322 (add-hook 'org-export-preprocess-after-blockquote-hook
2323 'org-export-odt-preprocess-latex-fragments)
2325 (defun org-export-odt-preprocess (parameters)
2326 (org-export-odt-preprocess-label-references))
2328 (declare-function archive-zip-extract "arc-mode.el" (archive name))
2329 (defun org-odt-zip-extract-one (archive member &optional target)
2330 (require 'arc-mode)
2331 (let* ((target (or target default-directory))
2332 (archive (expand-file-name archive))
2333 (archive-zip-extract
2334 (list "unzip" "-qq" "-o" "-d" target))
2335 exit-code command-output)
2336 (setq command-output
2337 (with-temp-buffer
2338 (setq exit-code (archive-zip-extract archive member))
2339 (buffer-string)))
2340 (unless (zerop exit-code)
2341 (message command-output)
2342 (error "Extraction failed"))))
2344 (defun org-odt-zip-extract (archive members &optional target)
2345 (when (atom members) (setq members (list members)))
2346 (mapc (lambda (member)
2347 (org-odt-zip-extract-one archive member target))
2348 members))
2350 (defun org-odt-copy-styles-file (&optional styles-file)
2351 ;; Non-availability of styles.xml is not a critical error. For now
2352 ;; throw an error purely for aesthetic reasons.
2353 (setq styles-file (or styles-file
2354 org-export-odt-styles-file
2355 (expand-file-name "OrgOdtStyles.xml"
2356 org-odt-styles-dir)
2357 (error "org-odt: Missing styles file?")))
2358 (cond
2359 ((listp styles-file)
2360 (let ((archive (nth 0 styles-file))
2361 (members (nth 1 styles-file)))
2362 (org-odt-zip-extract archive members)
2363 (mapc
2364 (lambda (member)
2365 (when (org-file-image-p member)
2366 (let* ((image-type (file-name-extension member))
2367 (media-type (format "image/%s" image-type)))
2368 (org-odt-create-manifest-file-entry media-type member))))
2369 members)))
2370 ((and (stringp styles-file) (file-exists-p styles-file))
2371 (let ((styles-file-type (file-name-extension styles-file)))
2372 (cond
2373 ((string= styles-file-type "xml")
2374 (copy-file styles-file "styles.xml" t))
2375 ((member styles-file-type '("odt" "ott"))
2376 (org-odt-zip-extract styles-file "styles.xml")))))
2378 (error (format "Invalid specification of styles.xml file: %S"
2379 org-export-odt-styles-file))))
2381 ;; create a manifest entry for styles.xml
2382 (org-odt-create-manifest-file-entry "text/xml" "styles.xml"))
2384 (defvar org-export-odt-factory-settings
2385 "d4328fb9d1b6cb211d4320ff546829f26700dc5e"
2386 "SHA1 hash of OrgOdtStyles.xml.")
2388 (defun org-odt-configure-outline-numbering (level)
2389 "Outline numbering is retained only upto LEVEL.
2390 To disable outline numbering pass a LEVEL of 0."
2391 (goto-char (point-min))
2392 (let ((regex
2393 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
2394 (replacement
2395 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
2396 (while (re-search-forward regex nil t)
2397 (when (> (string-to-number (match-string 2)) level)
2398 (replace-match replacement t nil))))
2399 (save-buffer 0))
2401 ;;;###autoload
2402 (defun org-export-as-odf (latex-frag &optional odf-file)
2403 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
2404 Use `org-create-math-formula' to convert LATEX-FRAG first to
2405 MathML. When invoked as an interactive command, use
2406 `org-latex-regexps' to infer LATEX-FRAG from currently active
2407 region. If no LaTeX fragments are found, prompt for it. Push
2408 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
2409 non-nil."
2410 (interactive
2411 `(,(let (frag)
2412 (setq frag (and (setq frag (and (region-active-p)
2413 (buffer-substring (region-beginning)
2414 (region-end))))
2415 (loop for e in org-latex-regexps
2416 thereis (when (string-match (nth 1 e) frag)
2417 (match-string (nth 2 e) frag)))))
2418 (read-string "LaTeX Fragment: " frag nil frag))
2419 ,(let ((odf-filename (expand-file-name
2420 (concat
2421 (file-name-sans-extension
2422 (or (file-name-nondirectory buffer-file-name)))
2423 "." "odf")
2424 (file-name-directory buffer-file-name))))
2425 (message "default val is %s" odf-filename)
2426 (read-file-name "ODF filename: " nil odf-filename nil
2427 (file-name-nondirectory odf-filename)))))
2428 (let* ((org-lparse-backend 'odf)
2429 org-lparse-opt-plist
2430 (filename (or odf-file
2431 (expand-file-name
2432 (concat
2433 (file-name-sans-extension
2434 (or (file-name-nondirectory buffer-file-name)))
2435 "." "odf")
2436 (file-name-directory buffer-file-name))))
2437 (buffer (find-file-noselect (org-odt-init-outfile filename)))
2438 (coding-system-for-write 'utf-8)
2439 (save-buffer-coding-system 'utf-8))
2440 (set-buffer buffer)
2441 (set-buffer-file-coding-system coding-system-for-write)
2442 (let ((mathml (org-create-math-formula latex-frag)))
2443 (unless mathml (error "No Math formula created"))
2444 (insert mathml)
2445 (or (org-export-push-to-kill-ring
2446 (upcase (symbol-name org-lparse-backend)))
2447 (message "Exporting... done")))
2448 (org-odt-save-as-outfile filename nil)))
2450 ;;;###autoload
2451 (defun org-export-as-odf-and-open ()
2452 "Export LaTeX fragment as OpenDocument formula and immediately open it.
2453 Use `org-export-as-odf' to read LaTeX fragment and OpenDocument
2454 formula file."
2455 (interactive)
2456 (org-lparse-and-open
2457 nil nil nil (call-interactively 'org-export-as-odf)))
2459 (provide 'org-odt)
2461 ;;; org-odt.el ends here