org-e-odt.el (org-e-odt--export-wrap): Use `condition-case'
[org-mode.git] / contrib / lisp / org-e-odt.el
blobb52d5efe78c4e51820653a640dcd2455405d23d4
1 ;;; org-e-odt.el --- OpenDocument Text exporter for Org-mode
3 ;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
5 ;; Author: Jambunathan K <kjambunathan at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp
7 ;; Homepage: http://orgmode.org
9 ;; This file is not part of GNU Emacs.
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;;; Code:
28 (eval-when-compile
29 (require 'cl)
30 (require 'table))
31 (require 'format-spec)
32 (require 'org-export)
34 ;;; Define Back-End
36 (org-export-define-backend e-odt
37 ((bold . org-e-odt-bold)
38 (center-block . org-e-odt-center-block)
39 (clock . org-e-odt-clock)
40 (code . org-e-odt-code)
41 (drawer . org-e-odt-drawer)
42 (dynamic-block . org-e-odt-dynamic-block)
43 (entity . org-e-odt-entity)
44 (example-block . org-e-odt-example-block)
45 (export-block . org-e-odt-export-block)
46 (export-snippet . org-e-odt-export-snippet)
47 (fixed-width . org-e-odt-fixed-width)
48 (footnote-definition . org-e-odt-footnote-definition)
49 (footnote-reference . org-e-odt-footnote-reference)
50 (headline . org-e-odt-headline)
51 (horizontal-rule . org-e-odt-horizontal-rule)
52 (inline-src-block . org-e-odt-inline-src-block)
53 (inlinetask . org-e-odt-inlinetask)
54 (italic . org-e-odt-italic)
55 (item . org-e-odt-item)
56 (keyword . org-e-odt-keyword)
57 (latex-environment . org-e-odt-latex-environment)
58 (latex-fragment . org-e-odt-latex-fragment)
59 (line-break . org-e-odt-line-break)
60 (link . org-e-odt-link)
61 (macro . org-e-odt-macro)
62 (paragraph . org-e-odt-paragraph)
63 (plain-list . org-e-odt-plain-list)
64 (plain-text . org-e-odt-plain-text)
65 (planning . org-e-odt-planning)
66 (property-drawer . org-e-odt-property-drawer)
67 (quote-block . org-e-odt-quote-block)
68 (quote-section . org-e-odt-quote-section)
69 (radio-target . org-e-odt-radio-target)
70 (section . org-e-odt-section)
71 (special-block . org-e-odt-special-block)
72 (src-block . org-e-odt-src-block)
73 (statistics-cookie . org-e-odt-statistics-cookie)
74 (strike-through . org-e-odt-strike-through)
75 (subscript . org-e-odt-subscript)
76 (superscript . org-e-odt-superscript)
77 (table . org-e-odt-table)
78 (table-cell . org-e-odt-table-cell)
79 (table-row . org-e-odt-table-row)
80 (target . org-e-odt-target)
81 (template . org-e-odt-template)
82 (timestamp . org-e-odt-timestamp)
83 (underline . org-e-odt-underline)
84 (verbatim . org-e-odt-verbatim)
85 (verse-block . org-e-odt-verse-block))
86 :export-block "ODT"
87 :options-alist
88 ((:odt-styles-file "ODT_STYLES_FILE" nil nil t)
89 (:LaTeX-fragments nil "LaTeX" org-export-with-LaTeX-fragments)))
92 ;;; Dependencies
94 ;;; Hooks
96 ;;; Function Declarations
98 (declare-function org-id-find-id-file "org-id" (id))
99 (declare-function hfy-face-to-style "htmlfontify" (fn))
100 (declare-function hfy-face-or-def-to-name "htmlfontify" (fn))
101 (declare-function archive-zip-extract "arc-mode" (archive name))
102 (declare-function org-create-math-formula "org" (latex-frag &optional mathml-file))
103 (declare-function browse-url-file-url "browse-url" (file))
104 (declare-function org-solidify-link-text "org-exp" (s &optional alist))
109 ;;; Internal Variables
111 (defconst org-e-odt-lib-dir
112 (file-name-directory load-file-name)
113 "Location of ODT exporter.
114 Use this to infer values of `org-e-odt-styles-dir' and
115 `org-e-odt-schema-dir'.")
117 (defvar org-e-odt-data-dir
118 (expand-file-name "../../etc/" org-e-odt-lib-dir)
119 "Data directory for ODT exporter.
120 Use this to infer values of `org-e-odt-styles-dir' and
121 `org-e-odt-schema-dir'.")
123 (defconst org-e-odt-special-string-regexps
124 '(("\\\\-" . "&#x00ad;\\1") ; shy
125 ("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
126 ("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
127 ("\\.\\.\\." . "&#x2026;")) ; hellip
128 "Regular expressions for special string conversion.")
130 (defconst org-e-odt-schema-dir-list
131 (list
132 (and org-e-odt-data-dir
133 (expand-file-name "./schema/" org-e-odt-data-dir)) ; bail out
134 (eval-when-compile
135 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
136 (expand-file-name "./schema/" org-e-odt-data-dir))))
137 "List of directories to search for OpenDocument schema files.
138 Use this list to set the default value of
139 `org-e-odt-schema-dir'. The entries in this list are
140 populated heuristically based on the values of `org-e-odt-lib-dir'
141 and `org-e-odt-data-dir'.")
143 (defconst org-e-odt-styles-dir-list
144 (list
145 (and org-e-odt-data-dir
146 (expand-file-name "./styles/" org-e-odt-data-dir)) ; bail out
147 (eval-when-compile
148 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
149 (expand-file-name "./styles/" org-e-odt-data-dir)))
150 (expand-file-name "../../etc/styles/" org-e-odt-lib-dir) ; git
151 (expand-file-name "./etc/styles/" org-e-odt-lib-dir) ; elpa
152 (expand-file-name "./org/" data-directory) ; system
154 "List of directories to search for OpenDocument styles files.
155 See `org-e-odt-styles-dir'. The entries in this list are populated
156 heuristically based on the values of `org-e-odt-lib-dir' and
157 `org-e-odt-data-dir'.")
159 (defconst org-e-odt-styles-dir
160 (let* ((styles-dir
161 (catch 'styles-dir
162 (message "Debug (org-e-odt): Searching for OpenDocument styles files...")
163 (mapc (lambda (styles-dir)
164 (when styles-dir
165 (message "Debug (org-e-odt): Trying %s..." styles-dir)
166 (when (and (file-readable-p
167 (expand-file-name
168 "OrgOdtContentTemplate.xml" styles-dir))
169 (file-readable-p
170 (expand-file-name
171 "OrgOdtStyles.xml" styles-dir)))
172 (message "Debug (org-e-odt): Using styles under %s"
173 styles-dir)
174 (throw 'styles-dir styles-dir))))
175 org-e-odt-styles-dir-list)
176 nil)))
177 (unless styles-dir
178 (error "Error (org-e-odt): Cannot find factory styles files, aborting"))
179 styles-dir)
180 "Directory that holds auxiliary XML files used by the ODT exporter.
182 This directory contains the following XML files -
183 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
184 XML files are used as the default values of
185 `org-e-odt-styles-file' and
186 `org-e-odt-content-template-file'.
188 The default value of this variable varies depending on the
189 version of org in use and is initialized from
190 `org-e-odt-styles-dir-list'. Note that the user could be using org
191 from one of: org's own private git repository, GNU ELPA tar or
192 standard Emacs.")
194 (defconst org-e-odt-bookmark-prefix "OrgXref.")
196 (defconst org-e-odt-manifest-file-entry-tag
197 "\n<manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
199 (defconst org-e-odt-file-extensions
200 '(("odt" . "OpenDocument Text")
201 ("ott" . "OpenDocument Text Template")
202 ("odm" . "OpenDocument Master Document")
203 ("ods" . "OpenDocument Spreadsheet")
204 ("ots" . "OpenDocument Spreadsheet Template")
205 ("odg" . "OpenDocument Drawing (Graphics)")
206 ("otg" . "OpenDocument Drawing Template")
207 ("odp" . "OpenDocument Presentation")
208 ("otp" . "OpenDocument Presentation Template")
209 ("odi" . "OpenDocument Image")
210 ("odf" . "OpenDocument Formula")
211 ("odc" . "OpenDocument Chart")))
213 (defvar org-e-odt-table-style-format
215 <style:style style:name=\"%s\" style:family=\"table\">
216 <style:table-properties style:rel-width=\"%d%%\" fo:margin-top=\"0cm\" fo:margin-bottom=\"0.20cm\" table:align=\"center\"/>
217 </style:style>
219 "Template for auto-generated Table styles.")
221 (defvar org-e-odt-automatic-styles '()
222 "Registry of automatic styles for various OBJECT-TYPEs.
223 The variable has the following form:
224 \(\(OBJECT-TYPE-A
225 \(\(OBJECT-NAME-A.1 OBJECT-PROPS-A.1\)
226 \(OBJECT-NAME-A.2 OBJECT-PROPS-A.2\) ...\)\)
227 \(OBJECT-TYPE-B
228 \(\(OBJECT-NAME-B.1 OBJECT-PROPS-B.1\)
229 \(OBJECT-NAME-B.2 OBJECT-PROPS-B.2\) ...\)\)
230 ...\).
232 OBJECT-TYPEs could be \"Section\", \"Table\", \"Figure\" etc.
233 OBJECT-PROPS is (typically) a plist created by passing
234 \"#+ATTR_ODT: \" option to `org-e-odt-parse-block-attributes'.
236 Use `org-e-odt-add-automatic-style' to add update this variable.'")
238 (defvar org-e-odt-object-counters nil
239 "Running counters for various OBJECT-TYPEs.
240 Use this to generate automatic names and style-names. See
241 `org-e-odt-add-automatic-style'.")
243 (defvar org-e-odt-src-block-paragraph-format
244 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
245 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
246 <style:background-image/>
247 </style:paragraph-properties>
248 <style:text-properties fo:color=\"%s\"/>
249 </style:style>"
250 "Custom paragraph style for colorized source and example blocks.
251 This style is much the same as that of \"OrgFixedWidthBlock\"
252 except that the foreground and background colors are set
253 according to the default face identified by the `htmlfontify'.")
255 (defvar hfy-optimisations)
256 (defvar org-e-odt-embedded-formulas-count 0)
257 (defvar org-e-odt-entity-frame-styles
258 '(("As-CharImage" "__Figure__" ("OrgInlineImage" nil "as-char"))
259 ("ParagraphImage" "__Figure__" ("OrgDisplayImage" nil "paragraph"))
260 ("PageImage" "__Figure__" ("OrgPageImage" nil "page"))
261 ("CaptionedAs-CharImage" "__Figure__"
262 ("OrgCaptionedImage"
263 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
264 ("OrgInlineImage" nil "as-char"))
265 ("CaptionedParagraphImage" "__Figure__"
266 ("OrgCaptionedImage"
267 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
268 ("OrgImageCaptionFrame" nil "paragraph"))
269 ("CaptionedPageImage" "__Figure__"
270 ("OrgCaptionedImage"
271 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
272 ("OrgPageImageCaptionFrame" nil "page"))
273 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil "as-char"))
274 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil "as-char"))
275 ("CaptionedDisplayFormula" "__MathFormula__"
276 ("OrgCaptionedFormula" nil "paragraph")
277 ("OrgFormulaCaptionFrame" nil "as-char"))))
279 (defvar org-e-odt-embedded-images-count 0)
280 (defvar org-e-odt-image-size-probe-method
281 (append (and (executable-find "identify") '(imagemagick)) ; See Bug#10675
282 '(emacs fixed))
283 "Ordered list of methods for determining image sizes.")
285 (defvar org-e-odt-default-image-sizes-alist
286 '(("as-char" . (5 . 0.4))
287 ("paragraph" . (5 . 5)))
288 "Hardcoded image dimensions one for each of the anchor
289 methods.")
291 ;; A4 page size is 21.0 by 29.7 cms
292 ;; The default page settings has 2cm margin on each of the sides. So
293 ;; the effective text area is 17.0 by 25.7 cm
294 (defvar org-e-odt-max-image-size '(17.0 . 20.0)
295 "Limiting dimensions for an embedded image.")
297 (defvar org-e-odt-label-styles
298 '(("math-formula" "%c" "text" "(%n)")
299 ("math-label" "(%n)" "text" "(%n)")
300 ("category-and-value" "%e %n: %c" "category-and-value" "%e %n")
301 ("value" "%e %n: %c" "value" "%n"))
302 "Specify how labels are applied and referenced.
303 This is an alist where each element is of the
304 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
305 LABEL-REF-FMT).
307 LABEL-ATTACH-FMT controls how labels and captions are attached to
308 an entity. It may contain following specifiers - %e, %n and %c.
309 %e is replaced with the CATEGORY-NAME. %n is replaced with
310 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
311 with CAPTION. See `org-e-odt-format-label-definition'.
313 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
314 are generated. The following XML is generated for a label
315 reference - \"<text:sequence-ref
316 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
317 </text:sequence-ref>\". LABEL-REF-FMT may contain following
318 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
319 %n is replaced with SEQNO. See
320 `org-e-odt-format-label-reference'.")
322 (defvar org-e-odt-category-map-alist
323 '(("__Table__" "Table" "value" "Table")
324 ("__Figure__" "Illustration" "value" "Figure")
325 ("__MathFormula__" "Text" "math-formula" "Equation")
326 ("__DvipngImage__" "Equation" "value" "Equation")
327 ("__Listing__" "Listing" "value" "Listing")
328 ;; ("__Table__" "Table" "category-and-value")
329 ;; ("__Figure__" "Figure" "category-and-value")
330 ;; ("__DvipngImage__" "Equation" "category-and-value")
332 "Map a CATEGORY-HANDLE to OD-VARIABLE and LABEL-STYLE.
333 This is a list where each entry is of the form \\(CATEGORY-HANDLE
334 OD-VARIABLE LABEL-STYLE CATEGORY-NAME\\). CATEGORY_HANDLE
335 identifies the captionable entity in question. OD-VARIABLE is
336 the OpenDocument sequence counter associated with the entity.
337 These counters are declared within
338 \"<text:sequence-decls>...</text:sequence-decls>\" block of
339 `org-e-odt-content-template-file'. LABEL-STYLE is a key into
340 `org-e-odt-label-styles' and specifies how a given entity should
341 be captioned and referenced. CATEGORY-NAME is used for
342 qualifying captions on export. You can modify the CATEGORY-NAME
343 used in the exported document by modifying
344 `org-export-dictionary'. For example, an embedded image in an
345 English document is captioned as \"Figure 1: Orgmode Logo\", by
346 default. If you want the image to be captioned as \"Illustration
347 1: Orgmode Logo\" instead, install an entry in
348 `org-export-dictionary' which translates \"Figure\" to
349 \"Illustration\" when the language is \"en\" and encoding is
350 `:utf-8'.")
352 (defvar org-e-odt-manifest-file-entries nil)
353 (defvar hfy-user-sheet-assoc)
355 (defvar org-e-odt-zip-dir nil
356 "Temporary work directory for OpenDocument exporter.")
360 ;;; User Configuration Variables
362 (defgroup org-export-e-odt nil
363 "Options for exporting Org mode files to ODT."
364 :tag "Org Export ODT"
365 :group 'org-export)
368 ;;;; Debugging
370 (defcustom org-e-odt-prettify-xml nil
371 "Specify whether or not the xml output should be prettified.
372 When this option is turned on, `indent-region' is run on all
373 component xml buffers before they are saved. Turn this off for
374 regular use. Turn this on if you need to examine the xml
375 visually."
376 :group 'org-export-e-odt
377 :version "24.1"
378 :type 'boolean)
381 ;;;; Document schema
383 (defcustom org-e-odt-schema-dir
384 (let* ((schema-dir
385 (catch 'schema-dir
386 (message "Debug (org-e-odt): Searching for OpenDocument schema files...")
387 (mapc
388 (lambda (schema-dir)
389 (when schema-dir
390 (message "Debug (org-e-odt): Trying %s..." schema-dir)
391 (when (and (file-readable-p
392 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc"
393 schema-dir))
394 (file-readable-p
395 (expand-file-name "od-schema-v1.2-cs01.rnc"
396 schema-dir))
397 (file-readable-p
398 (expand-file-name "schemas.xml" schema-dir)))
399 (message "Debug (org-e-odt): Using schema files under %s"
400 schema-dir)
401 (throw 'schema-dir schema-dir))))
402 org-e-odt-schema-dir-list)
403 (message "Debug (org-e-odt): No OpenDocument schema files installed")
404 nil)))
405 schema-dir)
406 "Directory that contains OpenDocument schema files.
408 This directory contains:
409 1. rnc files for OpenDocument schema
410 2. a \"schemas.xml\" file that specifies locating rules needed
411 for auto validation of OpenDocument XML files.
413 Use the customize interface to set this variable. This ensures
414 that `rng-schema-locating-files' is updated and auto-validation
415 of OpenDocument XML takes place based on the value
416 `rng-nxml-auto-validate-flag'.
418 The default value of this variable varies depending on the
419 version of org in use and is initialized from
420 `org-e-odt-schema-dir-list'. The OASIS schema files are available
421 only in the org's private git repository. It is *not* bundled
422 with GNU ELPA tar or standard Emacs distribution."
423 :type '(choice
424 (const :tag "Not set" nil)
425 (directory :tag "Schema directory"))
426 :group 'org-export-e-odt
427 :version "24.1"
428 :set
429 (lambda (var value)
430 "Set `org-e-odt-schema-dir'.
431 Also add it to `rng-schema-locating-files'."
432 (let ((schema-dir value))
433 (set var
434 (if (and
435 (file-readable-p
436 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir))
437 (file-readable-p
438 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir))
439 (file-readable-p
440 (expand-file-name "schemas.xml" schema-dir)))
441 schema-dir
442 (when value
443 (message "Error (org-e-odt): %s has no OpenDocument schema files"
444 value))
445 nil)))
446 (when org-e-odt-schema-dir
447 (eval-after-load 'rng-loc
448 '(add-to-list 'rng-schema-locating-files
449 (expand-file-name "schemas.xml"
450 org-e-odt-schema-dir))))))
453 ;;;; Document styles
455 (defcustom org-e-odt-content-template-file nil
456 "Template file for \"content.xml\".
457 The exporter embeds the exported content just before
458 \"</office:text>\" element.
460 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
461 under `org-e-odt-styles-dir' is used."
462 :type 'file
463 :group 'org-export-e-odt
464 :version "24.1")
466 (defcustom org-e-odt-styles-file nil
467 "Default styles file for use with ODT export.
468 Valid values are one of:
469 1. nil
470 2. path to a styles.xml file
471 3. path to a *.odt or a *.ott file
472 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
473 ...))
475 In case of option 1, an in-built styles.xml is used. See
476 `org-e-odt-styles-dir' for more information.
478 In case of option 3, the specified file is unzipped and the
479 styles.xml embedded therein is used.
481 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
482 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
483 generated odt file. Use relative path for specifying the
484 FILE-MEMBERS. styles.xml must be specified as one of the
485 FILE-MEMBERS.
487 Use options 1, 2 or 3 only if styles.xml alone suffices for
488 achieving the desired formatting. Use option 4, if the styles.xml
489 references additional files like header and footer images for
490 achieving the desired formatting.
492 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
493 a per-file basis. For example,
495 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
496 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
497 :group 'org-export-e-odt
498 :version "24.1"
499 :type
500 '(choice
501 (const :tag "Factory settings" nil)
502 (file :must-match t :tag "styles.xml")
503 (file :must-match t :tag "ODT or OTT file")
504 (list :tag "ODT or OTT file + Members"
505 (file :must-match t :tag "ODF Text or Text Template file")
506 (cons :tag "Members"
507 (file :tag " Member" "styles.xml")
508 (repeat (file :tag "Member"))))))
510 (defcustom org-e-odt-display-outline-level 2
511 "Outline levels considered for enumerating captioned entities."
512 :group 'org-export-e-odt
513 :version "24.2"
514 :type 'integer)
516 ;;;; Document conversion
518 (defcustom org-e-odt-convert-processes
519 '(("LibreOffice"
520 "soffice --headless --convert-to %f%x --outdir %d %i")
521 ("unoconv"
522 "unoconv -f %f -o %d %i"))
523 "Specify a list of document converters and their usage.
524 The converters in this list are offered as choices while
525 customizing `org-e-odt-convert-process'.
527 This variable is a list where each element is of the
528 form (CONVERTER-NAME CONVERTER-CMD). CONVERTER-NAME is the name
529 of the converter. CONVERTER-CMD is the shell command for the
530 converter and can contain format specifiers. These format
531 specifiers are interpreted as below:
533 %i input file name in full
534 %I input file name as a URL
535 %f format of the output file
536 %o output file name in full
537 %O output file name as a URL
538 %d output dir in full
539 %D output dir as a URL.
540 %x extra options as set in `org-e-odt-convert-capabilities'."
541 :group 'org-export-e-odt
542 :version "24.1"
543 :type
544 '(choice
545 (const :tag "None" nil)
546 (alist :tag "Converters"
547 :key-type (string :tag "Converter Name")
548 :value-type (group (string :tag "Command line")))))
550 (defcustom org-e-odt-convert-process "LibreOffice"
551 "Use this converter to convert from \"odt\" format to other formats.
552 During customization, the list of converter names are populated
553 from `org-e-odt-convert-processes'."
554 :group 'org-export-e-odt
555 :version "24.1"
556 :type '(choice :convert-widget
557 (lambda (w)
558 (apply 'widget-convert (widget-type w)
559 (eval (car (widget-get w :args)))))
560 `((const :tag "None" nil)
561 ,@(mapcar (lambda (c)
562 `(const :tag ,(car c) ,(car c)))
563 org-e-odt-convert-processes))))
565 (defcustom org-e-odt-convert-capabilities
566 '(("Text"
567 ("odt" "ott" "doc" "rtf" "docx")
568 (("pdf" "pdf") ("odt" "odt") ("rtf" "rtf") ("ott" "ott")
569 ("doc" "doc" ":\"MS Word 97\"") ("docx" "docx") ("html" "html")))
570 ("Web"
571 ("html")
572 (("pdf" "pdf") ("odt" "odt") ("html" "html")))
573 ("Spreadsheet"
574 ("ods" "ots" "xls" "csv" "xlsx")
575 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
576 ("xls" "xls") ("xlsx" "xlsx")))
577 ("Presentation"
578 ("odp" "otp" "ppt" "pptx")
579 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("otp" "otp") ("ppt" "ppt")
580 ("pptx" "pptx") ("odg" "odg"))))
581 "Specify input and output formats of `org-e-odt-convert-process'.
582 More correctly, specify the set of input and output formats that
583 the user is actually interested in.
585 This variable is an alist where each element is of the
586 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
587 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
588 alist where each element is of the form (OUTPUT-FMT
589 OUTPUT-FILE-EXTENSION EXTRA-OPTIONS).
591 The variable is interpreted as follows:
592 `org-e-odt-convert-process' can take any document that is in
593 INPUT-FMT-LIST and produce any document that is in the
594 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
595 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
596 serves dual purposes:
597 - It is used for populating completion candidates during
598 `org-e-odt-convert' commands.
599 - It is used as the value of \"%f\" specifier in
600 `org-e-odt-convert-process'.
602 EXTRA-OPTIONS is used as the value of \"%x\" specifier in
603 `org-e-odt-convert-process'.
605 DOCUMENT-CLASS is used to group a set of file formats in
606 INPUT-FMT-LIST in to a single class.
608 Note that this variable inherently captures how LibreOffice based
609 converters work. LibreOffice maps documents of various formats
610 to classes like Text, Web, Spreadsheet, Presentation etc and
611 allow document of a given class (irrespective of it's source
612 format) to be converted to any of the export formats associated
613 with that class.
615 See default setting of this variable for an typical
616 configuration."
617 :group 'org-export-e-odt
618 :version "24.1"
619 :type
620 '(choice
621 (const :tag "None" nil)
622 (alist :tag "Capabilities"
623 :key-type (string :tag "Document Class")
624 :value-type
625 (group (repeat :tag "Input formats" (string :tag "Input format"))
626 (alist :tag "Output formats"
627 :key-type (string :tag "Output format")
628 :value-type
629 (group (string :tag "Output file extension")
630 (choice
631 (const :tag "None" nil)
632 (string :tag "Extra options"))))))))
634 (defcustom org-e-odt-preferred-output-format nil
635 "Automatically post-process to this format after exporting to \"odt\".
636 Interactive commands `org-export-as-e-odt' and
637 `org-export-as-e-odt-and-open' export first to \"odt\" format and
638 then use `org-e-odt-convert-process' to convert the
639 resulting document to this format. During customization of this
640 variable, the list of valid values are populated based on
641 `org-e-odt-convert-capabilities'."
642 :group 'org-export-e-odt
643 :version "24.1"
644 :type '(choice :convert-widget
645 (lambda (w)
646 (apply 'widget-convert (widget-type w)
647 (eval (car (widget-get w :args)))))
648 `((const :tag "None" nil)
649 ,@(mapcar (lambda (c)
650 `(const :tag ,c ,c))
651 (org-e-odt-reachable-formats "odt")))))
654 ;;;; Drawers
656 (defcustom org-e-odt-format-drawer-function nil
657 "Function called to format a drawer in HTML code.
659 The function must accept two parameters:
660 NAME the drawer name, like \"LOGBOOK\"
661 CONTENTS the contents of the drawer.
663 The function should return the string to be exported.
665 For example, the variable could be set to the following function
666 in order to mimic default behaviour:
668 \(defun org-e-odt-format-drawer-default \(name contents\)
669 \"Format a drawer element for HTML export.\"
670 contents\)"
671 :group 'org-export-e-odt
672 :type 'function)
675 ;;;; Headline
677 (defcustom org-e-odt-format-headline-function nil
678 "Function to format headline text.
680 This function will be called with 5 arguments:
681 TODO the todo keyword \(string or nil\).
682 TODO-TYPE the type of todo \(symbol: `todo', `done', nil\)
683 PRIORITY the priority of the headline \(integer or nil\)
684 TEXT the main headline text \(string\).
685 TAGS the tags string, separated with colons \(string or nil\).
687 The function result will be used in the section format string.
689 As an example, one could set the variable to the following, in
690 order to reproduce the default set-up:
692 \(defun org-e-odt-format-headline \(todo todo-type priority text tags\)
693 \"Default format function for an headline.\"
694 \(concat \(when todo
695 \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo\)\)
696 \(when priority
697 \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
698 text
699 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)"
700 :group 'org-export-e-odt
701 :type 'function)
704 ;;;; Inlinetasks
706 (defcustom org-e-odt-format-inlinetask-function nil
707 "Function called to format an inlinetask in HTML code.
709 The function must accept six parameters:
710 TODO the todo keyword, as a string
711 TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
712 PRIORITY the inlinetask priority, as a string
713 NAME the inlinetask name, as a string.
714 TAGS the inlinetask tags, as a string.
715 CONTENTS the contents of the inlinetask, as a string.
717 The function should return the string to be exported.
719 For example, the variable could be set to the following function
720 in order to mimic default behaviour:
722 \(defun org-e-odt-format-inlinetask \(todo type priority name tags contents\)
723 \"Format an inline task element for HTML export.\"
724 \(let \(\(full-title
725 \(concat
726 \(when todo
727 \(format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo\)\)
728 \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
729 title
730 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)\)
731 \(format \(concat \"\\\\begin{center}\\n\"
732 \"\\\\fbox{\\n\"
733 \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\"
734 \"%s\\n\\n\"
735 \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\"
736 \"%s\"
737 \"\\\\end{minipage}}\"
738 \"\\\\end{center}\"\)
739 full-title contents\)\)"
740 :group 'org-export-e-odt
741 :type 'function)
744 ;;;; Links
746 (defcustom org-e-odt-inline-image-rules
747 '(("file" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\)\\'"))
748 "Rules characterizing image files that can be inlined into HTML.
750 A rule consists in an association whose key is the type of link
751 to consider, and value is a regexp that will be matched against
752 link's path.
754 Note that, by default, the image extension *actually* allowed
755 depend on the way the HTML file is processed. When used with
756 pdflatex, pdf, jpg and png images are OK. When processing
757 through dvi to Postscript, only ps and eps are allowed. The
758 default we use here encompasses both."
759 :group 'org-export-e-odt
760 :type '(alist :key-type (string :tag "Type")
761 :value-type (regexp :tag "Path")))
763 (defcustom org-e-odt-pixels-per-inch display-pixels-per-inch
764 "Scaling factor for converting images pixels to inches.
765 Use this for sizing of embedded images. See Info node `(org)
766 Images in ODT export' for more information."
767 :type 'float
768 :group 'org-export-e-odt
769 :version "24.1")
772 ;;;; Plain text
774 (defcustom org-e-odt-quotes
775 '(("fr"
776 ("\\(\\s-\\|[[(]\\|^\\)\"" . "« ")
777 ("\\(\\S-\\)\"" . "» ")
778 ("\\(\\s-\\|(\\|^\\)'" . "'"))
779 ("en"
780 ("\\(\\s-\\|[[(]\\|^\\)\"" . "“")
781 ("\\(\\S-\\)\"" . "”")
782 ("\\(\\s-\\|(\\|^\\)'" . "‘")
783 ("\\(\\S-\\)'" . "’")))
784 "Alist for quotes to use when converting english double-quotes.
786 The CAR of each item in this alist is the language code.
787 The CDR of each item in this alist is a list of three CONS:
788 - the first CONS defines the opening quote;
789 - the second CONS defines the closing quote;
790 - the last CONS defines single quotes.
792 For each item in a CONS, the first string is a regexp
793 for allowed characters before/after the quote, the second
794 string defines the replacement string for this quote."
795 :group 'org-export-e-odt
796 :type '(list
797 (cons :tag "Opening quote"
798 (string :tag "Regexp for char before")
799 (string :tag "Replacement quote "))
800 (cons :tag "Closing quote"
801 (string :tag "Regexp for char after ")
802 (string :tag "Replacement quote "))
803 (cons :tag "Single quote"
804 (string :tag "Regexp for char before")
805 (string :tag "Replacement quote "))))
808 ;;;; Src Block
810 (defcustom org-e-odt-create-custom-styles-for-srcblocks t
811 "Whether custom styles for colorized source blocks be automatically created.
812 When this option is turned on, the exporter creates custom styles
813 for source blocks based on the advice of `htmlfontify'. Creation
814 of custom styles happen as part of `org-e-odt-hfy-face-to-css'.
816 When this option is turned off exporter does not create such
817 styles.
819 Use the latter option if you do not want the custom styles to be
820 based on your current display settings. It is necessary that the
821 styles.xml already contains needed styles for colorizing to work.
823 This variable is effective only if
824 `org-e-odt-fontify-srcblocks' is turned on."
825 :group 'org-export-e-odt
826 :version "24.1"
827 :type 'boolean)
829 (defcustom org-e-odt-fontify-srcblocks t
830 "Specify whether or not source blocks need to be fontified.
831 Turn this option on if you want to colorize the source code
832 blocks in the exported file. For colorization to work, you need
833 to make available an enhanced version of `htmlfontify' library."
834 :type 'boolean
835 :group 'org-export-e-odt
836 :version "24.1")
839 ;;;; Table
841 (defcustom org-e-odt-table-caption-above t
842 "When non-nil, place caption string at the beginning of the table.
843 Otherwise, place it near the end."
844 :group 'org-export-e-odt
845 :type 'boolean)
847 (defcustom org-e-odt-table-styles
848 '(("OrgEquation" "OrgEquation"
849 ((use-first-column-styles . t)
850 (use-last-column-styles . t))))
851 "Specify how Table Styles should be derived from a Table Template.
852 This is a list where each element is of the
853 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
855 TABLE-STYLE-NAME is the style associated with the table through
856 \"#+ATTR_ODT: :style TABLE-STYLE-NAME\" line.
858 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
859 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
860 below) that is included in
861 `org-e-odt-content-template-file'.
863 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
864 \"TableCell\"
865 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
866 \"TableParagraph\"
867 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
868 \"FirstRow\" | \"LastRow\" |
869 \"EvenRow\" | \"OddRow\" |
870 \"EvenColumn\" | \"OddColumn\" | \"\"
871 where \"+\" above denotes string concatenation.
873 TABLE-CELL-OPTIONS is an alist where each element is of the
874 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
875 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
876 `use-last-row-styles' |
877 `use-first-column-styles' |
878 `use-last-column-styles' |
879 `use-banding-rows-styles' |
880 `use-banding-columns-styles' |
881 `use-first-row-styles'
882 ON-OR-OFF := `t' | `nil'
884 For example, with the following configuration
886 \(setq org-e-odt-table-styles
887 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
888 \(\(use-first-row-styles . t\)
889 \(use-first-column-styles . t\)\)\)
890 \(\"TableWithHeaderColumns\" \"Custom\"
891 \(\(use-first-column-styles . t\)\)\)\)\)
893 1. A table associated with \"TableWithHeaderRowsAndColumns\"
894 style will use the following table-cell styles -
895 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
896 \"CustomTableCell\" and the following paragraph styles
897 \"CustomFirstRowTableParagraph\",
898 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
899 as appropriate.
901 2. A table associated with \"TableWithHeaderColumns\" style will
902 use the following table-cell styles -
903 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
904 following paragraph styles
905 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
906 as appropriate..
908 Note that TABLE-TEMPLATE-NAME corresponds to the
909 \"<table:table-template>\" elements contained within
910 \"<office:styles>\". The entries (TABLE-STYLE-NAME
911 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
912 \"table:template-name\" and \"table:use-first-row-styles\" etc
913 attributes of \"<table:table>\" element. Refer ODF-1.2
914 specification for more information. Also consult the
915 implementation filed under `org-e-odt-get-table-cell-styles'.
917 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
918 formatting of numbered display equations. Do not delete this
919 style from the list."
920 :group 'org-export-e-odt
921 :version "24.1"
922 :type '(choice
923 (const :tag "None" nil)
924 (repeat :tag "Table Styles"
925 (list :tag "Table Style Specification"
926 (string :tag "Table Style Name")
927 (string :tag "Table Template Name")
928 (alist :options (use-first-row-styles
929 use-last-row-styles
930 use-first-column-styles
931 use-last-column-styles
932 use-banding-rows-styles
933 use-banding-columns-styles)
934 :key-type symbol
935 :value-type (const :tag "True" t))))))
939 ;;; Internal functions
941 ;;;; Date
943 (defun org-e-odt--date (&optional org-ts fmt)
944 (save-match-data
945 (let* ((time
946 (and (stringp org-ts)
947 (string-match org-ts-regexp0 org-ts)
948 (apply 'encode-time
949 (org-fix-decoded-time
950 (org-parse-time-string (match-string 0 org-ts) t)))))
951 date)
952 (cond
953 (fmt (format-time-string fmt time))
954 (t (setq date (format-time-string "%Y-%m-%dT%H:%M:%S%z" time))
955 (format "%s:%s" (substring date 0 -2) (substring date -2)))))))
957 ;;;; Frame
959 (defun org-e-odt--frame (text width height style &optional extra
960 anchor-type)
961 (let ((frame-attrs
962 (concat
963 (if width (format " svg:width=\"%0.2fcm\"" width) "")
964 (if height (format " svg:height=\"%0.2fcm\"" height) "")
965 extra
966 (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph")))))
967 (format
968 "\n<draw:frame draw:style-name=\"%s\"%s>\n%s\n</draw:frame>"
969 style frame-attrs
970 (concat text
971 (let ((title (get-text-property 0 :title text))
972 (desc (get-text-property 0 :description text)))
973 (concat (and title
974 (format "<svg:title>%s</svg:title>"
975 (org-e-odt-encode-plain-text title t)))
976 (and desc
977 (format "<svg:desc>%s</svg:desc>"
978 (org-e-odt-encode-plain-text desc t)))))))))
980 ;;;; Library wrappers
982 (defun org-e-odt--adopt-elements (parent &rest children)
983 (prog1 parent
984 (mapc (lambda (child)
985 (let ((parent-1 (org-element-adopt-element parent child nil)))
986 (assert (eq parent-1 parent))))
987 children)))
989 (defun org-e-odt--zip-extract (archive members target)
990 (when (atom members) (setq members (list members)))
991 (mapc (lambda (archive member target)
992 (require 'arc-mode)
993 (let* ((--quote-file-name
994 ;; This is shamelessly stolen from `archive-zip-extract'.
995 (lambda (name)
996 (if (or (not (memq system-type '(windows-nt ms-dos)))
997 (and (boundp 'w32-quote-process-args)
998 (null w32-quote-process-args)))
999 (shell-quote-argument name)
1000 name)))
1001 (target (funcall --quote-file-name target))
1002 (archive (expand-file-name archive))
1003 (archive-zip-extract
1004 (list "unzip" "-qq" "-o" "-d" target))
1005 exit-code command-output)
1006 (setq command-output
1007 (with-temp-buffer
1008 (setq exit-code (archive-zip-extract archive member))
1009 (buffer-string)))
1010 (unless (zerop exit-code)
1011 (message command-output)
1012 (error "Extraction failed"))))
1013 members))
1015 ;;;; Textbox
1017 (defun org-e-odt--textbox (text width height style &optional
1018 extra anchor-type)
1019 (org-e-odt--frame
1020 (format "\n<draw:text-box %s>%s\n</draw:text-box>"
1021 (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
1022 (and (not width)
1023 (format " fo:min-width=\"%0.2fcm\"" (or width .2))))
1024 text)
1025 width nil style extra anchor-type))
1029 ;;;; Table of Contents
1031 (defun org-e-odt-begin-toc (index-title depth)
1032 (concat
1033 (format "
1034 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
1035 <text:table-of-content-source text:outline-level=\"%d\">
1036 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
1037 " depth index-title)
1039 (let ((levels (number-sequence 1 10)))
1040 (mapconcat
1041 (lambda (level)
1042 (format
1044 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
1045 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
1046 <text:index-entry-chapter/>
1047 <text:index-entry-text/>
1048 <text:index-entry-link-end/>
1049 </text:table-of-content-entry-template>
1050 " level level)) levels ""))
1052 (format "
1053 </text:table-of-content-source>
1055 <text:index-body>
1056 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
1057 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
1058 </text:index-title>
1059 " index-title)))
1061 (defun org-e-odt-end-toc ()
1062 (format "
1063 </text:index-body>
1064 </text:table-of-content>
1069 (defun* org-e-odt-format-toc-headline
1070 (todo todo-type priority text tags
1071 &key level section-number headline-label &allow-other-keys)
1072 (setq text (concat
1073 (and org-export-with-section-numbers
1074 (concat section-number ". "))
1075 text
1076 (and tags
1077 (concat
1078 "<text:tab/>"
1079 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1080 "OrgTag" tags)))))
1081 (when todo
1082 (setq text (format "<text:span text:style-name=\"%s\">%s</text:span>"
1083 "OrgTodo" text)))
1084 (org-e-odt-format-link text (concat "#" headline-label) t))
1086 (defun org-e-odt-toc (depth info)
1087 (assert (wholenump depth))
1088 (let* ((title (org-export-translate "Table of Contents" :utf-8 info))
1089 (headlines (org-export-collect-headlines info depth)))
1091 (when headlines
1092 (concat
1093 (org-e-odt-begin-toc title depth)
1095 (mapconcat
1096 (lambda (headline)
1097 (let* ((entry (org-e-odt-format-headline--wrap
1098 headline info 'org-e-odt-format-toc-headline))
1099 (level (org-export-get-relative-level headline info))
1100 (style (format "Contents_20_%d" level)))
1101 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1102 style entry)))
1103 headlines "\n")
1105 (org-e-odt-end-toc)))))
1108 ;;;; Document styles
1110 (defun org-e-odt-add-automatic-style (object-type &optional object-props)
1111 "Create an automatic style of type OBJECT-TYPE with param OBJECT-PROPS.
1112 OBJECT-PROPS is (typically) a plist created by passing
1113 \"#+ATTR_ODT: \" option of the object in question to
1114 `org-e-odt-parse-block-attributes'.
1116 Use `org-e-odt-object-counters' to generate an automatic
1117 OBJECT-NAME and STYLE-NAME. If OBJECT-PROPS is non-nil, add a
1118 new entry in `org-e-odt-automatic-styles'. Return (OBJECT-NAME
1119 . STYLE-NAME)."
1120 (assert (stringp object-type))
1121 (let* ((object (intern object-type))
1122 (seqvar object)
1123 (seqno (1+ (or (plist-get org-e-odt-object-counters seqvar) 0)))
1124 (object-name (format "%s%d" object-type seqno)) style-name)
1125 (setq org-e-odt-object-counters
1126 (plist-put org-e-odt-object-counters seqvar seqno))
1127 (when object-props
1128 (setq style-name (format "Org%s" object-name))
1129 (setq org-e-odt-automatic-styles
1130 (plist-put org-e-odt-automatic-styles object
1131 (append (list (list style-name object-props))
1132 (plist-get org-e-odt-automatic-styles object)))))
1133 (cons object-name style-name)))
1136 ;;;; Caption and Labels
1139 (defun org-e-odt--wrap-label (element output)
1140 "Wrap label associated to ELEMENT around OUTPUT, if appropriate.
1141 This function shouldn't be used for floats. See
1142 `org-e-odt--caption/label-string'."
1143 ;; (let ((label (org-element-property :name element)))
1144 ;; (if (or (not output) (not label) (string= output "") (string= label ""))
1145 ;; output
1146 ;; (concat (format "\\label{%s}\n" label) output)))
1147 output)
1150 (defun org-e-odt--caption/label-string (caption label info)
1151 "Return caption and label HTML string for floats.
1153 CAPTION is a cons cell of secondary strings, the car being the
1154 standard caption and the cdr its short form. LABEL is a string
1155 representing the label. INFO is a plist holding contextual
1156 information.
1158 If there's no caption nor label, return the empty string.
1160 For non-floats, see `org-e-odt--wrap-label'."
1161 (setq label nil) ;; FIXME
1163 (let ((label-str (if label (format "\\label{%s}" label) "")))
1164 (cond
1165 ((and (not caption) (not label)) "")
1166 ((not caption) (format "\\label{%s}\n" label))
1167 ;; Option caption format with short name.
1168 ((cdr caption)
1169 (format "\\caption[%s]{%s%s}\n"
1170 (org-export-data (cdr caption) info)
1171 label-str
1172 (org-export-data (car caption) info)))
1173 ;; Standard caption format.
1174 ;; (t (format "\\caption{%s%s}\n"
1175 ;; label-str
1176 ;; (org-export-data (car caption) info)))
1177 (t (org-export-data (car caption) info)))))
1179 ;;;; Checkbox
1181 (defun org-e-odt--checkbox (item)
1182 "Return check-box string associated to ITEM."
1183 (let ((checkbox (org-element-property :checkbox item)))
1184 (if (not checkbox) ""
1185 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1186 "OrgCode" (case checkbox
1187 (on "[&#x2713;] ") ; CHECK MARK
1188 (off "[ ] ")
1189 (trans "[-] "))))))
1193 ;;; Template
1195 (defun org-e-odt-template (contents info)
1196 "Return complete document string after HTML conversion.
1197 CONTENTS is the transcoded contents string. RAW-DATA is the
1198 original parsed data. INFO is a plist holding export options."
1199 ;; Write meta file.
1200 (let ((title (org-export-data (plist-get info :title) info))
1201 (author (let ((author (plist-get info :author)))
1202 (if (not author) "" (org-export-data author info))))
1203 (date (org-e-odt--date
1204 (org-export-data (plist-get info :date) info)))
1205 (email (plist-get info :email))
1206 (keywords (plist-get info :keywords))
1207 (description (plist-get info :description)))
1208 (write-region
1209 (concat
1210 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1211 <office:document-meta
1212 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
1213 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
1214 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
1215 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
1216 xmlns:ooo=\"http://openoffice.org/2004/office\"
1217 office:version=\"1.2\">
1218 <office:meta>\n"
1219 (format "<dc:creator>%s</dc:creator>\n" author)
1220 (format "<meta:initial-creator>%s</meta:initial-creator>\n" author)
1221 (format "<dc:date>%s</dc:date>\n" date)
1222 (format "<meta:creation-date>%s</meta:creation-date>\n" date)
1223 (format "<meta:generator>%s</meta:generator>\n"
1224 (let ((creator-info (plist-get info :with-creator)))
1225 (if (or (not creator-info) (eq creator-info 'comment)) ""
1226 (plist-get info :creator))))
1227 (format "<meta:keyword>%s</meta:keyword>\n" keywords)
1228 (format "<dc:subject>%s</dc:subject>\n" description)
1229 (format "<dc:title>%s</dc:title>\n" title)
1230 "\n"
1231 " </office:meta>\n" "</office:document-meta>")
1232 nil (concat org-e-odt-zip-dir "meta.xml"))
1233 ;; Add meta.xml in to manifest.
1234 (org-e-odt-create-manifest-file-entry "text/xml" "meta.xml"))
1236 ;; Update styles file.
1237 ;; Copy styles.xml. Also dump htmlfontify styles, if there is any.
1238 ;; Write styles file.
1239 (let* ((styles-file (plist-get info :odt-styles-file))
1240 (styles-file (and styles-file (read (org-trim styles-file))))
1241 ;; Non-availability of styles.xml is not a critical
1242 ;; error. For now throw an error purely for aesthetic
1243 ;; reasons.
1244 (styles-file (or styles-file
1245 org-e-odt-styles-file
1246 (expand-file-name "OrgOdtStyles.xml"
1247 org-e-odt-styles-dir)
1248 (error "org-e-odt: Missing styles file?"))))
1249 (cond
1250 ((listp styles-file)
1251 (let ((archive (nth 0 styles-file))
1252 (members (nth 1 styles-file)))
1253 (org-e-odt--zip-extract archive members org-e-odt-zip-dir)
1254 (mapc
1255 (lambda (member)
1256 (when (org-file-image-p member)
1257 (let* ((image-type (file-name-extension member))
1258 (media-type (format "image/%s" image-type)))
1259 (org-e-odt-create-manifest-file-entry media-type member))))
1260 members)))
1261 ((and (stringp styles-file) (file-exists-p styles-file))
1262 (let ((styles-file-type (file-name-extension styles-file)))
1263 (cond
1264 ((string= styles-file-type "xml")
1265 (copy-file styles-file (concat org-e-odt-zip-dir "styles.xml") t))
1266 ((member styles-file-type '("odt" "ott"))
1267 (org-e-odt--zip-extract styles-file "styles.xml" org-e-odt-zip-dir)))))
1269 (error (format "Invalid specification of styles.xml file: %S"
1270 org-e-odt-styles-file))))
1272 ;; create a manifest entry for styles.xml
1273 (org-e-odt-create-manifest-file-entry "text/xml" "styles.xml")
1275 ;; FIXME: Who is opening an empty styles.xml before this point?
1276 (with-current-buffer
1277 (find-file-noselect (concat org-e-odt-zip-dir "styles.xml") t)
1278 (revert-buffer t t)
1280 ;; Write custom styles for source blocks
1281 ;; Save STYLES used for colorizing of source blocks.
1282 ;; Update styles.xml with styles that were collected as part of
1283 ;; `org-e-odt-hfy-face-to-css' callbacks.
1284 (let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
1285 hfy-user-sheet-assoc "")))
1286 (when styles
1287 (goto-char (point-min))
1288 (when (re-search-forward "</office:styles>" nil t)
1289 (goto-char (match-beginning 0))
1290 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
1292 ;; Update styles.xml - take care of outline numbering
1294 ;; Don't make automatic backup of styles.xml file. This setting
1295 ;; prevents the backed-up styles.xml file from being zipped in to
1296 ;; odt file. This is more of a hackish fix. Better alternative
1297 ;; would be to fix the zip command so that the output odt file
1298 ;; includes only the needed files and excludes any auto-generated
1299 ;; extra files like backups and auto-saves etc etc. Note that
1300 ;; currently the zip command zips up the entire temp directory so
1301 ;; that any auto-generated files created under the hood ends up in
1302 ;; the resulting odt file.
1303 (set (make-local-variable 'backup-inhibited) t)
1305 ;; Outline numbering is retained only upto LEVEL.
1306 ;; To disable outline numbering pass a LEVEL of 0.
1308 (goto-char (point-min))
1309 (let ((regex
1310 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
1311 (replacement
1312 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
1313 (while (re-search-forward regex nil t)
1314 (unless (let ((sec-num (plist-get info :section-numbers))
1315 (level (string-to-number (match-string 2))))
1316 (if (wholenump sec-num) (<= level sec-num) sec-num))
1317 (replace-match replacement t nil))))
1318 (save-buffer 0)))
1319 ;; Update content.xml.
1320 (with-temp-buffer
1321 (insert-file-contents
1322 (or org-e-odt-content-template-file
1323 (expand-file-name "OrgOdtContentTemplate.xml"
1324 org-e-odt-styles-dir)))
1325 ;; Write automatic styles.
1326 ;; - Position the cursor.
1327 (goto-char (point-min))
1328 (re-search-forward " </office:automatic-styles>" nil t)
1329 (goto-char (match-beginning 0))
1330 ;; - Dump automatic table styles
1331 (loop for (style-name props) in
1332 (plist-get org-e-odt-automatic-styles 'Table) do
1333 (when (setq props (or (plist-get props :rel-width) 96))
1334 (insert (format org-e-odt-table-style-format style-name props))))
1335 ;; Update display level.
1336 ;; - Remove existing sequence decls. Also position the cursor.
1337 (goto-char (point-min))
1338 (when (re-search-forward "<text:sequence-decls" nil t)
1339 (delete-region (match-beginning 0)
1340 (re-search-forward "</text:sequence-decls>" nil nil)))
1341 ;; Update sequence decls according to user preference.
1342 (insert
1343 (format
1344 "\n<text:sequence-decls>\n%s\n</text:sequence-decls>"
1345 (mapconcat
1346 (lambda (x)
1347 (format
1348 "<text:sequence-decl text:display-outline-level=\"%d\" text:name=\"%s\"/>"
1349 org-e-odt-display-outline-level (nth 1 x)))
1350 org-e-odt-category-map-alist "\n")))
1351 ;; Position the cursor to document body.
1352 (goto-char (point-min))
1353 (re-search-forward "</office:text>" nil nil)
1354 (goto-char (match-beginning 0))
1356 ;; Preamble - Title, Author, Date etc.
1357 (insert
1358 (let* ((title (org-export-data (plist-get info :title) info))
1359 (author (and (plist-get info :with-author)
1360 (let ((auth (plist-get info :author)))
1361 (and auth (org-export-data auth info)))))
1362 (date (org-export-data (plist-get info :date) info))
1363 (iso-date (org-e-odt--date date))
1364 (date (org-e-odt--date date "%d %b %Y"))
1365 (email (plist-get info :email))
1366 ;; switch on or off above vars based on user settings
1367 (author (and (plist-get info :with-author) (or author email)))
1368 ;; (date (and (plist-get info :time-stamp-file) date))
1369 (email (and (plist-get info :with-email) email)))
1370 (concat
1371 ;; title
1372 (when title
1373 (concat
1374 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1375 "OrgTitle" (format "\n<text:title>%s</text:title>" title))
1376 ;; separator
1377 "\n<text:p text:style-name=\"OrgTitle\"/>"))
1378 (cond
1379 ((and author (not email))
1380 ;; author only
1381 (concat
1382 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1383 "OrgSubtitle"
1384 (format "<text:initial-creator>%s</text:initial-creator>" author))
1385 ;; separator
1386 "\n<text:p text:style-name=\"OrgSubtitle\"/>"))
1387 ((and author email)
1388 ;; author and email
1389 (concat
1390 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1391 "OrgSubtitle"
1392 (org-e-odt-format-link
1393 (format "<text:initial-creator>%s</text:initial-creator>" author)
1394 (concat "mailto:" email)))
1395 ;; separator
1396 "\n<text:p text:style-name=\"OrgSubtitle\"/>")))
1397 ;; date
1398 (when date
1399 (concat
1400 (format
1401 "\n<text:p text:style-name=\"%s\">%s</text:p>"
1402 "OrgSubtitle"
1403 (format
1404 "\n<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">%s</text:date>"
1406 "N75" iso-date date))
1407 ;; separator
1408 "<text:p text:style-name=\"OrgSubtitle\"/>")))))
1410 ;; Table of Contents
1411 (let ((depth (plist-get info :with-toc)))
1412 (when (wholenump depth) (insert (org-e-odt-toc depth info))))
1413 ;; Contents.
1414 (insert contents)
1415 ;; Return contents.
1416 (buffer-substring-no-properties (point-min) (point-max))))
1420 ;;; Transcode Functions
1422 ;;;; Bold
1424 (defun org-e-odt-bold (bold contents info)
1425 "Transcode BOLD from Org to ODT.
1426 CONTENTS is the text with bold markup. INFO is a plist holding
1427 contextual information."
1428 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1429 "Bold" contents))
1432 ;;;; Center Block
1434 (defun org-e-odt-center-block (center-block contents info)
1435 "Transcode a CENTER-BLOCK element from Org to ODT.
1436 CONTENTS holds the contents of the center block. INFO is a plist
1437 holding contextual information."
1438 (org-e-odt--wrap-label center-block contents))
1441 ;;;; Clock
1443 (defun org-e-odt-clock (clock contents info)
1444 "Transcode a CLOCK element from Org to ODT.
1445 CONTENTS is nil. INFO is a plist used as a communication
1446 channel."
1447 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1448 "OrgTimestampWrapper"
1449 (concat
1450 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1451 "OrgTimestampKeyword" org-clock-string)
1452 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1453 "OrgTimestamp"
1454 (concat (org-translate-time
1455 (org-element-property :value clock))
1456 (let ((time (org-element-property :time clock)))
1457 (and time (format " (%s)" time))))))))
1460 ;;;; Code
1462 (defun org-e-odt-code (code contents info)
1463 "Transcode a CODE object from Org to ODT.
1464 CONTENTS is nil. INFO is a plist used as a communication
1465 channel."
1466 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1467 "OrgCode" (org-element-property :value code)))
1470 ;;;; Comment
1472 ;; Comments are ignored.
1475 ;;;; Comment Block
1477 ;; Comment Blocks are ignored.
1480 ;;;; Drawer
1482 (defun org-e-odt-drawer (drawer contents info)
1483 "Transcode a DRAWER element from Org to ODT.
1484 CONTENTS holds the contents of the block. INFO is a plist
1485 holding contextual information."
1486 (let* ((name (org-element-property :drawer-name drawer))
1487 (output (if (functionp org-e-odt-format-drawer-function)
1488 (funcall org-e-odt-format-drawer-function
1489 name contents)
1490 ;; If there's no user defined function: simply
1491 ;; display contents of the drawer.
1492 contents)))
1493 (org-e-odt--wrap-label drawer output)))
1496 ;;;; Dynamic Block
1498 (defun org-e-odt-dynamic-block (dynamic-block contents info)
1499 "Transcode a DYNAMIC-BLOCK element from Org to ODT.
1500 CONTENTS holds the contents of the block. INFO is a plist
1501 holding contextual information. See `org-export-data'."
1502 (org-e-odt--wrap-label dynamic-block contents))
1505 ;;;; Entity
1507 (defun org-e-odt-entity (entity contents info)
1508 "Transcode an ENTITY object from Org to ODT.
1509 CONTENTS are the definition itself. INFO is a plist holding
1510 contextual information."
1511 ;; (let ((ent (org-element-property :latex entity)))
1512 ;; (if (org-element-property :latex-math-p entity)
1513 ;; (format "$%s$" ent)
1514 ;; ent))
1515 (org-element-property :utf-8 entity))
1518 ;;;; Example Block
1520 (defun org-e-odt-example-block (example-block contents info)
1521 "Transcode a EXAMPLE-BLOCK element from Org to ODT.
1522 CONTENTS is nil. INFO is a plist holding contextual information."
1523 (org-e-odt--wrap-label
1524 example-block (org-e-odt-format-code example-block info)))
1527 ;;;; Export Snippet
1529 (defun org-e-odt-export-snippet (export-snippet contents info)
1530 "Transcode a EXPORT-SNIPPET object from Org to ODT.
1531 CONTENTS is nil. INFO is a plist holding contextual information."
1532 (when (eq (org-export-snippet-backend export-snippet) 'e-odt)
1533 (org-element-property :value export-snippet)))
1536 ;;;; Export Block
1538 (defun org-e-odt-export-block (export-block contents info)
1539 "Transcode a EXPORT-BLOCK element from Org to ODT.
1540 CONTENTS is nil. INFO is a plist holding contextual information."
1541 (when (string= (org-element-property :type export-block) "ODT")
1542 (org-remove-indentation (org-element-property :value export-block))))
1545 ;;;; Fixed Width
1547 (defun org-e-odt-fixed-width (fixed-width contents info)
1548 "Transcode a FIXED-WIDTH element from Org to ODT.
1549 CONTENTS is nil. INFO is a plist holding contextual information."
1550 (org-e-odt--wrap-label
1551 fixed-width (org-e-odt-do-format-code
1552 (org-element-property :value fixed-width))))
1555 ;;;; Footnote Definition
1557 ;; Footnote Definitions are ignored.
1560 ;;;; Footnote Reference
1562 (defun org-e-odt-footnote-reference (footnote-reference contents info)
1563 "Transcode a FOOTNOTE-REFERENCE element from Org to ODT.
1564 CONTENTS is nil. INFO is a plist holding contextual information."
1565 (let ((--format-footnote-definition
1566 (function
1567 (lambda (n def)
1568 (setq n (format "%d" n))
1569 (let ((id (concat "fn" n))
1570 (note-class "footnote")
1571 (par-style "Footnote"))
1572 (format
1573 "<text:note text:id=\"%s\" text:note-class=\"%s\">%s</text:note>"
1574 id note-class
1575 (concat
1576 (format "<text:note-citation>%s</text:note-citation>" n)
1577 (format "<text:note-body>%s</text:note-body>" def)))))))
1578 (--format-footnote-reference
1579 (function
1580 (lambda (n)
1581 (setq n (format "%d" n))
1582 (let ((note-class "footnote")
1583 (ref-format "text")
1584 (ref-name (concat "fn" n)))
1585 (format
1586 "<text:span text:style-name=\"%s\">%s</text:span>"
1587 "OrgSuperscript"
1588 (format "<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:note-ref>"
1589 note-class ref-format ref-name n)))))))
1590 (concat
1591 ;; Insert separator between two footnotes in a row.
1592 (let ((prev (org-export-get-previous-element footnote-reference info)))
1593 (and (eq (org-element-type prev) 'footnote-reference)
1594 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1595 "OrgSuperscript" ",")))
1596 ;; Trancode footnote reference.
1597 (let ((n (org-export-get-footnote-number footnote-reference info)))
1598 (cond
1599 ((not (org-export-footnote-first-reference-p footnote-reference info))
1600 (funcall --format-footnote-reference n))
1601 ;; Inline definitions are secondary strings.
1602 ;; Non-inline footnotes definitions are full Org data.
1604 (let* ((raw (org-export-get-footnote-definition footnote-reference
1605 info))
1606 (def (let ((def (org-trim (org-export-data raw info))))
1607 (if (eq (org-element-type raw) 'org-data) def
1608 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1609 "Footnote" def)))))
1610 (funcall --format-footnote-definition n def))))))))
1613 ;;;; Headline
1615 (defun* org-e-odt-format-headline
1616 (todo todo-type priority text tags
1617 &key level section-number headline-label &allow-other-keys)
1618 (concat
1619 ;; Todo.
1620 (and todo
1621 (concat
1622 (let ((style (if (member todo org-done-keywords) "OrgDone" "OrgTodo")))
1623 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1624 style todo)) " "))
1625 ;; Title.
1626 text
1627 ;; Tags.
1628 (and tags
1629 (concat "<text:tab/>"
1630 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1631 "OrgTag" (mapconcat 'org-trim tags " : "))))))
1633 (defun org-e-odt-format-headline--wrap (headline info
1634 &optional format-function
1635 &rest extra-keys)
1636 "Transcode an HEADLINE element from Org to ODT.
1637 CONTENTS holds the contents of the headline. INFO is a plist
1638 holding contextual information."
1639 (let* ((level (+ (org-export-get-relative-level headline info)))
1640 (headline-number (org-export-get-headline-number headline info))
1641 (section-number (and (org-export-numbered-headline-p headline info)
1642 (mapconcat 'number-to-string
1643 headline-number ".")))
1644 (todo (and (plist-get info :with-todo-keywords)
1645 (let ((todo (org-element-property :todo-keyword headline)))
1646 (and todo (org-export-data todo info)))))
1647 (todo-type (and todo (org-element-property :todo-type headline)))
1648 (priority (and (plist-get info :with-priority)
1649 (org-element-property :priority headline)))
1650 (text (org-export-data (org-element-property :title headline) info))
1651 (tags (and (plist-get info :with-tags)
1652 (org-export-get-tags headline info)))
1653 (headline-label (concat "sec-" (mapconcat 'number-to-string
1654 headline-number "-")))
1655 (format-function (cond
1656 ((functionp format-function) format-function)
1657 ((functionp org-e-odt-format-headline-function)
1658 (function*
1659 (lambda (todo todo-type priority text tags
1660 &allow-other-keys)
1661 (funcall org-e-odt-format-headline-function
1662 todo todo-type priority text tags))))
1663 (t 'org-e-odt-format-headline))))
1664 (apply format-function
1665 todo todo-type priority text tags
1666 :headline-label headline-label :level level
1667 :section-number section-number extra-keys)))
1670 (defun org-e-odt-begin-plain-list (ltype &optional continue-numbering)
1671 (unless (member ltype '(ordered unordered descriptive))
1672 (error "Unknown list type: %s" ltype))
1673 (let ((style-name (assoc-default ltype
1674 '((ordered . "OrgNumberedList")
1675 (unordered . "OrgBulletedList")
1676 (descriptive . "OrgDescriptionList")))))
1677 (format "<text:list text:style-name=\"%s\" text:continue-numbering=\"%s\">"
1678 style-name (if continue-numbering "true" "false"))))
1680 (defun org-e-odt-headline (headline contents info)
1681 "Transcode an HEADLINE element from Org to ODT.
1682 CONTENTS holds the contents of the headline. INFO is a plist
1683 holding contextual information."
1684 (let* ((numberedp (org-export-numbered-headline-p headline info))
1685 ;; Get level relative to current parsed data.
1686 (level (org-export-get-relative-level headline info))
1687 (text (org-export-data (org-element-property :title headline) info))
1688 ;; Create the headline text.
1689 (full-text (org-e-odt-format-headline--wrap headline info)))
1690 (cond
1691 ;; Case 1: This is a footnote section: ignore it.
1692 ((org-element-property :footnote-section-p headline) nil)
1693 ;; Case 2. This is a deep sub-tree: export it as a list item.
1694 ;; Also export as items headlines for which no section
1695 ;; format has been found.
1696 ;; FIXME
1697 ;; ((org-export-low-level-p headline info)
1698 ;; ;; Build the real contents of the sub-tree.
1699 ;; (let* ((type (if numberedp 'unordered 'unordered)) ; FIXME
1700 ;; (itemized-body (org-e-odt-format-list-item
1701 ;; contents type nil nil full-text)))
1702 ;; (concat
1703 ;; (and (org-export-first-sibling-p headline info)
1704 ;; (org-e-odt-begin-plain-list type))
1705 ;; itemized-body
1706 ;; (and (org-export-last-sibling-p headline info)
1707 ;; "</text:list>"))))
1708 ;; Case 3. Standard headline. Export it as a section.
1710 (let* ((extra-ids (list (org-element-property :custom-id headline)
1711 (org-element-property :id headline)))
1712 (extra-ids nil) ; FIXME
1713 (id (concat "sec-" (mapconcat 'number-to-string
1714 (org-export-get-headline-number
1715 headline info) "-"))))
1716 (concat
1717 (format
1718 "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\">%s%s</text:h>"
1719 (format "Heading_20_%s" level)
1720 level
1721 ;; Extra targets.
1722 (mapconcat (lambda (x)
1723 (when x
1724 (let ((x (if (org-uuidgen-p x) (concat "ID-" x) x)))
1725 (org-e-odt-format-target
1726 "" (org-export-solidify-link-text x)))))
1727 extra-ids "")
1728 ;; Title.
1729 (org-e-odt-format-target full-text id))
1730 contents))))))
1733 ;;;; Horizontal Rule
1735 (defun org-e-odt-horizontal-rule (horizontal-rule contents info)
1736 "Transcode an HORIZONTAL-RULE object from Org to ODT.
1737 CONTENTS is nil. INFO is a plist holding contextual information."
1738 (org-e-odt--wrap-label
1739 horizontal-rule
1740 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1741 "Horizontal_20_Line" "")))
1744 ;;;; Inline Babel Call
1746 ;; Inline Babel Calls are ignored.
1749 ;;;; Inline Src Block
1751 (defun org-e-odt--find-verb-separator (s)
1752 "Return a character not used in string S.
1753 This is used to choose a separator for constructs like \\verb."
1754 (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
1755 (loop for c across ll
1756 when (not (string-match (regexp-quote (char-to-string c)) s))
1757 return (char-to-string c))))
1759 (defun org-e-odt-inline-src-block (inline-src-block contents info)
1760 "Transcode an INLINE-SRC-BLOCK element from Org to ODT.
1761 CONTENTS holds the contents of the item. INFO is a plist holding
1762 contextual information."
1763 (let* ((org-lang (org-element-property :language inline-src-block))
1764 (code (org-element-property :value inline-src-block))
1765 (separator (org-e-odt--find-verb-separator code)))
1766 (error "FIXME")))
1769 ;;;; Inlinetask
1771 (defun org-e-odt-inlinetask (inlinetask contents info)
1772 "Transcode an INLINETASK element from Org to ODT.
1773 CONTENTS holds the contents of the block. INFO is a plist
1774 holding contextual information."
1775 (cond
1776 ;; If `org-e-odt-format-inlinetask-function' is provided, call it
1777 ;; with appropriate arguments.
1778 ((functionp org-e-odt-format-inlinetask-function)
1779 (let ((format-function
1780 (function*
1781 (lambda (todo todo-type priority text tags
1782 &key contents &allow-other-keys)
1783 (funcall org-e-odt-format-inlinetask-function
1784 todo todo-type priority text tags contents)))))
1785 (org-e-odt-format-headline--wrap
1786 inlinetask info format-function :contents contents)))
1787 ;; Otherwise, use a default template.
1788 (t (org-e-odt--wrap-label
1789 inlinetask
1790 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1791 "Text_20_body"
1792 (org-e-odt--textbox
1793 (concat
1794 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1795 "OrgInlineTaskHeading"
1796 (org-e-odt-format-headline--wrap
1797 inlinetask info))
1798 contents)
1799 nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\""))))))
1801 ;;;; Italic
1803 (defun org-e-odt-italic (italic contents info)
1804 "Transcode ITALIC from Org to ODT.
1805 CONTENTS is the text with italic markup. INFO is a plist holding
1806 contextual information."
1807 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1808 "Emphasis" contents))
1811 ;;;; Item
1813 (defun org-e-odt-item (item contents info)
1814 "Transcode an ITEM element from Org to ODT.
1815 CONTENTS holds the contents of the item. INFO is a plist holding
1816 contextual information."
1817 (let* ((plain-list (org-export-get-parent item))
1818 (type (org-element-property :type plain-list))
1819 (counter (org-element-property :counter item))
1820 (tag (let ((tag (org-element-property :tag item)))
1821 (and tag
1822 (concat (org-e-odt--checkbox item)
1823 (org-export-data tag info))))))
1824 (case type
1825 ((ordered unordered)
1826 (format "\n<text:list-item>\n%s\n%s"
1827 contents
1828 (let* ((--element-has-a-table-p
1829 (function
1830 (lambda (element info)
1831 (loop for el in (org-element-contents element)
1832 thereis (eq (org-element-type el) 'table))))))
1833 (cond
1834 ((funcall --element-has-a-table-p item info)
1835 "</text:list-header>")
1836 (t "</text:list-item>")))))
1837 (descriptive
1838 (concat
1839 (let ((term (or tag "(no term)")))
1840 (concat
1841 (format "\n<text:list-item>\n%s\n</text:list-item>"
1842 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1843 "Text_20_body_20_bold" term))
1844 (format
1845 "\n<text:list-item>\n%s\n</text:list-item>"
1846 (format "\n<text:list text:style-name=\"%s\" %s>\n%s\n</text:list>"
1847 "OrgDescriptionList"
1848 "text:continue-numbering=\"false\""
1849 (format "\n<text:list-item>\n%s\n</text:list-item>"
1850 contents)))))))
1851 (t (error "Unknown list type: %S" type)))))
1854 ;;;; Keyword
1856 (defun org-e-odt-keyword (keyword contents info)
1857 "Transcode a KEYWORD element from Org to ODT.
1858 CONTENTS is nil. INFO is a plist holding contextual information."
1859 (let ((key (org-element-property :key keyword))
1860 (value (org-element-property :value keyword)))
1861 (cond
1862 ((string= key "LATEX") value)
1863 ((string= key "INDEX") (format "\\index{%s}" value))
1864 ((string= key "TARGET") nil ; FIXME
1865 ;; (format "\\label{%s}" (org-export-solidify-link-text value))
1867 ((string= key "toc")
1868 (let ((value (downcase value)))
1869 (cond
1870 ((string-match "\\<headlines\\>" value)
1871 (let ((depth (or (and (string-match "[0-9]+" value)
1872 (string-to-number (match-string 0 value)))
1873 (plist-get info :with-toc))))
1874 (when (wholenump depth) (org-e-odt-toc depth info))))
1875 ((string= "tables" value) "FIXME")
1876 ((string= "figures" value) "FIXME")
1877 ((string= "listings" value)
1878 (cond
1879 ;; At the moment, src blocks with a caption are wrapped
1880 ;; into a figure environment.
1881 (t "FIXME")))))))))
1884 ;;;; Latex Environment
1887 (eval-after-load 'org-odt
1888 '(ad-deactivate 'org-format-latex-as-mathml))
1890 ;; (defadvice org-format-latex-as-mathml ; FIXME
1891 ;; (after org-e-odt-protect-latex-fragment activate)
1892 ;; "Encode LaTeX fragment as XML.
1893 ;; Do this when translation to MathML fails."
1894 ;; (when (or (not (> (length ad-return-value) 0))
1895 ;; (get-text-property 0 'org-protected ad-return-value))
1896 ;; (setq ad-return-value
1897 ;; (org-propertize (org-e-odt-encode-plain-text (ad-get-arg 0))
1898 ;; 'org-protected t))))
1900 (defun org-e-odt-format-latex (latex-frag processing-type info)
1901 (let* ((prefix (case processing-type
1902 (dvipng "ltxpng/")
1903 (mathml "ltxmathml/")))
1904 (input-file (plist-get info :input-file))
1905 (cache-subdir
1906 (concat prefix (file-name-sans-extension
1907 (file-name-nondirectory input-file))))
1908 (cache-dir (file-name-directory input-file))
1909 (display-msg (case processing-type
1910 (dvipng "Creating LaTeX Image...")
1911 (mathml "Creating MathML snippet..."))))
1912 (with-temp-buffer
1913 (insert latex-frag)
1914 (org-format-latex cache-subdir cache-dir nil display-msg
1915 nil nil processing-type)
1916 (buffer-string))))
1918 (defun org-e-odt-latex-environment (latex-environment contents info)
1919 "Transcode a LATEX-ENVIRONMENT element from Org to ODT.
1920 CONTENTS is nil. INFO is a plist holding contextual information."
1921 (org-e-odt--wrap-label
1922 latex-environment
1923 (let* ((latex-frag
1924 (org-remove-indentation
1925 (org-element-property :value latex-environment)))
1926 (processing-type (plist-get info :LaTeX-fragments))
1927 (caption (org-element-property :caption latex-environment))
1928 (short-caption (and (cdr caption)
1929 (org-export-data (cdr caption) info)))
1930 (caption (and (car caption) (org-export-data (car caption) info)))
1931 (label (org-element-property :name latex-environment))
1932 (attr nil) ; FIXME
1933 (label (org-element-property :name latex-environment)))
1935 (when (memq processing-type '(t mathjax))
1936 (unless (and (fboundp 'org-format-latex-mathml-available-p)
1937 (org-format-latex-mathml-available-p))
1938 (message "LaTeX to MathML converter not available. Trying dvinpng...")
1939 (setq processing-type 'dvipng)))
1941 (when (eq processing-type 'dvipng)
1942 (unless (and (org-check-external-command "latex" "" t)
1943 (org-check-external-command "dvipng" "" t))
1944 (message "LaTeX to PNG converter not available. Using verbatim.")
1945 (setq processing-type 'verbatim)))
1947 (case processing-type
1948 ((t mathjax)
1949 (org-e-odt-format-formula latex-environment info))
1950 (dvipng
1951 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1952 "Text_20_body"
1953 (org-e-odt-link--inline-image latex-environment info)))
1954 (t (org-e-odt-do-format-code latex-frag))))))
1957 ;;;; Latex Fragment
1960 ;; (when latex-frag ; FIXME
1961 ;; (setq href (org-propertize href :title "LaTeX Fragment"
1962 ;; :description latex-frag)))
1963 ;; handle verbatim
1964 ;; provide descriptions
1966 (defun org-e-odt-latex-fragment (latex-fragment contents info)
1967 "Transcode a LATEX-FRAGMENT object from Org to ODT.
1968 CONTENTS is nil. INFO is a plist holding contextual information."
1969 (let* ((latex-frag (org-element-property :value latex-fragment))
1970 (processing-type (plist-get info :LaTeX-fragments)))
1971 (cond
1972 ((member processing-type '(t mathjax))
1973 (org-e-odt-format-formula latex-fragment info))
1974 ((eq processing-type 'dvipng)
1975 (org-e-odt-link--inline-image latex-fragment info))
1976 (t (org-e-odt-encode-plain-text latex-frag t)))))
1979 ;;;; Line Break
1981 (defun org-e-odt-line-break (line-break contents info)
1982 "Transcode a LINE-BREAK object from Org to ODT.
1983 CONTENTS is nil. INFO is a plist holding contextual information."
1984 "<text:line-break/>\n")
1987 ;;;; Link
1991 ;;;; Links :: Generic
1993 (defun org-e-odt-format-link (desc href &optional suppress-xref)
1994 (cond
1995 ((and (= (string-to-char href) ?#) (not suppress-xref))
1996 (setq href (substring href 1))
1997 (let ((xref-format "text"))
1998 (when (numberp desc)
1999 (setq desc (format "%d" desc) xref-format "number"))
2000 (when (listp desc)
2001 (setq desc (mapconcat 'number-to-string desc ".") xref-format "chapter"))
2002 (setq href (concat org-e-odt-bookmark-prefix href))
2003 (format
2004 "<text:bookmark-ref text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:bookmark-ref>"
2005 xref-format href desc)))
2006 ;; (org-lparse-link-description-is-image
2007 ;; (format "\n<draw:a xlink:type=\"simple\" xlink:href=\"%s\">\n%s\n</draw:a>"
2008 ;; href desc))
2009 (t (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
2010 href desc))))
2012 (defun org-e-odt-format-internal-link (text href)
2013 (org-e-odt-format-link text (concat "#" href)))
2015 ;;;; Links :: Label references
2017 (defun org-e-odt-enumerate-element (element info &optional predicate n)
2018 (let* ((--numbered-parent-headline-at-<=-n
2019 (function
2020 (lambda (element n info)
2021 (loop for x in (org-export-get-genealogy element)
2022 thereis (and (eq (org-element-type x) 'headline)
2023 (<= (org-export-get-relative-level x info) n)
2024 (org-export-numbered-headline-p x info)
2025 x)))))
2026 (--enumerate
2027 (function
2028 (lambda (element scope info &optional predicate)
2029 (let ((counter 0))
2030 (org-element-map
2031 (or scope (plist-get info :parse-tree))
2032 (org-element-type element)
2033 (lambda (el)
2034 (and (or (not predicate) (funcall predicate el info))
2035 (incf counter)
2036 (eq element el)
2037 counter))
2038 info 'first-match)))))
2039 (scope (funcall --numbered-parent-headline-at-<=-n
2040 element (or n org-e-odt-display-outline-level) info))
2041 (ordinal (funcall --enumerate element scope info predicate))
2042 (tag
2043 (concat
2044 ;; Section number.
2045 (and scope
2046 (mapconcat 'number-to-string
2047 (org-export-get-headline-number scope info) "."))
2048 ;; Separator.
2049 (and scope ".")
2050 ;; Ordinal.
2051 (number-to-string ordinal))))
2052 tag))
2054 (defun org-e-odt-format-label (element info op)
2055 (let* ((caption-from
2056 (case (org-element-type element)
2057 (link (org-export-get-parent-element element))
2058 (t element)))
2059 ;; get label and caption.
2060 (label (org-element-property :name caption-from))
2061 (caption (org-element-property :caption caption-from))
2062 (short-caption (cdr caption))
2063 ;; transcode captions.
2064 (caption (and (car caption) (org-export-data (car caption) info)))
2065 (short-caption (and short-caption
2066 (org-export-data short-caption info))))
2067 (when (or label caption)
2068 (let* ((default-category
2069 (cond
2070 ((eq (org-element-type element) 'table)
2071 "__Table__")
2072 ((org-e-odt-standalone-image-p element info)
2073 "__Figure__")
2074 ((member (org-element-type element)
2075 '(latex-environment latex-fragment))
2076 (let ((processing-type (plist-get info :LaTeX-fragments)))
2077 (cond
2078 ((eq processing-type 'dvipng) "__DvipngImage__")
2079 ((eq processing-type 'mathjax) "__MathFormula__")
2080 ((eq processing-type 't) "__MathFormula__")
2081 (t (error "Handle LaTeX:verbatim")))))
2082 ((eq (org-element-type element) 'src-block)
2083 "__Listing__")
2084 (t (error "Handle enumeration of %S" element))))
2085 (predicate
2086 (cond
2087 ((member (org-element-type element)
2088 '(table latex-environment src-block))
2089 nil)
2090 ((org-e-odt-standalone-image-p element info)
2091 'org-e-odt-standalone-image-p)
2092 (t (error "Handle enumeration of %S" element))))
2093 (seqno (org-e-odt-enumerate-element
2094 element info predicate)) ; FIXME
2095 ;; handle label props.
2096 (label-props (assoc default-category org-e-odt-category-map-alist))
2097 ;; identify opendocument counter
2098 (counter (nth 1 label-props))
2099 ;; identify label style
2100 (label-style (nth 2 label-props))
2101 ;; retrieve localized category sting
2102 (category (org-export-translate (nth 3 label-props) :utf-8 info)))
2103 (case op
2104 (definition
2105 ;; assign an internal label, if user has not provided one
2106 (setq label (or label (format "%s-%s" default-category seqno)))
2107 (setq label (org-export-solidify-link-text label))
2109 (cons
2110 (format-spec
2111 (cadr (assoc-string label-style org-e-odt-label-styles t))
2112 `((?e . ,category)
2113 (?n . ,(format
2114 "<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">%s</text:sequence>"
2115 label counter counter seqno))
2116 (?c . ,(or caption ""))))
2117 short-caption))
2118 (reference
2119 (assert label)
2120 (setq label (org-export-solidify-link-text label))
2121 (let* ((fmt (cddr (assoc-string label-style org-e-odt-label-styles t)))
2122 (fmt1 (car fmt))
2123 (fmt2 (cadr fmt)))
2124 (format "<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:sequence-ref>"
2125 fmt1 label (format-spec fmt2 `((?e . ,category)
2126 (?n . ,seqno))))))
2127 (t (error "Unknow %S on label" op)))))))
2129 ;;;; Links :: Embedded images
2131 (defun org-e-odt-copy-image-file (path)
2132 "Returns the internal name of the file"
2133 (let* ((image-type (file-name-extension path))
2134 (media-type (format "image/%s" image-type))
2135 (target-dir "Images/")
2136 (target-file
2137 (format "%s%04d.%s" target-dir
2138 (incf org-e-odt-embedded-images-count) image-type)))
2139 (message "Embedding %s as %s ..."
2140 (substring-no-properties path) target-file)
2142 (when (= 1 org-e-odt-embedded-images-count)
2143 (make-directory (concat org-e-odt-zip-dir target-dir))
2144 (org-e-odt-create-manifest-file-entry "" target-dir))
2146 (copy-file path (concat org-e-odt-zip-dir target-file) 'overwrite)
2147 (org-e-odt-create-manifest-file-entry media-type target-file)
2148 target-file))
2150 (defun org-e-odt-image-size-from-file (file &optional user-width
2151 user-height scale dpi embed-as)
2152 (let* ((--pixels-to-cms
2153 (function (lambda (pixels dpi)
2154 (let ((cms-per-inch 2.54)
2155 (inches (/ pixels dpi)))
2156 (* cms-per-inch inches)))))
2157 (--size-in-cms
2158 (function
2159 (lambda (size-in-pixels dpi)
2160 (and size-in-pixels
2161 (cons (funcall --pixels-to-cms (car size-in-pixels) dpi)
2162 (funcall --pixels-to-cms (cdr size-in-pixels) dpi))))))
2163 (dpi (or dpi org-e-odt-pixels-per-inch))
2164 (anchor-type (or embed-as "paragraph"))
2165 (user-width (and (not scale) user-width))
2166 (user-height (and (not scale) user-height))
2167 (size
2168 (and
2169 (not (and user-height user-width))
2171 ;; Use Imagemagick.
2172 (and (executable-find "identify")
2173 (let ((size-in-pixels
2174 (let ((dim (shell-command-to-string
2175 (format "identify -format \"%%w:%%h\" \"%s\""
2176 file))))
2177 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
2178 (cons (string-to-number (match-string 1 dim))
2179 (string-to-number (match-string 2 dim)))))))
2180 (funcall --size-in-cms size-in-pixels dpi)))
2181 ;; Use Emacs.
2182 (let ((size-in-pixels
2183 (ignore-errors ; Emacs could be in batch mode
2184 (clear-image-cache)
2185 (image-size (create-image file) 'pixels))))
2186 (funcall --size-in-cms size-in-pixels dpi))
2187 ;; Use hard-coded values.
2188 (cdr (assoc-string anchor-type
2189 org-e-odt-default-image-sizes-alist))
2190 ;; Error out.
2191 (error "Cannot determine image size, aborting"))))
2192 (width (car size)) (height (cdr size)))
2193 (cond
2194 (scale
2195 (setq width (* width scale) height (* height scale)))
2196 ((and user-height user-width)
2197 (setq width user-width height user-height))
2198 (user-height
2199 (setq width (* user-height (/ width height)) height user-height))
2200 (user-width
2201 (setq height (* user-width (/ height width)) width user-width))
2202 (t (ignore)))
2203 ;; ensure that an embedded image fits comfortably within a page
2204 (let ((max-width (car org-e-odt-max-image-size))
2205 (max-height (cdr org-e-odt-max-image-size)))
2206 (when (or (> width max-width) (> height max-height))
2207 (let* ((scale1 (/ max-width width))
2208 (scale2 (/ max-height height))
2209 (scale (min scale1 scale2)))
2210 (setq width (* scale width) height (* scale height)))))
2211 (cons width height)))
2213 ;;;; Links :: Math formula
2215 (defun org-e-odt-format-formula (element info)
2216 (let* ((src (cond
2217 ((eq (org-element-type element) 'link) ; FIXME
2218 (let* ((type (org-element-property :type element))
2219 (raw-path (org-element-property :path element)))
2220 (cond
2221 ((file-name-absolute-p raw-path)
2222 (expand-file-name raw-path))
2223 (t raw-path))))
2224 ((member (org-element-type element)
2225 '(latex-fragment latex-environment))
2226 (let* ((latex-frag (org-remove-indentation
2227 (org-element-property :value element)))
2228 (formula-link (org-e-odt-format-latex
2229 latex-frag 'mathml info)))
2230 (and formula-link
2231 (string-match "file:\\([^]]*\\)" formula-link)
2232 (match-string 1 formula-link))))
2233 (t (error "what is this?"))))
2234 (full-src (if (file-name-absolute-p src) src
2235 (expand-file-name src (file-name-directory
2236 (plist-get info :input-file)))))
2237 (caption-from
2238 (case (org-element-type element)
2239 (link (org-export-get-parent-element element))
2240 (t element)))
2241 (captions (org-e-odt-format-label caption-from info 'definition))
2242 (caption (car captions))
2243 (href
2244 (format "\n<draw:object %s xlink:href=\"%s\" xlink:type=\"simple\"/>"
2245 " xlink:show=\"embed\" xlink:actuate=\"onLoad\""
2246 (file-name-directory (org-e-odt-copy-formula-file full-src))))
2247 (embed-as (if caption 'paragraph 'character))
2248 width height)
2249 (cond
2250 ((eq embed-as 'character)
2251 (org-e-odt-format-entity "InlineFormula" href width height))
2253 (let* ((equation (org-e-odt-format-entity
2254 "CaptionedDisplayFormula" href width height captions))
2255 (label
2256 (let* ((org-e-odt-category-map-alist
2257 '(("__Table__" "Table" "value")
2258 ("__Figure__" "Illustration" "value")
2259 ("__MathFormula__" "Text" "math-label")
2260 ("__DvipngImage__" "Equation" "value")
2261 ("__Listing__" "Listing" "value"))))
2262 (car (org-e-odt-format-label caption-from info 'definition))))
2263 (formula-tree
2264 (org-e-odt--adopt-elements
2265 `(table (:type org :attr_odt (":style \"OrgEquation\"")))
2266 (org-e-odt--adopt-elements
2267 `(table-row (:type standard))
2268 `(table-cell nil "<c8>") `(table-cell nil "<c1>"))
2269 (org-e-odt--adopt-elements
2270 `(table-row (:type standard))
2271 (org-e-odt--adopt-elements
2272 `(table-cell nil) `(export-block
2273 (:type "ODT" :value ,equation)))
2274 (org-e-odt--adopt-elements
2275 `(table-cell nil) `(export-block
2276 (:type "ODT" :value ,label))))))
2277 (formula-info
2278 (org-export-collect-tree-properties
2279 formula-tree (org-export-get-environment 'e-odt))))
2280 (org-export-data formula-tree formula-info))))))
2282 (defun org-e-odt-copy-formula-file (src-file)
2283 "Returns the internal name of the file"
2284 (let* ((target-dir (format "Formula-%04d/"
2285 (incf org-e-odt-embedded-formulas-count)))
2286 (target-file (concat target-dir "content.xml")))
2287 ;; Create a directory for holding formula file. Also enter it in
2288 ;; to manifest.
2289 (make-directory (concat org-e-odt-zip-dir target-dir))
2290 (org-e-odt-create-manifest-file-entry
2291 "application/vnd.oasis.opendocument.formula" target-dir "1.2")
2292 ;; Copy over the formula file from user directory to zip
2293 ;; directory.
2294 (message "Embedding %s as %s ..." src-file target-file)
2295 (let ((case-fold-search nil))
2296 (cond
2297 ;; Case 1: Mathml.
2298 ((string-match "\\.\\(mathml\\|mml\\)\\'" src-file)
2299 (copy-file src-file (concat org-e-odt-zip-dir target-file) 'overwrite))
2300 ;; Case 2: OpenDocument formula.
2301 ((string-match "\\.odf\\'" src-file)
2302 (org-e-odt--zip-extract src-file "content.xml"
2303 (concat org-e-odt-zip-dir target-dir)))
2304 (t (error "%s is not a formula file" src-file))))
2305 ;; Enter the formula file in to manifest.
2306 (org-e-odt-create-manifest-file-entry "text/xml" target-file)
2307 target-file))
2309 ;;;; Targets
2311 (defun org-e-odt-format-target (text id)
2312 (let ((name (concat org-e-odt-bookmark-prefix id)))
2313 (concat
2314 (and id (format "\n<text:bookmark-start text:name=\"%s\"/>" name))
2315 (concat (and id (format "\n<text:bookmark text:name=\"%s\"/>" id)) text)
2316 (and id (format "\n<text:bookmark-end text:name=\"%s\"/>" name)))))
2318 (defun org-e-odt-link--inline-image (element info)
2319 "Return HTML code for an inline image.
2320 LINK is the link pointing to the inline image. INFO is a plist
2321 used as a communication channel."
2322 (let* ((src (cond
2323 ((eq (org-element-type element) 'link)
2324 (let* ((type (org-element-property :type element))
2325 (raw-path (org-element-property :path element)))
2326 (cond ((member type '("http" "https"))
2327 (concat type ":" raw-path))
2328 ((file-name-absolute-p raw-path)
2329 (expand-file-name raw-path))
2330 (t raw-path))))
2331 ((member (org-element-type element)
2332 '(latex-fragment latex-environment))
2333 (let* ((latex-frag (org-remove-indentation
2334 (org-element-property :value element)))
2335 (formula-link (org-e-odt-format-latex
2336 latex-frag 'dvipng info)))
2337 (and formula-link
2338 (string-match "file:\\([^]]*\\)" formula-link)
2339 (match-string 1 formula-link))))
2340 (t (error "what is this?"))))
2341 (src-expanded (if (file-name-absolute-p src) src
2342 (expand-file-name src (file-name-directory
2343 (plist-get info :input-file)))))
2344 (href (format
2345 "\n<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>"
2346 (org-e-odt-copy-image-file src-expanded)))
2347 ;; extract attributes from #+ATTR_ODT line.
2348 (attr-from (case (org-element-type element)
2349 (link (org-export-get-parent-element element))
2350 (t element)))
2351 ;; convert attributes to a plist.
2352 (attr-plist (org-export-read-attribute :attr_odt attr-from))
2353 ;; handle `:anchor', `:style' and `:attributes' properties.
2354 (user-frame-anchor
2355 (car (assoc-string (plist-get attr-plist :anchor)
2356 '(("as-char") ("paragraph") ("page")) t)))
2357 (user-frame-style
2358 (and user-frame-anchor (plist-get attr-plist :style)))
2359 (user-frame-attrs
2360 (and user-frame-anchor (plist-get attr-plist :attributes)))
2361 (user-frame-params
2362 (list user-frame-style user-frame-attrs user-frame-anchor))
2363 ;; (embed-as (or embed-as user-frame-anchor "paragraph"))
2364 ;; extrac
2365 ;; handle `:width', `:height' and `:scale' properties.
2366 (size (org-e-odt-image-size-from-file
2367 src-expanded (plist-get attr-plist :width)
2368 (plist-get attr-plist :height)
2369 (plist-get attr-plist :scale) nil ;; embed-as
2370 "paragraph" ; FIXME
2372 (width (car size)) (height (cdr size))
2373 (embed-as
2374 (case (org-element-type element)
2375 ((org-e-odt-standalone-image-p element info) "paragraph")
2376 (latex-fragment "as-char")
2377 (latex-environment "paragraph")
2378 (t "paragraph")))
2379 (captions (org-e-odt-format-label element info 'definition))
2380 (caption (car captions)) (short-caption (cdr captions))
2381 (entity (concat (and caption "Captioned") embed-as "Image")))
2382 (org-e-odt-format-entity entity href width height
2383 captions user-frame-params )))
2385 (defun org-e-odt-format-entity (entity href width height &optional
2386 captions user-frame-params)
2387 (let* ((caption (car captions)) (short-caption (cdr captions))
2388 (entity-style (assoc-string entity org-e-odt-entity-frame-styles t))
2389 default-frame-params frame-params
2390 (--merge-frame-params
2391 (function
2392 (lambda (default-frame-params user-frame-params)
2393 (if (not user-frame-params) default-frame-params
2394 (assert (= (length default-frame-params) 3))
2395 (assert (= (length user-frame-params) 3))
2396 (loop for user-frame-param in user-frame-params
2397 for default-frame-param in default-frame-params
2398 collect (or user-frame-param default-frame-param)))))))
2399 (cond
2400 ((not caption)
2401 (setq default-frame-params (nth 2 entity-style))
2402 (setq frame-params (funcall --merge-frame-params
2403 default-frame-params user-frame-params))
2404 (apply 'org-e-odt--frame href width height frame-params))
2406 (setq default-frame-params (nth 3 entity-style))
2407 (setq frame-params (funcall --merge-frame-params
2408 default-frame-params user-frame-params))
2409 (apply 'org-e-odt--textbox
2410 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2411 "Illustration"
2412 (concat
2413 (apply 'org-e-odt--frame href width height
2414 (let ((entity-style-1 (copy-sequence
2415 (nth 2 entity-style))))
2416 (setcar (cdr entity-style-1)
2417 (concat
2418 (cadr entity-style-1)
2419 (and short-caption
2420 (format " draw:name=\"%s\" "
2421 short-caption))))
2422 entity-style-1))
2423 caption))
2424 width height frame-params)))))
2426 (defun org-e-odt-standalone-image-p (element info)
2427 "Test if ELEMENT is a standalone image for the purpose ODT export.
2428 INFO is a plist holding contextual information.
2430 Return non-nil, if ELEMENT is of type paragraph and it's sole
2431 content, save for whitespaces, is a link that qualifies as an
2432 inline image.
2434 Return non-nil, if ELEMENT is of type link and it's containing
2435 paragraph has no other content save for leading and trailing
2436 whitespaces.
2438 Return nil, otherwise.
2440 Bind `org-e-odt-standalone-image-predicate' to constrain
2441 paragraph further. For example, to check for only captioned
2442 standalone images, do the following.
2444 \(setq org-e-odt-standalone-image-predicate
2445 \(lambda \(paragraph\)
2446 \(org-element-property :caption paragraph\)\)\)
2448 (let ((--standalone-image-predicate
2449 (function (lambda (paragraph)
2450 (or (org-element-property :caption paragraph)
2451 (org-element-property :name paragraph)))))
2452 (paragraph (case (org-element-type element)
2453 (paragraph element)
2454 (link (and (org-export-inline-image-p
2455 element org-e-odt-inline-image-rules)
2456 (org-export-get-parent element)))
2457 (t nil))))
2458 (when paragraph
2459 (assert (eq (org-element-type paragraph) 'paragraph))
2460 (when (funcall --standalone-image-predicate paragraph)
2461 (let ((contents (org-element-contents paragraph)))
2462 (loop for x in contents
2463 with inline-image-count = 0
2464 always (cond
2465 ((eq (org-element-type x) 'plain-text)
2466 (not (org-string-nw-p x)))
2467 ((eq (org-element-type x) 'link)
2468 (when (org-export-inline-image-p
2469 x org-e-odt-inline-image-rules)
2470 (= (incf inline-image-count) 1)))
2471 (t nil))))))))
2473 (defun org-e-odt-link (link desc info)
2474 "Transcode a LINK object from Org to ODT.
2476 DESC is the description part of the link, or the empty string.
2477 INFO is a plist holding contextual information. See
2478 `org-export-data'."
2479 (let* ((type (org-element-property :type link))
2480 (raw-path (org-element-property :path link))
2481 ;; Ensure DESC really exists, or set it to nil.
2482 (desc (and (not (string= desc "")) desc))
2483 (imagep (org-export-inline-image-p
2484 link org-e-odt-inline-image-rules))
2485 (path (cond
2486 ((member type '("http" "https" "ftp" "mailto"))
2487 (concat type ":" raw-path))
2488 ((string= type "file")
2489 (when (string-match "\\(.+\\)::.+" raw-path)
2490 (setq raw-path (match-string 1 raw-path)))
2491 (if (file-name-absolute-p raw-path)
2492 (concat "file://" (expand-file-name raw-path))
2493 (concat "file://" raw-path)))
2494 (t raw-path)))
2495 protocol)
2496 (cond
2497 ;; Image file.
2498 ((and (not desc) (org-export-inline-image-p
2499 link org-e-odt-inline-image-rules))
2500 (org-e-odt-link--inline-image link info))
2501 ;; Radio target: Transcode target's contents and use them as
2502 ;; link's description.
2503 ((string= type "radio")
2504 (let ((destination (org-export-resolve-radio-link link info)))
2505 (when destination
2506 (org-e-odt-format-internal-link
2507 (org-export-data (org-element-contents destination) info)
2508 (org-export-solidify-link-text path)))))
2509 ;; Links pointing to an headline: Find destination and build
2510 ;; appropriate referencing command.
2511 ((member type '("custom-id" "fuzzy" "id"))
2512 (let ((destination (if (string= type "fuzzy")
2513 (org-export-resolve-fuzzy-link link info)
2514 (org-export-resolve-id-link link info))))
2515 (case (org-element-type destination)
2516 ;; Fuzzy link points nowhere.
2517 ('nil
2518 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2519 "Emphasis" (or desc (org-export-data
2520 (org-element-property
2521 :raw-link link) info))))
2522 ;; Fuzzy link points to an invisible target.
2523 (keyword nil)
2524 ;; LINK points to an headline. Check if LINK should display
2525 ;; section numbers.
2526 (headline
2527 (let* ((headline-no (org-export-get-headline-number destination info))
2528 (label (format "sec-%s" (mapconcat 'number-to-string
2529 headline-no "-")))
2530 (desc
2531 ;; Case 1: Headline is numbered and LINK has no
2532 ;; description or LINK's description matches
2533 ;; headline's title. Display section number.
2534 (if (and (org-export-numbered-headline-p destination info)
2535 (or (not desc)
2536 (string= desc (org-element-property
2537 :raw-value destination))))
2538 headline-no
2539 ;; Case 2: Either the headline is un-numbered or
2540 ;; LINK has a custom description. Display LINK's
2541 ;; description or headline's title.
2542 (or desc (org-export-data (org-element-property
2543 :title destination) info)))))
2544 (org-e-odt-format-internal-link desc label)))
2545 ;; Fuzzy link points to a target. Do as above.
2546 (otherwise
2547 ;; (unless desc
2548 ;; (setq number (cond
2549 ;; ((org-e-odt-standalone-image-p destination info)
2550 ;; (org-export-get-ordinal
2551 ;; (assoc 'link (org-element-contents destination))
2552 ;; info 'link 'org-e-odt-standalone-image-p))
2553 ;; (t (org-export-get-ordinal destination info))))
2554 ;; (setq desc (when number
2555 ;; (if (atom number) (number-to-string number)
2556 ;; (mapconcat 'number-to-string number ".")))))
2558 (let ((label-reference
2559 (org-e-odt-format-label destination info 'reference)))
2560 (assert label-reference)
2561 label-reference)))))
2562 ;; Coderef: replace link with the reference name or the
2563 ;; equivalent line number.
2564 ((string= type "coderef")
2565 (let* ((fmt (org-export-get-coderef-format path desc))
2566 (res (org-export-resolve-coderef path info))
2567 (href (concat "#coderef-" path)))
2568 (format fmt (org-e-odt-format-link res href))))
2569 ;; Link type is handled by a special function.
2570 ((functionp (setq protocol (nth 2 (assoc type org-link-protocols))))
2571 (funcall protocol (org-link-unescape path) desc 'odt))
2572 ;; External link with a description part.
2573 ((and path desc) (org-e-odt-format-link desc path))
2574 ;; External link without a description part.
2575 (path (org-e-odt-format-link path path))
2576 ;; No path, only description. Try to do something useful.
2577 (t (format "<text:span text:style-name=\"%s\">%s</text:span>"
2578 "Emphasis" desc)))))
2581 ;;;; Babel Call
2583 ;; Babel Calls are ignored.
2586 ;;;; Macro
2588 (defun org-e-odt-macro (macro contents info)
2589 "Transcode a MACRO element from Org to ODT.
2590 CONTENTS is nil. INFO is a plist holding contextual information."
2591 ;; Use available tools.
2592 (org-export-expand-macro macro info))
2595 ;;;; Paragraph
2597 (defun org-e-odt-paragraph (paragraph contents info)
2598 "Transcode a PARAGRAPH element from Org to ODT.
2599 CONTENTS is the contents of the paragraph, as a string. INFO is
2600 the plist used as a communication channel."
2601 (let* ((parent (org-export-get-parent paragraph))
2602 (parent-type (org-element-type parent))
2603 (style (case parent-type
2604 (quote-block "Quotations")
2605 (center-block "OrgCenter")
2606 (footnote-definition "Footnote")
2607 (t "Text_20_body"))))
2608 ;; If this paragraph is a leading paragraph in a non-descriptive
2609 ;; item and the item has a checkbox, splice the checkbox and
2610 ;; paragraph contents together.
2611 (when (and (eq (org-element-type parent) 'item)
2612 (not (eq (org-element-property :type
2613 (org-export-get-parent parent))
2614 'descriptive))
2615 (eq paragraph (car (org-element-contents parent))))
2616 (setq contents (concat (org-e-odt--checkbox parent) contents)))
2617 (assert style)
2618 (format "\n<text:p text:style-name=\"%s\">%s</text:p>" style contents)))
2621 ;;;; Plain List
2623 (defun org-e-odt-plain-list (plain-list contents info)
2624 "Transcode a PLAIN-LIST element from Org to ODT.
2625 CONTENTS is the contents of the list. INFO is a plist holding
2626 contextual information."
2627 (let* ((type (org-element-property :type plain-list))
2628 (continue-numbering nil))
2629 (assert (member type '(ordered unordered descriptive)))
2630 (org-e-odt--wrap-label
2631 plain-list
2632 (format "\n<text:list text:style-name=\"%s\" %s>\n%s</text:list>"
2633 (assoc-default type '((ordered . "OrgNumberedList")
2634 (unordered . "OrgBulletedList")
2635 (descriptive . "OrgDescriptionList")))
2636 ;; If top-level list, re-start numbering. Otherwise,
2637 ;; continue numbering.
2638 (format "text:continue-numbering=\"%s\""
2639 (let* ((parent (org-export-get-parent plain-list)))
2640 (if (and parent (eq (org-element-type parent) 'item))
2641 "true" "false")))
2642 contents))))
2644 ;;;; Plain Text
2646 (defun org-e-odt-fill-tabs-and-spaces (line)
2647 (replace-regexp-in-string
2648 "\\([\t]\\|\\([ ]+\\)\\)"
2649 (lambda (s)
2650 (cond
2651 ((string= s "\t") "<text:tab/>")
2652 (t (let ((n (length s)))
2653 (cond
2654 ((= n 1) " ")
2655 ((> n 1) (concat " " (format "<text:s text:c=\"%d\"/>" (1- n))))
2656 (t ""))))))
2657 line))
2659 (defun org-e-odt-encode-plain-text (text &optional no-whitespace-filling)
2660 (mapc
2661 (lambda (pair)
2662 (setq text (replace-regexp-in-string (car pair) (cdr pair) text t t)))
2663 '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
2664 (if no-whitespace-filling text
2665 (org-e-odt-fill-tabs-and-spaces text)))
2667 (defun org-e-odt--quotation-marks (text info)
2668 "Export quotation marks depending on language conventions.
2669 TEXT is a string containing quotation marks to be replaced. INFO
2670 is a plist used as a communication channel."
2671 (mapc (lambda(l)
2672 (let ((start 0))
2673 (while (setq start (string-match (car l) text start))
2674 (let ((new-quote (concat (match-string 1 text) (cdr l))))
2675 (setq text (replace-match new-quote t t text))))))
2676 (cdr (or (assoc (plist-get info :language) org-e-odt-quotes)
2677 ;; Falls back on English.
2678 (assoc "en" org-e-odt-quotes))))
2679 text)
2681 (defun org-e-odt-plain-text (text info)
2682 "Transcode a TEXT string from Org to ODT.
2683 TEXT is the string to transcode. INFO is a plist holding
2684 contextual information."
2685 ;; Protect &, < and >.
2686 (setq text (org-e-odt-encode-plain-text text t))
2687 ;; Handle quotation marks
2688 (setq text (org-e-odt--quotation-marks text info))
2689 ;; Convert special strings.
2690 (when (plist-get info :with-special-strings)
2691 (mapc
2692 (lambda (pair)
2693 (setq text (replace-regexp-in-string (car pair) (cdr pair) text t nil)))
2694 org-e-odt-special-string-regexps))
2695 ;; Handle break preservation if required.
2696 (when (plist-get info :preserve-breaks)
2697 (setq text (replace-regexp-in-string
2698 "\\(\\\\\\\\\\)?[ \t]*\n" "<text:line-break/>\n" text t)))
2699 ;; Return value.
2700 text)
2703 ;;;; Planning
2705 (defun org-e-odt-planning (planning contents info)
2706 "Transcode a PLANNING element from Org to ODT.
2707 CONTENTS is nil. INFO is a plist used as a communication
2708 channel."
2709 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2710 "OrgTimestampWrapper"
2711 (concat
2712 (let ((closed (org-element-property :closed planning)))
2713 (when closed
2714 (concat
2715 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2716 "OrgTimestampKeyword" org-closed-string)
2717 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2718 "OrgTimestamp" (org-translate-time closed)))))
2719 (let ((deadline (org-element-property :deadline planning)))
2720 (when deadline
2721 (concat
2722 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2723 "OrgTimestampKeyword" org-deadline-string)
2724 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2725 "OrgTimestamp" (org-translate-time deadline)))))
2726 (let ((scheduled (org-element-property :scheduled planning)))
2727 (when scheduled
2728 (concat
2729 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2730 "OrgTimestampKeyword" org-scheduled-string)
2731 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2732 "OrgTimestamp" (org-translate-time scheduled))))))))
2735 ;;;; Property Drawer
2737 (defun org-e-odt-property-drawer (property-drawer contents info)
2738 "Transcode a PROPERTY-DRAWER element from Org to ODT.
2739 CONTENTS is nil. INFO is a plist holding contextual
2740 information."
2741 ;; The property drawer isn't exported but we want separating blank
2742 ;; lines nonetheless.
2746 ;;;; Quote Block
2748 (defun org-e-odt-quote-block (quote-block contents info)
2749 "Transcode a QUOTE-BLOCK element from Org to ODT.
2750 CONTENTS holds the contents of the block. INFO is a plist
2751 holding contextual information."
2752 (org-e-odt--wrap-label quote-block contents))
2755 ;;;; Quote Section
2757 (defun org-e-odt-quote-section (quote-section contents info)
2758 "Transcode a QUOTE-SECTION element from Org to ODT.
2759 CONTENTS is nil. INFO is a plist holding contextual information."
2760 (let ((value (org-remove-indentation
2761 (org-element-property :value quote-section))))
2762 (when value (org-e-odt-do-format-code value))))
2765 ;;;; Section
2768 (defun org-e-odt-format-section (text style &optional name)
2769 (let ((default-name (car (org-e-odt-add-automatic-style "Section"))))
2770 (format "\n<text:section text:style-name=\"%s\" %s>\n%s</text:section>"
2771 style
2772 (format "text:name=\"%s\"" (or name default-name))
2773 text)))
2776 (defun org-e-odt-section (section contents info) ; FIXME
2777 "Transcode a SECTION element from Org to ODT.
2778 CONTENTS holds the contents of the section. INFO is a plist
2779 holding contextual information."
2780 contents)
2782 ;;;; Radio Target
2784 (defun org-e-odt-radio-target (radio-target text info)
2785 "Transcode a RADIO-TARGET object from Org to ODT.
2786 TEXT is the text of the target. INFO is a plist holding
2787 contextual information."
2788 (org-e-odt-format-target
2789 text (org-export-solidify-link-text
2790 (org-element-property :value radio-target))))
2793 ;;;; Special Block
2795 (defun org-e-odt-special-block (special-block contents info)
2796 "Transcode a SPECIAL-BLOCK element from Org to ODT.
2797 CONTENTS holds the contents of the block. INFO is a plist
2798 holding contextual information."
2799 (let ((type (downcase (org-element-property :type special-block)))
2800 (attributes (org-export-read-attribute :attr_odt special-block)))
2801 (org-e-odt--wrap-label
2802 special-block
2803 (cond
2804 ;; Annotation.
2805 ((string= type "annotation")
2806 (let ((author (or (plist-get attributes :author)
2807 (let ((author (plist-get info :author)))
2808 (and author (org-export-data author info)))))
2809 (date (or (plist-get attributes :date)
2810 (plist-get info :date))))
2812 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2813 "Text_20_body"
2814 (format "<office:annotation>\n%s\n</office:annotation>"
2815 (concat
2816 (and author
2817 (format "<dc:creator>%s</dc:creator>" author))
2818 (and date
2819 (format "<dc:date>%s</dc:date>"
2820 (org-e-odt--date date)))
2821 contents)))))
2822 ;; Textbox.
2823 ((string= type "textbox")
2824 (let ((width (plist-get attributes :width))
2825 (height (plist-get attributes :height))
2826 (style (plist-get attributes :style))
2827 (extra (plist-get attributes :extra))
2828 (anchor (plist-get attributes :anchor)))
2829 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2830 "Text_20_body" (org-e-odt--textbox contents width height
2831 style extra anchor))))
2832 (t contents)))))
2835 ;;;; Src Block
2838 (defun org-e-odt-hfy-face-to-css (fn)
2839 "Create custom style for face FN.
2840 When FN is the default face, use it's foreground and background
2841 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
2842 use it's color attribute to create a character style whose name
2843 is obtained from FN. Currently all attributes of FN other than
2844 color are ignored.
2846 The style name for a face FN is derived using the following
2847 operations on the face name in that order - de-dash, CamelCase
2848 and prefix with \"OrgSrc\". For example,
2849 `font-lock-function-name-face' is associated with
2850 \"OrgSrcFontLockFunctionNameFace\"."
2851 (let* ((css-list (hfy-face-to-style fn))
2852 (style-name ((lambda (fn)
2853 (concat "OrgSrc"
2854 (mapconcat
2855 'capitalize (split-string
2856 (hfy-face-or-def-to-name fn) "-")
2857 ""))) fn))
2858 (color-val (cdr (assoc "color" css-list)))
2859 (background-color-val (cdr (assoc "background" css-list)))
2860 (style (and org-e-odt-create-custom-styles-for-srcblocks
2861 (cond
2862 ((eq fn 'default)
2863 (format org-e-odt-src-block-paragraph-format
2864 background-color-val color-val))
2866 (format
2868 <style:style style:name=\"%s\" style:family=\"text\">
2869 <style:text-properties fo:color=\"%s\"/>
2870 </style:style>" style-name color-val))))))
2871 (cons style-name style)))
2873 (defun org-e-odt-htmlfontify-string (line)
2874 (let* ((hfy-html-quote-regex "\\([<\"&> ]\\)")
2875 (hfy-html-quote-map '(("\"" "&quot;")
2876 ("<" "&lt;")
2877 ("&" "&amp;")
2878 (">" "&gt;")
2879 (" " "<text:s/>")
2880 (" " "<text:tab/>")))
2881 (hfy-face-to-css 'org-e-odt-hfy-face-to-css)
2882 (hfy-optimisations-1 (copy-seq hfy-optimisations))
2883 (hfy-optimisations (add-to-list 'hfy-optimisations-1
2884 'body-text-only))
2885 (hfy-begin-span-handler
2886 (lambda (style text-block text-id text-begins-block-p)
2887 (insert (format "<text:span text:style-name=\"%s\">" style))))
2888 (hfy-end-span-handler (lambda nil (insert "</text:span>"))))
2889 (org-no-warnings (htmlfontify-string line))))
2891 (defun org-e-odt-do-format-code
2892 (code &optional lang refs retain-labels num-start)
2893 (let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
2894 (lang-mode (and lang (intern (format "%s-mode" lang))))
2895 (code-lines (org-split-string code "\n"))
2896 (code-length (length code-lines))
2897 (use-htmlfontify-p (and (functionp lang-mode)
2898 org-e-odt-fontify-srcblocks
2899 (require 'htmlfontify nil t)
2900 (fboundp 'htmlfontify-string)))
2901 (code (if (not use-htmlfontify-p) code
2902 (with-temp-buffer
2903 (insert code)
2904 (funcall lang-mode)
2905 (font-lock-fontify-buffer)
2906 (buffer-string))))
2907 (fontifier (if use-htmlfontify-p 'org-e-odt-htmlfontify-string
2908 'org-e-odt-encode-plain-text))
2909 (par-style (if use-htmlfontify-p "OrgSrcBlock"
2910 "OrgFixedWidthBlock"))
2911 (i 0))
2912 (assert (= code-length (length (org-split-string code "\n"))))
2913 (setq code
2914 (org-export-format-code
2915 code
2916 (lambda (loc line-num ref)
2917 (setq par-style
2918 (concat par-style (and (= (incf i) code-length) "LastLine")))
2920 (setq loc (concat loc (and ref retain-labels (format " (%s)" ref))))
2921 (setq loc (funcall fontifier loc))
2922 (when ref
2923 (setq loc (org-e-odt-format-target loc (concat "coderef-" ref))))
2924 (assert par-style)
2925 (setq loc (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2926 par-style loc))
2927 (if (not line-num) loc
2928 (format "\n<text:list-item>%s\n</text:list-item>" loc)))
2929 num-start refs))
2930 (cond
2931 ((not num-start) code)
2932 ((= num-start 0)
2933 (format
2934 "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
2935 " text:continue-numbering=\"false\"" code))
2937 (format
2938 "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
2939 " text:continue-numbering=\"true\"" code)))))
2941 (defun org-e-odt-format-code (element info)
2942 (let* ((lang (org-element-property :language element))
2943 ;; Extract code and references.
2944 (code-info (org-export-unravel-code element))
2945 (code (car code-info))
2946 (refs (cdr code-info))
2947 ;; Does the src block contain labels?
2948 (retain-labels (org-element-property :retain-labels element))
2949 ;; Does it have line numbers?
2950 (num-start (case (org-element-property :number-lines element)
2951 (continued (org-export-get-loc element info))
2952 (new 0))))
2953 (org-e-odt-do-format-code code lang refs retain-labels num-start)))
2955 (defun org-e-odt-src-block (src-block contents info)
2956 "Transcode a SRC-BLOCK element from Org to ODT.
2957 CONTENTS holds the contents of the item. INFO is a plist holding
2958 contextual information."
2959 (let* ((lang (org-element-property :language src-block))
2960 (caption (org-element-property :caption src-block))
2961 (short-caption (and (cdr caption)
2962 (org-export-data (cdr caption) info)))
2963 (caption (and (car caption) (org-export-data (car caption) info)))
2964 (label (org-element-property :name src-block))
2965 (attributes (org-export-read-attribute :attr_odt src-block)))
2966 ;; FIXME: Handle caption
2967 ;; caption-str (when caption)
2968 ;; (main (org-export-data (car caption) info))
2969 ;; (secondary (org-export-data (cdr caption) info))
2970 ;; (caption-str (org-e-odt--caption/label-string caption label info))
2971 (let* ((captions (org-e-odt-format-label src-block info 'definition))
2972 (caption (car captions)) (short-caption (cdr captions)))
2973 (concat
2974 (and caption
2975 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2976 "Listing" caption))
2977 (let ((--src-block (org-e-odt-format-code src-block info)))
2978 (if (not (plist-get attributes :textbox)) --src-block
2979 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2980 "Text_20_body"
2981 (org-e-odt--textbox --src-block nil nil nil))))))))
2984 ;;;; Statistics Cookie
2986 (defun org-e-odt-statistics-cookie (statistics-cookie contents info)
2987 "Transcode a STATISTICS-COOKIE object from Org to ODT.
2988 CONTENTS is nil. INFO is a plist holding contextual information."
2989 (let ((cookie-value (org-element-property :value statistics-cookie)))
2990 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2991 "OrgCode" cookie-value)))
2994 ;;;; Strike-Through
2996 (defun org-e-odt-strike-through (strike-through contents info)
2997 "Transcode STRIKE-THROUGH from Org to ODT.
2998 CONTENTS is the text with strike-through markup. INFO is a plist
2999 holding contextual information."
3000 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3001 "Strikethrough" contents))
3004 ;;;; Subscript
3006 (defun org-e-odt-subscript (subscript contents info)
3007 "Transcode a SUBSCRIPT object from Org to ODT.
3008 CONTENTS is the contents of the object. INFO is a plist holding
3009 contextual information."
3010 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3011 "OrgSubscript" contents))
3014 ;;;; Superscript
3016 (defun org-e-odt-superscript (superscript contents info)
3017 "Transcode a SUPERSCRIPT object from Org to ODT.
3018 CONTENTS is the contents of the object. INFO is a plist holding
3019 contextual information."
3020 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3021 "OrgSuperscript" contents))
3024 ;;;; Table Cell
3026 (defun org-e-odt-table-style-spec (element info)
3027 (let* ((table (org-export-get-parent-table element))
3028 (table-attributes (org-export-read-attribute :attr_odt table))
3029 (table-style (plist-get table-attributes :style)))
3030 (assoc table-style org-e-odt-table-styles)))
3032 (defun org-e-odt-get-table-cell-styles (table-cell info)
3033 "Retrieve styles applicable to a table cell.
3034 R and C are (zero-based) row and column numbers of the table
3035 cell. STYLE-SPEC is an entry in `org-e-odt-table-styles'
3036 applicable to the current table. It is `nil' if the table is not
3037 associated with any style attributes.
3039 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
3041 When STYLE-SPEC is nil, style the table cell the conventional way
3042 - choose cell borders based on row and column groupings and
3043 choose paragraph alignment based on `org-col-cookies' text
3044 property. See also
3045 `org-e-odt-get-paragraph-style-cookie-for-table-cell'.
3047 When STYLE-SPEC is non-nil, ignore the above cookie and return
3048 styles congruent with the ODF-1.2 specification."
3049 (let* ((table-cell-address (org-export-table-cell-address table-cell info))
3050 (r (car table-cell-address)) (c (cdr table-cell-address))
3051 (style-spec (org-e-odt-table-style-spec table-cell info))
3052 (table-dimensions (org-export-table-dimensions
3053 (org-export-get-parent-table table-cell)
3054 info)))
3055 (when style-spec
3056 ;; LibreOffice - particularly the Writer - honors neither table
3057 ;; templates nor custom table-cell styles. Inorder to retain
3058 ;; inter-operability with LibreOffice, only automatic styles are
3059 ;; used for styling of table-cells. The current implementation is
3060 ;; congruent with ODF-1.2 specification and hence is
3061 ;; future-compatible.
3063 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
3064 ;; which recognizes as many as 16 different cell types - is much
3065 ;; richer. Unfortunately it is NOT amenable to easy configuration
3066 ;; by hand.
3067 (let* ((template-name (nth 1 style-spec))
3068 (cell-style-selectors (nth 2 style-spec))
3069 (cell-type
3070 (cond
3071 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors))
3072 (= c 0)) "FirstColumn")
3073 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors))
3074 (= (1+ c) (cdr table-dimensions)))
3075 "LastColumn")
3076 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors))
3077 (= r 0)) "FirstRow")
3078 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors))
3079 (= (1+ r) (car table-dimensions)))
3080 "LastRow")
3081 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
3082 (= (% r 2) 1)) "EvenRow")
3083 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
3084 (= (% r 2) 0)) "OddRow")
3085 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
3086 (= (% c 2) 1)) "EvenColumn")
3087 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
3088 (= (% c 2) 0)) "OddColumn")
3089 (t ""))))
3090 (concat template-name cell-type)))))
3092 (defun org-e-odt-table-cell (table-cell contents info)
3093 "Transcode a TABLE-CELL element from Org to ODT.
3094 CONTENTS is nil. INFO is a plist used as a communication
3095 channel."
3096 (let* ((table-cell-address (org-export-table-cell-address table-cell info))
3097 (r (car table-cell-address))
3098 (c (cdr table-cell-address))
3099 (horiz-span (or (org-export-table-cell-width table-cell info) 0))
3100 (table-row (org-export-get-parent table-cell))
3101 (custom-style-prefix (org-e-odt-get-table-cell-styles
3102 table-cell info))
3103 (paragraph-style
3105 (and custom-style-prefix
3106 (format "%sTableParagraph" custom-style-prefix))
3107 (concat
3108 (cond
3109 ((and (= 1 (org-export-table-row-group table-row info))
3110 (org-export-table-has-header-p
3111 (org-export-get-parent-table table-row) info))
3112 "OrgTableHeading")
3113 ((let* ((table (org-export-get-parent-table table-cell))
3114 (table-attrs (org-export-read-attribute :attr_odt table))
3115 (table-header-columns (plist-get table-attrs
3116 :header-columns)))
3117 (<= c (cond ((wholenump table-header-columns)
3118 (- table-header-columns 1))
3119 (table-header-columns 0)
3120 (t -1))))
3121 "OrgTableHeading")
3122 (t "OrgTableContents"))
3123 (capitalize (symbol-name (org-export-table-cell-alignment
3124 table-cell info))))))
3125 (cell-style-name
3127 (and custom-style-prefix (format "%sTableCell"
3128 custom-style-prefix))
3129 (concat
3130 "OrgTblCell"
3131 (when (or (org-export-table-row-starts-rowgroup-p table-row info)
3132 (zerop r)) "T")
3133 (when (org-export-table-row-ends-rowgroup-p table-row info) "B")
3134 (when (and (org-export-table-cell-starts-colgroup-p table-cell info)
3135 (not (zerop c)) ) "L"))))
3136 (cell-attributes
3137 (concat
3138 (format " table:style-name=\"%s\"" cell-style-name)
3139 (and (> horiz-span 0)
3140 (format " table:number-columns-spanned=\"%d\""
3141 (1+ horiz-span))))))
3142 (unless contents (setq contents ""))
3143 (concat
3144 (assert paragraph-style)
3145 (format "\n<table:table-cell%s>\n%s\n</table:table-cell>"
3146 cell-attributes
3147 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3148 paragraph-style contents))
3149 (let (s)
3150 (dotimes (i horiz-span s)
3151 (setq s (concat s "\n<table:covered-table-cell/>"))))
3152 "\n")))
3155 ;;;; Table Row
3157 (defun org-e-odt-table-row (table-row contents info)
3158 "Transcode a TABLE-ROW element from Org to ODT.
3159 CONTENTS is the contents of the row. INFO is a plist used as a
3160 communication channel."
3161 ;; Rules are ignored since table separators are deduced from
3162 ;; borders of the current row.
3163 (when (eq (org-element-property :type table-row) 'standard)
3164 (let* ((rowgroup-tags
3165 (if (and (= 1 (org-export-table-row-group table-row info))
3166 (org-export-table-has-header-p
3167 (org-export-get-parent-table table-row) info))
3168 ;; If the row belongs to the first rowgroup and the
3169 ;; table has more than one row groups, then this row
3170 ;; belongs to the header row group.
3171 '("\n<table:table-header-rows>" . "\n</table:table-header-rows>")
3172 ;; Otherwise, it belongs to non-header row group.
3173 '("\n<table:table-rows>" . "\n</table:table-rows>"))))
3174 (concat
3175 ;; Does this row begin a rowgroup?
3176 (when (org-export-table-row-starts-rowgroup-p table-row info)
3177 (car rowgroup-tags))
3178 ;; Actual table row
3179 (format "\n<table:table-row>\n%s\n</table:table-row>" contents)
3180 ;; Does this row end a rowgroup?
3181 (when (org-export-table-row-ends-rowgroup-p table-row info)
3182 (cdr rowgroup-tags))))))
3185 ;;;; Table
3187 (defun org-e-odt-table-first-row-data-cells (table info)
3188 (let ((table-row
3189 (org-element-map
3190 table 'table-row
3191 (lambda (row)
3192 (unless (eq (org-element-property :type row) 'rule) row))
3193 info 'first-match))
3194 (special-column-p (org-export-table-has-special-column-p table)))
3195 (if (not special-column-p) (org-element-contents table-row)
3196 (cdr (org-element-contents table-row)))))
3198 (defun org-e-odt--table (table contents info)
3199 "Transcode a TABLE element from Org to ODT.
3200 CONTENTS is the contents of the table. INFO is a plist holding
3201 contextual information."
3202 (case (org-element-property :type table)
3203 ;; Case 1: table.el doesn't support export to OD format. Strip
3204 ;; such tables from export.
3205 (table.el
3206 (prog1 nil
3207 (message
3208 (concat
3209 "(org-e-odt): Found table.el-type table in the source Org file."
3210 " table.el doesn't support export to ODT format."
3211 " Stripping the table from export."))))
3212 ;; Case 2: Native Org tables.
3213 (otherwise
3214 (let* ((captions (org-e-odt-format-label table info 'definition))
3215 (caption (car captions)) (short-caption (cdr captions))
3216 (attributes (org-export-read-attribute :attr_odt table))
3217 (custom-table-style (nth 1 (org-e-odt-table-style-spec table info)))
3218 (table-column-specs
3219 (function
3220 (lambda (table info)
3221 (let* ((table-style (or custom-table-style "OrgTable"))
3222 (column-style (format "%sColumn" table-style)))
3223 (mapconcat
3224 (lambda (table-cell)
3225 (let ((width (1+ (or (org-export-table-cell-width
3226 table-cell info) 0)))
3227 (s (format
3228 "\n<table:table-column table:style-name=\"%s\"/>"
3229 column-style))
3230 out)
3231 (dotimes (i width out) (setq out (concat s out)))))
3232 (org-e-odt-table-first-row-data-cells table info) "\n"))))))
3233 (concat
3234 ;; caption.
3235 (when caption
3236 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3237 "Table" caption))
3238 ;; begin table.
3239 (let* ((automatic-name
3240 (org-e-odt-add-automatic-style "Table" attributes)))
3241 (format
3242 "\n<table:table table:name=\"%s\" table:style-name=\"%s\">"
3243 (or short-caption (car automatic-name))
3244 (or custom-table-style (cdr automatic-name) "OrgTable")))
3245 ;; column specification.
3246 (funcall table-column-specs table info)
3247 ;; actual contents.
3248 "\n" contents
3249 ;; end table.
3250 "</table:table>")))))
3252 (defun org-e-odt-table (table contents info)
3253 "Transcode a TABLE element from Org to ODT.
3254 CONTENTS is the contents of the table. INFO is a plist holding
3255 contextual information."
3256 (let* ((--get-previous-elements
3257 (function
3258 (lambda (blob info)
3259 (let ((parent (org-export-get-parent blob)))
3260 (cdr (member blob (reverse (org-element-contents parent))))))))
3261 (--element-preceded-by-table-p
3262 (function
3263 (lambda (element info)
3264 (loop for el in (funcall --get-previous-elements element info)
3265 thereis (eq (org-element-type el) 'table)))))
3266 (--walk-list-genealogy-and-collect-tags
3267 (function
3268 (lambda (table info)
3269 (let* ((genealogy (org-export-get-genealogy table))
3270 (list-genealogy
3271 (when (eq (org-element-type (car genealogy)) 'item)
3272 (loop for el in genealogy
3273 when (member (org-element-type el)
3274 '(item plain-list))
3275 collect el))))
3276 (loop for el in list-genealogy
3277 with parent-list collect
3278 (case (org-element-type el)
3279 (plain-list
3280 (setq parent-list el)
3281 `("</text:list>"
3282 . ,(let ((type (org-element-property :type el)))
3283 (format
3284 "<text:list text:style-name=\"%s\" %s>"
3285 (assoc-default
3286 type '((ordered . "OrgNumberedList")
3287 (unordered . "OrgBulletedList")
3288 (descriptive . "OrgDescriptionList")))
3289 "text:continue-numbering=\"true\""))))
3290 (item
3291 (cond
3292 ((not parent-list)
3293 (if (funcall --element-preceded-by-table-p table info)
3294 '("</text:list-header>" . "<text:list-header>")
3295 '("</text:list-item>" . "<text:list-header>")))
3296 ((funcall --element-preceded-by-table-p
3297 parent-list info)
3298 '("</text:list-header>" . "<text:list-header>"))
3299 (t '("</text:list-item>" . "<text:list-item>"))))))))))
3300 (close-open-tags (funcall --walk-list-genealogy-and-collect-tags
3301 table info)))
3302 ;; OpenDocument schema does not permit table to occur within a
3303 ;; list item. So, to typeset an indented table, we make use of
3304 ;; list continuations.
3305 (concat "\n"
3306 ;; Discontinue the list.
3307 (mapconcat 'car close-open-tags "\n")
3308 ;; Put the table in an indented section.
3309 (let* ((table (org-e-odt--table table contents info))
3310 (level (/ (length (mapcar 'car close-open-tags)) 2))
3311 (style (format "OrgIndentedSection-Level-%d" level)))
3312 (when table (org-e-odt-format-section table style)))
3313 ;; Continue the list.
3314 (mapconcat 'cdr (nreverse close-open-tags) "\n"))))
3317 ;;;; Target
3319 (defun org-e-odt-target (target contents info)
3320 "Transcode a TARGET object from Org to ODT.
3321 CONTENTS is nil. INFO is a plist holding contextual
3322 information."
3323 (org-e-odt-format-target
3324 "" (org-export-solidify-link-text (org-element-property :value target))))
3327 ;;;; Timestamp
3329 (defun org-e-odt-timestamp (timestamp contents info)
3330 "Transcode a TIMESTAMP object from Org to ODT.
3331 CONTENTS is nil. INFO is a plist used as a communication
3332 channel."
3333 (let ((timestamp-1 (org-element-property :value timestamp))
3334 (timestamp-2 (org-element-property :range-end timestamp)))
3335 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3336 "OrgTimestampWrapper"
3337 (concat
3338 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3339 "OrgTimestamp" (org-translate-time timestamp-1))
3340 (and timestamp-2
3341 "&#x2013;"
3342 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3343 "OrgTimestamp" (org-translate-time timestamp-2)))))))
3346 ;;;; Underline
3348 (defun org-e-odt-underline (underline contents info)
3349 "Transcode UNDERLINE from Org to ODT.
3350 CONTENTS is the text with underline markup. INFO is a plist
3351 holding contextual information."
3352 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3353 "Underline" contents))
3356 ;;;; Verbatim
3358 (defun org-e-odt-verbatim (verbatim contents info)
3359 "Transcode a VERBATIM object from Org to ODT.
3360 CONTENTS is nil. INFO is a plist used as a communication
3361 channel."
3362 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3363 "OrgCode" (org-element-property :value verbatim)))
3366 ;;;; Verse Block
3368 (defun org-e-odt-verse-block (verse-block contents info)
3369 "Transcode a VERSE-BLOCK element from Org to ODT.
3370 CONTENTS is verse block contents. INFO is a plist holding
3371 contextual information."
3372 ;; Add line breaks to each line of verse.
3373 (setq contents (replace-regexp-in-string
3374 "\\(<text:line-break/>\\)?[ \t]*\n"
3375 "<text:line-break/>" contents))
3376 ;; Replace tabs and spaces.
3377 (setq contents (org-e-odt-fill-tabs-and-spaces contents))
3378 ;; Surround it in a verse environment.
3379 (org-e-odt--wrap-label
3380 verse-block
3381 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3382 "OrgVerse" contents)))
3388 ;;; Interactive functions
3390 (defun org-e-odt-create-manifest-file-entry (&rest args)
3391 (push args org-e-odt-manifest-file-entries))
3393 (defun org-e-odt-write-manifest-file ()
3394 (make-directory (concat org-e-odt-zip-dir "META-INF"))
3395 (let ((manifest-file (concat org-e-odt-zip-dir "META-INF/manifest.xml")))
3396 (with-current-buffer
3397 (let ((nxml-auto-insert-xml-declaration-flag nil))
3398 (find-file-noselect manifest-file t))
3399 (insert
3400 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
3401 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
3402 (mapc
3403 (lambda (file-entry)
3404 (let* ((version (nth 2 file-entry))
3405 (extra (if (not version) ""
3406 (format " manifest:version=\"%s\"" version))))
3407 (insert
3408 (format org-e-odt-manifest-file-entry-tag
3409 (nth 0 file-entry) (nth 1 file-entry) extra))))
3410 org-e-odt-manifest-file-entries)
3411 (insert "\n</manifest:manifest>"))))
3413 (defmacro org-e-odt--export-wrap (out-file &rest body)
3414 `(let* ((--out-file ,out-file)
3415 (out-file-type (file-name-extension --out-file))
3416 (org-e-odt-xml-files '("META-INF/manifest.xml" "content.xml"
3417 "meta.xml" "styles.xml"))
3418 ;; Initialize workarea. All files that end up in the
3419 ;; exported get created here.
3420 (org-e-odt-zip-dir (file-name-as-directory
3421 (make-temp-file (format "%s-" out-file-type) t)))
3422 (org-e-odt-manifest-file-entries nil)
3423 (--cleanup-xml-buffers
3424 (function
3425 (lambda nil
3426 ;; Kill all XML buffers.
3427 (mapc (lambda (file)
3428 (let ((buf (get-file-buffer
3429 (concat org-e-odt-zip-dir file))))
3430 (when buf
3431 (set-buffer-modified-p nil)
3432 (kill-buffer buf))))
3433 org-e-odt-xml-files)
3434 ;; Delete temporary directory and also other embedded
3435 ;; files that get copied there.
3436 (delete-directory org-e-odt-zip-dir t)))))
3437 (condition-case
3439 (progn
3440 (unless (executable-find "zip")
3441 ;; Not at all OSes ship with zip by default
3442 (error "Executable \"zip\" needed for creating OpenDocument files"))
3443 ;; Do export. This creates a bunch of xml files ready to be
3444 ;; saved and zipped.
3445 (progn ,@body)
3446 ;; Create a manifest entry for content.xml.
3447 (org-e-odt-create-manifest-file-entry "text/xml" "content.xml")
3449 ;; Write mimetype file
3450 (let* ((mimetypes
3451 '(("odt" . "application/vnd.oasis.opendocument.text")
3452 ("odf" . "application/vnd.oasis.opendocument.formula")))
3453 (mimetype (cdr (assoc-string out-file-type mimetypes t))))
3454 (unless mimetype
3455 (error "Unknown OpenDocument backend %S" out-file-type))
3456 (write-region mimetype nil (concat org-e-odt-zip-dir "mimetype"))
3457 (org-e-odt-create-manifest-file-entry mimetype "/" "1.2"))
3458 ;; Write out the manifest entries before zipping
3459 (org-e-odt-write-manifest-file)
3460 ;; Save all XML files.
3461 (mapc (lambda (file)
3462 (let ((buf (get-file-buffer (concat org-e-odt-zip-dir file))))
3463 (when buf
3464 (with-current-buffer buf
3465 ;; Prettify output if needed.
3466 (when org-e-odt-prettify-xml
3467 (indent-region (point-min) (point-max)))
3468 (save-buffer 0)))))
3469 org-e-odt-xml-files)
3470 ;; Run zip.
3471 (let* ((target --out-file)
3472 (target-name (file-name-nondirectory target))
3473 (target-dir (file-name-directory target))
3474 (cmds `(("zip" "-mX0" ,target-name "mimetype")
3475 ("zip" "-rmTq" ,target-name "."))))
3476 ;; If a file with same name as the desired output file
3477 ;; exists, remove it.
3478 (when (file-exists-p target)
3479 (delete-file target))
3480 ;; Zip up the xml files.
3481 (let ((coding-system-for-write 'no-conversion) exitcode err-string)
3482 (message "Creating ODT file...")
3483 ;; Switch temporarily to content.xml. This way Zip
3484 ;; process will inherit `org-e-odt-zip-dir' as the current
3485 ;; directory.
3486 (with-current-buffer
3487 (find-file-noselect (concat org-e-odt-zip-dir "content.xml") t)
3488 (mapc
3489 (lambda (cmd)
3490 (message "Running %s" (mapconcat 'identity cmd " "))
3491 (setq err-string
3492 (with-output-to-string
3493 (setq exitcode
3494 (apply 'call-process (car cmd)
3495 nil standard-output nil (cdr cmd)))))
3496 (or (zerop exitcode)
3497 (error (concat "Unable to create OpenDocument file."
3498 (format " Zip failed with error (%s)"
3499 err-string)))))
3500 cmds)
3501 ;; Zip file is now in the rightful place.
3502 (rename-file target-name target)))
3503 (message "Created %s" target)
3504 ;; Cleanup work directory and work files.
3505 (funcall --cleanup-xml-buffers)
3506 ;; Open the OpenDocument file in archive-mode for
3507 ;; examination.
3508 (find-file-noselect target t)
3509 ;; Return exported file.
3510 (cond
3511 ;; Case 1: Conversion desired on exported file. Run the
3512 ;; converter on the OpenDocument file. Return the
3513 ;; converted file.
3514 (org-e-odt-preferred-output-format
3515 (or (org-e-odt-convert target org-e-odt-preferred-output-format)
3516 target))
3517 ;; Case 2: No further conversion. Return exported
3518 ;; OpenDocument file.
3519 (t target))))
3520 ((quit error)
3521 ;; Cleanup work directory and work files.
3522 (funcall --cleanup-xml-buffers)
3523 (message "OpenDocument export failed: %s"
3524 (error-message-string err))))))
3528 ;;;###autoload
3529 (defun org-e-odt-export-as-odf (latex-frag &optional odf-file)
3530 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
3531 Use `org-create-math-formula' to convert LATEX-FRAG first to
3532 MathML. When invoked as an interactive command, use
3533 `org-latex-regexps' to infer LATEX-FRAG from currently active
3534 region. If no LaTeX fragments are found, prompt for it. Push
3535 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
3536 non-nil."
3537 (interactive
3538 `(,(let (frag)
3539 (setq frag (and (setq frag (and (region-active-p)
3540 (buffer-substring (region-beginning)
3541 (region-end))))
3542 (loop for e in org-latex-regexps
3543 thereis (when (string-match (nth 1 e) frag)
3544 (match-string (nth 2 e) frag)))))
3545 (read-string "LaTeX Fragment: " frag nil frag))
3546 ,(let ((odf-filename (expand-file-name
3547 (concat
3548 (file-name-sans-extension
3549 (or (file-name-nondirectory buffer-file-name)))
3550 "." "odf")
3551 (file-name-directory buffer-file-name))))
3552 (read-file-name "ODF filename: " nil odf-filename nil
3553 (file-name-nondirectory odf-filename)))))
3554 (let ((filename (or odf-file
3555 (expand-file-name
3556 (concat
3557 (file-name-sans-extension
3558 (or (file-name-nondirectory buffer-file-name)))
3559 "." "odf")
3560 (file-name-directory buffer-file-name)))))
3561 (org-e-odt--export-wrap
3562 filename
3563 (let* ((buffer (progn
3564 (require 'nxml-mode)
3565 (let ((nxml-auto-insert-xml-declaration-flag nil))
3566 (find-file-noselect (concat org-e-odt-zip-dir
3567 "content.xml") t))))
3568 (coding-system-for-write 'utf-8)
3569 (save-buffer-coding-system 'utf-8))
3570 (set-buffer buffer)
3571 (set-buffer-file-coding-system coding-system-for-write)
3572 (let ((mathml (org-create-math-formula latex-frag)))
3573 (unless mathml (error "No Math formula created"))
3574 (insert mathml)
3575 ;; Add MathML to kill ring, if needed.
3576 (when org-export-copy-to-kill-ring
3577 (org-kill-new (buffer-string))))))))
3579 ;;;###autoload
3580 (defun org-e-odt-export-as-odf-and-open ()
3581 "Export LaTeX fragment as OpenDocument formula and immediately open it.
3582 Use `org-e-odt-export-as-odf' to read LaTeX fragment and OpenDocument
3583 formula file."
3584 (interactive)
3585 (org-open-file (call-interactively 'org-e-odt-export-as-odf)))
3587 ;;;###autoload
3588 (defun org-e-odt-export-to-odt
3589 (&optional subtreep visible-only body-only ext-plist pub-dir)
3590 "Export current buffer to a HTML file.
3592 If narrowing is active in the current buffer, only export its
3593 narrowed part.
3595 If a region is active, export that region.
3597 When optional argument SUBTREEP is non-nil, export the sub-tree
3598 at point, extracting information from the headline properties
3599 first.
3601 When optional argument VISIBLE-ONLY is non-nil, don't export
3602 contents of hidden elements.
3604 When optional argument BODY-ONLY is non-nil, only write code
3605 between \"\\begin{document}\" and \"\\end{document}\".
3607 EXT-PLIST, when provided, is a property list with external
3608 parameters overriding Org default settings, but still inferior to
3609 file-local settings.
3611 When optional argument PUB-DIR is set, use it as the publishing
3612 directory.
3614 Return output file's name."
3615 (interactive)
3616 (org-e-odt--export-wrap
3617 (org-export-output-file-name ".odt" subtreep pub-dir)
3618 (let* ((org-e-odt-embedded-images-count 0)
3619 (org-e-odt-embedded-formulas-count 0)
3620 (org-e-odt-automatic-styles nil)
3621 (org-e-odt-object-counters nil)
3622 ;; Let `htmlfontify' know that we are interested in collecting
3623 ;; styles.
3624 (hfy-user-sheet-assoc nil))
3625 ;; Initialize content.xml and kick-off the export process.
3626 (let ((out-buf (progn
3627 (require 'nxml-mode)
3628 (let ((nxml-auto-insert-xml-declaration-flag nil))
3629 (find-file-noselect
3630 (concat org-e-odt-zip-dir "content.xml") t)))))
3631 (org-export-to-buffer 'e-odt out-buf subtreep visible-only body-only)))))
3636 (defun org-e-odt-reachable-p (in-fmt out-fmt)
3637 "Return non-nil if IN-FMT can be converted to OUT-FMT."
3638 (catch 'done
3639 (let ((reachable-formats (org-e-odt-do-reachable-formats in-fmt)))
3640 (dolist (e reachable-formats)
3641 (let ((out-fmt-spec (assoc out-fmt (cdr e))))
3642 (when out-fmt-spec
3643 (throw 'done (cons (car e) out-fmt-spec))))))))
3645 (defun org-e-odt-do-convert (in-file out-fmt &optional prefix-arg)
3646 "Workhorse routine for `org-e-odt-convert'."
3647 (require 'browse-url)
3648 (let* ((in-file (expand-file-name (or in-file buffer-file-name)))
3649 (dummy (or (file-readable-p in-file)
3650 (error "Cannot read %s" in-file)))
3651 (in-fmt (file-name-extension in-file))
3652 (out-fmt (or out-fmt (error "Output format unspecified")))
3653 (how (or (org-e-odt-reachable-p in-fmt out-fmt)
3654 (error "Cannot convert from %s format to %s format?"
3655 in-fmt out-fmt)))
3656 (convert-process (car how))
3657 (out-file (concat (file-name-sans-extension in-file) "."
3658 (nth 1 (or (cdr how) out-fmt))))
3659 (extra-options (or (nth 2 (cdr how)) ""))
3660 (out-dir (file-name-directory in-file))
3661 (cmd (format-spec convert-process
3662 `((?i . ,(shell-quote-argument in-file))
3663 (?I . ,(browse-url-file-url in-file))
3664 (?f . ,out-fmt)
3665 (?o . ,out-file)
3666 (?O . ,(browse-url-file-url out-file))
3667 (?d . , (shell-quote-argument out-dir))
3668 (?D . ,(browse-url-file-url out-dir))
3669 (?x . ,extra-options)))))
3670 (when (file-exists-p out-file)
3671 (delete-file out-file))
3673 (message "Executing %s" cmd)
3674 (let ((cmd-output (shell-command-to-string cmd)))
3675 (message "%s" cmd-output))
3677 (cond
3678 ((file-exists-p out-file)
3679 (message "Exported to %s" out-file)
3680 (when prefix-arg
3681 (message "Opening %s..." out-file)
3682 (org-open-file out-file))
3683 out-file)
3685 (message "Export to %s failed" out-file)
3686 nil))))
3688 (defun org-e-odt-do-reachable-formats (in-fmt)
3689 "Return verbose info about formats to which IN-FMT can be converted.
3690 Return a list where each element is of the
3691 form (CONVERTER-PROCESS . OUTPUT-FMT-ALIST). See
3692 `org-e-odt-convert-processes' for CONVERTER-PROCESS and see
3693 `org-e-odt-convert-capabilities' for OUTPUT-FMT-ALIST."
3694 (let* ((converter
3695 (and org-e-odt-convert-process
3696 (cadr (assoc-string org-e-odt-convert-process
3697 org-e-odt-convert-processes t))))
3698 (capabilities
3699 (and org-e-odt-convert-process
3700 (cadr (assoc-string org-e-odt-convert-process
3701 org-e-odt-convert-processes t))
3702 org-e-odt-convert-capabilities))
3703 reachable-formats)
3704 (when converter
3705 (dolist (c capabilities)
3706 (when (member in-fmt (nth 1 c))
3707 (push (cons converter (nth 2 c)) reachable-formats))))
3708 reachable-formats))
3710 (defun org-e-odt-reachable-formats (in-fmt)
3711 "Return list of formats to which IN-FMT can be converted.
3712 The list of the form (OUTPUT-FMT-1 OUTPUT-FMT-2 ...)."
3713 (let (l)
3714 (mapc (lambda (e) (add-to-list 'l e))
3715 (apply 'append (mapcar
3716 (lambda (e) (mapcar 'car (cdr e)))
3717 (org-e-odt-do-reachable-formats in-fmt))))
3720 (defun org-e-odt-convert-read-params ()
3721 "Return IN-FILE and OUT-FMT params for `org-e-odt-do-convert'.
3722 This is a helper routine for interactive use."
3723 (let* ((input (if (featurep 'ido) 'ido-completing-read 'completing-read))
3724 (in-file (read-file-name "File to be converted: "
3725 nil buffer-file-name t))
3726 (in-fmt (file-name-extension in-file))
3727 (out-fmt-choices (org-e-odt-reachable-formats in-fmt))
3728 (out-fmt
3729 (or (and out-fmt-choices
3730 (funcall input "Output format: "
3731 out-fmt-choices nil nil nil))
3732 (error
3733 "No known converter or no known output formats for %s files"
3734 in-fmt))))
3735 (list in-file out-fmt)))
3737 ;;;###autoload
3738 (defun org-e-odt-convert (&optional in-file out-fmt prefix-arg)
3739 "Convert IN-FILE to format OUT-FMT using a command line converter.
3740 IN-FILE is the file to be converted. If unspecified, it defaults
3741 to variable `buffer-file-name'. OUT-FMT is the desired output
3742 format. Use `org-e-odt-convert-process' as the converter.
3743 If PREFIX-ARG is non-nil then the newly converted file is opened
3744 using `org-open-file'."
3745 (interactive
3746 (append (org-e-odt-convert-read-params) current-prefix-arg))
3747 (org-e-odt-do-convert in-file out-fmt prefix-arg))
3749 ;;; Library Initializations
3751 (mapc
3752 (lambda (desc)
3753 ;; Let Org open all OpenDocument files using system-registered app
3754 (add-to-list 'org-file-apps
3755 (cons (concat "\\." (car desc) "\\'") 'system))
3756 ;; Let Emacs open all OpenDocument files in archive mode
3757 (add-to-list 'auto-mode-alist
3758 (cons (concat "\\." (car desc) "\\'") 'archive-mode)))
3759 org-e-odt-file-extensions)
3761 (provide 'org-e-odt)
3763 ;;; org-e-odt.el ends here