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