org.el: fix error message when C-u C-u C-c C-c on a list.
[org-mode.git] / lisp / org-odt.el
blobeba8139ad69dd9cf7ace136f04f04f755f408f49
1 ;;; org-odt.el --- OpenDocument Text exporter for Org-mode
3 ;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
5 ;; Author: Jambunathan K <kjambunathan at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp
7 ;; Homepage: http://orgmode.org
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;;; Code:
27 (eval-when-compile
28 (require 'cl))
29 (require 'org-lparse)
30 (require 'org-compat)
32 (defgroup org-export-odt nil
33 "Options specific for ODT export of Org-mode files."
34 :tag "Org Export ODT"
35 :group 'org-export
36 :version "24.1")
38 (defvar org-lparse-dyn-first-heading-pos) ; let bound during org-do-lparse
39 (defun org-odt-insert-toc ()
40 (goto-char (point-min))
41 (cond
42 ((re-search-forward
43 "\\(<text:p [^>]*>\\)?\\s-*\\[TABLE-OF-CONTENTS\\]\\s-*\\(</text:p>\\)?"
44 nil t)
45 (replace-match ""))
47 (goto-char org-lparse-dyn-first-heading-pos)))
48 (insert (org-odt-format-toc)))
50 (defun org-odt-end-export ()
51 (org-odt-insert-toc)
52 (org-odt-fixup-label-references)
54 ;; remove empty paragraphs
55 (goto-char (point-min))
56 (while (re-search-forward
57 "<text:p\\( text:style-name=\"Text_20_body\"\\)?>[ \r\n\t]*</text:p>"
58 nil t)
59 (replace-match ""))
60 (goto-char (point-min))
62 ;; Convert whitespace place holders
63 (goto-char (point-min))
64 (let (beg end n)
65 (while (setq beg (next-single-property-change (point) 'org-whitespace))
66 (setq n (get-text-property beg 'org-whitespace)
67 end (next-single-property-change beg 'org-whitespace))
68 (goto-char beg)
69 (delete-region beg end)
70 (insert (format "<span style=\"visibility:hidden;\">%s</span>"
71 (make-string n ?x)))))
73 ;; Remove empty lines at the beginning of the file.
74 (goto-char (point-min))
75 (when (looking-at "\\s-+\n") (replace-match ""))
77 ;; Remove display properties
78 (remove-text-properties (point-min) (point-max) '(display t)))
80 (defvar org-odt-suppress-xref nil)
81 (defconst org-export-odt-special-string-regexps
82 '(("\\\\-" . "&#x00ad;\\1") ; shy
83 ("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
84 ("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
85 ("\\.\\.\\." . "&#x2026;")) ; hellip
86 "Regular expressions for special string conversion.")
88 (defconst org-odt-lib-dir (file-name-directory load-file-name)
89 "Location of ODT exporter.
90 Use this to infer values of `org-odt-styles-dir' and
91 `org-export-odt-schema-dir'.")
93 (defvar org-odt-data-dir nil
94 "Data directory for ODT exporter.
95 Use this to infer values of `org-odt-styles-dir' and
96 `org-export-odt-schema-dir'.")
98 (defconst org-odt-schema-dir-list
99 (list
100 (and org-odt-data-dir
101 (expand-file-name "./schema/" org-odt-data-dir)) ; bail out
102 (eval-when-compile
103 (and (boundp 'org-odt-data-dir) org-odt-data-dir ; see make install
104 (expand-file-name "./schema/" org-odt-data-dir))))
105 "List of directories to search for OpenDocument schema files.
106 Use this list to set the default value of
107 `org-export-odt-schema-dir'. The entries in this list are
108 populated heuristically based on the values of `org-odt-lib-dir'
109 and `org-odt-data-dir'.")
111 (defcustom org-export-odt-schema-dir
112 (let* ((schema-dir
113 (catch 'schema-dir
114 (message "Debug (org-odt): Searching for OpenDocument schema files...")
115 (mapc
116 (lambda (schema-dir)
117 (when schema-dir
118 (message "Debug (org-odt): Trying %s..." schema-dir)
119 (when (and (file-expand-wildcards
120 (expand-file-name "od-manifest-schema*.rnc"
121 schema-dir))
122 (file-expand-wildcards
123 (expand-file-name "od-schema*.rnc"
124 schema-dir))
125 (file-readable-p
126 (expand-file-name "schemas.xml" schema-dir)))
127 (message "Debug (org-odt): Using schema files under %s"
128 schema-dir)
129 (throw 'schema-dir schema-dir))))
130 org-odt-schema-dir-list)
131 (message "Debug (org-odt): No OpenDocument schema files installed")
132 nil)))
133 schema-dir)
134 "Directory that contains OpenDocument schema files.
136 This directory contains:
137 1. rnc files for OpenDocument schema
138 2. a \"schemas.xml\" file that specifies locating rules needed
139 for auto validation of OpenDocument XML files.
141 Use the customize interface to set this variable. This ensures
142 that `rng-schema-locating-files' is updated and auto-validation
143 of OpenDocument XML takes place based on the value
144 `rng-nxml-auto-validate-flag'.
146 The default value of this variable varies depending on the
147 version of org in use and is initialized from
148 `org-odt-schema-dir-list'. The OASIS schema files are available
149 only in the org's private git repository. It is *not* bundled
150 with GNU ELPA tar or standard Emacs distribution."
151 :type '(choice
152 (const :tag "Not set" nil)
153 (directory :tag "Schema directory"))
154 :group 'org-export-odt
155 :version "24.1"
156 :set
157 (lambda (var value)
158 "Set `org-export-odt-schema-dir'.
159 Also add it to `rng-schema-locating-files'."
160 (let ((schema-dir value))
161 (set var
162 (if (and
163 (file-expand-wildcards
164 (expand-file-name "od-manifest-schema*.rnc" schema-dir))
165 (file-expand-wildcards
166 (expand-file-name "od-schema*.rnc" schema-dir))
167 (file-readable-p
168 (expand-file-name "schemas.xml" schema-dir)))
169 schema-dir
170 (when value
171 (message "Error (org-odt): %s has no OpenDocument schema files"
172 value))
173 nil)))
174 (when org-export-odt-schema-dir
175 (eval-after-load 'rng-loc
176 '(add-to-list 'rng-schema-locating-files
177 (expand-file-name "schemas.xml"
178 org-export-odt-schema-dir))))))
180 (defconst org-odt-styles-dir-list
181 (list
182 (and org-odt-data-dir
183 (expand-file-name "./styles/" org-odt-data-dir)) ; bail out
184 (eval-when-compile
185 (and (boundp 'org-odt-data-dir) org-odt-data-dir ; see make install
186 (expand-file-name "./styles/" org-odt-data-dir)))
187 (expand-file-name "../etc/styles/" org-odt-lib-dir) ; git
188 (expand-file-name "./etc/styles/" org-odt-lib-dir) ; elpa
189 (expand-file-name "./org/" data-directory) ; system
191 "List of directories to search for OpenDocument styles files.
192 See `org-odt-styles-dir'. The entries in this list are populated
193 heuristically based on the values of `org-odt-lib-dir' and
194 `org-odt-data-dir'.")
196 (defconst org-odt-styles-dir
197 (let* ((styles-dir
198 (catch 'styles-dir
199 (message "Debug (org-odt): Searching for OpenDocument styles files...")
200 (mapc (lambda (styles-dir)
201 (when styles-dir
202 (message "Debug (org-odt): Trying %s..." styles-dir)
203 (when (and (file-readable-p
204 (expand-file-name
205 "OrgOdtContentTemplate.xml" styles-dir))
206 (file-readable-p
207 (expand-file-name
208 "OrgOdtStyles.xml" styles-dir)))
209 (message "Debug (org-odt): Using styles under %s"
210 styles-dir)
211 (throw 'styles-dir styles-dir))))
212 org-odt-styles-dir-list)
213 nil)))
214 (unless styles-dir
215 (error "Error (org-odt): Cannot find factory styles files, aborting"))
216 styles-dir)
217 "Directory that holds auxiliary XML files used by the ODT exporter.
219 This directory contains the following XML files -
220 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
221 XML files are used as the default values of
222 `org-export-odt-styles-file' and
223 `org-export-odt-content-template-file'.
225 The default value of this variable varies depending on the
226 version of org in use and is initialized from
227 `org-odt-styles-dir-list'. Note that the user could be using org
228 from one of: org's own private git repository, GNU ELPA tar or
229 standard Emacs.")
231 (defvar org-odt-file-extensions
232 '(("odt" . "OpenDocument Text")
233 ("ott" . "OpenDocument Text Template")
234 ("odm" . "OpenDocument Master Document")
235 ("ods" . "OpenDocument Spreadsheet")
236 ("ots" . "OpenDocument Spreadsheet Template")
237 ("odg" . "OpenDocument Drawing (Graphics)")
238 ("otg" . "OpenDocument Drawing Template")
239 ("odp" . "OpenDocument Presentation")
240 ("otp" . "OpenDocument Presentation Template")
241 ("odi" . "OpenDocument Image")
242 ("odf" . "OpenDocument Formula")
243 ("odc" . "OpenDocument Chart")))
245 (mapc
246 (lambda (desc)
247 ;; Let Emacs open all OpenDocument files in archive mode
248 (add-to-list 'auto-mode-alist
249 (cons (concat "\\." (car desc) "\\'") 'archive-mode)))
250 org-odt-file-extensions)
252 ;; register the odt exporter with the pre-processor
253 (add-to-list 'org-export-backends 'odt)
255 ;; register the odt exporter with org-lparse library
256 (org-lparse-register-backend 'odt)
258 (defun org-odt-unload-function ()
259 (org-lparse-unregister-backend 'odt)
260 (remove-hook 'org-export-preprocess-after-blockquote-hook
261 'org-export-odt-preprocess-latex-fragments)
262 nil)
264 (defcustom org-export-odt-content-template-file nil
265 "Template file for \"content.xml\".
266 The exporter embeds the exported content just before
267 \"</office:text>\" element.
269 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
270 under `org-odt-styles-dir' is used."
271 :type 'file
272 :group 'org-export-odt
273 :version "24.1")
275 (defcustom org-export-odt-styles-file nil
276 "Default styles file for use with ODT export.
277 Valid values are one of:
278 1. nil
279 2. path to a styles.xml file
280 3. path to a *.odt or a *.ott file
281 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
282 ...))
284 In case of option 1, an in-built styles.xml is used. See
285 `org-odt-styles-dir' for more information.
287 In case of option 3, the specified file is unzipped and the
288 styles.xml embedded therein is used.
290 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
291 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
292 generated odt file. Use relative path for specifying the
293 FILE-MEMBERS. styles.xml must be specified as one of the
294 FILE-MEMBERS.
296 Use options 1, 2 or 3 only if styles.xml alone suffices for
297 achieving the desired formatting. Use option 4, if the styles.xml
298 references additional files like header and footer images for
299 achieving the desired formatting.
301 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
302 a per-file basis. For example,
304 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
305 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
306 :group 'org-export-odt
307 :version "24.1"
308 :type
309 '(choice
310 (const :tag "Factory settings" nil)
311 (file :must-match t :tag "styles.xml")
312 (file :must-match t :tag "ODT or OTT file")
313 (list :tag "ODT or OTT file + Members"
314 (file :must-match t :tag "ODF Text or Text Template file")
315 (cons :tag "Members"
316 (file :tag " Member" "styles.xml")
317 (repeat (file :tag "Member"))))))
319 (eval-after-load 'org-exp
320 '(add-to-list 'org-export-inbuffer-options-extra
321 '("ODT_STYLES_FILE" :odt-styles-file)))
323 (defconst org-export-odt-tmpdir-prefix "%s-")
324 (defconst org-export-odt-bookmark-prefix "OrgXref.")
325 (defvar org-odt-zip-dir nil
326 "Temporary directory that holds XML files during export.")
328 (defvar org-export-odt-embed-images t
329 "Should the images be copied in to the odt file or just linked?")
331 (defvar org-export-odt-inline-images 'maybe)
332 (defcustom org-export-odt-inline-image-extensions
333 '("png" "jpeg" "jpg" "gif")
334 "Extensions of image files that can be inlined into HTML."
335 :type '(repeat (string :tag "Extension"))
336 :group 'org-export-odt
337 :version "24.1")
339 (defcustom org-export-odt-pixels-per-inch display-pixels-per-inch
340 "Scaling factor for converting images pixels to inches.
341 Use this for sizing of embedded images. See Info node `(org)
342 Images in ODT export' for more information."
343 :type 'float
344 :group 'org-export-odt
345 :version "24.1")
347 (defcustom org-export-odt-create-custom-styles-for-srcblocks t
348 "Whether custom styles for colorized source blocks be automatically created.
349 When this option is turned on, the exporter creates custom styles
350 for source blocks based on the advice of `htmlfontify'. Creation
351 of custom styles happen as part of `org-odt-hfy-face-to-css'.
353 When this option is turned off exporter does not create such
354 styles.
356 Use the latter option if you do not want the custom styles to be
357 based on your current display settings. It is necessary that the
358 styles.xml already contains needed styles for colorizing to work.
360 This variable is effective only if
361 `org-export-odt-fontify-srcblocks' is turned on."
362 :group 'org-export-odt
363 :version "24.1"
364 :type 'boolean)
366 (defvar org-export-odt-default-org-styles-alist
367 '((paragraph . ((default . "Text_20_body")
368 (fixedwidth . "OrgFixedWidthBlock")
369 (verse . "OrgVerse")
370 (quote . "Quotations")
371 (blockquote . "Quotations")
372 (center . "OrgCenter")
373 (left . "OrgLeft")
374 (right . "OrgRight")
375 (title . "OrgTitle")
376 (subtitle . "OrgSubtitle")
377 (footnote . "Footnote")
378 (src . "OrgSrcBlock")
379 (illustration . "Illustration")
380 (table . "Table")
381 (definition-term . "Text_20_body_20_bold")
382 (horizontal-line . "Horizontal_20_Line")))
383 (character . ((default . "Default")
384 (bold . "Bold")
385 (emphasis . "Emphasis")
386 (code . "OrgCode")
387 (verbatim . "OrgCode")
388 (strike . "Strikethrough")
389 (underline . "Underline")
390 (subscript . "OrgSubscript")
391 (superscript . "OrgSuperscript")))
392 (list . ((ordered . "OrgNumberedList")
393 (unordered . "OrgBulletedList")
394 (description . "OrgDescriptionList"))))
395 "Default styles for various entities.")
397 (defvar org-export-odt-org-styles-alist org-export-odt-default-org-styles-alist)
398 (defun org-odt-get-style-name-for-entity (category &optional entity)
399 (let ((entity (or entity 'default)))
401 (cdr (assoc entity (cdr (assoc category
402 org-export-odt-org-styles-alist))))
403 (cdr (assoc entity (cdr (assoc category
404 org-export-odt-default-org-styles-alist))))
405 (error "Cannot determine style name for entity %s of type %s"
406 entity category))))
408 (defcustom org-export-odt-preferred-output-format nil
409 "Automatically post-process to this format after exporting to \"odt\".
410 Interactive commands `org-export-as-odt' and
411 `org-export-as-odt-and-open' export first to \"odt\" format and
412 then use `org-export-odt-convert-process' to convert the
413 resulting document to this format. During customization of this
414 variable, the list of valid values are populated based on
415 `org-export-odt-convert-capabilities'.
417 You can set this option on per-file basis using file local
418 values. See Info node `(emacs) File Variables'."
419 :group 'org-export-odt
420 :version "24.1"
421 :type '(choice :convert-widget
422 (lambda (w)
423 (apply 'widget-convert (widget-type w)
424 (eval (car (widget-get w :args)))))
425 `((const :tag "None" nil)
426 ,@(mapcar (lambda (c)
427 `(const :tag ,c ,c))
428 (org-lparse-reachable-formats "odt")))))
429 ;;;###autoload
430 (put 'org-export-odt-preferred-output-format 'safe-local-variable 'stringp)
432 (defmacro org-odt-cleanup-xml-buffers (&rest body)
433 `(let ((org-odt-zip-dir
434 (make-temp-file
435 (format org-export-odt-tmpdir-prefix "odf") t))
436 (--cleanup-xml-buffers
437 (function
438 (lambda nil
439 (let ((xml-files '("mimetype" "META-INF/manifest.xml" "content.xml"
440 "meta.xml" "styles.xml")))
441 ;; kill all xml buffers
442 (mapc (lambda (file)
443 (with-current-buffer
444 (find-file-noselect
445 (expand-file-name file org-odt-zip-dir) t)
446 (set-buffer-modified-p nil)
447 (kill-buffer)))
448 xml-files))
449 ;; delete temporary directory.
450 (org-delete-directory org-odt-zip-dir t)))))
451 (condition-case err
452 (prog1 (progn ,@body)
453 (funcall --cleanup-xml-buffers))
454 ((quit error)
455 (funcall --cleanup-xml-buffers)
456 (message "OpenDocument export failed: %s"
457 (error-message-string err))))))
459 ;;;###autoload
460 (defun org-export-as-odt-and-open (arg)
461 "Export the outline as ODT and immediately open it with a browser.
462 If there is an active region, export only the region.
463 The prefix ARG specifies how many levels of the outline should become
464 headlines. The default is 3. Lower levels will become bulleted lists."
465 (interactive "P")
466 (org-odt-cleanup-xml-buffers
467 (org-lparse-and-open
468 (or org-export-odt-preferred-output-format "odt") "odt" arg)))
470 ;;;###autoload
471 (defun org-export-as-odt-batch ()
472 "Call the function `org-lparse-batch'.
473 This function can be used in batch processing as:
474 emacs --batch
475 --load=$HOME/lib/emacs/org.el
476 --eval \"(setq org-export-headline-levels 2)\"
477 --visit=MyFile --funcall org-export-as-odt-batch"
478 (org-odt-cleanup-xml-buffers (org-lparse-batch "odt")))
480 ;;; org-export-as-odt
481 ;;;###autoload
482 (defun org-export-as-odt (arg &optional hidden ext-plist
483 to-buffer body-only pub-dir)
484 "Export the outline as a OpenDocumentText file.
485 If there is an active region, export only the region. The prefix
486 ARG specifies how many levels of the outline should become
487 headlines. The default is 3. Lower levels will become bulleted
488 lists. HIDDEN is obsolete and does nothing.
489 EXT-PLIST is a property list with external parameters overriding
490 org-mode's default settings, but still inferior to file-local
491 settings. When TO-BUFFER is non-nil, create a buffer with that
492 name and export to that buffer. If TO-BUFFER is the symbol
493 `string', don't leave any buffer behind but just return the
494 resulting XML as a string. When BODY-ONLY is set, don't produce
495 the file header and footer, simply return the content of
496 <body>...</body>, without even the body tags themselves. When
497 PUB-DIR is set, use this as the publishing directory."
498 (interactive "P")
499 (org-odt-cleanup-xml-buffers
500 (org-lparse (or org-export-odt-preferred-output-format "odt")
501 "odt" arg hidden ext-plist to-buffer body-only pub-dir)))
503 (defvar org-odt-entity-control-callbacks-alist
504 `((EXPORT
505 . (org-odt-begin-export org-odt-end-export))
506 (DOCUMENT-CONTENT
507 . (org-odt-begin-document-content org-odt-end-document-content))
508 (DOCUMENT-BODY
509 . (org-odt-begin-document-body org-odt-end-document-body))
510 (TOC
511 . (org-odt-begin-toc org-odt-end-toc))
512 (ENVIRONMENT
513 . (org-odt-begin-environment org-odt-end-environment))
514 (FOOTNOTE-DEFINITION
515 . (org-odt-begin-footnote-definition org-odt-end-footnote-definition))
516 (TABLE
517 . (org-odt-begin-table org-odt-end-table))
518 (TABLE-ROWGROUP
519 . (org-odt-begin-table-rowgroup org-odt-end-table-rowgroup))
520 (LIST
521 . (org-odt-begin-list org-odt-end-list))
522 (LIST-ITEM
523 . (org-odt-begin-list-item org-odt-end-list-item))
524 (OUTLINE
525 . (org-odt-begin-outline org-odt-end-outline))
526 (OUTLINE-TEXT
527 . (org-odt-begin-outline-text org-odt-end-outline-text))
528 (PARAGRAPH
529 . (org-odt-begin-paragraph org-odt-end-paragraph)))
532 (defvar org-odt-entity-format-callbacks-alist
533 `((EXTRA-TARGETS . org-lparse-format-extra-targets)
534 (ORG-TAGS . org-lparse-format-org-tags)
535 (SECTION-NUMBER . org-lparse-format-section-number)
536 (HEADLINE . org-odt-format-headline)
537 (TOC-ENTRY . org-odt-format-toc-entry)
538 (TOC-ITEM . org-odt-format-toc-item)
539 (TAGS . org-odt-format-tags)
540 (SPACES . org-odt-format-spaces)
541 (TABS . org-odt-format-tabs)
542 (LINE-BREAK . org-odt-format-line-break)
543 (FONTIFY . org-odt-format-fontify)
544 (TODO . org-lparse-format-todo)
545 (LINK . org-odt-format-link)
546 (INLINE-IMAGE . org-odt-format-inline-image)
547 (ORG-LINK . org-odt-format-org-link)
548 (HEADING . org-odt-format-heading)
549 (ANCHOR . org-odt-format-anchor)
550 (TABLE . org-lparse-format-table)
551 (TABLE-ROW . org-odt-format-table-row)
552 (TABLE-CELL . org-odt-format-table-cell)
553 (FOOTNOTES-SECTION . ignore)
554 (FOOTNOTE-REFERENCE . org-odt-format-footnote-reference)
555 (HORIZONTAL-LINE . org-odt-format-horizontal-line)
556 (COMMENT . org-odt-format-comment)
557 (LINE . org-odt-format-line)
558 (ORG-ENTITY . org-odt-format-org-entity))
561 ;;;_. callbacks
562 ;;;_. control callbacks
563 ;;;_ , document body
564 (defun org-odt-begin-office-body ()
565 ;; automatic styles
566 (insert-file-contents
567 (or org-export-odt-content-template-file
568 (expand-file-name "OrgOdtContentTemplate.xml"
569 org-odt-styles-dir)))
570 (goto-char (point-min))
571 (re-search-forward "</office:text>" nil nil)
572 (delete-region (match-beginning 0) (point-max)))
574 ;; Following variable is let bound when `org-do-lparse' is in
575 ;; progress. See org-html.el.
576 (defvar org-lparse-toc)
577 (defun org-odt-format-toc ()
578 (if (not org-lparse-toc) "" (concat "\n" org-lparse-toc "\n")))
580 (defun org-odt-format-preamble (opt-plist)
581 (let* ((title (plist-get opt-plist :title))
582 (author (plist-get opt-plist :author))
583 (date (plist-get opt-plist :date))
584 (iso-date (org-odt-format-date date))
585 (date (org-odt-format-date date "%d %b %Y"))
586 (email (plist-get opt-plist :email))
587 ;; switch on or off above vars based on user settings
588 (author (and (plist-get opt-plist :author-info) (or author email)))
589 (email (and (plist-get opt-plist :email-info) email))
590 (date (and (plist-get opt-plist :time-stamp-file) date)))
591 (concat
592 ;; title
593 (when title
594 (concat
595 (org-odt-format-stylized-paragraph
596 'title (org-odt-format-tags
597 '("<text:title>" . "</text:title>") title))
598 ;; separator
599 "<text:p text:style-name=\"OrgTitle\"/>"))
600 (cond
601 ((and author (not email))
602 ;; author only
603 (concat
604 (org-odt-format-stylized-paragraph
605 'subtitle
606 (org-odt-format-tags
607 '("<text:initial-creator>" . "</text:initial-creator>")
608 author))
609 ;; separator
610 "<text:p text:style-name=\"OrgSubtitle\"/>"))
611 ((and author email)
612 ;; author and email
613 (concat
614 (org-odt-format-stylized-paragraph
615 'subtitle
616 (org-odt-format-link
617 (org-odt-format-tags
618 '("<text:initial-creator>" . "</text:initial-creator>")
619 author) (concat "mailto:" email)))
620 ;; separator
621 "<text:p text:style-name=\"OrgSubtitle\"/>")))
622 ;; date
623 (when date
624 (concat
625 (org-odt-format-stylized-paragraph
626 'subtitle
627 (org-odt-format-tags
628 '("<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">"
629 . "</text:date>") date "N75" iso-date))
630 ;; separator
631 "<text:p text:style-name=\"OrgSubtitle\"/>")))))
633 (defun org-odt-begin-document-body (opt-plist)
634 (org-odt-begin-office-body)
635 (insert (org-odt-format-preamble opt-plist))
636 (setq org-lparse-dyn-first-heading-pos (point)))
638 (defvar org-lparse-body-only) ; let bound during org-do-lparse
639 (defvar org-lparse-to-buffer) ; let bound during org-do-lparse
640 (defun org-odt-end-document-body (opt-plist)
641 (unless org-lparse-body-only
642 (org-lparse-insert-tag "</office:text>")
643 (org-lparse-insert-tag "</office:body>")))
645 (defun org-odt-begin-document-content (opt-plist)
646 (ignore))
648 (defun org-odt-end-document-content ()
649 (org-lparse-insert-tag "</office:document-content>"))
651 (defun org-odt-begin-outline (level1 snumber title tags
652 target extra-targets class)
653 (org-lparse-insert
654 'HEADING (org-lparse-format
655 'HEADLINE title extra-targets tags snumber level1)
656 level1 target))
658 (defun org-odt-end-outline ()
659 (ignore))
661 (defun org-odt-begin-outline-text (level1 snumber class)
662 (ignore))
664 (defun org-odt-end-outline-text ()
665 (ignore))
667 (defun org-odt-begin-section (style &optional name)
668 (let ((default-name (car (org-odt-add-automatic-style "Section"))))
669 (org-lparse-insert-tag
670 "<text:section text:style-name=\"%s\" text:name=\"%s\">"
671 style (or name default-name))))
673 (defun org-odt-end-section ()
674 (org-lparse-insert-tag "</text:section>"))
676 (defun org-odt-begin-paragraph (&optional style)
677 (org-lparse-insert-tag
678 "<text:p%s>" (org-odt-get-extra-attrs-for-paragraph-style style)))
680 (defun org-odt-end-paragraph ()
681 (org-lparse-insert-tag "</text:p>"))
683 (defun org-odt-get-extra-attrs-for-paragraph-style (style)
684 (let (style-name)
685 (setq style-name
686 (cond
687 ((stringp style) style)
688 ((symbolp style) (org-odt-get-style-name-for-entity
689 'paragraph style))))
690 (unless style-name
691 (error "Don't know how to handle paragraph style %s" style))
692 (format " text:style-name=\"%s\"" style-name)))
694 (defun org-odt-format-stylized-paragraph (style text)
695 (org-odt-format-tags
696 '("<text:p%s>" . "</text:p>") text
697 (org-odt-get-extra-attrs-for-paragraph-style style)))
699 (defvar org-lparse-opt-plist) ; bound during org-do-lparse
700 (defun org-odt-format-author (&optional author)
701 (when (setq author (or author (plist-get org-lparse-opt-plist :author)))
702 (org-odt-format-tags '("<dc:creator>" . "</dc:creator>") author)))
704 (defun org-odt-format-date (&optional org-ts fmt)
705 (save-match-data
706 (let* ((time
707 (and (stringp org-ts)
708 (string-match org-ts-regexp0 org-ts)
709 (apply 'encode-time
710 (org-fix-decoded-time
711 (org-parse-time-string (match-string 0 org-ts) t)))))
712 date)
713 (cond
714 (fmt (format-time-string fmt time))
715 (t (setq date (format-time-string "%Y-%m-%dT%H:%M:%S%z" time))
716 (format "%s:%s" (substring date 0 -2) (substring date -2)))))))
718 (defun org-odt-begin-annotation (&optional author date)
719 (org-lparse-insert-tag "<office:annotation>")
720 (when (setq author (org-odt-format-author author))
721 (insert author))
722 (insert (org-odt-format-tags
723 '("<dc:date>" . "</dc:date>")
724 (org-odt-format-date
725 (or date (plist-get org-lparse-opt-plist :date)))))
726 (org-lparse-begin-paragraph))
728 (defun org-odt-end-annotation ()
729 (org-lparse-insert-tag "</office:annotation>"))
731 (defun org-odt-begin-environment (style env-options-plist)
732 (case style
733 (annotation
734 (org-lparse-stash-save-paragraph-state)
735 (org-odt-begin-annotation (plist-get env-options-plist 'author)
736 (plist-get env-options-plist 'date)))
737 ((blockquote verse center quote)
738 (org-lparse-begin-paragraph style)
739 (list))
740 ((fixedwidth native)
741 (org-lparse-end-paragraph)
742 (list))
743 (t (error "Unknown environment %s" style))))
745 (defun org-odt-end-environment (style env-options-plist)
746 (case style
747 (annotation
748 (org-lparse-end-paragraph)
749 (org-odt-end-annotation)
750 (org-lparse-stash-pop-paragraph-state))
751 ((blockquote verse center quote)
752 (org-lparse-end-paragraph)
753 (list))
754 ((fixedwidth native)
755 (org-lparse-begin-paragraph)
756 (list))
757 (t (error "Unknown environment %s" style))))
759 (defvar org-lparse-list-stack) ; dynamically bound in org-do-lparse
760 (defvar org-odt-list-stack-stashed)
761 (defun org-odt-begin-list (ltype)
762 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
763 ltype))
764 (let* ((style-name (org-odt-get-style-name-for-entity 'list ltype))
765 (extra (concat (if (or org-lparse-list-table-p
766 (and (= 1 (length org-lparse-list-stack))
767 (null org-odt-list-stack-stashed)))
768 " text:continue-numbering=\"false\""
769 " text:continue-numbering=\"true\"")
770 (when style-name
771 (format " text:style-name=\"%s\"" style-name)))))
772 (case ltype
773 ((ordered unordered description)
774 (org-lparse-end-paragraph)
775 (org-lparse-insert-tag "<text:list%s>" extra))
776 (t (error "Unknown list type: %s" ltype)))))
778 (defun org-odt-end-list (ltype)
779 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
780 ltype))
781 (if ltype
782 (org-lparse-insert-tag "</text:list>")
783 (error "Unknown list type: %s" ltype)))
785 (defun org-odt-begin-list-item (ltype &optional arg headline)
786 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
787 ltype))
788 (case ltype
789 (ordered
790 (assert (not headline) t)
791 (let* ((counter arg) (extra ""))
792 (org-lparse-insert-tag (if (= (length org-lparse-list-stack)
793 (length org-odt-list-stack-stashed))
794 "<text:list-header>" "<text:list-item>"))
795 (org-lparse-begin-paragraph)))
796 (unordered
797 (let* ((id arg) (extra ""))
798 (org-lparse-insert-tag (if (= (length org-lparse-list-stack)
799 (length org-odt-list-stack-stashed))
800 "<text:list-header>" "<text:list-item>"))
801 (org-lparse-begin-paragraph)
802 (insert (if headline (org-odt-format-target headline id)
803 (org-odt-format-bookmark "" id)))))
804 (description
805 (assert (not headline) t)
806 (let ((term (or arg "(no term)")))
807 (insert
808 (org-odt-format-tags
809 '("<text:list-item>" . "</text:list-item>")
810 (org-odt-format-stylized-paragraph 'definition-term term)))
811 (org-lparse-begin-list-item 'unordered)
812 (org-lparse-begin-list 'description)
813 (org-lparse-begin-list-item 'unordered)))
814 (t (error "Unknown list type"))))
816 (defun org-odt-end-list-item (ltype)
817 (setq ltype (or (org-lparse-html-list-type-to-canonical-list-type ltype)
818 ltype))
819 (case ltype
820 ((ordered unordered)
821 (org-lparse-insert-tag (if (= (length org-lparse-list-stack)
822 (length org-odt-list-stack-stashed))
823 (prog1 "</text:list-header>"
824 (setq org-odt-list-stack-stashed nil))
825 "</text:list-item>")))
826 (description
827 (org-lparse-end-list-item-1)
828 (org-lparse-end-list 'description)
829 (org-lparse-end-list-item-1))
830 (t (error "Unknown list type"))))
832 (defun org-odt-discontinue-list ()
833 (let ((stashed-stack org-lparse-list-stack))
834 (loop for list-type in stashed-stack
835 do (org-lparse-end-list-item-1 list-type)
836 (org-lparse-end-list list-type))
837 (setq org-odt-list-stack-stashed stashed-stack)))
839 (defun org-odt-continue-list ()
840 (setq org-odt-list-stack-stashed (nreverse org-odt-list-stack-stashed))
841 (loop for list-type in org-odt-list-stack-stashed
842 do (org-lparse-begin-list list-type)
843 (org-lparse-begin-list-item list-type)))
845 ;; Following variables are let bound when table emission is in
846 ;; progress. See org-lparse.el.
847 (defvar org-lparse-table-begin-marker)
848 (defvar org-lparse-table-ncols)
849 (defvar org-lparse-table-rowgrp-open)
850 (defvar org-lparse-table-rownum)
851 (defvar org-lparse-table-cur-rowgrp-is-hdr)
852 (defvar org-lparse-table-is-styled)
853 (defvar org-lparse-table-rowgrp-info)
854 (defvar org-lparse-table-colalign-vector)
856 (defvar org-odt-table-style nil
857 "Table style specified by \"#+ATTR_ODT: <style-name>\" line.
858 This is set during `org-odt-begin-table'.")
860 (defvar org-odt-table-style-spec nil
861 "Entry for `org-odt-table-style' in `org-export-odt-table-styles'.")
863 (defcustom org-export-odt-table-styles
864 '(("OrgEquation" "OrgEquation"
865 ((use-first-column-styles . t)
866 (use-last-column-styles . t))))
867 "Specify how Table Styles should be derived from a Table Template.
868 This is a list where each element is of the
869 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
871 TABLE-STYLE-NAME is the style associated with the table through
872 `org-odt-table-style'.
874 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
875 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
876 below) that is included in
877 `org-export-odt-content-template-file'.
879 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
880 \"TableCell\"
881 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
882 \"TableParagraph\"
883 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
884 \"FirstRow\" | \"LastRow\" |
885 \"EvenRow\" | \"OddRow\" |
886 \"EvenColumn\" | \"OddColumn\" | \"\"
887 where \"+\" above denotes string concatenation.
889 TABLE-CELL-OPTIONS is an alist where each element is of the
890 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
891 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
892 `use-last-row-styles' |
893 `use-first-column-styles' |
894 `use-last-column-styles' |
895 `use-banding-rows-styles' |
896 `use-banding-columns-styles' |
897 `use-first-row-styles'
898 ON-OR-OFF := `t' | `nil'
900 For example, with the following configuration
902 \(setq org-export-odt-table-styles
903 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
904 \(\(use-first-row-styles . t\)
905 \(use-first-column-styles . t\)\)\)
906 \(\"TableWithHeaderColumns\" \"Custom\"
907 \(\(use-first-column-styles . t\)\)\)\)\)
909 1. A table associated with \"TableWithHeaderRowsAndColumns\"
910 style will use the following table-cell styles -
911 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
912 \"CustomTableCell\" and the following paragraph styles
913 \"CustomFirstRowTableParagraph\",
914 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
915 as appropriate.
917 2. A table associated with \"TableWithHeaderColumns\" style will
918 use the following table-cell styles -
919 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
920 following paragraph styles
921 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
922 as appropriate..
924 Note that TABLE-TEMPLATE-NAME corresponds to the
925 \"<table:table-template>\" elements contained within
926 \"<office:styles>\". The entries (TABLE-STYLE-NAME
927 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
928 \"table:template-name\" and \"table:use-first-row-styles\" etc
929 attributes of \"<table:table>\" element. Refer ODF-1.2
930 specification for more information. Also consult the
931 implementation filed under `org-odt-get-table-cell-styles'.
933 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
934 formatting of numbered display equations. Do not delete this
935 style from the list."
936 :group 'org-export-odt
937 :version "24.1"
938 :type '(choice
939 (const :tag "None" nil)
940 (repeat :tag "Table Styles"
941 (list :tag "Table Style Specification"
942 (string :tag "Table Style Name")
943 (string :tag "Table Template Name")
944 (alist :options (use-first-row-styles
945 use-last-row-styles
946 use-first-column-styles
947 use-last-column-styles
948 use-banding-rows-styles
949 use-banding-columns-styles)
950 :key-type symbol
951 :value-type (const :tag "True" t))))))
953 (defvar org-odt-table-style-format
955 <style:style style:name=\"%s\" style:family=\"table\">
956 <style:table-properties style:rel-width=\"%d%%\" fo:margin-top=\"0cm\" fo:margin-bottom=\"0.20cm\" table:align=\"center\"/>
957 </style:style>
959 "Template for auto-generated Table styles.")
961 (defvar org-odt-automatic-styles '()
962 "Registry of automatic styles for various OBJECT-TYPEs.
963 The variable has the following form:
964 \(\(OBJECT-TYPE-A
965 \(\(OBJECT-NAME-A.1 OBJECT-PROPS-A.1\)
966 \(OBJECT-NAME-A.2 OBJECT-PROPS-A.2\) ...\)\)
967 \(OBJECT-TYPE-B
968 \(\(OBJECT-NAME-B.1 OBJECT-PROPS-B.1\)
969 \(OBJECT-NAME-B.2 OBJECT-PROPS-B.2\) ...\)\)
970 ...\).
972 OBJECT-TYPEs could be \"Section\", \"Table\", \"Figure\" etc.
973 OBJECT-PROPS is (typically) a plist created by passing
974 \"#+ATTR_ODT: \" option to `org-lparse-get-block-params'.
976 Use `org-odt-add-automatic-style' to add update this variable.'")
978 (defvar org-odt-object-counters nil
979 "Running counters for various OBJECT-TYPEs.
980 Use this to generate automatic names and style-names. See
981 `org-odt-add-automatic-style'.")
983 (defun org-odt-write-automatic-styles ()
984 "Write automatic styles to \"content.xml\"."
985 (with-current-buffer
986 (find-file-noselect (expand-file-name "content.xml") t)
987 ;; position the cursor
988 (goto-char (point-min))
989 (re-search-forward " </office:automatic-styles>" nil t)
990 (goto-char (match-beginning 0))
991 ;; write automatic table styles
992 (loop for (style-name props) in
993 (plist-get org-odt-automatic-styles 'Table) do
994 (when (setq props (or (plist-get props :rel-width) 96))
995 (insert (format org-odt-table-style-format style-name props))))))
997 (defun org-odt-add-automatic-style (object-type &optional object-props)
998 "Create an automatic style of type OBJECT-TYPE with param OBJECT-PROPS.
999 OBJECT-PROPS is (typically) a plist created by passing
1000 \"#+ATTR_ODT: \" option of the object in question to
1001 `org-lparse-get-block-params'.
1003 Use `org-odt-object-counters' to generate an automatic
1004 OBJECT-NAME and STYLE-NAME. If OBJECT-PROPS is non-nil, add a
1005 new entry in `org-odt-automatic-styles'. Return (OBJECT-NAME
1006 . STYLE-NAME)."
1007 (assert (stringp object-type))
1008 (let* ((object (intern object-type))
1009 (seqvar object)
1010 (seqno (1+ (or (plist-get org-odt-object-counters seqvar) 0)))
1011 (object-name (format "%s%d" object-type seqno)) style-name)
1012 (setq org-odt-object-counters
1013 (plist-put org-odt-object-counters seqvar seqno))
1014 (when object-props
1015 (setq style-name (format "Org%s" object-name))
1016 (setq org-odt-automatic-styles
1017 (plist-put org-odt-automatic-styles object
1018 (append (list (list style-name object-props))
1019 (plist-get org-odt-automatic-styles object)))))
1020 (cons object-name style-name)))
1022 (defvar org-odt-table-indentedp nil)
1023 (defun org-odt-begin-table (caption label attributes short-caption)
1024 (setq org-odt-table-indentedp (not (null org-lparse-list-stack)))
1025 (when org-odt-table-indentedp
1026 ;; Within the Org file, the table is appearing within a list item.
1027 ;; OpenDocument doesn't allow table to appear within list items.
1028 ;; Temporarily terminate the list, emit the table and then
1029 ;; re-continue the list.
1030 (org-odt-discontinue-list)
1031 ;; Put the Table in an indented section.
1032 (let ((level (length org-odt-list-stack-stashed)))
1033 (org-odt-begin-section (format "OrgIndentedSection-Level-%d" level))))
1034 (setq attributes (org-lparse-get-block-params attributes))
1035 (setq org-odt-table-style (plist-get attributes :style))
1036 (setq org-odt-table-style-spec
1037 (assoc org-odt-table-style org-export-odt-table-styles))
1038 (when (or label caption)
1039 (insert
1040 (org-odt-format-stylized-paragraph
1041 'table (org-odt-format-entity-caption label caption "__Table__"))))
1042 (let ((automatic-name (org-odt-add-automatic-style "Table" attributes)))
1043 (org-lparse-insert-tag
1044 "<table:table table:name=\"%s\" table:style-name=\"%s\">"
1045 (or short-caption (car automatic-name))
1046 (or (nth 1 org-odt-table-style-spec)
1047 (cdr automatic-name) "OrgTable")))
1048 (setq org-lparse-table-begin-marker (point)))
1050 (defvar org-lparse-table-colalign-info)
1051 (defun org-odt-end-table ()
1052 (goto-char org-lparse-table-begin-marker)
1053 (loop for level from 0 below org-lparse-table-ncols
1054 do (let* ((col-cookie (and org-lparse-table-is-styled
1055 (cdr (assoc (1+ level)
1056 org-lparse-table-colalign-info))))
1057 (extra-columns (or (nth 1 col-cookie) 0)))
1058 (dotimes (i (1+ extra-columns))
1059 (insert
1060 (org-odt-format-tags
1061 "<table:table-column table:style-name=\"%sColumn\"/>"
1062 "" (or (nth 1 org-odt-table-style-spec) "OrgTable"))))
1063 (insert "\n")))
1064 ;; fill style attributes for table cells
1065 (when org-lparse-table-is-styled
1066 (while (re-search-forward "@@\\(table-cell:p\\|table-cell:style-name\\)@@\\([0-9]+\\)@@\\([0-9]+\\)@@" nil t)
1067 (let* ((spec (match-string 1))
1068 (r (string-to-number (match-string 2)))
1069 (c (string-to-number (match-string 3)))
1070 (cell-styles (org-odt-get-table-cell-styles
1071 r c org-odt-table-style-spec))
1072 (table-cell-style (car cell-styles))
1073 (table-cell-paragraph-style (cdr cell-styles)))
1074 (cond
1075 ((equal spec "table-cell:p")
1076 (replace-match table-cell-paragraph-style t t))
1077 ((equal spec "table-cell:style-name")
1078 (replace-match table-cell-style t t))))))
1079 (goto-char (point-max))
1080 (org-lparse-insert-tag "</table:table>")
1081 (when org-odt-table-indentedp
1082 (org-odt-end-section)
1083 (org-odt-continue-list)))
1085 (defun org-odt-begin-table-rowgroup (&optional is-header-row)
1086 (when org-lparse-table-rowgrp-open
1087 (org-lparse-end 'TABLE-ROWGROUP))
1088 (org-lparse-insert-tag (if is-header-row
1089 "<table:table-header-rows>"
1090 "<table:table-rows>"))
1091 (setq org-lparse-table-rowgrp-open t)
1092 (setq org-lparse-table-cur-rowgrp-is-hdr is-header-row))
1094 (defun org-odt-end-table-rowgroup ()
1095 (when org-lparse-table-rowgrp-open
1096 (setq org-lparse-table-rowgrp-open nil)
1097 (org-lparse-insert-tag
1098 (if org-lparse-table-cur-rowgrp-is-hdr
1099 "</table:table-header-rows>" "</table:table-rows>"))))
1101 (defun org-odt-format-table-row (row)
1102 (org-odt-format-tags
1103 '("<table:table-row>" . "</table:table-row>") row))
1105 (defun org-odt-get-table-cell-styles (r c &optional style-spec)
1106 "Retrieve styles applicable to a table cell.
1107 R and C are (zero-based) row and column numbers of the table
1108 cell. STYLE-SPEC is an entry in `org-export-odt-table-styles'
1109 applicable to the current table. It is `nil' if the table is not
1110 associated with any style attributes.
1112 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
1114 When STYLE-SPEC is nil, style the table cell the conventional way
1115 - choose cell borders based on row and column groupings and
1116 choose paragraph alignment based on `org-col-cookies' text
1117 property. See also
1118 `org-odt-get-paragraph-style-cookie-for-table-cell'.
1120 When STYLE-SPEC is non-nil, ignore the above cookie and return
1121 styles congruent with the ODF-1.2 specification."
1122 (cond
1123 (style-spec
1125 ;; LibreOffice - particularly the Writer - honors neither table
1126 ;; templates nor custom table-cell styles. Inorder to retain
1127 ;; inter-operability with LibreOffice, only automatic styles are
1128 ;; used for styling of table-cells. The current implementation is
1129 ;; congruent with ODF-1.2 specification and hence is
1130 ;; future-compatible.
1132 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
1133 ;; which recognizes as many as 16 different cell types - is much
1134 ;; richer. Unfortunately it is NOT amenable to easy configuration
1135 ;; by hand.
1137 (let* ((template-name (nth 1 style-spec))
1138 (cell-style-selectors (nth 2 style-spec))
1139 (cell-type
1140 (cond
1141 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors))
1142 (= c 0)) "FirstColumn")
1143 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors))
1144 (= c (1- org-lparse-table-ncols))) "LastColumn")
1145 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors))
1146 (= r 0)) "FirstRow")
1147 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors))
1148 (= r org-lparse-table-rownum))
1149 "LastRow")
1150 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
1151 (= (% r 2) 1)) "EvenRow")
1152 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
1153 (= (% r 2) 0)) "OddRow")
1154 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
1155 (= (% c 2) 1)) "EvenColumn")
1156 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
1157 (= (% c 2) 0)) "OddColumn")
1158 (t ""))))
1159 (cons
1160 (concat template-name cell-type "TableCell")
1161 (concat template-name cell-type "TableParagraph"))))
1163 (cons
1164 (concat
1165 "OrgTblCell"
1166 (cond
1167 ((= r 0) "T")
1168 ((eq (cdr (assoc r org-lparse-table-rowgrp-info)) :start) "T")
1169 (t ""))
1170 (when (= r org-lparse-table-rownum) "B")
1171 (cond
1172 ((= c 0) "")
1173 ((or (memq (nth c org-table-colgroup-info) '(:start :startend))
1174 (memq (nth (1- c) org-table-colgroup-info) '(:end :startend))) "L")
1175 (t "")))
1176 (capitalize (aref org-lparse-table-colalign-vector c))))))
1178 (defun org-odt-get-paragraph-style-cookie-for-table-cell (r c)
1179 (concat
1180 (and (not org-odt-table-style-spec)
1181 (cond
1182 (org-lparse-table-cur-rowgrp-is-hdr "OrgTableHeading")
1183 ((and (= c 0) (org-lparse-get 'TABLE-FIRST-COLUMN-AS-LABELS))
1184 "OrgTableHeading")
1185 (t "OrgTableContents")))
1186 (and org-lparse-table-is-styled
1187 (format "@@table-cell:p@@%03d@@%03d@@" r c))))
1189 (defun org-odt-get-style-name-cookie-for-table-cell (r c)
1190 (when org-lparse-table-is-styled
1191 (format "@@table-cell:style-name@@%03d@@%03d@@" r c)))
1193 (defun org-odt-format-table-cell (data r c horiz-span)
1194 (concat
1195 (let* ((paragraph-style-cookie
1196 (org-odt-get-paragraph-style-cookie-for-table-cell r c))
1197 (style-name-cookie
1198 (org-odt-get-style-name-cookie-for-table-cell r c))
1199 (extra (and style-name-cookie
1200 (format " table:style-name=\"%s\"" style-name-cookie)))
1201 (extra (concat extra
1202 (and (> horiz-span 0)
1203 (format " table:number-columns-spanned=\"%d\""
1204 (1+ horiz-span))))))
1205 (org-odt-format-tags
1206 '("<table:table-cell%s>" . "</table:table-cell>")
1207 (if org-lparse-list-table-p data
1208 (org-odt-format-stylized-paragraph paragraph-style-cookie data)) extra))
1209 (let (s)
1210 (dotimes (i horiz-span)
1211 (setq s (concat s "\n<table:covered-table-cell/>"))) s)
1212 "\n"))
1214 (defun org-odt-begin-footnote-definition (n)
1215 (org-lparse-begin-paragraph 'footnote))
1217 (defun org-odt-end-footnote-definition (n)
1218 (org-lparse-end-paragraph))
1220 (defun org-odt-begin-toc (lang-specific-heading max-level)
1221 ;; Strings in `org-export-language-setup' can contain named html
1222 ;; entities. Replace those with utf-8 equivalents.
1223 (let ((i 0) entity rpl)
1224 (while (string-match "&\\([^#].*?\\);" lang-specific-heading i)
1225 (setq entity (match-string 1 lang-specific-heading))
1226 (if (not (setq rpl (org-entity-get-representation entity 'utf8)))
1227 (setq i (match-end 0))
1228 (setq i (+ (match-beginning 0) (length rpl)))
1229 (setq lang-specific-heading
1230 (replace-match rpl t t lang-specific-heading)))))
1231 (insert
1232 (format "
1233 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
1234 <text:table-of-content-source text:outline-level=\"%d\">
1235 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
1236 " max-level lang-specific-heading))
1237 (loop for level from 1 upto 10
1238 do (insert (format
1240 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
1241 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
1242 <text:index-entry-chapter/>
1243 <text:index-entry-text/>
1244 <text:index-entry-link-end/>
1245 </text:table-of-content-entry-template>
1246 " level level)))
1248 (insert
1249 (format "
1250 </text:table-of-content-source>
1252 <text:index-body>
1253 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
1254 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
1255 </text:index-title>
1256 " lang-specific-heading)))
1258 (defun org-odt-end-toc ()
1259 (insert "
1260 </text:index-body>
1261 </text:table-of-content>
1264 (defun org-odt-format-toc-entry (snumber todo headline tags href)
1265 (setq headline (concat
1266 (and org-export-with-section-numbers
1267 (concat snumber ". "))
1268 headline
1269 (and tags
1270 (concat
1271 (org-lparse-format 'SPACES 3)
1272 (org-lparse-format 'FONTIFY tags "tag")))))
1273 (when todo
1274 (setq headline (org-lparse-format 'FONTIFY headline "todo")))
1276 (let ((org-odt-suppress-xref t))
1277 (org-odt-format-link headline (concat "#" href))))
1279 (defun org-odt-format-toc-item (toc-entry level org-last-level)
1280 (let ((style (format "Contents_20_%d"
1281 (+ level (or (org-lparse-get 'TOPLEVEL-HLEVEL) 1) -1))))
1282 (insert "\n" (org-odt-format-stylized-paragraph style toc-entry) "\n")))
1284 ;; Following variable is let bound during 'ORG-LINK callback. See
1285 ;; org-html.el
1286 (defvar org-lparse-link-description-is-image nil)
1287 (defun org-odt-format-link (desc href &optional attr)
1288 (cond
1289 ((and (= (string-to-char href) ?#) (not org-odt-suppress-xref))
1290 (setq href (substring href 1))
1291 (let ((xref-format "text"))
1292 (when (numberp desc)
1293 (setq desc (format "%d" desc) xref-format "number"))
1294 (when (listp desc)
1295 (setq desc (mapconcat 'identity desc ".") xref-format "chapter"))
1296 (setq href (concat org-export-odt-bookmark-prefix href))
1297 (org-odt-format-tags
1298 '("<text:bookmark-ref text:reference-format=\"%s\" text:ref-name=\"%s\">" .
1299 "</text:bookmark-ref>")
1300 desc xref-format href)))
1301 (org-lparse-link-description-is-image
1302 (org-odt-format-tags
1303 '("<draw:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</draw:a>")
1304 desc href (or attr "")))
1306 (org-odt-format-tags
1307 '("<text:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" . "</text:a>")
1308 desc href (or attr "")))))
1310 (defun org-odt-format-spaces (n)
1311 (cond
1312 ((= n 1) " ")
1313 ((> n 1) (concat
1314 " " (org-odt-format-tags "<text:s text:c=\"%d\"/>" "" (1- n))))
1315 (t "")))
1317 (defun org-odt-format-tabs (&optional n)
1318 (let ((tab "<text:tab/>")
1319 (n (or n 1)))
1320 (insert tab)))
1322 (defun org-odt-format-line-break ()
1323 (org-odt-format-tags "<text:line-break/>" ""))
1325 (defun org-odt-format-horizontal-line ()
1326 (org-odt-format-stylized-paragraph 'horizontal-line ""))
1328 (defun org-odt-encode-plain-text (line &optional no-whitespace-filling)
1329 (setq line (org-xml-encode-plain-text line))
1330 (if no-whitespace-filling line
1331 (org-odt-fill-tabs-and-spaces line)))
1333 (defun org-odt-format-line (line)
1334 (case org-lparse-dyn-current-environment
1335 (fixedwidth (concat
1336 (org-odt-format-stylized-paragraph
1337 'fixedwidth (org-odt-encode-plain-text line)) "\n"))
1338 (t (concat line "\n"))))
1340 (defun org-odt-format-comment (fmt &rest args)
1341 (let ((comment (apply 'format fmt args)))
1342 (format "\n<!-- %s -->\n" comment)))
1344 (defun org-odt-format-org-entity (wd)
1345 (org-entity-get-representation wd 'utf8))
1347 (defun org-odt-fill-tabs-and-spaces (line)
1348 (replace-regexp-in-string
1349 "\\([\t]\\|\\([ ]+\\)\\)" (lambda (s)
1350 (cond
1351 ((string= s "\t") (org-odt-format-tabs))
1352 (t (org-odt-format-spaces (length s))))) line))
1354 (defcustom org-export-odt-fontify-srcblocks t
1355 "Specify whether or not source blocks need to be fontified.
1356 Turn this option on if you want to colorize the source code
1357 blocks in the exported file. For colorization to work, you need
1358 to make available an enhanced version of `htmlfontify' library."
1359 :type 'boolean
1360 :group 'org-export-odt
1361 :version "24.1")
1363 (defun org-odt-format-source-line-with-line-number-and-label
1364 (line rpllbl num fontifier par-style)
1366 (let ((keep-label (not (numberp rpllbl)))
1367 (ref (org-find-text-property-in-string 'org-coderef line)))
1368 (setq line (concat line (and keep-label ref (format "(%s)" ref))))
1369 (setq line (funcall fontifier line))
1370 (when ref
1371 (setq line (org-odt-format-target line (concat "coderef-" ref))))
1372 (setq line (org-odt-format-stylized-paragraph par-style line))
1373 (if (not num) line
1374 (org-odt-format-tags '("<text:list-item>" . "</text:list-item>") line))))
1376 (defun org-odt-format-source-code-or-example-plain
1377 (lines lang caption textareap cols rows num cont rpllbl fmt)
1378 "Format source or example blocks much like fixedwidth blocks.
1379 Use this when `org-export-odt-fontify-srcblocks' option is turned
1380 off."
1381 (let* ((lines (org-split-string lines "[\r\n]"))
1382 (line-count (length lines))
1383 (i 0))
1384 (mapconcat
1385 (lambda (line)
1386 (incf i)
1387 (org-odt-format-source-line-with-line-number-and-label
1388 line rpllbl num 'org-odt-encode-plain-text
1389 (if (= i line-count) "OrgFixedWidthBlockLastLine"
1390 "OrgFixedWidthBlock")))
1391 lines "\n")))
1393 (defvar org-src-block-paragraph-format
1394 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
1395 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
1396 <style:background-image/>
1397 </style:paragraph-properties>
1398 <style:text-properties fo:color=\"%s\"/>
1399 </style:style>"
1400 "Custom paragraph style for colorized source and example blocks.
1401 This style is much the same as that of \"OrgFixedWidthBlock\"
1402 except that the foreground and background colors are set
1403 according to the default face identified by the `htmlfontify'.")
1405 (defvar hfy-optimisations)
1406 (declare-function hfy-face-to-style "htmlfontify" (fn))
1407 (declare-function hfy-face-or-def-to-name "htmlfontify" (fn))
1409 (defun org-odt-hfy-face-to-css (fn)
1410 "Create custom style for face FN.
1411 When FN is the default face, use it's foreground and background
1412 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
1413 use it's color attribute to create a character style whose name
1414 is obtained from FN. Currently all attributes of FN other than
1415 color are ignored.
1417 The style name for a face FN is derived using the following
1418 operations on the face name in that order - de-dash, CamelCase
1419 and prefix with \"OrgSrc\". For example,
1420 `font-lock-function-name-face' is associated with
1421 \"OrgSrcFontLockFunctionNameFace\"."
1422 (let* ((css-list (hfy-face-to-style fn))
1423 (style-name ((lambda (fn)
1424 (concat "OrgSrc"
1425 (mapconcat
1426 'capitalize (split-string
1427 (hfy-face-or-def-to-name fn) "-")
1428 ""))) fn))
1429 (color-val (cdr (assoc "color" css-list)))
1430 (background-color-val (cdr (assoc "background" css-list)))
1431 (style (and org-export-odt-create-custom-styles-for-srcblocks
1432 (cond
1433 ((eq fn 'default)
1434 (format org-src-block-paragraph-format
1435 background-color-val color-val))
1437 (format
1439 <style:style style:name=\"%s\" style:family=\"text\">
1440 <style:text-properties fo:color=\"%s\"/>
1441 </style:style>" style-name color-val))))))
1442 (cons style-name style)))
1444 (defun org-odt-insert-custom-styles-for-srcblocks (styles)
1445 "Save STYLES used for colorizing of source blocks.
1446 Update styles.xml with styles that were collected as part of
1447 `org-odt-hfy-face-to-css' callbacks."
1448 (when styles
1449 (with-current-buffer
1450 (find-file-noselect (expand-file-name "styles.xml") t)
1451 (goto-char (point-min))
1452 (when (re-search-forward "</office:styles>" nil t)
1453 (goto-char (match-beginning 0))
1454 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n")))))
1456 (defun org-odt-format-source-code-or-example-colored
1457 (lines lang caption textareap cols rows num cont rpllbl fmt)
1458 "Format source or example blocks using `htmlfontify-string'.
1459 Use this routine when `org-export-odt-fontify-srcblocks' option
1460 is turned on."
1461 (let* ((lang-m (and lang (or (cdr (assoc lang org-src-lang-modes)) lang)))
1462 (mode (and lang-m (intern (concat (if (symbolp lang-m)
1463 (symbol-name lang-m)
1464 lang-m) "-mode"))))
1465 (org-inhibit-startup t)
1466 (org-startup-folded nil)
1467 (lines (with-temp-buffer
1468 (insert lines)
1469 (if (functionp mode) (funcall mode) (fundamental-mode))
1470 (font-lock-fontify-buffer)
1471 (buffer-string)))
1472 (hfy-html-quote-regex "\\([<\"&> ]\\)")
1473 (hfy-html-quote-map '(("\"" "&quot;")
1474 ("<" "&lt;")
1475 ("&" "&amp;")
1476 (">" "&gt;")
1477 (" " "<text:s/>")
1478 (" " "<text:tab/>")))
1479 (hfy-face-to-css 'org-odt-hfy-face-to-css)
1480 (hfy-optimisations-1 (copy-sequence hfy-optimisations))
1481 (hfy-optimisations (add-to-list 'hfy-optimisations-1
1482 'body-text-only))
1483 (hfy-begin-span-handler
1484 (lambda (style text-block text-id text-begins-block-p)
1485 (insert (format "<text:span text:style-name=\"%s\">" style))))
1486 (hfy-end-span-handler (lambda nil (insert "</text:span>"))))
1487 (when (fboundp 'htmlfontify-string)
1488 (let* ((lines (org-split-string lines "[\r\n]"))
1489 (line-count (length lines))
1490 (i 0))
1491 (mapconcat
1492 (lambda (line)
1493 (incf i)
1494 (org-odt-format-source-line-with-line-number-and-label
1495 line rpllbl num 'htmlfontify-string
1496 (if (= i line-count) "OrgSrcBlockLastLine" "OrgSrcBlock")))
1497 lines "\n")))))
1499 (defun org-odt-format-source-code-or-example (lines lang caption textareap
1500 cols rows num cont
1501 rpllbl fmt)
1502 "Format source or example blocks for export.
1503 Use `org-odt-format-source-code-or-example-plain' or
1504 `org-odt-format-source-code-or-example-colored' depending on the
1505 value of `org-export-odt-fontify-srcblocks."
1506 (setq lines (org-export-number-lines
1507 lines 0 0 num cont rpllbl fmt 'preprocess)
1508 lines (funcall
1509 (or (and org-export-odt-fontify-srcblocks
1510 (or (featurep 'htmlfontify)
1511 ;; htmlfontify.el was introduced in Emacs 23.2
1512 ;; So load it with some caution
1513 (require 'htmlfontify nil t))
1514 (fboundp 'htmlfontify-string)
1515 'org-odt-format-source-code-or-example-colored)
1516 'org-odt-format-source-code-or-example-plain)
1517 lines lang caption textareap cols rows num cont rpllbl fmt))
1518 (if (not num) lines
1519 (let ((extra (format " text:continue-numbering=\"%s\""
1520 (if cont "true" "false"))))
1521 (org-odt-format-tags
1522 '("<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>"
1523 . "</text:list>") lines extra))))
1525 (defun org-odt-remap-stylenames (style-name)
1527 (cdr (assoc style-name '(("timestamp-wrapper" . "OrgTimestampWrapper")
1528 ("timestamp" . "OrgTimestamp")
1529 ("timestamp-kwd" . "OrgTimestampKeyword")
1530 ("tag" . "OrgTag")
1531 ("todo" . "OrgTodo")
1532 ("done" . "OrgDone")
1533 ("target" . "OrgTarget"))))
1534 style-name))
1536 (defun org-odt-format-fontify (text style &optional id)
1537 (let* ((style-name
1538 (cond
1539 ((stringp style)
1540 (org-odt-remap-stylenames style))
1541 ((symbolp style)
1542 (org-odt-get-style-name-for-entity 'character style))
1543 ((listp style)
1544 (assert (< 1 (length style)))
1545 (let ((parent-style (pop style)))
1546 (mapconcat (lambda (s)
1547 ;; (assert (stringp s) t)
1548 (org-odt-remap-stylenames s)) style "")
1549 (org-odt-remap-stylenames parent-style)))
1550 (t (error "Don't how to handle style %s" style)))))
1551 (org-odt-format-tags
1552 '("<text:span text:style-name=\"%s\">" . "</text:span>")
1553 text style-name)))
1555 (defun org-odt-relocate-relative-path (path dir)
1556 (if (file-name-absolute-p path) path
1557 (file-relative-name (expand-file-name path dir)
1558 (expand-file-name "eyecandy" dir))))
1560 (defun org-odt-format-inline-image (thefile)
1561 (let* ((thelink (if (file-name-absolute-p thefile) thefile
1562 (org-xml-format-href
1563 (org-odt-relocate-relative-path
1564 thefile org-current-export-file))))
1565 (href
1566 (org-odt-format-tags
1567 "<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1568 (if org-export-odt-embed-images
1569 (org-odt-copy-image-file thefile) thelink))))
1570 (org-export-odt-format-image thefile href)))
1572 (defvar org-odt-entity-labels-alist nil
1573 "Associate Labels with the Labeled entities.
1574 Each element of the alist is of the form (LABEL-NAME
1575 CATEGORY-NAME SEQNO LABEL-STYLE-NAME). LABEL-NAME is same as
1576 that specified by \"#+LABEL: ...\" line. CATEGORY-NAME is the
1577 type of the entity that LABEL-NAME is attached to. CATEGORY-NAME
1578 can be one of \"Table\", \"Figure\" or \"Equation\". SEQNO is
1579 the unique number assigned to the referenced entity on a
1580 per-CATEGORY basis. It is generated sequentially and is 1-based.
1581 LABEL-STYLE-NAME is a key `org-odt-label-styles'.
1583 See `org-odt-add-label-definition' and
1584 `org-odt-fixup-label-references'.")
1586 (defun org-export-odt-format-formula (src href)
1587 (save-match-data
1588 (let* ((caption (org-find-text-property-in-string 'org-caption src))
1589 (short-caption
1590 (or (org-find-text-property-in-string 'org-caption-shortn src)
1591 caption))
1592 (caption (and caption (org-xml-format-desc caption)))
1593 (short-caption (and short-caption
1594 (org-xml-encode-plain-text short-caption)))
1595 (label (org-find-text-property-in-string 'org-label src))
1596 (latex-frag (org-find-text-property-in-string 'org-latex-src src))
1597 (embed-as (or (and latex-frag
1598 (org-find-text-property-in-string
1599 'org-latex-src-embed-type src))
1600 (if (or caption label) 'paragraph 'character)))
1601 width height)
1602 (when latex-frag
1603 (setq href (org-propertize href :title "LaTeX Fragment"
1604 :description latex-frag)))
1605 (cond
1606 ((eq embed-as 'character)
1607 (org-odt-format-entity "InlineFormula" href width height))
1609 (org-lparse-end-paragraph)
1610 (org-lparse-insert-list-table
1611 `((,(org-odt-format-entity
1612 (if (not (or caption label)) "DisplayFormula"
1613 "CaptionedDisplayFormula")
1614 href width height :caption caption :label label
1615 :short-caption short-caption)
1616 ,(if (not (or caption label)) ""
1617 (let* ((label-props (car org-odt-entity-labels-alist)))
1618 (setcar (last label-props) "math-label")
1619 (apply 'org-odt-format-label-definition
1620 caption label-props)))))
1621 nil nil nil ":style \"OrgEquation\"" nil '((1 "c" 8) (2 "c" 1)))
1622 (throw 'nextline nil))))))
1624 (defvar org-odt-embedded-formulas-count 0)
1625 (defun org-odt-copy-formula-file (path)
1626 "Returns the internal name of the file"
1627 (let* ((src-file (expand-file-name
1628 path (file-name-directory org-current-export-file)))
1629 (target-dir (format "Formula-%04d/"
1630 (incf org-odt-embedded-formulas-count)))
1631 (target-file (concat target-dir "content.xml")))
1632 (when (not org-lparse-to-buffer)
1633 (message "Embedding %s as %s ..."
1634 (substring-no-properties path) target-file)
1636 (make-directory target-dir)
1637 (org-odt-create-manifest-file-entry
1638 "application/vnd.oasis.opendocument.formula" target-dir "1.2")
1640 (case (org-odt-is-formula-link-p src-file)
1641 (mathml
1642 (copy-file src-file target-file 'overwrite))
1643 (odf
1644 (org-odt-zip-extract-one src-file "content.xml" target-dir))
1646 (error "%s is not a formula file" src-file)))
1648 (org-odt-create-manifest-file-entry "text/xml" target-file))
1649 target-file))
1651 (defun org-odt-format-inline-formula (thefile)
1652 (let* ((thelink (if (file-name-absolute-p thefile) thefile
1653 (org-xml-format-href
1654 (org-odt-relocate-relative-path
1655 thefile org-current-export-file))))
1656 (href
1657 (org-odt-format-tags
1658 "<draw:object xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1659 (file-name-directory (org-odt-copy-formula-file thefile)))))
1660 (org-export-odt-format-formula thefile href)))
1662 (defun org-odt-is-formula-link-p (file)
1663 (let ((case-fold-search nil))
1664 (cond
1665 ((string-match "\\.\\(mathml\\|mml\\)\\'" file)
1666 'mathml)
1667 ((string-match "\\.odf\\'" file)
1668 'odf))))
1670 (defun org-odt-format-org-link (opt-plist type-1 path fragment desc attr
1671 descp)
1672 "Make a OpenDocument link.
1673 OPT-PLIST is an options list.
1674 TYPE-1 is the device-type of the link (THIS://foo.html).
1675 PATH is the path of the link (http://THIS#location).
1676 FRAGMENT is the fragment part of the link, if any (foo.html#THIS).
1677 DESC is the link description, if any.
1678 ATTR is a string of other attributes of the a element."
1679 (declare (special org-lparse-par-open))
1680 (save-match-data
1681 (let* ((may-inline-p
1682 (and (member type-1 '("http" "https" "file"))
1683 (org-lparse-should-inline-p path descp)
1684 (not fragment)))
1685 (type (if (equal type-1 "id") "file" type-1))
1686 (filename path)
1687 (thefile path)
1688 sec-frag sec-nos)
1689 (cond
1690 ;; check for inlined images
1691 ((and (member type '("file"))
1692 (not fragment)
1693 (org-file-image-p
1694 filename org-export-odt-inline-image-extensions)
1695 (or (eq t org-export-odt-inline-images)
1696 (and org-export-odt-inline-images (not descp))))
1697 (org-odt-format-inline-image thefile))
1698 ;; check for embedded formulas
1699 ((and (member type '("file"))
1700 (not fragment)
1701 (org-odt-is-formula-link-p filename)
1702 (or (not descp)))
1703 (org-odt-format-inline-formula thefile))
1704 ;; code references
1705 ((string= type "coderef")
1706 (let* ((ref fragment)
1707 (lineno-or-ref (cdr (assoc ref org-export-code-refs)))
1708 (desc (and descp desc))
1709 (org-odt-suppress-xref nil)
1710 (href (org-xml-format-href (concat "#coderef-" ref))))
1711 (cond
1712 ((and (numberp lineno-or-ref) (not desc))
1713 (org-odt-format-link lineno-or-ref href))
1714 ((and (numberp lineno-or-ref) desc
1715 (string-match (regexp-quote (concat "(" ref ")")) desc))
1716 (format (replace-match "%s" t t desc)
1717 (org-odt-format-link lineno-or-ref href)))
1719 (setq desc (format
1720 (if (and desc (string-match
1721 (regexp-quote (concat "(" ref ")"))
1722 desc))
1723 (replace-match "%s" t t desc)
1724 (or desc "%s"))
1725 lineno-or-ref))
1726 (org-odt-format-link (org-xml-format-desc desc) href)))))
1727 ;; links to headlines
1728 ((and (string= type "")
1729 (or (not thefile) (string= thefile ""))
1730 (plist-get org-lparse-opt-plist :section-numbers)
1731 (get-text-property 0 'org-no-description fragment)
1732 (setq sec-frag fragment)
1733 (or (string-match "\\`sec\\(\\(-[0-9]+\\)+\\)" sec-frag)
1734 (and (setq sec-frag
1735 (loop for alias in org-export-target-aliases do
1736 (when (member fragment (cdr alias))
1737 (return (car alias)))))
1738 (string-match "\\`sec\\(\\(-[0-9]+\\)+\\)" sec-frag)))
1739 (setq sec-nos (org-split-string (match-string 1 sec-frag) "-"))
1740 (<= (length sec-nos) (plist-get org-lparse-opt-plist
1741 :headline-levels)))
1742 (let ((org-odt-suppress-xref nil))
1743 (org-odt-format-link sec-nos (concat "#" sec-frag) attr)))
1745 (when (string= type "file")
1746 (setq thefile
1747 (cond
1748 ((file-name-absolute-p path)
1749 (concat "file://" (expand-file-name path)))
1750 (t (org-odt-relocate-relative-path
1751 thefile org-current-export-file)))))
1753 (when (and (member type '("" "http" "https" "file")) fragment)
1754 (setq thefile (concat thefile "#" fragment)))
1756 (setq thefile (org-xml-format-href thefile))
1758 (when (not (member type '("" "file")))
1759 (setq thefile (concat type ":" thefile)))
1761 (let ((org-odt-suppress-xref
1762 ;; Typeset link to headlines with description, as a
1763 ;; regular hyperlink.
1764 (and (string= type "")
1765 (not (get-text-property 0 'org-no-description fragment)))))
1766 (org-odt-format-link
1767 (org-xml-format-desc desc) thefile attr)))))))
1769 (defun org-odt-format-heading (text level &optional id)
1770 (let* ((text (if id (org-odt-format-target text id) text)))
1771 (org-odt-format-tags
1772 '("<text:h text:style-name=\"Heading_20_%s\" text:outline-level=\"%s\">" .
1773 "</text:h>") text level level)))
1775 (defun org-odt-format-headline (title extra-targets tags
1776 &optional snumber level)
1777 (concat
1778 (org-lparse-format 'EXTRA-TARGETS extra-targets)
1780 ;; No need to generate section numbers. They are auto-generated by
1781 ;; the application
1783 ;; (concat (org-lparse-format 'SECTION-NUMBER snumber level) " ")
1784 title
1785 (and tags (concat (org-lparse-format 'SPACES 3)
1786 (org-lparse-format 'ORG-TAGS tags)))))
1788 (defun org-odt-format-anchor (text name &optional class)
1789 (org-odt-format-target text name))
1791 (defun org-odt-format-bookmark (text id)
1792 (if id
1793 (org-odt-format-tags "<text:bookmark text:name=\"%s\"/>" text id)
1794 text))
1796 (defun org-odt-format-target (text id)
1797 (let ((name (concat org-export-odt-bookmark-prefix id)))
1798 (concat
1799 (and id (org-odt-format-tags
1800 "<text:bookmark-start text:name=\"%s\"/>" "" name))
1801 (org-odt-format-bookmark text id)
1802 (and id (org-odt-format-tags
1803 "<text:bookmark-end text:name=\"%s\"/>" "" name)))))
1805 (defun org-odt-format-footnote (n def)
1806 (let ((id (concat "fn" n))
1807 (note-class "footnote")
1808 (par-style "Footnote"))
1809 (org-odt-format-tags
1810 '("<text:note text:id=\"%s\" text:note-class=\"%s\">" .
1811 "</text:note>")
1812 (concat
1813 (org-odt-format-tags
1814 '("<text:note-citation>" . "</text:note-citation>")
1816 (org-odt-format-tags
1817 '("<text:note-body>" . "</text:note-body>")
1818 def))
1819 id note-class)))
1821 (defun org-odt-format-footnote-reference (n def refcnt)
1822 (if (= refcnt 1)
1823 (org-odt-format-footnote n def)
1824 (org-odt-format-footnote-ref n)))
1826 (defun org-odt-format-footnote-ref (n)
1827 (let ((note-class "footnote")
1828 (ref-format "text")
1829 (ref-name (concat "fn" n)))
1830 (org-odt-format-tags
1831 '("<text:span text:style-name=\"%s\">" . "</text:span>")
1832 (org-odt-format-tags
1833 '("<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">" . "</text:note-ref>")
1834 n note-class ref-format ref-name)
1835 "OrgSuperscript")))
1837 (defun org-odt-get-image-name (file-name)
1838 (require 'sha1)
1839 (file-relative-name
1840 (expand-file-name
1841 (concat (sha1 file-name) "." (file-name-extension file-name)) "Pictures")))
1843 (defun org-export-odt-format-image (src href)
1844 "Create image tag with source and attributes."
1845 (save-match-data
1846 (let* ((caption (org-find-text-property-in-string 'org-caption src))
1847 (short-caption
1848 (or (org-find-text-property-in-string 'org-caption-shortn src)
1849 caption))
1850 (caption (and caption (org-xml-format-desc caption)))
1851 (short-caption (and short-caption
1852 (org-xml-encode-plain-text short-caption)))
1853 (attr (org-find-text-property-in-string 'org-attributes src))
1854 (label (org-find-text-property-in-string 'org-label src))
1855 (latex-frag (org-find-text-property-in-string
1856 'org-latex-src src))
1857 (category (and latex-frag "__DvipngImage__"))
1858 (attr-plist (org-lparse-get-block-params attr))
1859 (user-frame-anchor
1860 (car (assoc-string (plist-get attr-plist :anchor)
1861 '(("as-char") ("paragraph") ("page")) t)))
1862 (user-frame-style
1863 (and user-frame-anchor (plist-get attr-plist :style)))
1864 (user-frame-attrs
1865 (and user-frame-anchor (plist-get attr-plist :attributes)))
1866 (user-frame-params
1867 (list user-frame-style user-frame-attrs user-frame-anchor))
1868 (embed-as (cond
1869 (latex-frag
1870 (symbol-name
1871 (case (org-find-text-property-in-string
1872 'org-latex-src-embed-type src)
1873 (paragraph 'paragraph)
1874 (t 'as-char))))
1875 (user-frame-anchor)
1876 (t "paragraph")))
1877 (size (org-odt-image-size-from-file
1878 src (plist-get attr-plist :width)
1879 (plist-get attr-plist :height)
1880 (plist-get attr-plist :scale) nil embed-as))
1881 (width (car size)) (height (cdr size)))
1882 (when latex-frag
1883 (setq href (org-propertize href :title "LaTeX Fragment"
1884 :description latex-frag)))
1885 (let ((frame-style-handle (concat (and (or caption label) "Captioned")
1886 embed-as "Image")))
1887 (org-odt-format-entity
1888 frame-style-handle href width height
1889 :caption caption :label label :category category
1890 :short-caption short-caption
1891 :user-frame-params user-frame-params)))))
1893 (defun org-odt-format-object-description (title description)
1894 (concat (and title (org-odt-format-tags
1895 '("<svg:title>" . "</svg:title>")
1896 (org-odt-encode-plain-text title t)))
1897 (and description (org-odt-format-tags
1898 '("<svg:desc>" . "</svg:desc>")
1899 (org-odt-encode-plain-text description t)))))
1901 (defun org-odt-format-frame (text width height style &optional
1902 extra anchor-type)
1903 (let ((frame-attrs
1904 (concat
1905 (if width (format " svg:width=\"%0.2fcm\"" width) "")
1906 (if height (format " svg:height=\"%0.2fcm\"" height) "")
1907 extra
1908 (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph")))))
1909 (org-odt-format-tags
1910 '("<draw:frame draw:style-name=\"%s\"%s>" . "</draw:frame>")
1911 (concat text (org-odt-format-object-description
1912 (get-text-property 0 :title text)
1913 (get-text-property 0 :description text)))
1914 style frame-attrs)))
1916 (defun org-odt-format-textbox (text width height style &optional
1917 extra anchor-type)
1918 (org-odt-format-frame
1919 (org-odt-format-tags
1920 '("<draw:text-box %s>" . "</draw:text-box>")
1921 text (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
1922 (unless width
1923 (format " fo:min-width=\"%0.2fcm\"" (or width .2)))))
1924 width nil style extra anchor-type))
1926 (defun org-odt-format-inlinetask (heading content
1927 &optional todo priority tags)
1928 (org-odt-format-stylized-paragraph
1929 nil (org-odt-format-textbox
1930 (concat (org-odt-format-stylized-paragraph
1931 "OrgInlineTaskHeading"
1932 (org-lparse-format
1933 'HEADLINE (concat (org-lparse-format-todo todo) " " heading)
1934 nil tags))
1935 content) nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\"")))
1937 (defvar org-odt-entity-frame-styles
1938 '(("As-CharImage" "__Figure__" ("OrgInlineImage" nil "as-char"))
1939 ("ParagraphImage" "__Figure__" ("OrgDisplayImage" nil "paragraph"))
1940 ("PageImage" "__Figure__" ("OrgPageImage" nil "page"))
1941 ("CaptionedAs-CharImage" "__Figure__"
1942 ("OrgCaptionedImage"
1943 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1944 ("OrgInlineImage" nil "as-char"))
1945 ("CaptionedParagraphImage" "__Figure__"
1946 ("OrgCaptionedImage"
1947 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1948 ("OrgImageCaptionFrame" nil "paragraph"))
1949 ("CaptionedPageImage" "__Figure__"
1950 ("OrgCaptionedImage"
1951 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1952 ("OrgPageImageCaptionFrame" nil "page"))
1953 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil "as-char"))
1954 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil "as-char"))
1955 ("CaptionedDisplayFormula" "__MathFormula__"
1956 ("OrgCaptionedFormula" nil "paragraph")
1957 ("OrgFormulaCaptionFrame" nil "as-char"))))
1959 (defun org-odt-merge-frame-params(default-frame-params user-frame-params)
1960 (if (not user-frame-params) default-frame-params
1961 (assert (= (length default-frame-params) 3))
1962 (assert (= (length user-frame-params) 3))
1963 (loop for user-frame-param in user-frame-params
1964 for default-frame-param in default-frame-params
1965 collect (or user-frame-param default-frame-param))))
1967 (defun* org-odt-format-entity (entity href width height
1968 &key caption label category
1969 user-frame-params short-caption)
1970 (let* ((entity-style (assoc-string entity org-odt-entity-frame-styles t))
1971 default-frame-params frame-params)
1972 (cond
1973 ((not (or caption label))
1974 (setq default-frame-params (nth 2 entity-style))
1975 (setq frame-params (org-odt-merge-frame-params
1976 default-frame-params user-frame-params))
1977 (apply 'org-odt-format-frame href width height frame-params))
1979 (setq default-frame-params (nth 3 entity-style))
1980 (setq frame-params (org-odt-merge-frame-params
1981 default-frame-params user-frame-params))
1982 (apply 'org-odt-format-textbox
1983 (org-odt-format-stylized-paragraph
1984 'illustration
1985 (concat
1986 (apply 'org-odt-format-frame href width height
1987 (let ((entity-style-1 (copy-sequence
1988 (nth 2 entity-style))))
1989 (setcar (cdr entity-style-1)
1990 (concat
1991 (cadr entity-style-1)
1992 (and short-caption
1993 (format " draw:name=\"%s\" "
1994 short-caption))))
1996 entity-style-1))
1997 (org-odt-format-entity-caption
1998 label caption (or category (nth 1 entity-style)))))
1999 width height frame-params)))))
2001 (defvar org-odt-embedded-images-count 0)
2002 (defun org-odt-copy-image-file (path)
2003 "Returns the internal name of the file"
2004 (let* ((image-type (file-name-extension path))
2005 (media-type (format "image/%s" image-type))
2006 (src-file (expand-file-name
2007 path (file-name-directory org-current-export-file)))
2008 (target-dir "Images/")
2009 (target-file
2010 (format "%s%04d.%s" target-dir
2011 (incf org-odt-embedded-images-count) image-type)))
2012 (when (not org-lparse-to-buffer)
2013 (message "Embedding %s as %s ..."
2014 (substring-no-properties path) target-file)
2016 (when (= 1 org-odt-embedded-images-count)
2017 (make-directory target-dir)
2018 (org-odt-create-manifest-file-entry "" target-dir))
2020 (copy-file src-file target-file 'overwrite)
2021 (org-odt-create-manifest-file-entry media-type target-file))
2022 target-file))
2024 (defvar org-export-odt-image-size-probe-method
2025 (append (and (executable-find "identify") '(imagemagick)) ; See Bug#10675
2026 '(emacs fixed))
2027 "Ordered list of methods for determining image sizes.")
2029 (defvar org-export-odt-default-image-sizes-alist
2030 '(("as-char" . (5 . 0.4))
2031 ("paragraph" . (5 . 5)))
2032 "Hardcoded image dimensions one for each of the anchor
2033 methods.")
2035 ;; A4 page size is 21.0 by 29.7 cms
2036 ;; The default page settings has 2cm margin on each of the sides. So
2037 ;; the effective text area is 17.0 by 25.7 cm
2038 (defvar org-export-odt-max-image-size '(17.0 . 20.0)
2039 "Limiting dimensions for an embedded image.")
2041 (defun org-odt-do-image-size (probe-method file &optional dpi anchor-type)
2042 (let* ((dpi (or dpi org-export-odt-pixels-per-inch))
2043 (anchor-type (or anchor-type "paragraph"))
2044 (--pixels-to-cms
2045 (function
2046 (lambda (pixels dpi)
2047 (let* ((cms-per-inch 2.54)
2048 (inches (/ pixels dpi)))
2049 (* cms-per-inch inches)))))
2050 (--size-in-cms
2051 (function
2052 (lambda (size-in-pixels dpi)
2053 (and size-in-pixels
2054 (cons (funcall --pixels-to-cms (car size-in-pixels) dpi)
2055 (funcall --pixels-to-cms (cdr size-in-pixels) dpi)))))))
2056 (case probe-method
2057 (emacs
2058 (let ((size-in-pixels
2059 (ignore-errors ; Emacs could be in batch mode
2060 (clear-image-cache)
2061 (image-size (create-image file) 'pixels))))
2062 (funcall --size-in-cms size-in-pixels dpi)))
2063 (imagemagick
2064 (let ((size-in-pixels
2065 (let ((dim (shell-command-to-string
2066 (format "identify -format \"%%w:%%h\" \"%s\"" file))))
2067 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
2068 (cons (string-to-number (match-string 1 dim))
2069 (string-to-number (match-string 2 dim)))))))
2070 (funcall --size-in-cms size-in-pixels dpi)))
2071 (t (cdr (assoc-string anchor-type
2072 org-export-odt-default-image-sizes-alist))))))
2074 (defun org-odt-image-size-from-file (file &optional user-width
2075 user-height scale dpi embed-as)
2076 (unless (file-name-absolute-p file)
2077 (setq file (expand-file-name
2078 file (file-name-directory org-current-export-file))))
2079 (let* (size width height)
2080 (unless (and user-height user-width)
2081 (loop for probe-method in org-export-odt-image-size-probe-method
2082 until size
2083 do (setq size (org-odt-do-image-size
2084 probe-method file dpi embed-as)))
2085 (or size (error "Cannot determine image size, aborting"))
2086 (setq width (car size) height (cdr size)))
2087 (cond
2088 (scale
2089 (setq width (* width scale) height (* height scale)))
2090 ((and user-height user-width)
2091 (setq width user-width height user-height))
2092 (user-height
2093 (setq width (* user-height (/ width height)) height user-height))
2094 (user-width
2095 (setq height (* user-width (/ height width)) width user-width))
2096 (t (ignore)))
2097 ;; ensure that an embedded image fits comfortably within a page
2098 (let ((max-width (car org-export-odt-max-image-size))
2099 (max-height (cdr org-export-odt-max-image-size)))
2100 (when (or (> width max-width) (> height max-height))
2101 (let* ((scale1 (/ max-width width))
2102 (scale2 (/ max-height height))
2103 (scale (min scale1 scale2)))
2104 (setq width (* scale width) height (* scale height)))))
2105 (cons width height)))
2107 (defvar org-odt-entity-counts-plist nil
2108 "Plist of running counters of SEQNOs for each of the CATEGORY-NAMEs.
2109 See `org-odt-entity-labels-alist' for known CATEGORY-NAMEs.")
2111 (defvar org-odt-label-styles
2112 '(("math-formula" "%c" "text" "(%n)")
2113 ("math-label" "(%n)" "text" "(%n)")
2114 ("category-and-value" "%e %n: %c" "category-and-value" "%e %n")
2115 ("value" "%e %n: %c" "value" "%n"))
2116 "Specify how labels are applied and referenced.
2117 This is an alist where each element is of the
2118 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
2119 LABEL-REF-FMT).
2121 LABEL-ATTACH-FMT controls how labels and captions are attached to
2122 an entity. It may contain following specifiers - %e, %n and %c.
2123 %e is replaced with the CATEGORY-NAME. %n is replaced with
2124 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
2125 with CAPTION. See `org-odt-format-label-definition'.
2127 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
2128 are generated. The following XML is generated for a label
2129 reference - \"<text:sequence-ref
2130 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
2131 </text:sequence-ref>\". LABEL-REF-FMT may contain following
2132 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
2133 %n is replaced with SEQNO. See
2134 `org-odt-format-label-reference'.")
2136 (defcustom org-export-odt-category-strings
2137 '(("en" "Table" "Figure" "Equation" "Equation"))
2138 "Specify category strings for various captionable entities.
2139 Captionable entity can be one of a Table, an Embedded Image, a
2140 LaTeX fragment (generated with dvipng) or a Math Formula.
2142 For example, when `org-export-default-language' is \"en\", an
2143 embedded image will be captioned as \"Figure 1: Orgmode Logo\".
2144 If you want the images to be captioned instead as \"Illustration
2145 1: Orgmode Logo\", then modify the entry for \"en\" as shown
2146 below.
2148 \(setq org-export-odt-category-strings
2149 '\(\(\"en\" \"Table\" \"Illustration\"
2150 \"Equation\" \"Equation\"\)\)\)"
2151 :group 'org-export-odt
2152 :version "24.1"
2153 :type '(repeat (list (string :tag "Language tag")
2154 (choice :tag "Table"
2155 (const :tag "Use Default" nil)
2156 (string :tag "Category string"))
2157 (choice :tag "Figure"
2158 (const :tag "Use Default" nil)
2159 (string :tag "Category string"))
2160 (choice :tag "Math Formula"
2161 (const :tag "Use Default" nil)
2162 (string :tag "Category string"))
2163 (choice :tag "Dvipng Image"
2164 (const :tag "Use Default" nil)
2165 (string :tag "Category string")))))
2167 (defvar org-odt-category-map-alist
2168 '(("__Table__" "Table" "value")
2169 ("__Figure__" "Illustration" "value")
2170 ("__MathFormula__" "Text" "math-formula")
2171 ("__DvipngImage__" "Equation" "value")
2172 ;; ("__Table__" "Table" "category-and-value")
2173 ;; ("__Figure__" "Figure" "category-and-value")
2174 ;; ("__DvipngImage__" "Equation" "category-and-value")
2176 "Map a CATEGORY-HANDLE to OD-VARIABLE and LABEL-STYLE.
2177 This is a list where each entry is of the form \\(CATEGORY-HANDLE
2178 OD-VARIABLE LABEL-STYLE\\). CATEGORY_HANDLE identifies the
2179 captionable entity in question. OD-VARIABLE is the OpenDocument
2180 sequence counter associated with the entity. These counters are
2181 declared within
2182 \"<text:sequence-decls>...</text:sequence-decls>\" block of
2183 `org-export-odt-content-template-file'. LABEL-STYLE is a key
2184 into `org-odt-label-styles' and specifies how a given entity
2185 should be captioned and referenced.
2187 The position of a CATEGORY-HANDLE in this list is used as an
2188 index in to per-language entry for
2189 `org-export-odt-category-strings' to retrieve a CATEGORY-NAME.
2190 This CATEGORY-NAME is then used for qualifying the user-specified
2191 captions on export.")
2193 (defun org-odt-add-label-definition (label default-category)
2194 "Create an entry in `org-odt-entity-labels-alist' and return it."
2195 (let* ((label-props (assoc default-category org-odt-category-map-alist))
2196 ;; identify the sequence number
2197 (counter (nth 1 label-props))
2198 (sequence-var (intern counter))
2199 (seqno (1+ (or (plist-get org-odt-entity-counts-plist sequence-var)
2200 0)))
2201 ;; assign an internal label, if user has not provided one
2202 (label (if label (substring-no-properties label)
2203 (format "%s-%s" default-category seqno)))
2204 ;; identify label style
2205 (label-style (nth 2 label-props))
2206 ;; grok language setting
2207 (en-strings (assoc-default "en" org-export-odt-category-strings))
2208 (lang (plist-get org-lparse-opt-plist :language))
2209 (lang-strings (assoc-default lang org-export-odt-category-strings))
2210 ;; retrieve localized category sting
2211 (pos (- (length org-odt-category-map-alist)
2212 (length (memq label-props org-odt-category-map-alist))))
2213 (category (or (nth pos lang-strings) (nth pos en-strings)))
2214 (label-props (list label category counter seqno label-style)))
2215 ;; synchronize internal counters
2216 (setq org-odt-entity-counts-plist
2217 (plist-put org-odt-entity-counts-plist sequence-var seqno))
2218 ;; stash label properties for later retrieval
2219 (push label-props org-odt-entity-labels-alist)
2220 label-props))
2222 (defun org-odt-format-label-definition (caption label category counter
2223 seqno label-style)
2224 (assert label)
2225 (format-spec
2226 (cadr (assoc-string label-style org-odt-label-styles t))
2227 `((?e . ,category)
2228 (?n . ,(org-odt-format-tags
2229 '("<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">" . "</text:sequence>")
2230 (format "%d" seqno) label counter counter))
2231 (?c . ,(or caption "")))))
2233 (defun org-odt-format-label-reference (label category counter
2234 seqno label-style)
2235 (assert label)
2236 (save-match-data
2237 (let* ((fmt (cddr (assoc-string label-style org-odt-label-styles t)))
2238 (fmt1 (car fmt))
2239 (fmt2 (cadr fmt)))
2240 (org-odt-format-tags
2241 '("<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">"
2242 . "</text:sequence-ref>")
2243 (format-spec fmt2 `((?e . ,category)
2244 (?n . ,(format "%d" seqno)))) fmt1 label))))
2246 (defun org-odt-fixup-label-references ()
2247 (goto-char (point-min))
2248 (while (re-search-forward
2249 "<text:sequence-ref text:ref-name=\"\\([^\"]+\\)\">[ \t\n]*</text:sequence-ref>"
2250 nil t)
2251 (let* ((label (match-string 1))
2252 (label-def (assoc label org-odt-entity-labels-alist))
2253 (rpl (and label-def
2254 (apply 'org-odt-format-label-reference label-def))))
2255 (if rpl (replace-match rpl t t)
2256 (org-lparse-warn
2257 (format "Unable to resolve reference to label \"%s\"" label))))))
2259 (defun org-odt-format-entity-caption (label caption category)
2260 (if (not (or label caption)) ""
2261 (apply 'org-odt-format-label-definition caption
2262 (org-odt-add-label-definition label category))))
2264 (defun org-odt-format-tags (tag text &rest args)
2265 (let ((prefix (when org-lparse-encode-pending "@"))
2266 (suffix (when org-lparse-encode-pending "@")))
2267 (apply 'org-lparse-format-tags tag text prefix suffix args)))
2269 (defvar org-odt-manifest-file-entries nil)
2270 (defun org-odt-init-outfile (filename)
2271 (unless (executable-find "zip")
2272 ;; Not at all OSes ship with zip by default
2273 (error "Executable \"zip\" needed for creating OpenDocument files"))
2275 (let* ((content-file (expand-file-name "content.xml" org-odt-zip-dir)))
2276 ;; init conten.xml
2277 (require 'nxml-mode)
2278 (let ((nxml-auto-insert-xml-declaration-flag nil))
2279 (find-file-noselect content-file t))
2281 ;; reset variables
2282 (setq org-odt-manifest-file-entries nil
2283 org-odt-embedded-images-count 0
2284 org-odt-embedded-formulas-count 0
2285 org-odt-entity-labels-alist nil
2286 org-odt-list-stack-stashed nil
2287 org-odt-automatic-styles nil
2288 org-odt-object-counters nil
2289 org-odt-entity-counts-plist nil)
2290 content-file))
2292 (defcustom org-export-odt-prettify-xml nil
2293 "Specify whether or not the xml output should be prettified.
2294 When this option is turned on, `indent-region' is run on all
2295 component xml buffers before they are saved. Turn this off for
2296 regular use. Turn this on if you need to examine the xml
2297 visually."
2298 :group 'org-export-odt
2299 :version "24.1"
2300 :type 'boolean)
2302 (defvar hfy-user-sheet-assoc) ; bound during org-do-lparse
2303 (defun org-odt-save-as-outfile (target opt-plist)
2304 ;; write automatic styles
2305 (org-odt-write-automatic-styles)
2307 ;; write meta file
2308 (org-odt-update-meta-file opt-plist)
2310 ;; write styles file
2311 (when (equal org-lparse-backend 'odt)
2312 (org-odt-update-styles-file opt-plist))
2314 ;; create mimetype file
2315 (let ((mimetype (org-odt-write-mimetype-file org-lparse-backend)))
2316 (org-odt-create-manifest-file-entry mimetype "/" "1.2"))
2318 ;; create a manifest entry for content.xml
2319 (org-odt-create-manifest-file-entry "text/xml" "content.xml")
2321 ;; write out the manifest entries before zipping
2322 (org-odt-write-manifest-file)
2324 (let ((xml-files '("mimetype" "META-INF/manifest.xml" "content.xml"
2325 "meta.xml")))
2326 (when (equal org-lparse-backend 'odt)
2327 (push "styles.xml" xml-files))
2329 ;; save all xml files
2330 (mapc (lambda (file)
2331 (with-current-buffer
2332 (find-file-noselect (expand-file-name file) t)
2333 ;; prettify output if needed
2334 (when org-export-odt-prettify-xml
2335 (indent-region (point-min) (point-max)))
2336 (save-buffer 0)))
2337 xml-files)
2339 (let* ((target-name (file-name-nondirectory target))
2340 (target-dir (file-name-directory target))
2341 (cmds `(("zip" "-mX0" ,target-name "mimetype")
2342 ("zip" "-rmTq" ,target-name "."))))
2343 (when (file-exists-p target)
2344 ;; FIXME: If the file is locked this throws a cryptic error
2345 (delete-file target))
2347 (let ((coding-system-for-write 'no-conversion) exitcode err-string)
2348 (message "Creating odt file...")
2349 (mapc
2350 (lambda (cmd)
2351 (message "Running %s" (mapconcat 'identity cmd " "))
2352 (setq err-string
2353 (with-output-to-string
2354 (setq exitcode
2355 (apply 'call-process (car cmd)
2356 nil standard-output nil (cdr cmd)))))
2357 (or (zerop exitcode)
2358 (ignore (message "%s" err-string))
2359 (error "Unable to create odt file (%S)" exitcode)))
2360 cmds))
2362 ;; move the file from outdir to target-dir
2363 (rename-file target-name target-dir)))
2365 (message "Created %s" target)
2366 (set-buffer (find-file-noselect target t)))
2368 (defconst org-odt-manifest-file-entry-tag
2370 <manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
2372 (defun org-odt-create-manifest-file-entry (&rest args)
2373 (push args org-odt-manifest-file-entries))
2375 (defun org-odt-write-manifest-file ()
2376 (make-directory "META-INF")
2377 (let ((manifest-file (expand-file-name "META-INF/manifest.xml")))
2378 (with-current-buffer
2379 (let ((nxml-auto-insert-xml-declaration-flag nil))
2380 (find-file-noselect manifest-file t))
2381 (insert
2382 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
2383 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
2384 (mapc
2385 (lambda (file-entry)
2386 (let* ((version (nth 2 file-entry))
2387 (extra (if version
2388 (format " manifest:version=\"%s\"" version)
2389 "")))
2390 (insert
2391 (format org-odt-manifest-file-entry-tag
2392 (nth 0 file-entry) (nth 1 file-entry) extra))))
2393 org-odt-manifest-file-entries)
2394 (insert "\n</manifest:manifest>"))))
2396 (defun org-odt-update-meta-file (opt-plist)
2397 (let ((date (org-odt-format-date (plist-get opt-plist :date)))
2398 (author (or (plist-get opt-plist :author) ""))
2399 (email (plist-get opt-plist :email))
2400 (keywords (plist-get opt-plist :keywords))
2401 (description (plist-get opt-plist :description))
2402 (title (plist-get opt-plist :title)))
2403 (write-region
2404 (concat
2405 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
2406 <office:document-meta
2407 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
2408 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2409 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
2410 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
2411 xmlns:ooo=\"http://openoffice.org/2004/office\"
2412 office:version=\"1.2\">
2413 <office:meta>" "\n"
2414 (org-odt-format-author)
2415 (org-odt-format-tags
2416 '("\n<meta:initial-creator>" . "</meta:initial-creator>") author)
2417 (org-odt-format-tags '("\n<dc:date>" . "</dc:date>") date)
2418 (org-odt-format-tags
2419 '("\n<meta:creation-date>" . "</meta:creation-date>") date)
2420 (org-odt-format-tags '("\n<meta:generator>" . "</meta:generator>")
2421 (when org-export-creator-info
2422 (format "Org-%s/Emacs-%s"
2423 (org-version)
2424 emacs-version)))
2425 (org-odt-format-tags '("\n<meta:keyword>" . "</meta:keyword>") keywords)
2426 (org-odt-format-tags '("\n<dc:subject>" . "</dc:subject>") description)
2427 (org-odt-format-tags '("\n<dc:title>" . "</dc:title>") title)
2428 "\n"
2429 " </office:meta>" "</office:document-meta>")
2430 nil (expand-file-name "meta.xml")))
2432 ;; create a manifest entry for meta.xml
2433 (org-odt-create-manifest-file-entry "text/xml" "meta.xml"))
2435 (defun org-odt-update-styles-file (opt-plist)
2436 ;; write styles file
2437 (let ((styles-file (plist-get opt-plist :odt-styles-file)))
2438 (org-odt-copy-styles-file (and styles-file
2439 (read (org-trim styles-file)))))
2441 ;; Update styles.xml - take care of outline numbering
2442 (with-current-buffer
2443 (find-file-noselect (expand-file-name "styles.xml") t)
2444 ;; Don't make automatic backup of styles.xml file. This setting
2445 ;; prevents the backed-up styles.xml file from being zipped in to
2446 ;; odt file. This is more of a hackish fix. Better alternative
2447 ;; would be to fix the zip command so that the output odt file
2448 ;; includes only the needed files and excludes any auto-generated
2449 ;; extra files like backups and auto-saves etc etc. Note that
2450 ;; currently the zip command zips up the entire temp directory so
2451 ;; that any auto-generated files created under the hood ends up in
2452 ;; the resulting odt file.
2453 (set (make-local-variable 'backup-inhibited) t)
2455 ;; Import local setting of `org-export-with-section-numbers'
2456 (org-lparse-bind-local-variables opt-plist)
2457 (org-odt-configure-outline-numbering
2458 (if org-export-with-section-numbers org-export-headline-levels 0)))
2460 ;; Write custom styles for source blocks
2461 (org-odt-insert-custom-styles-for-srcblocks
2462 (mapconcat
2463 (lambda (style)
2464 (format " %s\n" (cddr style)))
2465 hfy-user-sheet-assoc "")))
2467 (defun org-odt-write-mimetype-file (format)
2468 ;; create mimetype file
2469 (let ((mimetype
2470 (case format
2471 (odt "application/vnd.oasis.opendocument.text")
2472 (odf "application/vnd.oasis.opendocument.formula")
2473 (t (error "Unknown OpenDocument backend %S" org-lparse-backend)))))
2474 (write-region mimetype nil (expand-file-name "mimetype"))
2475 mimetype))
2477 (defun org-odt-finalize-outfile ()
2478 (org-odt-delete-empty-paragraphs))
2480 (defun org-odt-delete-empty-paragraphs ()
2481 (goto-char (point-min))
2482 (let ((open "<text:p[^>]*>")
2483 (close "</text:p>"))
2484 (while (re-search-forward (format "%s[ \r\n\t]*%s" open close) nil t)
2485 (replace-match ""))))
2487 (defcustom org-export-odt-convert-processes
2488 '(("LibreOffice"
2489 "soffice --headless --convert-to %f%x --outdir %d %i")
2490 ("unoconv"
2491 "unoconv -f %f -o %d %i"))
2492 "Specify a list of document converters and their usage.
2493 The converters in this list are offered as choices while
2494 customizing `org-export-odt-convert-process'.
2496 This variable is a list where each element is of the
2497 form (CONVERTER-NAME CONVERTER-CMD). CONVERTER-NAME is the name
2498 of the converter. CONVERTER-CMD is the shell command for the
2499 converter and can contain format specifiers. These format
2500 specifiers are interpreted as below:
2502 %i input file name in full
2503 %I input file name as a URL
2504 %f format of the output file
2505 %o output file name in full
2506 %O output file name as a URL
2507 %d output dir in full
2508 %D output dir as a URL.
2509 %x extra options as set in `org-export-odt-convert-capabilities'."
2510 :group 'org-export-odt
2511 :version "24.1"
2512 :type
2513 '(choice
2514 (const :tag "None" nil)
2515 (alist :tag "Converters"
2516 :key-type (string :tag "Converter Name")
2517 :value-type (group (string :tag "Command line")))))
2519 (defcustom org-export-odt-convert-process "LibreOffice"
2520 "Use this converter to convert from \"odt\" format to other formats.
2521 During customization, the list of converter names are populated
2522 from `org-export-odt-convert-processes'."
2523 :group 'org-export-odt
2524 :version "24.1"
2525 :type '(choice :convert-widget
2526 (lambda (w)
2527 (apply 'widget-convert (widget-type w)
2528 (eval (car (widget-get w :args)))))
2529 `((const :tag "None" nil)
2530 ,@(mapcar (lambda (c)
2531 `(const :tag ,(car c) ,(car c)))
2532 org-export-odt-convert-processes))))
2534 (defcustom org-export-odt-convert-capabilities
2535 '(("Text"
2536 ("odt" "ott" "doc" "rtf" "docx")
2537 (("pdf" "pdf") ("odt" "odt") ("rtf" "rtf") ("ott" "ott")
2538 ("doc" "doc" ":\"MS Word 97\"") ("docx" "docx") ("html" "html")))
2539 ("Web"
2540 ("html")
2541 (("pdf" "pdf") ("odt" "odt") ("html" "html")))
2542 ("Spreadsheet"
2543 ("ods" "ots" "xls" "csv" "xlsx")
2544 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
2545 ("xls" "xls") ("xlsx" "xlsx")))
2546 ("Presentation"
2547 ("odp" "otp" "ppt" "pptx")
2548 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("otp" "otp") ("ppt" "ppt")
2549 ("pptx" "pptx") ("odg" "odg"))))
2550 "Specify input and output formats of `org-export-odt-convert-process'.
2551 More correctly, specify the set of input and output formats that
2552 the user is actually interested in.
2554 This variable is an alist where each element is of the
2555 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
2556 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
2557 alist where each element is of the form (OUTPUT-FMT
2558 OUTPUT-FILE-EXTENSION EXTRA-OPTIONS).
2560 The variable is interpreted as follows:
2561 `org-export-odt-convert-process' can take any document that is in
2562 INPUT-FMT-LIST and produce any document that is in the
2563 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
2564 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
2565 serves dual purposes:
2566 - It is used for populating completion candidates during
2567 `org-export-odt-convert' commands.
2568 - It is used as the value of \"%f\" specifier in
2569 `org-export-odt-convert-process'.
2571 EXTRA-OPTIONS is used as the value of \"%x\" specifier in
2572 `org-export-odt-convert-process'.
2574 DOCUMENT-CLASS is used to group a set of file formats in
2575 INPUT-FMT-LIST in to a single class.
2577 Note that this variable inherently captures how LibreOffice based
2578 converters work. LibreOffice maps documents of various formats
2579 to classes like Text, Web, Spreadsheet, Presentation etc and
2580 allow document of a given class (irrespective of it's source
2581 format) to be converted to any of the export formats associated
2582 with that class.
2584 See default setting of this variable for an typical
2585 configuration."
2586 :group 'org-export-odt
2587 :version "24.1"
2588 :type
2589 '(choice
2590 (const :tag "None" nil)
2591 (alist :tag "Capabilities"
2592 :key-type (string :tag "Document Class")
2593 :value-type
2594 (group (repeat :tag "Input formats" (string :tag "Input format"))
2595 (alist :tag "Output formats"
2596 :key-type (string :tag "Output format")
2597 :value-type
2598 (group (string :tag "Output file extension")
2599 (choice
2600 (const :tag "None" nil)
2601 (string :tag "Extra options"))))))))
2603 (declare-function org-create-math-formula "org"
2604 (latex-frag &optional mathml-file))
2606 ;;;###autoload
2607 (defun org-export-odt-convert (&optional in-file out-fmt prefix-arg)
2608 "Convert IN-FILE to format OUT-FMT using a command line converter.
2609 IN-FILE is the file to be converted. If unspecified, it defaults
2610 to variable `buffer-file-name'. OUT-FMT is the desired output
2611 format. Use `org-export-odt-convert-process' as the converter.
2612 If PREFIX-ARG is non-nil then the newly converted file is opened
2613 using `org-open-file'."
2614 (interactive
2615 (append (org-lparse-convert-read-params) current-prefix-arg))
2616 (org-lparse-do-convert in-file out-fmt prefix-arg))
2618 (defun org-odt-get (what &optional opt-plist)
2619 (case what
2620 (BACKEND 'odt)
2621 (EXPORT-DIR (org-export-directory :html opt-plist))
2622 (FILE-NAME-EXTENSION "odt")
2623 (EXPORT-BUFFER-NAME "*Org ODT Export*")
2624 (ENTITY-CONTROL org-odt-entity-control-callbacks-alist)
2625 (ENTITY-FORMAT org-odt-entity-format-callbacks-alist)
2626 (INIT-METHOD 'org-odt-init-outfile)
2627 (FINAL-METHOD 'org-odt-finalize-outfile)
2628 (SAVE-METHOD 'org-odt-save-as-outfile)
2629 (CONVERT-METHOD
2630 (and org-export-odt-convert-process
2631 (cadr (assoc-string org-export-odt-convert-process
2632 org-export-odt-convert-processes t))))
2633 (CONVERT-CAPABILITIES
2634 (and org-export-odt-convert-process
2635 (cadr (assoc-string org-export-odt-convert-process
2636 org-export-odt-convert-processes t))
2637 org-export-odt-convert-capabilities))
2638 (TOPLEVEL-HLEVEL 1)
2639 (SPECIAL-STRING-REGEXPS org-export-odt-special-string-regexps)
2640 (INLINE-IMAGES 'maybe)
2641 (INLINE-IMAGE-EXTENSIONS '("png" "jpeg" "jpg" "gif" "svg"))
2642 (PLAIN-TEXT-MAP '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
2643 (TABLE-FIRST-COLUMN-AS-LABELS nil)
2644 (FOOTNOTE-SEPARATOR (org-lparse-format 'FONTIFY "," 'superscript))
2645 (CODING-SYSTEM-FOR-WRITE 'utf-8)
2646 (CODING-SYSTEM-FOR-SAVE 'utf-8)
2647 (t (error "Unknown property: %s" what))))
2649 (defvar org-lparse-latex-fragment-fallback) ; set by org-do-lparse
2650 (defun org-export-odt-do-preprocess-latex-fragments ()
2651 "Convert LaTeX fragments to images."
2652 (let* ((latex-frag-opt (plist-get org-lparse-opt-plist :LaTeX-fragments))
2653 (latex-frag-opt ; massage the options
2654 (or (and (member latex-frag-opt '(mathjax t))
2655 (not (and (fboundp 'org-format-latex-mathml-available-p)
2656 (org-format-latex-mathml-available-p)))
2657 (prog1 org-lparse-latex-fragment-fallback
2658 (org-lparse-warn
2659 (concat
2660 "LaTeX to MathML converter not available. "
2661 (format "Using %S instead."
2662 org-lparse-latex-fragment-fallback)))))
2663 latex-frag-opt))
2664 cache-dir display-msg)
2665 (cond
2666 ((eq latex-frag-opt 'dvipng)
2667 (setq cache-dir org-latex-preview-ltxpng-directory)
2668 (setq display-msg "Creating LaTeX image %s"))
2669 ((member latex-frag-opt '(mathjax t))
2670 (setq latex-frag-opt 'mathml)
2671 (setq cache-dir "ltxmathml/")
2672 (setq display-msg "Creating MathML formula %s")))
2673 (when (and org-current-export-file)
2674 (org-format-latex
2675 (concat cache-dir (file-name-sans-extension
2676 (file-name-nondirectory org-current-export-file)))
2677 org-current-export-dir nil display-msg
2678 nil nil latex-frag-opt))))
2680 (defadvice org-format-latex-as-mathml
2681 (after org-odt-protect-latex-fragment activate)
2682 "Encode LaTeX fragment as XML.
2683 Do this when translation to MathML fails."
2684 (when (or (not (> (length ad-return-value) 0))
2685 (get-text-property 0 'org-protected ad-return-value))
2686 (setq ad-return-value
2687 (org-propertize (org-odt-encode-plain-text (ad-get-arg 0))
2688 'org-protected t))))
2690 (defun org-export-odt-preprocess-latex-fragments ()
2691 (when (equal org-export-current-backend 'odt)
2692 (org-export-odt-do-preprocess-latex-fragments)))
2694 (defun org-export-odt-preprocess-label-references ()
2695 (goto-char (point-min))
2696 (let (label label-components category value pretty-label)
2697 (while (re-search-forward "\\\\ref{\\([^{}\n]+\\)}" nil t)
2698 (org-if-unprotected-at (match-beginning 1)
2699 (replace-match
2700 (let ((org-lparse-encode-pending t)
2701 (label (match-string 1)))
2702 ;; markup generated below is mostly an eye-candy. At
2703 ;; pre-processing stage, there is no information on which
2704 ;; entity a label reference points to. The actual markup
2705 ;; is generated as part of `org-odt-fixup-label-references'
2706 ;; which gets called at the fag end of export. By this
2707 ;; time we would have seen and collected all the label
2708 ;; definitions in `org-odt-entity-labels-alist'.
2709 (org-odt-format-tags
2710 '("<text:sequence-ref text:ref-name=\"%s\">" .
2711 "</text:sequence-ref>")
2712 "" (org-add-props label '(org-protected t)))) t t)))))
2714 ;; process latex fragments as part of
2715 ;; `org-export-preprocess-after-blockquote-hook'. Note that this hook
2716 ;; is the one that is closest and well before the call to
2717 ;; `org-export-attach-captions-and-attributes' in
2718 ;; `org-export-preprocess-string'. The above arrangement permits
2719 ;; captions, labels and attributes to be attached to png images
2720 ;; generated out of latex equations.
2721 (add-hook 'org-export-preprocess-after-blockquote-hook
2722 'org-export-odt-preprocess-latex-fragments)
2724 (defun org-export-odt-preprocess (parameters)
2725 (org-export-odt-preprocess-label-references))
2727 (declare-function archive-zip-extract "arc-mode" (archive name))
2728 (defun org-odt-zip-extract-one (archive member &optional target)
2729 (require 'arc-mode)
2730 (let* ((target (or target default-directory))
2731 (archive (expand-file-name archive))
2732 (archive-zip-extract
2733 (list "unzip" "-qq" "-o" "-d" target))
2734 exit-code command-output)
2735 (setq command-output
2736 (with-temp-buffer
2737 (setq exit-code (archive-zip-extract archive member))
2738 (buffer-string)))
2739 (unless (zerop exit-code)
2740 (message command-output)
2741 (error "Extraction failed"))))
2743 (defun org-odt-zip-extract (archive members &optional target)
2744 (when (atom members) (setq members (list members)))
2745 (mapc (lambda (member)
2746 (org-odt-zip-extract-one archive member target))
2747 members))
2749 (defun org-odt-copy-styles-file (&optional styles-file)
2750 ;; Non-availability of styles.xml is not a critical error. For now
2751 ;; throw an error purely for aesthetic reasons.
2752 (setq styles-file (or styles-file
2753 org-export-odt-styles-file
2754 (expand-file-name "OrgOdtStyles.xml"
2755 org-odt-styles-dir)
2756 (error "org-odt: Missing styles file?")))
2757 (cond
2758 ((listp styles-file)
2759 (let ((archive (nth 0 styles-file))
2760 (members (nth 1 styles-file)))
2761 (org-odt-zip-extract archive members)
2762 (mapc
2763 (lambda (member)
2764 (when (org-file-image-p member)
2765 (let* ((image-type (file-name-extension member))
2766 (media-type (format "image/%s" image-type)))
2767 (org-odt-create-manifest-file-entry media-type member))))
2768 members)))
2769 ((and (stringp styles-file) (file-exists-p styles-file))
2770 (let ((styles-file-type (file-name-extension styles-file)))
2771 (cond
2772 ((string= styles-file-type "xml")
2773 (copy-file styles-file "styles.xml" t))
2774 ((member styles-file-type '("odt" "ott"))
2775 (org-odt-zip-extract styles-file "styles.xml")))))
2777 (error (format "Invalid specification of styles.xml file: %S"
2778 org-export-odt-styles-file))))
2780 ;; create a manifest entry for styles.xml
2781 (org-odt-create-manifest-file-entry "text/xml" "styles.xml"))
2783 (defun org-odt-configure-outline-numbering (level)
2784 "Outline numbering is retained only upto LEVEL.
2785 To disable outline numbering pass a LEVEL of 0."
2786 (goto-char (point-min))
2787 (let ((regex
2788 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
2789 (replacement
2790 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
2791 (while (re-search-forward regex nil t)
2792 (when (> (string-to-number (match-string 2)) level)
2793 (replace-match replacement t nil))))
2794 (save-buffer 0))
2796 ;;;###autoload
2797 (defun org-export-as-odf (latex-frag &optional odf-file)
2798 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
2799 Use `org-create-math-formula' to convert LATEX-FRAG first to
2800 MathML. When invoked as an interactive command, use
2801 `org-latex-regexps' to infer LATEX-FRAG from currently active
2802 region. If no LaTeX fragments are found, prompt for it. Push
2803 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
2804 non-nil."
2805 (interactive
2806 `(,(let (frag)
2807 (setq frag (and (setq frag (and (org-region-active-p)
2808 (buffer-substring (region-beginning)
2809 (region-end))))
2810 (loop for e in org-latex-regexps
2811 thereis (when (string-match (nth 1 e) frag)
2812 (match-string (nth 2 e) frag)))))
2813 (read-string "LaTeX Fragment: " frag nil frag))
2814 ,(let ((odf-filename (expand-file-name
2815 (concat
2816 (file-name-sans-extension
2817 (or (file-name-nondirectory buffer-file-name)))
2818 "." "odf")
2819 (file-name-directory buffer-file-name))))
2820 (read-file-name "ODF filename: " nil odf-filename nil
2821 (file-name-nondirectory odf-filename)))))
2822 (org-odt-cleanup-xml-buffers
2823 (let* ((org-lparse-backend 'odf)
2824 org-lparse-opt-plist
2825 (filename (or odf-file
2826 (expand-file-name
2827 (concat
2828 (file-name-sans-extension
2829 (or (file-name-nondirectory buffer-file-name)))
2830 "." "odf")
2831 (file-name-directory buffer-file-name))))
2832 (buffer (find-file-noselect (org-odt-init-outfile filename)))
2833 (coding-system-for-write 'utf-8)
2834 (save-buffer-coding-system 'utf-8))
2835 (set-buffer buffer)
2836 (set-buffer-file-coding-system coding-system-for-write)
2837 (let ((mathml (org-create-math-formula latex-frag)))
2838 (unless mathml (error "No Math formula created"))
2839 (insert mathml)
2840 (or (org-export-push-to-kill-ring
2841 (upcase (symbol-name org-lparse-backend)))
2842 (message "Exporting... done")))
2843 (org-odt-save-as-outfile filename nil))))
2845 ;;;###autoload
2846 (defun org-export-as-odf-and-open ()
2847 "Export LaTeX fragment as OpenDocument formula and immediately open it.
2848 Use `org-export-as-odf' to read LaTeX fragment and OpenDocument
2849 formula file."
2850 (interactive)
2851 (org-lparse-and-open
2852 nil nil nil (call-interactively 'org-export-as-odf)))
2854 (provide 'org-odt)
2856 ;; Local variables:
2857 ;; generated-autoload-file: "org-loaddefs.el"
2858 ;; End:
2860 ;;; org-odt.el ends here