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