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