org-e-html/org-e-odt: Autoload (org-export-define-backend ...)
[org-mode.git] / contrib / lisp / org-e-odt.el
blobab5a1aa8af702531456722e331f1bfbe1d06b4a1
1 ;;; org-e-odt.el --- OpenDocument Text exporter for Org-mode
3 ;; Copyright (C) 2010-2012 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 ;;;###autoload
37 (org-export-define-backend e-odt
38 ((bold . org-e-odt-bold)
39 (center-block . org-e-odt-center-block)
40 (clock . org-e-odt-clock)
41 (code . org-e-odt-code)
42 (drawer . org-e-odt-drawer)
43 (dynamic-block . org-e-odt-dynamic-block)
44 (entity . org-e-odt-entity)
45 (example-block . org-e-odt-example-block)
46 (export-block . org-e-odt-export-block)
47 (export-snippet . org-e-odt-export-snippet)
48 (fixed-width . org-e-odt-fixed-width)
49 (footnote-definition . org-e-odt-footnote-definition)
50 (footnote-reference . org-e-odt-footnote-reference)
51 (headline . org-e-odt-headline)
52 (horizontal-rule . org-e-odt-horizontal-rule)
53 (inline-src-block . org-e-odt-inline-src-block)
54 (inlinetask . org-e-odt-inlinetask)
55 (italic . org-e-odt-italic)
56 (item . org-e-odt-item)
57 (keyword . org-e-odt-keyword)
58 (latex-environment . org-e-odt-latex-environment)
59 (latex-fragment . org-e-odt-latex-fragment)
60 (line-break . org-e-odt-line-break)
61 (link . org-e-odt-link)
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 :filters-alist ((:filter-parse-tree
88 . (org-e-odt--translate-description-lists
89 org-e-odt--translate-list-tables)))
90 :menu-entry
91 (?o "Export to ODT"
92 ((?o "As ODT file" org-e-odt-export-to-odt)
93 (?O "As ODT file and open"
94 (lambda (s v b)
95 (org-open-file (org-e-odt-export-to-odt s v b) 'system)))))
96 :options-alist
97 ((:odt-styles-file "ODT_STYLES_FILE" nil nil t)
98 (:LaTeX-fragments nil "LaTeX" org-export-with-LaTeX-fragments)))
101 ;;; Dependencies
103 ;;; Hooks
105 ;;; Function Declarations
107 (declare-function org-id-find-id-file "org-id" (id))
108 (declare-function hfy-face-to-style "htmlfontify" (fn))
109 (declare-function hfy-face-or-def-to-name "htmlfontify" (fn))
110 (declare-function archive-zip-extract "arc-mode.el" (archive name))
111 (declare-function org-create-math-formula "org" (latex-frag &optional
112 mathml-file))
113 (declare-function browse-url-file-url "browse-url" (file))
117 ;;; Internal Variables
119 (defconst org-e-odt-lib-dir
120 (file-name-directory load-file-name)
121 "Location of ODT exporter.
122 Use this to infer values of `org-e-odt-styles-dir' and
123 `org-e-odt-schema-dir'.")
125 (defvar org-e-odt-data-dir
126 (expand-file-name "../../etc/" org-e-odt-lib-dir)
127 "Data directory for ODT exporter.
128 Use this to infer values of `org-e-odt-styles-dir' and
129 `org-e-odt-schema-dir'.")
131 (defconst org-e-odt-special-string-regexps
132 '(("\\\\-" . "&#x00ad;\\1") ; shy
133 ("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
134 ("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
135 ("\\.\\.\\." . "&#x2026;")) ; hellip
136 "Regular expressions for special string conversion.")
138 (defconst org-e-odt-schema-dir-list
139 (list
140 (and org-e-odt-data-dir
141 (expand-file-name "./schema/" org-e-odt-data-dir)) ; bail out
142 (eval-when-compile
143 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
144 (expand-file-name "./schema/" org-e-odt-data-dir))))
145 "List of directories to search for OpenDocument schema files.
146 Use this list to set the default value of
147 `org-e-odt-schema-dir'. The entries in this list are
148 populated heuristically based on the values of `org-e-odt-lib-dir'
149 and `org-e-odt-data-dir'.")
151 (defconst org-e-odt-styles-dir-list
152 (list
153 (and org-e-odt-data-dir
154 (expand-file-name "./styles/" org-e-odt-data-dir)) ; bail out
155 (eval-when-compile
156 (and (boundp 'org-e-odt-data-dir) org-e-odt-data-dir ; see make install
157 (expand-file-name "./styles/" org-e-odt-data-dir)))
158 (expand-file-name "../../etc/styles/" org-e-odt-lib-dir) ; git
159 (expand-file-name "./etc/styles/" org-e-odt-lib-dir) ; elpa
160 (expand-file-name "./org/" data-directory) ; system
162 "List of directories to search for OpenDocument styles files.
163 See `org-e-odt-styles-dir'. The entries in this list are populated
164 heuristically based on the values of `org-e-odt-lib-dir' and
165 `org-e-odt-data-dir'.")
167 (defconst org-e-odt-styles-dir
168 (let* ((styles-dir
169 (catch 'styles-dir
170 (message "Debug (org-e-odt): Searching for OpenDocument styles files...")
171 (mapc (lambda (styles-dir)
172 (when styles-dir
173 (message "Debug (org-e-odt): Trying %s..." styles-dir)
174 (when (and (file-readable-p
175 (expand-file-name
176 "OrgOdtContentTemplate.xml" styles-dir))
177 (file-readable-p
178 (expand-file-name
179 "OrgOdtStyles.xml" styles-dir)))
180 (message "Debug (org-e-odt): Using styles under %s"
181 styles-dir)
182 (throw 'styles-dir styles-dir))))
183 org-e-odt-styles-dir-list)
184 nil)))
185 (unless styles-dir
186 (error "Error (org-e-odt): Cannot find factory styles files. Aborting."))
187 styles-dir)
188 "Directory that holds auxiliary XML files used by the ODT exporter.
190 This directory contains the following XML files -
191 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
192 XML files are used as the default values of
193 `org-e-odt-styles-file' and
194 `org-e-odt-content-template-file'.
196 The default value of this variable varies depending on the
197 version of org in use and is initialized from
198 `org-e-odt-styles-dir-list'. Note that the user could be using org
199 from one of: org's own private git repository, GNU ELPA tar or
200 standard Emacs.")
202 (defconst org-e-odt-bookmark-prefix "OrgXref.")
204 (defconst org-e-odt-manifest-file-entry-tag
205 "\n<manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
207 (defconst org-e-odt-file-extensions
208 '(("odt" . "OpenDocument Text")
209 ("ott" . "OpenDocument Text Template")
210 ("odm" . "OpenDocument Master Document")
211 ("ods" . "OpenDocument Spreadsheet")
212 ("ots" . "OpenDocument Spreadsheet Template")
213 ("odg" . "OpenDocument Drawing (Graphics)")
214 ("otg" . "OpenDocument Drawing Template")
215 ("odp" . "OpenDocument Presentation")
216 ("otp" . "OpenDocument Presentation Template")
217 ("odi" . "OpenDocument Image")
218 ("odf" . "OpenDocument Formula")
219 ("odc" . "OpenDocument Chart")))
221 (defvar org-e-odt-table-style-format
223 <style:style style:name=\"%s\" style:family=\"table\">
224 <style:table-properties style:rel-width=\"%d%%\" fo:margin-top=\"0cm\" fo:margin-bottom=\"0.20cm\" table:align=\"center\"/>
225 </style:style>
227 "Template for auto-generated Table styles.")
229 (defvar org-e-odt-automatic-styles '()
230 "Registry of automatic styles for various OBJECT-TYPEs.
231 The variable has the following form:
232 \(\(OBJECT-TYPE-A
233 \(\(OBJECT-NAME-A.1 OBJECT-PROPS-A.1\)
234 \(OBJECT-NAME-A.2 OBJECT-PROPS-A.2\) ...\)\)
235 \(OBJECT-TYPE-B
236 \(\(OBJECT-NAME-B.1 OBJECT-PROPS-B.1\)
237 \(OBJECT-NAME-B.2 OBJECT-PROPS-B.2\) ...\)\)
238 ...\).
240 OBJECT-TYPEs could be \"Section\", \"Table\", \"Figure\" etc.
241 OBJECT-PROPS is (typically) a plist created by passing
242 \"#+ATTR_ODT: \" option to `org-e-odt-parse-block-attributes'.
244 Use `org-e-odt-add-automatic-style' to add update this variable.'")
246 (defvar org-e-odt-object-counters nil
247 "Running counters for various OBJECT-TYPEs.
248 Use this to generate automatic names and style-names. See
249 `org-e-odt-add-automatic-style'.")
251 (defvar org-e-odt-src-block-paragraph-format
252 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
253 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
254 <style:background-image/>
255 </style:paragraph-properties>
256 <style:text-properties fo:color=\"%s\"/>
257 </style:style>"
258 "Custom paragraph style for colorized source and example blocks.
259 This style is much the same as that of \"OrgFixedWidthBlock\"
260 except that the foreground and background colors are set
261 according to the default face identified by the `htmlfontify'.")
263 (defvar hfy-optimisations)
264 (defvar org-e-odt-embedded-formulas-count 0)
265 (defvar org-e-odt-entity-frame-styles
266 '(("As-CharImage" "__Figure__" ("OrgInlineImage" nil "as-char"))
267 ("ParagraphImage" "__Figure__" ("OrgDisplayImage" nil "paragraph"))
268 ("PageImage" "__Figure__" ("OrgPageImage" nil "page"))
269 ("CaptionedAs-CharImage" "__Figure__"
270 ("OrgCaptionedImage"
271 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
272 ("OrgInlineImage" nil "as-char"))
273 ("CaptionedParagraphImage" "__Figure__"
274 ("OrgCaptionedImage"
275 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
276 ("OrgImageCaptionFrame" nil "paragraph"))
277 ("CaptionedPageImage" "__Figure__"
278 ("OrgCaptionedImage"
279 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
280 ("OrgPageImageCaptionFrame" nil "page"))
281 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil "as-char"))
282 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil "as-char"))
283 ("CaptionedDisplayFormula" "__MathFormula__"
284 ("OrgCaptionedFormula" nil "paragraph")
285 ("OrgFormulaCaptionFrame" nil "as-char"))))
287 (defvar org-e-odt-embedded-images-count 0)
288 (defvar org-e-odt-image-size-probe-method
289 (append (and (executable-find "identify") '(imagemagick)) ; See Bug#10675
290 '(emacs fixed))
291 "Ordered list of methods for determining image sizes.")
293 (defvar org-e-odt-default-image-sizes-alist
294 '(("as-char" . (5 . 0.4))
295 ("paragraph" . (5 . 5)))
296 "Hardcoded image dimensions one for each of the anchor
297 methods.")
299 ;; A4 page size is 21.0 by 29.7 cms
300 ;; The default page settings has 2cm margin on each of the sides. So
301 ;; the effective text area is 17.0 by 25.7 cm
302 (defvar org-e-odt-max-image-size '(17.0 . 20.0)
303 "Limiting dimensions for an embedded image.")
305 (defvar org-e-odt-label-styles
306 '(("math-formula" "%c" "text" "(%n)")
307 ("math-label" "(%n)" "text" "(%n)")
308 ("category-and-value" "%e %n: %c" "category-and-value" "%e %n")
309 ("value" "%e %n: %c" "value" "%n"))
310 "Specify how labels are applied and referenced.
311 This is an alist where each element is of the
312 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
313 LABEL-REF-FMT).
315 LABEL-ATTACH-FMT controls how labels and captions are attached to
316 an entity. It may contain following specifiers - %e, %n and %c.
317 %e is replaced with the CATEGORY-NAME. %n is replaced with
318 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
319 with CAPTION. See `org-e-odt-format-label-definition'.
321 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
322 are generated. The following XML is generated for a label
323 reference - \"<text:sequence-ref
324 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
325 </text:sequence-ref>\". LABEL-REF-FMT may contain following
326 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
327 %n is replaced with SEQNO. See
328 `org-e-odt-format-label-reference'.")
330 (defvar org-e-odt-category-map-alist
331 '(("__Table__" "Table" "value" "Table")
332 ("__Figure__" "Illustration" "value" "Figure")
333 ("__MathFormula__" "Text" "math-formula" "Equation")
334 ("__DvipngImage__" "Equation" "value" "Equation")
335 ("__Listing__" "Listing" "value" "Listing")
336 ;; ("__Table__" "Table" "category-and-value")
337 ;; ("__Figure__" "Figure" "category-and-value")
338 ;; ("__DvipngImage__" "Equation" "category-and-value")
340 "Map a CATEGORY-HANDLE to OD-VARIABLE and LABEL-STYLE.
341 This is a list where each entry is of the form \\(CATEGORY-HANDLE
342 OD-VARIABLE LABEL-STYLE CATEGORY-NAME\\). CATEGORY_HANDLE
343 identifies the captionable entity in question. OD-VARIABLE is
344 the OpenDocument sequence counter associated with the entity.
345 These counters are declared within
346 \"<text:sequence-decls>...</text:sequence-decls>\" block of
347 `org-e-odt-content-template-file'. LABEL-STYLE is a key into
348 `org-e-odt-label-styles' and specifies how a given entity should
349 be captioned and referenced. CATEGORY-NAME is used for
350 qualifying captions on export. You can modify the CATEGORY-NAME
351 used in the exported document by modifying
352 `org-export-dictionary'. For example, an embedded image in an
353 English document is captioned as \"Figure 1: Orgmode Logo\", by
354 default. If you want the image to be captioned as \"Illustration
355 1: Orgmode Logo\" instead, install an entry in
356 `org-export-dictionary' which translates \"Figure\" to
357 \"Illustration\" when the language is \"en\" and encoding is
358 `:utf-8'.")
360 (defvar org-e-odt-manifest-file-entries nil)
361 (defvar hfy-user-sheet-assoc)
363 (defvar org-e-odt-zip-dir nil
364 "Temporary work directory for OpenDocument exporter.")
368 ;;; User Configuration Variables
370 (defgroup org-export-e-odt nil
371 "Options for exporting Org mode files to ODT."
372 :tag "Org Export ODT"
373 :group 'org-export)
376 ;;;; Debugging
378 (defcustom org-e-odt-prettify-xml nil
379 "Specify whether or not the xml output should be prettified.
380 When this option is turned on, `indent-region' is run on all
381 component xml buffers before they are saved. Turn this off for
382 regular use. Turn this on if you need to examine the xml
383 visually."
384 :group 'org-export-e-odt
385 :version "24.1"
386 :type 'boolean)
389 ;;;; Document schema
391 (defcustom org-e-odt-schema-dir
392 (let* ((schema-dir
393 (catch 'schema-dir
394 (message "Debug (org-e-odt): Searching for OpenDocument schema files...")
395 (mapc
396 (lambda (schema-dir)
397 (when schema-dir
398 (message "Debug (org-e-odt): Trying %s..." schema-dir)
399 (when (and (file-readable-p
400 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc"
401 schema-dir))
402 (file-readable-p
403 (expand-file-name "od-schema-v1.2-cs01.rnc"
404 schema-dir))
405 (file-readable-p
406 (expand-file-name "schemas.xml" schema-dir)))
407 (message "Debug (org-e-odt): Using schema files under %s"
408 schema-dir)
409 (throw 'schema-dir schema-dir))))
410 org-e-odt-schema-dir-list)
411 (message "Debug (org-e-odt): No OpenDocument schema files installed")
412 nil)))
413 schema-dir)
414 "Directory that contains OpenDocument schema files.
416 This directory contains:
417 1. rnc files for OpenDocument schema
418 2. a \"schemas.xml\" file that specifies locating rules needed
419 for auto validation of OpenDocument XML files.
421 Use the customize interface to set this variable. This ensures
422 that `rng-schema-locating-files' is updated and auto-validation
423 of OpenDocument XML takes place based on the value
424 `rng-nxml-auto-validate-flag'.
426 The default value of this variable varies depending on the
427 version of org in use and is initialized from
428 `org-e-odt-schema-dir-list'. The OASIS schema files are available
429 only in the org's private git repository. It is *not* bundled
430 with GNU ELPA tar or standard Emacs distribution."
431 :type '(choice
432 (const :tag "Not set" nil)
433 (directory :tag "Schema directory"))
434 :group 'org-export-e-odt
435 :version "24.1"
436 :set
437 (lambda (var value)
438 "Set `org-e-odt-schema-dir'.
439 Also add it to `rng-schema-locating-files'."
440 (let ((schema-dir value))
441 (set var
442 (if (and
443 (file-readable-p
444 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir))
445 (file-readable-p
446 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir))
447 (file-readable-p
448 (expand-file-name "schemas.xml" schema-dir)))
449 schema-dir
450 (when value
451 (message "Error (org-e-odt): %s has no OpenDocument schema files"
452 value))
453 nil)))
454 (when org-e-odt-schema-dir
455 (eval-after-load 'rng-loc
456 '(add-to-list 'rng-schema-locating-files
457 (expand-file-name "schemas.xml"
458 org-e-odt-schema-dir))))))
461 ;;;; Document styles
463 (defcustom org-e-odt-content-template-file nil
464 "Template file for \"content.xml\".
465 The exporter embeds the exported content just before
466 \"</office:text>\" element.
468 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
469 under `org-e-odt-styles-dir' is used."
470 :type 'file
471 :group 'org-export-e-odt
472 :version "24.1")
474 (defcustom org-e-odt-styles-file nil
475 "Default styles file for use with ODT export.
476 Valid values are one of:
477 1. nil
478 2. path to a styles.xml file
479 3. path to a *.odt or a *.ott file
480 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
481 ...))
483 In case of option 1, an in-built styles.xml is used. See
484 `org-e-odt-styles-dir' for more information.
486 In case of option 3, the specified file is unzipped and the
487 styles.xml embedded therein is used.
489 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
490 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
491 generated odt file. Use relative path for specifying the
492 FILE-MEMBERS. styles.xml must be specified as one of the
493 FILE-MEMBERS.
495 Use options 1, 2 or 3 only if styles.xml alone suffices for
496 achieving the desired formatting. Use option 4, if the styles.xml
497 references additional files like header and footer images for
498 achieving the desired formatting.
500 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
501 a per-file basis. For example,
503 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
504 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
505 :group 'org-export-e-odt
506 :version "24.1"
507 :type
508 '(choice
509 (const :tag "Factory settings" nil)
510 (file :must-match t :tag "styles.xml")
511 (file :must-match t :tag "ODT or OTT file")
512 (list :tag "ODT or OTT file + Members"
513 (file :must-match t :tag "ODF Text or Text Template file")
514 (cons :tag "Members"
515 (file :tag " Member" "styles.xml")
516 (repeat (file :tag "Member"))))))
518 (defcustom org-e-odt-display-outline-level 2
519 "Outline levels considered for enumerating captioned entities."
520 :group 'org-export-e-odt
521 :version "24.2"
522 :type 'integer)
524 ;;;; Document conversion
526 (defcustom org-e-odt-convert-processes
527 '(("LibreOffice"
528 "soffice --headless --convert-to %f%x --outdir %d %i")
529 ("unoconv"
530 "unoconv -f %f -o %d %i"))
531 "Specify a list of document converters and their usage.
532 The converters in this list are offered as choices while
533 customizing `org-e-odt-convert-process'.
535 This variable is a list where each element is of the
536 form (CONVERTER-NAME CONVERTER-CMD). CONVERTER-NAME is the name
537 of the converter. CONVERTER-CMD is the shell command for the
538 converter and can contain format specifiers. These format
539 specifiers are interpreted as below:
541 %i input file name in full
542 %I input file name as a URL
543 %f format of the output file
544 %o output file name in full
545 %O output file name as a URL
546 %d output dir in full
547 %D output dir as a URL.
548 %x extra options as set in `org-e-odt-convert-capabilities'."
549 :group 'org-export-e-odt
550 :version "24.1"
551 :type
552 '(choice
553 (const :tag "None" nil)
554 (alist :tag "Converters"
555 :key-type (string :tag "Converter Name")
556 :value-type (group (string :tag "Command line")))))
558 (defcustom org-e-odt-convert-process "LibreOffice"
559 "Use this converter to convert from \"odt\" format to other formats.
560 During customization, the list of converter names are populated
561 from `org-e-odt-convert-processes'."
562 :group 'org-export-e-odt
563 :version "24.1"
564 :type '(choice :convert-widget
565 (lambda (w)
566 (apply 'widget-convert (widget-type w)
567 (eval (car (widget-get w :args)))))
568 `((const :tag "None" nil)
569 ,@(mapcar (lambda (c)
570 `(const :tag ,(car c) ,(car c)))
571 org-e-odt-convert-processes))))
573 (defcustom org-e-odt-convert-capabilities
574 '(("Text"
575 ("odt" "ott" "doc" "rtf" "docx")
576 (("pdf" "pdf") ("odt" "odt") ("rtf" "rtf") ("ott" "ott")
577 ("doc" "doc" ":\"MS Word 97\"") ("docx" "docx") ("html" "html")))
578 ("Web"
579 ("html")
580 (("pdf" "pdf") ("odt" "odt") ("html" "html")))
581 ("Spreadsheet"
582 ("ods" "ots" "xls" "csv" "xlsx")
583 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
584 ("xls" "xls") ("xlsx" "xlsx")))
585 ("Presentation"
586 ("odp" "otp" "ppt" "pptx")
587 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("otp" "otp") ("ppt" "ppt")
588 ("pptx" "pptx") ("odg" "odg"))))
589 "Specify input and output formats of `org-e-odt-convert-process'.
590 More correctly, specify the set of input and output formats that
591 the user is actually interested in.
593 This variable is an alist where each element is of the
594 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
595 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
596 alist where each element is of the form (OUTPUT-FMT
597 OUTPUT-FILE-EXTENSION EXTRA-OPTIONS).
599 The variable is interpreted as follows:
600 `org-e-odt-convert-process' can take any document that is in
601 INPUT-FMT-LIST and produce any document that is in the
602 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
603 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
604 serves dual purposes:
605 - It is used for populating completion candidates during
606 `org-e-odt-convert' commands.
607 - It is used as the value of \"%f\" specifier in
608 `org-e-odt-convert-process'.
610 EXTRA-OPTIONS is used as the value of \"%x\" specifier in
611 `org-e-odt-convert-process'.
613 DOCUMENT-CLASS is used to group a set of file formats in
614 INPUT-FMT-LIST in to a single class.
616 Note that this variable inherently captures how LibreOffice based
617 converters work. LibreOffice maps documents of various formats
618 to classes like Text, Web, Spreadsheet, Presentation etc and
619 allow document of a given class (irrespective of it's source
620 format) to be converted to any of the export formats associated
621 with that class.
623 See default setting of this variable for an typical
624 configuration."
625 :group 'org-export-e-odt
626 :version "24.1"
627 :type
628 '(choice
629 (const :tag "None" nil)
630 (alist :tag "Capabilities"
631 :key-type (string :tag "Document Class")
632 :value-type
633 (group (repeat :tag "Input formats" (string :tag "Input format"))
634 (alist :tag "Output formats"
635 :key-type (string :tag "Output format")
636 :value-type
637 (group (string :tag "Output file extension")
638 (choice
639 (const :tag "None" nil)
640 (string :tag "Extra options"))))))))
642 (defcustom org-e-odt-preferred-output-format nil
643 "Automatically post-process to this format after exporting to \"odt\".
644 Command `org-e-odt-export-to-odt' exports first to \"odt\" format
645 and then uses `org-e-odt-convert-process' to convert the
646 resulting document to this format. During customization of this
647 variable, the list of valid values are populated based on
648 `org-e-odt-convert-capabilities'.
650 You can set this option on per-file basis using file local
651 values. See Info node `(emacs) File Variables'."
652 :group 'org-export-e-odt
653 :version "24.1"
654 :type '(choice :convert-widget
655 (lambda (w)
656 (apply 'widget-convert (widget-type w)
657 (eval (car (widget-get w :args)))))
658 `((const :tag "None" nil)
659 ,@(mapcar (lambda (c)
660 `(const :tag ,c ,c))
661 (org-e-odt-reachable-formats "odt")))))
662 ;;;###autoload
663 (put 'org-e-odt-preferred-output-format 'safe-local-variable 'stringp)
666 ;;;; Drawers
668 (defcustom org-e-odt-format-drawer-function nil
669 "Function called to format a drawer in HTML code.
671 The function must accept two parameters:
672 NAME the drawer name, like \"LOGBOOK\"
673 CONTENTS the contents of the drawer.
675 The function should return the string to be exported.
677 For example, the variable could be set to the following function
678 in order to mimic default behaviour:
680 \(defun org-e-odt-format-drawer-default \(name contents\)
681 \"Format a drawer element for HTML export.\"
682 contents\)"
683 :group 'org-export-e-odt
684 :type 'function)
687 ;;;; Headline
689 (defcustom org-e-odt-format-headline-function nil
690 "Function to format headline text.
692 This function will be called with 5 arguments:
693 TODO the todo keyword \(string or nil\).
694 TODO-TYPE the type of todo \(symbol: `todo', `done', nil\)
695 PRIORITY the priority of the headline \(integer or nil\)
696 TEXT the main headline text \(string\).
697 TAGS the tags string, separated with colons \(string or nil\).
699 The function result will be used in the section format string.
701 As an example, one could set the variable to the following, in
702 order to reproduce the default set-up:
704 \(defun org-e-odt-format-headline \(todo todo-type priority text tags\)
705 \"Default format function for an headline.\"
706 \(concat \(when todo
707 \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo\)\)
708 \(when priority
709 \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
710 text
711 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)"
712 :group 'org-export-e-odt
713 :type 'function)
716 ;;;; Inlinetasks
718 (defcustom org-e-odt-format-inlinetask-function nil
719 "Function called to format an inlinetask in HTML code.
721 The function must accept six parameters:
722 TODO the todo keyword, as a string
723 TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
724 PRIORITY the inlinetask priority, as a string
725 NAME the inlinetask name, as a string.
726 TAGS the inlinetask tags, as a string.
727 CONTENTS the contents of the inlinetask, as a string.
729 The function should return the string to be exported.
731 For example, the variable could be set to the following function
732 in order to mimic default behaviour:
734 \(defun org-e-odt-format-inlinetask \(todo type priority name tags contents\)
735 \"Format an inline task element for HTML export.\"
736 \(let \(\(full-title
737 \(concat
738 \(when todo
739 \(format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo\)\)
740 \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
741 title
742 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)\)
743 \(format \(concat \"\\\\begin{center}\\n\"
744 \"\\\\fbox{\\n\"
745 \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\"
746 \"%s\\n\\n\"
747 \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\"
748 \"%s\"
749 \"\\\\end{minipage}}\"
750 \"\\\\end{center}\"\)
751 full-title contents\)\)"
752 :group 'org-export-e-odt
753 :type 'function)
756 ;;;; Links
758 (defcustom org-e-odt-inline-image-rules
759 '(("file" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\)\\'"))
760 "Rules characterizing image files that can be inlined into HTML.
762 A rule consists in an association whose key is the type of link
763 to consider, and value is a regexp that will be matched against
764 link's path.
766 Note that, by default, the image extension *actually* allowed
767 depend on the way the HTML file is processed. When used with
768 pdflatex, pdf, jpg and png images are OK. When processing
769 through dvi to Postscript, only ps and eps are allowed. The
770 default we use here encompasses both."
771 :group 'org-export-e-odt
772 :type '(alist :key-type (string :tag "Type")
773 :value-type (regexp :tag "Path")))
775 (defcustom org-e-odt-pixels-per-inch display-pixels-per-inch
776 "Scaling factor for converting images pixels to inches.
777 Use this for sizing of embedded images. See Info node `(org)
778 Images in ODT export' for more information."
779 :type 'float
780 :group 'org-export-e-odt
781 :version "24.1")
784 ;;;; Plain text
786 (defcustom org-e-odt-quotes
787 '(("fr"
788 ("\\(\\s-\\|[[(]\\|^\\)\"" . "« ")
789 ("\\(\\S-\\)\"" . "» ")
790 ("\\(\\s-\\|(\\|^\\)'" . "'"))
791 ("en"
792 ("\\(\\s-\\|[[(]\\|^\\)\"" . "“")
793 ("\\(\\S-\\)\"" . "”")
794 ("\\(\\s-\\|(\\|^\\)'" . "‘")
795 ("\\(\\S-\\)'" . "’")))
796 "Alist for quotes to use when converting english double-quotes.
798 The CAR of each item in this alist is the language code.
799 The CDR of each item in this alist is a list of three CONS:
800 - the first CONS defines the opening quote;
801 - the second CONS defines the closing quote;
802 - the last CONS defines single quotes.
804 For each item in a CONS, the first string is a regexp
805 for allowed characters before/after the quote, the second
806 string defines the replacement string for this quote."
807 :group 'org-export-e-odt
808 :type '(list
809 (cons :tag "Opening quote"
810 (string :tag "Regexp for char before")
811 (string :tag "Replacement quote "))
812 (cons :tag "Closing quote"
813 (string :tag "Regexp for char after ")
814 (string :tag "Replacement quote "))
815 (cons :tag "Single quote"
816 (string :tag "Regexp for char before")
817 (string :tag "Replacement quote "))))
820 ;;;; Src Block
822 (defcustom org-e-odt-create-custom-styles-for-srcblocks t
823 "Whether custom styles for colorized source blocks be automatically created.
824 When this option is turned on, the exporter creates custom styles
825 for source blocks based on the advice of `htmlfontify'. Creation
826 of custom styles happen as part of `org-e-odt-hfy-face-to-css'.
828 When this option is turned off exporter does not create such
829 styles.
831 Use the latter option if you do not want the custom styles to be
832 based on your current display settings. It is necessary that the
833 styles.xml already contains needed styles for colorizing to work.
835 This variable is effective only if
836 `org-e-odt-fontify-srcblocks' is turned on."
837 :group 'org-export-e-odt
838 :version "24.1"
839 :type 'boolean)
841 (defcustom org-e-odt-fontify-srcblocks t
842 "Specify whether or not source blocks need to be fontified.
843 Turn this option on if you want to colorize the source code
844 blocks in the exported file. For colorization to work, you need
845 to make available an enhanced version of `htmlfontify' library."
846 :type 'boolean
847 :group 'org-export-e-odt
848 :version "24.1")
851 ;;;; Table
853 (defcustom org-e-odt-table-caption-above t
854 "When non-nil, place caption string at the beginning of the table.
855 Otherwise, place it near the end."
856 :group 'org-export-e-odt
857 :type 'boolean)
859 (defcustom org-e-odt-table-styles
860 '(("OrgEquation" "OrgEquation"
861 ((use-first-column-styles . t)
862 (use-last-column-styles . t)))
863 ("TableWithHeaderRowAndColumn" "Custom"
864 ((use-first-row-styles . t)
865 (use-first-column-styles . t)))
866 ("TableWithFirstRowandLastRow" "Custom"
867 ((use-first-row-styles . t)
868 (use-last-row-styles . t)))
869 ("GriddedTable" "Custom" nil))
870 "Specify how Table Styles should be derived from a Table Template.
871 This is a list where each element is of the
872 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
874 TABLE-STYLE-NAME is the style associated with the table through
875 \"#+ATTR_ODT: :style TABLE-STYLE-NAME\" line.
877 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
878 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
879 below) that is included in
880 `org-e-odt-content-template-file'.
882 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
883 \"TableCell\"
884 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
885 \"TableParagraph\"
886 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
887 \"FirstRow\" | \"LastRow\" |
888 \"EvenRow\" | \"OddRow\" |
889 \"EvenColumn\" | \"OddColumn\" | \"\"
890 where \"+\" above denotes string concatenation.
892 TABLE-CELL-OPTIONS is an alist where each element is of the
893 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
894 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
895 `use-last-row-styles' |
896 `use-first-column-styles' |
897 `use-last-column-styles' |
898 `use-banding-rows-styles' |
899 `use-banding-columns-styles' |
900 `use-first-row-styles'
901 ON-OR-OFF := `t' | `nil'
903 For example, with the following configuration
905 \(setq org-e-odt-table-styles
906 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
907 \(\(use-first-row-styles . t\)
908 \(use-first-column-styles . t\)\)\)
909 \(\"TableWithHeaderColumns\" \"Custom\"
910 \(\(use-first-column-styles . t\)\)\)\)\)
912 1. A table associated with \"TableWithHeaderRowsAndColumns\"
913 style will use the following table-cell styles -
914 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
915 \"CustomTableCell\" and the following paragraph styles
916 \"CustomFirstRowTableParagraph\",
917 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
918 as appropriate.
920 2. A table associated with \"TableWithHeaderColumns\" style will
921 use the following table-cell styles -
922 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
923 following paragraph styles
924 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
925 as appropriate..
927 Note that TABLE-TEMPLATE-NAME corresponds to the
928 \"<table:table-template>\" elements contained within
929 \"<office:styles>\". The entries (TABLE-STYLE-NAME
930 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
931 \"table:template-name\" and \"table:use-first-row-styles\" etc
932 attributes of \"<table:table>\" element. Refer ODF-1.2
933 specification for more information. Also consult the
934 implementation filed under `org-e-odt-get-table-cell-styles'.
936 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
937 formatting of numbered display equations. Do not delete this
938 style from the list."
939 :group 'org-export-e-odt
940 :version "24.1"
941 :type '(choice
942 (const :tag "None" nil)
943 (repeat :tag "Table Styles"
944 (list :tag "Table Style Specification"
945 (string :tag "Table Style Name")
946 (string :tag "Table Template Name")
947 (alist :options (use-first-row-styles
948 use-last-row-styles
949 use-first-column-styles
950 use-last-column-styles
951 use-banding-rows-styles
952 use-banding-columns-styles)
953 :key-type symbol
954 :value-type (const :tag "True" t))))))
958 ;;; Internal functions
960 ;;;; Date
962 (defun org-e-odt--date (&optional org-ts fmt)
963 (save-match-data
964 (let* ((time
965 (and (stringp org-ts)
966 (string-match org-ts-regexp0 org-ts)
967 (apply 'encode-time
968 (org-fix-decoded-time
969 (org-parse-time-string (match-string 0 org-ts) t)))))
970 date)
971 (cond
972 (fmt (format-time-string fmt time))
973 (t (setq date (format-time-string "%Y-%m-%dT%H:%M:%S%z" time))
974 (format "%s:%s" (substring date 0 -2) (substring date -2)))))))
976 ;;;; Frame
978 (defun org-e-odt--frame (text width height style &optional extra
979 anchor-type)
980 (let ((frame-attrs
981 (concat
982 (if width (format " svg:width=\"%0.2fcm\"" width) "")
983 (if height (format " svg:height=\"%0.2fcm\"" height) "")
984 extra
985 (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph")))))
986 (format
987 "\n<draw:frame draw:style-name=\"%s\"%s>\n%s\n</draw:frame>"
988 style frame-attrs
989 (concat text
990 (let ((title (get-text-property 0 :title text))
991 (desc (get-text-property 0 :description text)))
992 (concat (and title
993 (format "<svg:title>%s</svg:title>"
994 (org-e-odt-encode-plain-text title t)))
995 (and desc
996 (format "<svg:desc>%s</svg:desc>"
997 (org-e-odt-encode-plain-text desc t)))))))))
1000 ;;;; Inline Images
1002 (defun org-e-odt--copy-image-file (path)
1003 "Returns the internal name of the file"
1004 (let* ((image-type (file-name-extension path))
1005 (media-type (format "image/%s" image-type))
1006 (target-dir "Images/")
1007 (target-file
1008 (format "%s%04d.%s" target-dir
1009 (incf org-e-odt-embedded-images-count) image-type)))
1010 (message "Embedding %s as %s ..."
1011 (substring-no-properties path) target-file)
1013 (when (= 1 org-e-odt-embedded-images-count)
1014 (make-directory (concat org-e-odt-zip-dir target-dir))
1015 (org-e-odt-create-manifest-file-entry "" target-dir))
1017 (copy-file path (concat org-e-odt-zip-dir target-file) 'overwrite)
1018 (org-e-odt-create-manifest-file-entry media-type target-file)
1019 target-file))
1021 (defun org-e-odt--image-size (file &optional user-width
1022 user-height scale dpi embed-as)
1023 (let* ((--pixels-to-cms
1024 (function (lambda (pixels dpi)
1025 (let ((cms-per-inch 2.54)
1026 (inches (/ pixels dpi)))
1027 (* cms-per-inch inches)))))
1028 (--size-in-cms
1029 (function
1030 (lambda (size-in-pixels dpi)
1031 (and size-in-pixels
1032 (cons (funcall --pixels-to-cms (car size-in-pixels) dpi)
1033 (funcall --pixels-to-cms (cdr size-in-pixels) dpi))))))
1034 (dpi (or dpi org-e-odt-pixels-per-inch))
1035 (anchor-type (or embed-as "paragraph"))
1036 (user-width (and (not scale) user-width))
1037 (user-height (and (not scale) user-height))
1038 (size
1039 (and
1040 (not (and user-height user-width))
1042 ;; Use Imagemagick.
1043 (and (executable-find "identify")
1044 (let ((size-in-pixels
1045 (let ((dim (shell-command-to-string
1046 (format "identify -format \"%%w:%%h\" \"%s\""
1047 file))))
1048 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
1049 (cons (string-to-number (match-string 1 dim))
1050 (string-to-number (match-string 2 dim)))))))
1051 (funcall --size-in-cms size-in-pixels dpi)))
1052 ;; Use Emacs.
1053 (let ((size-in-pixels
1054 (ignore-errors ; Emacs could be in batch mode
1055 (clear-image-cache)
1056 (image-size (create-image file) 'pixels))))
1057 (funcall --size-in-cms size-in-pixels dpi))
1058 ;; Use hard-coded values.
1059 (cdr (assoc-string anchor-type
1060 org-e-odt-default-image-sizes-alist))
1061 ;; Error out.
1062 (error "Cannot determine Image size. Aborting ..."))))
1063 (width (car size)) (height (cdr size)))
1064 (cond
1065 (scale
1066 (setq width (* width scale) height (* height scale)))
1067 ((and user-height user-width)
1068 (setq width user-width height user-height))
1069 (user-height
1070 (setq width (* user-height (/ width height)) height user-height))
1071 (user-width
1072 (setq height (* user-width (/ height width)) width user-width))
1073 (t (ignore)))
1074 ;; ensure that an embedded image fits comfortably within a page
1075 (let ((max-width (car org-e-odt-max-image-size))
1076 (max-height (cdr org-e-odt-max-image-size)))
1077 (when (or (> width max-width) (> height max-height))
1078 (let* ((scale1 (/ max-width width))
1079 (scale2 (/ max-height height))
1080 (scale (min scale1 scale2)))
1081 (setq width (* scale width) height (* scale height)))))
1082 (cons width height)))
1084 (defun org-e-odt-link--inline-image (element info)
1085 "Return HTML code for an inline image.
1086 LINK is the link pointing to the inline image. INFO is a plist
1087 used as a communication channel."
1088 (let* ((src (cond
1089 ((eq (org-element-type element) 'link)
1090 (let* ((type (org-element-property :type element))
1091 (raw-path (org-element-property :path element)))
1092 (cond ((member type '("http" "https"))
1093 (concat type ":" raw-path))
1094 ((file-name-absolute-p raw-path)
1095 (expand-file-name raw-path))
1096 (t raw-path))))
1097 ((member (org-element-type element)
1098 '(latex-fragment latex-environment))
1099 (let* ((latex-frag (org-remove-indentation
1100 (org-element-property :value element)))
1101 (formula-link (org-e-odt-format-latex
1102 latex-frag 'dvipng info)))
1103 (and formula-link
1104 (string-match "file:\\([^]]*\\)" formula-link)
1105 (match-string 1 formula-link))))
1106 (t (error "what is this?"))))
1107 (src-expanded (if (file-name-absolute-p src) src
1108 (expand-file-name src (file-name-directory
1109 (plist-get info :input-file)))))
1110 (href (format
1111 "\n<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>"
1112 (org-e-odt--copy-image-file src-expanded)))
1113 ;; extract attributes from #+ATTR_ODT line.
1114 (attr-from (case (org-element-type element)
1115 (link (org-export-get-parent-element element))
1116 (t element)))
1117 ;; convert attributes to a plist.
1118 (attr-plist (org-export-read-attribute :attr_odt attr-from))
1119 ;; handle `:anchor', `:style' and `:attributes' properties.
1120 (user-frame-anchor
1121 (car (assoc-string (plist-get attr-plist :anchor)
1122 '(("as-char") ("paragraph") ("page")) t)))
1123 (user-frame-style
1124 (and user-frame-anchor (plist-get attr-plist :style)))
1125 (user-frame-attrs
1126 (and user-frame-anchor (plist-get attr-plist :attributes)))
1127 (user-frame-params
1128 (list user-frame-style user-frame-attrs user-frame-anchor))
1129 ;; (embed-as (or embed-as user-frame-anchor "paragraph"))
1130 ;; extrac
1131 ;; handle `:width', `:height' and `:scale' properties.
1132 (size (org-e-odt--image-size
1133 src-expanded (plist-get attr-plist :width)
1134 (plist-get attr-plist :height)
1135 (plist-get attr-plist :scale) nil ;; embed-as
1136 "paragraph" ; FIXME
1138 (width (car size)) (height (cdr size))
1139 (embed-as
1140 (case (org-element-type element)
1141 ((org-e-odt-standalone-image-p element info) "paragraph")
1142 (latex-fragment "as-char")
1143 (latex-environment "paragraph")
1144 (t "paragraph")))
1145 (captions (org-e-odt-format-label element info 'definition))
1146 (caption (car captions)) (short-caption (cdr captions))
1147 (entity (concat (and caption "Captioned") embed-as "Image")))
1148 (org-e-odt-format-entity entity href width height
1149 captions user-frame-params )))
1151 ;;;; Library wrappers
1153 (defun org-e-odt--zip-extract (archive members target)
1154 (when (atom members) (setq members (list members)))
1155 (mapc (lambda (archive member target)
1156 (require 'arc-mode)
1157 (let* ((--quote-file-name
1158 ;; This is shamelessly stolen from `archive-zip-extract'.
1159 (lambda (name)
1160 (if (or (not (memq system-type '(windows-nt ms-dos)))
1161 (and (boundp 'w32-quote-process-args)
1162 (null w32-quote-process-args)))
1163 (shell-quote-argument name)
1164 name)))
1165 (target (funcall --quote-file-name target))
1166 (archive (expand-file-name archive))
1167 (archive-zip-extract
1168 (list "unzip" "-qq" "-o" "-d" target))
1169 exit-code command-output)
1170 (setq command-output
1171 (with-temp-buffer
1172 (setq exit-code (archive-zip-extract archive member))
1173 (buffer-string)))
1174 (unless (zerop exit-code)
1175 (message command-output)
1176 (error "Extraction failed"))))
1177 members))
1180 ;;;; Target
1182 (defun org-e-odt--target (text id)
1183 (if (not id) text
1184 (concat
1185 (format "\n<text:bookmark-start text:name=\"OrgXref.%s\"/>" id)
1186 (format "\n<text:bookmark text:name=\"%s\"/>" id) text
1187 (format "\n<text:bookmark-end text:name=\"OrgXref.%s\"/>" id))))
1189 ;;;; Textbox
1191 (defun org-e-odt--textbox (text width height style &optional
1192 extra anchor-type)
1193 (org-e-odt--frame
1194 (format "\n<draw:text-box %s>%s\n</draw:text-box>"
1195 (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
1196 (and (not width)
1197 (format " fo:min-width=\"%0.2fcm\"" (or width .2))))
1198 text)
1199 width nil style extra anchor-type))
1203 ;;;; Table of Contents
1205 (defun org-e-odt-begin-toc (index-title depth)
1206 (concat
1207 (format "
1208 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
1209 <text:table-of-content-source text:outline-level=\"%d\">
1210 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
1211 " depth index-title)
1213 (let ((levels (number-sequence 1 10)))
1214 (mapconcat
1215 (lambda (level)
1216 (format
1218 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
1219 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
1220 <text:index-entry-chapter/>
1221 <text:index-entry-text/>
1222 <text:index-entry-link-end/>
1223 </text:table-of-content-entry-template>
1224 " level level)) levels ""))
1226 (format "
1227 </text:table-of-content-source>
1229 <text:index-body>
1230 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
1231 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
1232 </text:index-title>
1233 " index-title)))
1235 (defun org-e-odt-end-toc ()
1236 (format "
1237 </text:index-body>
1238 </text:table-of-content>
1243 (defun* org-e-odt-format-toc-headline
1244 (todo todo-type priority text tags
1245 &key level section-number headline-label &allow-other-keys)
1246 (setq text (concat
1247 (and org-export-with-section-numbers
1248 (concat section-number ". "))
1249 text
1250 (and tags
1251 (concat
1252 "<text:tab/>"
1253 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1254 "OrgTag" tags)))))
1255 (when todo
1256 (setq text (format "<text:span text:style-name=\"%s\">%s</text:span>"
1257 "OrgTodo" text)))
1258 (format "<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
1259 headline-label text))
1261 (defun org-e-odt-toc (depth info)
1262 (assert (wholenump depth))
1263 (let* ((title (org-export-translate "Table of Contents" :utf-8 info))
1264 (headlines (org-export-collect-headlines
1265 info (and (wholenump depth) depth))))
1266 (when headlines
1267 (concat
1268 (org-e-odt-begin-toc title depth)
1269 (mapconcat
1270 (lambda (headline)
1271 (let* ((entry (org-e-odt-format-headline--wrap
1272 headline info 'org-e-odt-format-toc-headline))
1273 (level (org-export-get-relative-level headline info))
1274 (style (format "Contents_20_%d" level)))
1275 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1276 style entry)))
1277 headlines "\n")
1278 (org-e-odt-end-toc)))))
1281 ;;;; Document styles
1283 (defun org-e-odt-add-automatic-style (object-type &optional object-props)
1284 "Create an automatic style of type OBJECT-TYPE with param OBJECT-PROPS.
1285 OBJECT-PROPS is (typically) a plist created by passing
1286 \"#+ATTR_ODT: \" option of the object in question to
1287 `org-e-odt-parse-block-attributes'.
1289 Use `org-e-odt-object-counters' to generate an automatic
1290 OBJECT-NAME and STYLE-NAME. If OBJECT-PROPS is non-nil, add a
1291 new entry in `org-e-odt-automatic-styles'. Return (OBJECT-NAME
1292 . STYLE-NAME)."
1293 (assert (stringp object-type))
1294 (let* ((object (intern object-type))
1295 (seqvar object)
1296 (seqno (1+ (or (plist-get org-e-odt-object-counters seqvar) 0)))
1297 (object-name (format "%s%d" object-type seqno)) style-name)
1298 (setq org-e-odt-object-counters
1299 (plist-put org-e-odt-object-counters seqvar seqno))
1300 (when object-props
1301 (setq style-name (format "Org%s" object-name))
1302 (setq org-e-odt-automatic-styles
1303 (plist-put org-e-odt-automatic-styles object
1304 (append (list (list style-name object-props))
1305 (plist-get org-e-odt-automatic-styles object)))))
1306 (cons object-name style-name)))
1309 ;;;; Caption and Labels
1312 (defun org-e-odt--wrap-label (element output)
1313 "Wrap label associated to ELEMENT around OUTPUT, if appropriate.
1314 This function shouldn't be used for floats. See
1315 `org-e-odt--caption/label-string'."
1316 ;; (let ((label (org-element-property :name element)))
1317 ;; (if (or (not output) (not label) (string= output "") (string= label ""))
1318 ;; output
1319 ;; (concat (format "\\label{%s}\n" label) output)))
1320 output)
1323 (defun org-e-odt--caption/label-string (caption label info)
1324 "Return caption and label HTML string for floats.
1326 CAPTION is a cons cell of secondary strings, the car being the
1327 standard caption and the cdr its short form. LABEL is a string
1328 representing the label. INFO is a plist holding contextual
1329 information.
1331 If there's no caption nor label, return the empty string.
1333 For non-floats, see `org-e-odt--wrap-label'."
1334 (setq label nil) ;; FIXME
1336 (let ((label-str (if label (format "\\label{%s}" label) "")))
1337 (cond
1338 ((and (not caption) (not label)) "")
1339 ((not caption) (format "\\label{%s}\n" label))
1340 ;; Option caption format with short name.
1341 ((cdr caption)
1342 (format "\\caption[%s]{%s%s}\n"
1343 (org-export-data (cdr caption) info)
1344 label-str
1345 (org-export-data (car caption) info)))
1346 ;; Standard caption format.
1347 ;; (t (format "\\caption{%s%s}\n"
1348 ;; label-str
1349 ;; (org-export-data (car caption) info)))
1350 (t (org-export-data (car caption) info)))))
1352 ;;;; Checkbox
1354 (defun org-e-odt--checkbox (item)
1355 "Return check-box string associated to ITEM."
1356 (let ((checkbox (org-element-property :checkbox item)))
1357 (if (not checkbox) ""
1358 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1359 "OrgCode" (case checkbox
1360 (on "[&#x2713;] ") ; CHECK MARK
1361 (off "[ ] ")
1362 (trans "[-] "))))))
1364 ;;; Template
1366 (defun org-e-odt-template (contents info)
1367 "Return complete document string after HTML conversion.
1368 CONTENTS is the transcoded contents string. RAW-DATA is the
1369 original parsed data. INFO is a plist holding export options."
1370 ;; Write meta file.
1371 (let ((title (org-export-data (plist-get info :title) info))
1372 (author (let ((author (plist-get info :author)))
1373 (if (not author) "" (org-export-data author info))))
1374 (date (org-e-odt--date
1375 (org-export-data (plist-get info :date) info)))
1376 (email (plist-get info :email))
1377 (keywords (plist-get info :keywords))
1378 (description (plist-get info :description)))
1379 (write-region
1380 (concat
1381 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1382 <office:document-meta
1383 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
1384 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
1385 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
1386 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
1387 xmlns:ooo=\"http://openoffice.org/2004/office\"
1388 office:version=\"1.2\">
1389 <office:meta>\n"
1390 (format "<dc:creator>%s</dc:creator>\n" author)
1391 (format "<meta:initial-creator>%s</meta:initial-creator>\n" author)
1392 (format "<dc:date>%s</dc:date>\n" date)
1393 (format "<meta:creation-date>%s</meta:creation-date>\n" date)
1394 (format "<meta:generator>%s</meta:generator>\n"
1395 (let ((creator-info (plist-get info :with-creator)))
1396 (if (or (not creator-info) (eq creator-info 'comment)) ""
1397 (plist-get info :creator))))
1398 (format "<meta:keyword>%s</meta:keyword>\n" keywords)
1399 (format "<dc:subject>%s</dc:subject>\n" description)
1400 (format "<dc:title>%s</dc:title>\n" title)
1401 "\n"
1402 " </office:meta>\n" "</office:document-meta>")
1403 nil (concat org-e-odt-zip-dir "meta.xml"))
1404 ;; Add meta.xml in to manifest.
1405 (org-e-odt-create-manifest-file-entry "text/xml" "meta.xml"))
1407 ;; Update styles file.
1408 ;; Copy styles.xml. Also dump htmlfontify styles, if there is any.
1409 ;; Write styles file.
1410 (let* ((styles-file (plist-get info :odt-styles-file))
1411 (styles-file (and styles-file (read (org-trim styles-file))))
1412 ;; Non-availability of styles.xml is not a critical
1413 ;; error. For now throw an error purely for aesthetic
1414 ;; reasons.
1415 (styles-file (or styles-file
1416 org-e-odt-styles-file
1417 (expand-file-name "OrgOdtStyles.xml"
1418 org-e-odt-styles-dir)
1419 (error "org-e-odt: Missing styles file?"))))
1420 (cond
1421 ((listp styles-file)
1422 (let ((archive (nth 0 styles-file))
1423 (members (nth 1 styles-file)))
1424 (org-e-odt--zip-extract archive members org-e-odt-zip-dir)
1425 (mapc
1426 (lambda (member)
1427 (when (org-file-image-p member)
1428 (let* ((image-type (file-name-extension member))
1429 (media-type (format "image/%s" image-type)))
1430 (org-e-odt-create-manifest-file-entry media-type member))))
1431 members)))
1432 ((and (stringp styles-file) (file-exists-p styles-file))
1433 (let ((styles-file-type (file-name-extension styles-file)))
1434 (cond
1435 ((string= styles-file-type "xml")
1436 (copy-file styles-file (concat org-e-odt-zip-dir "styles.xml") t))
1437 ((member styles-file-type '("odt" "ott"))
1438 (org-e-odt--zip-extract styles-file "styles.xml" org-e-odt-zip-dir)))))
1440 (error (format "Invalid specification of styles.xml file: %S"
1441 org-e-odt-styles-file))))
1443 ;; create a manifest entry for styles.xml
1444 (org-e-odt-create-manifest-file-entry "text/xml" "styles.xml")
1446 ;; FIXME: Who is opening an empty styles.xml before this point?
1447 (with-current-buffer
1448 (find-file-noselect (concat org-e-odt-zip-dir "styles.xml") t)
1449 (revert-buffer t t)
1451 ;; Write custom styles for source blocks
1452 ;; Save STYLES used for colorizing of source blocks.
1453 ;; Update styles.xml with styles that were collected as part of
1454 ;; `org-e-odt-hfy-face-to-css' callbacks.
1455 (let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
1456 hfy-user-sheet-assoc "")))
1457 (when styles
1458 (goto-char (point-min))
1459 (when (re-search-forward "</office:styles>" nil t)
1460 (goto-char (match-beginning 0))
1461 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
1463 ;; Update styles.xml - take care of outline numbering
1465 ;; Don't make automatic backup of styles.xml file. This setting
1466 ;; prevents the backed-up styles.xml file from being zipped in to
1467 ;; odt file. This is more of a hackish fix. Better alternative
1468 ;; would be to fix the zip command so that the output odt file
1469 ;; includes only the needed files and excludes any auto-generated
1470 ;; extra files like backups and auto-saves etc etc. Note that
1471 ;; currently the zip command zips up the entire temp directory so
1472 ;; that any auto-generated files created under the hood ends up in
1473 ;; the resulting odt file.
1474 (set (make-local-variable 'backup-inhibited) t)
1476 ;; Outline numbering is retained only upto LEVEL.
1477 ;; To disable outline numbering pass a LEVEL of 0.
1479 (goto-char (point-min))
1480 (let ((regex
1481 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
1482 (replacement
1483 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
1484 (while (re-search-forward regex nil t)
1485 (unless (let ((sec-num (plist-get info :section-numbers))
1486 (level (string-to-number (match-string 2))))
1487 (if (wholenump sec-num) (<= level sec-num) sec-num))
1488 (replace-match replacement t nil))))
1489 (save-buffer 0)))
1490 ;; Update content.xml.
1491 (with-temp-buffer
1492 (insert-file-contents
1493 (or org-e-odt-content-template-file
1494 (expand-file-name "OrgOdtContentTemplate.xml"
1495 org-e-odt-styles-dir)))
1496 ;; Write automatic styles.
1497 ;; - Position the cursor.
1498 (goto-char (point-min))
1499 (re-search-forward " </office:automatic-styles>" nil t)
1500 (goto-char (match-beginning 0))
1501 ;; - Dump automatic table styles
1502 (loop for (style-name props) in
1503 (plist-get org-e-odt-automatic-styles 'Table) do
1504 (when (setq props (or (plist-get props :rel-width) 96))
1505 (insert (format org-e-odt-table-style-format style-name props))))
1506 ;; Update display level.
1507 ;; - Remove existing sequence decls. Also position the cursor.
1508 (goto-char (point-min))
1509 (when (re-search-forward "<text:sequence-decls" nil t)
1510 (delete-region (match-beginning 0)
1511 (re-search-forward "</text:sequence-decls>" nil nil)))
1512 ;; Update sequence decls according to user preference.
1513 (insert
1514 (format
1515 "\n<text:sequence-decls>\n%s\n</text:sequence-decls>"
1516 (mapconcat
1517 (lambda (x)
1518 (format
1519 "<text:sequence-decl text:display-outline-level=\"%d\" text:name=\"%s\"/>"
1520 org-e-odt-display-outline-level (nth 1 x)))
1521 org-e-odt-category-map-alist "\n")))
1522 ;; Position the cursor to document body.
1523 (goto-char (point-min))
1524 (re-search-forward "</office:text>" nil nil)
1525 (goto-char (match-beginning 0))
1527 ;; Preamble - Title, Author, Date etc.
1528 (insert
1529 (let* ((title (org-export-data (plist-get info :title) info))
1530 (author (and (plist-get info :with-author)
1531 (let ((auth (plist-get info :author)))
1532 (and auth (org-export-data auth info)))))
1533 (date (org-export-data (plist-get info :date) info))
1534 (iso-date (org-e-odt--date date))
1535 (date (org-e-odt--date date "%d %b %Y"))
1536 (email (plist-get info :email))
1537 ;; switch on or off above vars based on user settings
1538 (author (and (plist-get info :with-author) (or author email)))
1539 ;; (date (and (plist-get info :time-stamp-file) date))
1540 (email (and (plist-get info :with-email) email)))
1541 (concat
1542 ;; title
1543 (when title
1544 (concat
1545 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1546 "OrgTitle" (format "\n<text:title>%s</text:title>" title))
1547 ;; separator
1548 "\n<text:p text:style-name=\"OrgTitle\"/>"))
1549 (cond
1550 ((and author (not email))
1551 ;; author only
1552 (concat
1553 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1554 "OrgSubtitle"
1555 (format "<text:initial-creator>%s</text:initial-creator>" author))
1556 ;; separator
1557 "\n<text:p text:style-name=\"OrgSubtitle\"/>"))
1558 ((and author email)
1559 ;; author and email
1560 (concat
1561 (format
1562 "\n<text:p text:style-name=\"%s\">%s</text:p>"
1563 "OrgSubtitle"
1564 (format
1565 "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
1566 (concat "mailto:" email)
1567 (format "<text:initial-creator>%s</text:initial-creator>" author)))
1568 ;; separator
1569 "\n<text:p text:style-name=\"OrgSubtitle\"/>")))
1570 ;; date
1571 (when date
1572 (concat
1573 (format
1574 "\n<text:p text:style-name=\"%s\">%s</text:p>"
1575 "OrgSubtitle"
1576 (format
1577 "\n<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">%s</text:date>"
1579 "OrgDate" iso-date date))
1580 ;; separator
1581 "<text:p text:style-name=\"OrgSubtitle\"/>")))))
1582 ;; Table of Contents
1583 (let* ((with-toc (plist-get info :with-toc))
1584 (depth (and with-toc (if (wholenump with-toc)
1585 with-toc
1586 (plist-get info :headline-levels)))))
1587 (when depth (insert (or (org-e-odt-toc depth info) ""))))
1588 ;; Contents.
1589 (insert contents)
1590 ;; Return contents.
1591 (buffer-substring-no-properties (point-min) (point-max))))
1595 ;;; Transcode Functions
1597 ;;;; Bold
1599 (defun org-e-odt-bold (bold contents info)
1600 "Transcode BOLD from Org to ODT.
1601 CONTENTS is the text with bold markup. INFO is a plist holding
1602 contextual information."
1603 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1604 "Bold" contents))
1607 ;;;; Center Block
1609 (defun org-e-odt-center-block (center-block contents info)
1610 "Transcode a CENTER-BLOCK element from Org to ODT.
1611 CONTENTS holds the contents of the center block. INFO is a plist
1612 holding contextual information."
1613 (org-e-odt--wrap-label center-block contents))
1616 ;;;; Clock
1618 (defun org-e-odt-clock (clock contents info)
1619 "Transcode a CLOCK element from Org to ODT.
1620 CONTENTS is nil. INFO is a plist used as a communication
1621 channel."
1622 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1623 "OrgTimestampWrapper"
1624 (concat
1625 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1626 "OrgTimestampKeyword" org-clock-string)
1627 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1628 "OrgTimestamp"
1629 (concat (org-translate-time
1630 (org-element-property :value clock))
1631 (let ((time (org-element-property :time clock)))
1632 (and time (format " (%s)" time))))))))
1635 ;;;; Code
1637 (defun org-e-odt-code (code contents info)
1638 "Transcode a CODE object from Org to ODT.
1639 CONTENTS is nil. INFO is a plist used as a communication
1640 channel."
1641 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1642 "OrgCode" (org-element-property :value code)))
1645 ;;;; Comment
1647 ;; Comments are ignored.
1650 ;;;; Comment Block
1652 ;; Comment Blocks are ignored.
1655 ;;;; Drawer
1657 (defun org-e-odt-drawer (drawer contents info)
1658 "Transcode a DRAWER element from Org to ODT.
1659 CONTENTS holds the contents of the block. INFO is a plist
1660 holding contextual information."
1661 (let* ((name (org-element-property :drawer-name drawer))
1662 (output (if (functionp org-e-odt-format-drawer-function)
1663 (funcall org-e-odt-format-drawer-function
1664 name contents)
1665 ;; If there's no user defined function: simply
1666 ;; display contents of the drawer.
1667 contents)))
1668 (org-e-odt--wrap-label drawer output)))
1671 ;;;; Dynamic Block
1673 (defun org-e-odt-dynamic-block (dynamic-block contents info)
1674 "Transcode a DYNAMIC-BLOCK element from Org to ODT.
1675 CONTENTS holds the contents of the block. INFO is a plist
1676 holding contextual information. See `org-export-data'."
1677 (org-e-odt--wrap-label dynamic-block contents))
1680 ;;;; Entity
1682 (defun org-e-odt-entity (entity contents info)
1683 "Transcode an ENTITY object from Org to ODT.
1684 CONTENTS are the definition itself. INFO is a plist holding
1685 contextual information."
1686 ;; (let ((ent (org-element-property :latex entity)))
1687 ;; (if (org-element-property :latex-math-p entity)
1688 ;; (format "$%s$" ent)
1689 ;; ent))
1690 (org-element-property :utf-8 entity))
1693 ;;;; Example Block
1695 (defun org-e-odt-example-block (example-block contents info)
1696 "Transcode a EXAMPLE-BLOCK element from Org to ODT.
1697 CONTENTS is nil. INFO is a plist holding contextual information."
1698 (org-e-odt--wrap-label
1699 example-block (org-e-odt-format-code example-block info)))
1702 ;;;; Export Snippet
1704 (defun org-e-odt-export-snippet (export-snippet contents info)
1705 "Transcode a EXPORT-SNIPPET object from Org to ODT.
1706 CONTENTS is nil. INFO is a plist holding contextual information."
1707 (when (eq (org-export-snippet-backend export-snippet) 'e-odt)
1708 (org-element-property :value export-snippet)))
1711 ;;;; Export Block
1713 (defun org-e-odt-export-block (export-block contents info)
1714 "Transcode a EXPORT-BLOCK element from Org to ODT.
1715 CONTENTS is nil. INFO is a plist holding contextual information."
1716 (when (string= (org-element-property :type export-block) "ODT")
1717 (org-remove-indentation (org-element-property :value export-block))))
1720 ;;;; Fixed Width
1722 (defun org-e-odt-fixed-width (fixed-width contents info)
1723 "Transcode a FIXED-WIDTH element from Org to ODT.
1724 CONTENTS is nil. INFO is a plist holding contextual information."
1725 (org-e-odt--wrap-label
1726 fixed-width (org-e-odt-do-format-code
1727 (org-element-property :value fixed-width))))
1730 ;;;; Footnote Definition
1732 ;; Footnote Definitions are ignored.
1735 ;;;; Footnote Reference
1737 (defun org-e-odt-footnote-reference (footnote-reference contents info)
1738 "Transcode a FOOTNOTE-REFERENCE element from Org to ODT.
1739 CONTENTS is nil. INFO is a plist holding contextual information."
1740 (let ((--format-footnote-definition
1741 (function
1742 (lambda (n def)
1743 (setq n (format "%d" n))
1744 (let ((id (concat "fn" n))
1745 (note-class "footnote")
1746 (par-style "Footnote"))
1747 (format
1748 "<text:note text:id=\"%s\" text:note-class=\"%s\">%s</text:note>"
1749 id note-class
1750 (concat
1751 (format "<text:note-citation>%s</text:note-citation>" n)
1752 (format "<text:note-body>%s</text:note-body>" def)))))))
1753 (--format-footnote-reference
1754 (function
1755 (lambda (n)
1756 (setq n (format "%d" n))
1757 (let ((note-class "footnote")
1758 (ref-format "text")
1759 (ref-name (concat "fn" n)))
1760 (format
1761 "<text:span text:style-name=\"%s\">%s</text:span>"
1762 "OrgSuperscript"
1763 (format "<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:note-ref>"
1764 note-class ref-format ref-name n)))))))
1765 (concat
1766 ;; Insert separator between two footnotes in a row.
1767 (let ((prev (org-export-get-previous-element footnote-reference info)))
1768 (and (eq (org-element-type prev) 'footnote-reference)
1769 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1770 "OrgSuperscript" ",")))
1771 ;; Trancode footnote reference.
1772 (let ((n (org-export-get-footnote-number footnote-reference info)))
1773 (cond
1774 ((not (org-export-footnote-first-reference-p footnote-reference info))
1775 (funcall --format-footnote-reference n))
1776 ;; Inline definitions are secondary strings.
1777 ;; Non-inline footnotes definitions are full Org data.
1779 (let* ((raw (org-export-get-footnote-definition footnote-reference
1780 info))
1781 (def (let ((def (org-trim (org-export-data raw info))))
1782 (if (eq (org-element-type raw) 'org-data) def
1783 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1784 "Footnote" def)))))
1785 (funcall --format-footnote-definition n def))))))))
1788 ;;;; Headline
1790 (defun* org-e-odt-format-headline
1791 (todo todo-type priority text tags
1792 &key level section-number headline-label &allow-other-keys)
1793 (concat
1794 ;; Todo.
1795 (and todo
1796 (concat
1797 (let ((style (if (member todo org-done-keywords) "OrgDone" "OrgTodo")))
1798 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1799 style todo)) " "))
1800 ;; Title.
1801 text
1802 ;; Tags.
1803 (and tags
1804 (concat "<text:tab/>"
1805 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1806 "OrgTag" (mapconcat 'org-trim tags " : "))))))
1808 (defun org-e-odt-format-headline--wrap (headline info
1809 &optional format-function
1810 &rest extra-keys)
1811 "Transcode an HEADLINE element from Org to ODT.
1812 CONTENTS holds the contents of the headline. INFO is a plist
1813 holding contextual information."
1814 (let* ((level (+ (org-export-get-relative-level headline info)))
1815 (headline-number (org-export-get-headline-number headline info))
1816 (section-number (and (org-export-numbered-headline-p headline info)
1817 (mapconcat 'number-to-string
1818 headline-number ".")))
1819 (todo (and (plist-get info :with-todo-keywords)
1820 (let ((todo (org-element-property :todo-keyword headline)))
1821 (and todo (org-export-data todo info)))))
1822 (todo-type (and todo (org-element-property :todo-type headline)))
1823 (priority (and (plist-get info :with-priority)
1824 (org-element-property :priority headline)))
1825 (text (org-export-data (org-element-property :title headline) info))
1826 (tags (and (plist-get info :with-tags)
1827 (org-export-get-tags headline info)))
1828 (headline-label (concat "sec-" (mapconcat 'number-to-string
1829 headline-number "-")))
1830 (format-function (cond
1831 ((functionp format-function) format-function)
1832 ((functionp org-e-odt-format-headline-function)
1833 (function*
1834 (lambda (todo todo-type priority text tags
1835 &allow-other-keys)
1836 (funcall org-e-odt-format-headline-function
1837 todo todo-type priority text tags))))
1838 (t 'org-e-odt-format-headline))))
1839 (apply format-function
1840 todo todo-type priority text tags
1841 :headline-label headline-label :level level
1842 :section-number section-number extra-keys)))
1844 (defun org-e-odt-headline (headline contents info)
1845 "Transcode an HEADLINE element from Org to ODT.
1846 CONTENTS holds the contents of the headline. INFO is a plist
1847 holding contextual information."
1848 ;; Case 1: This is a footnote section: ignore it.
1849 (unless (org-element-property :footnote-section-p headline)
1850 (let* ((text (org-export-data (org-element-property :title headline) info))
1851 ;; Create the headline text.
1852 (full-text (org-e-odt-format-headline--wrap headline info))
1853 ;; Get level relative to current parsed data.
1854 (level (org-export-get-relative-level headline info))
1855 ;; Get canonical label for the headline.
1856 (id (concat "sec-" (mapconcat 'number-to-string
1857 (org-export-get-headline-number
1858 headline info) "-")))
1859 ;; Get user-specified labels for the headline.
1860 (extra-ids (list (org-element-property :custom-id headline)
1861 (org-element-property :id headline)))
1862 ;; Extra targets.
1863 (extra-targets
1864 (mapconcat (lambda (x)
1865 (when x
1866 (let ((x (if (org-uuidgen-p x) (concat "ID-" x) x)))
1867 (org-e-odt--target
1868 "" (org-export-solidify-link-text x)))))
1869 extra-ids ""))
1870 ;; Title.
1871 (anchored-title (org-e-odt--target full-text id)))
1872 (cond
1873 ;; Case 2. This is a deep sub-tree: export it as a list item.
1874 ;; Also export as items headlines for which no section
1875 ;; format has been found.
1876 ((org-export-low-level-p headline info)
1877 ;; Build the real contents of the sub-tree.
1878 (concat
1879 (and (org-export-first-sibling-p headline info)
1880 (format "\n<text:list text:style-name=\"%s\" %s>"
1881 ;; Choose style based on list type.
1882 (if (org-export-numbered-headline-p headline info)
1883 "OrgNumberedList" "OrgBulletedList")
1884 ;; If top-level list, re-start numbering. Otherwise,
1885 ;; continue numbering.
1886 (format "text:continue-numbering=\"%s\""
1887 (let* ((parent (org-export-get-parent-headline
1888 headline)))
1889 (if (and parent
1890 (org-export-low-level-p parent info))
1891 "true" "false")))))
1892 (let ((headline-has-table-p
1893 (let ((section (assq 'section (org-element-contents headline))))
1894 (assq 'table (and section (org-element-contents section))))))
1895 (format "\n<text:list-item>\n%s\n%s"
1896 (concat
1897 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1898 "Text_20_body"
1899 (concat extra-targets anchored-title))
1900 contents)
1901 (if headline-has-table-p
1902 "</text:list-header>"
1903 "</text:list-item>")))
1904 (and (org-export-last-sibling-p headline info)
1905 "</text:list>")))
1906 ;; Case 3. Standard headline. Export it as a section.
1908 (concat
1909 (format
1910 "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\">%s</text:h>"
1911 (format "Heading_20_%s" level)
1912 level
1913 (concat extra-targets anchored-title))
1914 contents))))))
1917 ;;;; Horizontal Rule
1919 (defun org-e-odt-horizontal-rule (horizontal-rule contents info)
1920 "Transcode an HORIZONTAL-RULE object from Org to ODT.
1921 CONTENTS is nil. INFO is a plist holding contextual information."
1922 (org-e-odt--wrap-label
1923 horizontal-rule
1924 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1925 "Horizontal_20_Line" "")))
1928 ;;;; Inline Babel Call
1930 ;; Inline Babel Calls are ignored.
1933 ;;;; Inline Src Block
1935 (defun org-e-odt--find-verb-separator (s)
1936 "Return a character not used in string S.
1937 This is used to choose a separator for constructs like \\verb."
1938 (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
1939 (loop for c across ll
1940 when (not (string-match (regexp-quote (char-to-string c)) s))
1941 return (char-to-string c))))
1943 (defun org-e-odt-inline-src-block (inline-src-block contents info)
1944 "Transcode an INLINE-SRC-BLOCK element from Org to ODT.
1945 CONTENTS holds the contents of the item. INFO is a plist holding
1946 contextual information."
1947 (let* ((org-lang (org-element-property :language inline-src-block))
1948 (code (org-element-property :value inline-src-block))
1949 (separator (org-e-odt--find-verb-separator code)))
1950 (error "FIXME")))
1953 ;;;; Inlinetask
1955 (defun org-e-odt-inlinetask (inlinetask contents info)
1956 "Transcode an INLINETASK element from Org to ODT.
1957 CONTENTS holds the contents of the block. INFO is a plist
1958 holding contextual information."
1959 (cond
1960 ;; If `org-e-odt-format-inlinetask-function' is provided, call it
1961 ;; with appropriate arguments.
1962 ((functionp org-e-odt-format-inlinetask-function)
1963 (let ((format-function
1964 (function*
1965 (lambda (todo todo-type priority text tags
1966 &key contents &allow-other-keys)
1967 (funcall org-e-odt-format-inlinetask-function
1968 todo todo-type priority text tags contents)))))
1969 (org-e-odt-format-headline--wrap
1970 inlinetask info format-function :contents contents)))
1971 ;; Otherwise, use a default template.
1972 (t (org-e-odt--wrap-label
1973 inlinetask
1974 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1975 "Text_20_body"
1976 (org-e-odt--textbox
1977 (concat
1978 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1979 "OrgInlineTaskHeading"
1980 (org-e-odt-format-headline--wrap
1981 inlinetask info))
1982 contents)
1983 nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\""))))))
1985 ;;;; Italic
1987 (defun org-e-odt-italic (italic contents info)
1988 "Transcode ITALIC from Org to ODT.
1989 CONTENTS is the text with italic markup. INFO is a plist holding
1990 contextual information."
1991 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1992 "Emphasis" contents))
1995 ;;;; Item
1997 (defun org-e-odt-item (item contents info)
1998 "Transcode an ITEM element from Org to ODT.
1999 CONTENTS holds the contents of the item. INFO is a plist holding
2000 contextual information."
2001 (let* ((plain-list (org-export-get-parent item))
2002 (type (org-element-property :type plain-list))
2003 (counter (org-element-property :counter item))
2004 (tag (let ((tag (org-element-property :tag item)))
2005 (and tag
2006 (concat (org-e-odt--checkbox item)
2007 (org-export-data tag info))))))
2008 (case type
2009 ((ordered unordered descriptive-1 descriptive-2)
2010 (format "\n<text:list-item>\n%s\n%s"
2011 contents
2012 (let* ((--element-has-a-table-p
2013 (function
2014 (lambda (element info)
2015 (loop for el in (org-element-contents element)
2016 thereis (eq (org-element-type el) 'table))))))
2017 (cond
2018 ((funcall --element-has-a-table-p item info)
2019 "</text:list-header>")
2020 (t "</text:list-item>")))))
2021 (t (error "Unknown list type: %S" type)))))
2023 ;;;; Keyword
2025 (defun org-e-odt-keyword (keyword contents info)
2026 "Transcode a KEYWORD element from Org to ODT.
2027 CONTENTS is nil. INFO is a plist holding contextual information."
2028 (let ((key (org-element-property :key keyword))
2029 (value (org-element-property :value keyword)))
2030 (cond
2031 ((string= key "ODT") value)
2032 ((string= key "INDEX") (format "\\index{%s}" value))
2033 ((string= key "TARGET") nil ; FIXME
2034 ;; (format "\\label{%s}" (org-export-solidify-link-text value))
2036 ((string= key "toc")
2037 (let ((value (downcase value)))
2038 (cond
2039 ((string-match "\\<headlines\\>" value)
2040 (let ((depth (or (and (string-match "[0-9]+" value)
2041 (string-to-number (match-string 0 value)))
2042 (plist-get info :with-toc))))
2043 (when (wholenump depth) (org-e-odt-toc depth info))))
2044 ((string= "tables" value) "FIXME")
2045 ((string= "figures" value) "FIXME")
2046 ((string= "listings" value)
2047 (cond
2048 ;; At the moment, src blocks with a caption are wrapped
2049 ;; into a figure environment.
2050 (t "FIXME")))))))))
2053 ;;;; Latex Environment
2056 (eval-after-load 'org-odt
2057 '(ad-deactivate 'org-format-latex-as-mathml))
2059 ;; (defadvice org-format-latex-as-mathml ; FIXME
2060 ;; (after org-e-odt-protect-latex-fragment activate)
2061 ;; "Encode LaTeX fragment as XML.
2062 ;; Do this when translation to MathML fails."
2063 ;; (when (or (not (> (length ad-return-value) 0))
2064 ;; (get-text-property 0 'org-protected ad-return-value))
2065 ;; (setq ad-return-value
2066 ;; (org-propertize (org-e-odt-encode-plain-text (ad-get-arg 0))
2067 ;; 'org-protected t))))
2069 (defun org-e-odt-format-latex (latex-frag processing-type info)
2070 (let* ((prefix (case processing-type
2071 (dvipng "ltxpng/")
2072 (mathml "ltxmathml/")))
2073 (input-file (plist-get info :input-file))
2074 (cache-subdir
2075 (concat prefix (file-name-sans-extension
2076 (file-name-nondirectory input-file))))
2077 (cache-dir (file-name-directory input-file))
2078 (display-msg (case processing-type
2079 (dvipng "Creating LaTeX Image...")
2080 (mathml "Creating MathML snippet..."))))
2081 (with-temp-buffer
2082 (insert latex-frag)
2083 (org-format-latex cache-subdir cache-dir nil display-msg
2084 nil nil processing-type)
2085 (buffer-string))))
2087 (defun org-e-odt-latex-environment (latex-environment contents info)
2088 "Transcode a LATEX-ENVIRONMENT element from Org to ODT.
2089 CONTENTS is nil. INFO is a plist holding contextual information."
2090 (org-e-odt--wrap-label
2091 latex-environment
2092 (let* ((latex-frag
2093 (org-remove-indentation
2094 (org-element-property :value latex-environment)))
2095 (processing-type (plist-get info :LaTeX-fragments))
2096 (caption (org-element-property :caption latex-environment))
2097 (short-caption (and (cdr caption)
2098 (org-export-data (cdr caption) info)))
2099 (caption (and (car caption) (org-export-data (car caption) info)))
2100 (label (org-element-property :name latex-environment))
2101 (attr nil) ; FIXME
2102 (label (org-element-property :name latex-environment)))
2104 (when (memq processing-type '(t mathjax))
2105 (unless (and (fboundp 'org-format-latex-mathml-available-p)
2106 (org-format-latex-mathml-available-p))
2107 (message "LaTeX to MathML converter not available. Trying dvinpng...")
2108 (setq processing-type 'dvipng)))
2110 (when (eq processing-type 'dvipng)
2111 (unless (and (org-check-external-command "latex" "" t)
2112 (org-check-external-command "dvipng" "" t))
2113 (message "LaTeX to PNG converter not available. Using verbatim.")
2114 (setq processing-type 'verbatim)))
2116 (case processing-type
2117 ((t mathjax)
2118 (org-e-odt-format-formula latex-environment info))
2119 (dvipng
2120 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2121 "Text_20_body"
2122 (org-e-odt-link--inline-image latex-environment info)))
2123 (t (org-e-odt-do-format-code latex-frag))))))
2126 ;;;; Latex Fragment
2129 ;; (when latex-frag ; FIXME
2130 ;; (setq href (org-propertize href :title "LaTeX Fragment"
2131 ;; :description latex-frag)))
2132 ;; handle verbatim
2133 ;; provide descriptions
2135 (defun org-e-odt-latex-fragment (latex-fragment contents info)
2136 "Transcode a LATEX-FRAGMENT object from Org to ODT.
2137 CONTENTS is nil. INFO is a plist holding contextual information."
2138 (let* ((latex-frag (org-element-property :value latex-fragment))
2139 (processing-type (plist-get info :LaTeX-fragments)))
2140 (cond
2141 ((member processing-type '(t mathjax))
2142 (org-e-odt-format-formula latex-fragment info))
2143 ((eq processing-type 'dvipng)
2144 (org-e-odt-link--inline-image latex-fragment info))
2145 (t (org-e-odt-encode-plain-text latex-frag t)))))
2148 ;;;; Line Break
2150 (defun org-e-odt-line-break (line-break contents info)
2151 "Transcode a LINE-BREAK object from Org to ODT.
2152 CONTENTS is nil. INFO is a plist holding contextual information."
2153 "<text:line-break/>\n")
2156 ;;;; Link
2160 ;;;; Links :: Generic
2162 ;; (org-lparse-link-description-is-image
2163 ;; (format "\n<draw:a xlink:type=\"simple\" xlink:href=\"%s\">\n%s\n</draw:a>"
2164 ;; href desc))
2166 ;;;; Links :: Label references
2168 (defun org-e-odt-enumerate-element (element info &optional predicate n)
2169 (let* ((--numbered-parent-headline-at-<=-n
2170 (function
2171 (lambda (element n info)
2172 (loop for x in (org-export-get-genealogy element)
2173 thereis (and (eq (org-element-type x) 'headline)
2174 (<= (org-export-get-relative-level x info) n)
2175 (org-export-numbered-headline-p x info)
2176 x)))))
2177 (--enumerate
2178 (function
2179 (lambda (element scope info &optional predicate)
2180 (let ((counter 0))
2181 (org-element-map
2182 (or scope (plist-get info :parse-tree))
2183 (org-element-type element)
2184 (lambda (el)
2185 (and (or (not predicate) (funcall predicate el info))
2186 (incf counter)
2187 (eq element el)
2188 counter))
2189 info 'first-match)))))
2190 (scope (funcall --numbered-parent-headline-at-<=-n
2191 element (or n org-e-odt-display-outline-level) info))
2192 (ordinal (funcall --enumerate element scope info predicate))
2193 (tag
2194 (concat
2195 ;; Section number.
2196 (and scope
2197 (mapconcat 'number-to-string
2198 (org-export-get-headline-number scope info) "."))
2199 ;; Separator.
2200 (and scope ".")
2201 ;; Ordinal.
2202 (number-to-string ordinal))))
2203 tag))
2205 (defun org-e-odt-format-label (element info op)
2206 (let* ((caption-from
2207 (case (org-element-type element)
2208 (link (org-export-get-parent-element element))
2209 (t element)))
2210 ;; get label and caption.
2211 (label (org-element-property :name caption-from))
2212 (caption (org-element-property :caption caption-from))
2213 (short-caption (cdr caption))
2214 ;; transcode captions.
2215 (caption (and (car caption) (org-export-data (car caption) info)))
2216 (short-caption (and short-caption
2217 (org-export-data short-caption info))))
2218 (when (or label caption)
2219 (let* ((default-category
2220 (cond
2221 ((eq (org-element-type element) 'table)
2222 "__Table__")
2223 ((org-e-odt-standalone-image-p element info)
2224 "__Figure__")
2225 ((member (org-element-type element)
2226 '(latex-environment latex-fragment))
2227 (let ((processing-type (plist-get info :LaTeX-fragments)))
2228 (cond
2229 ((eq processing-type 'dvipng) "__DvipngImage__")
2230 ((eq processing-type 'mathjax) "__MathFormula__")
2231 ((eq processing-type 't) "__MathFormula__")
2232 (t (error "Handle LaTeX:verbatim")))))
2233 ((eq (org-element-type element) 'src-block)
2234 "__Listing__")
2235 (t (error "Handle enumeration of %S" element))))
2236 (predicate
2237 (cond
2238 ((member (org-element-type element)
2239 '(table latex-environment src-block))
2240 nil)
2241 ((org-e-odt-standalone-image-p element info)
2242 'org-e-odt-standalone-image-p)
2243 (t (error "Handle enumeration of %S" element))))
2244 (seqno (org-e-odt-enumerate-element
2245 element info predicate)) ; FIXME
2246 ;; handle label props.
2247 (label-props (assoc default-category org-e-odt-category-map-alist))
2248 ;; identify opendocument counter
2249 (counter (nth 1 label-props))
2250 ;; identify label style
2251 (label-style (nth 2 label-props))
2252 ;; retrieve localized category sting
2253 (category (org-export-translate (nth 3 label-props) :utf-8 info)))
2254 (case op
2255 (definition
2256 ;; assign an internal label, if user has not provided one
2257 (setq label (or label (format "%s-%s" default-category seqno)))
2258 (setq label (org-export-solidify-link-text label))
2260 (cons
2261 (format-spec
2262 (cadr (assoc-string label-style org-e-odt-label-styles t))
2263 `((?e . ,category)
2264 (?n . ,(format
2265 "<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">%s</text:sequence>"
2266 label counter counter seqno))
2267 (?c . ,(or caption ""))))
2268 short-caption))
2269 (reference
2270 (assert label)
2271 (setq label (org-export-solidify-link-text label))
2272 (let* ((fmt (cddr (assoc-string label-style org-e-odt-label-styles t)))
2273 (fmt1 (car fmt))
2274 (fmt2 (cadr fmt)))
2275 (format "<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:sequence-ref>"
2276 fmt1 label (format-spec fmt2 `((?e . ,category)
2277 (?n . ,seqno))))))
2278 (t (error "Unknow %S on label" op)))))))
2280 ;;;; Links :: Math formula
2282 (defun org-e-odt-format-formula (element info)
2283 (let* ((src (cond
2284 ((eq (org-element-type element) 'link) ; FIXME
2285 (let* ((type (org-element-property :type element))
2286 (raw-path (org-element-property :path element)))
2287 (cond
2288 ((file-name-absolute-p raw-path)
2289 (expand-file-name raw-path))
2290 (t raw-path))))
2291 ((member (org-element-type element)
2292 '(latex-fragment latex-environment))
2293 (let* ((latex-frag (org-remove-indentation
2294 (org-element-property :value element)))
2295 (formula-link (org-e-odt-format-latex
2296 latex-frag 'mathml info)))
2297 (and formula-link
2298 (string-match "file:\\([^]]*\\)" formula-link)
2299 (match-string 1 formula-link))))
2300 (t (error "what is this?"))))
2301 (full-src (if (file-name-absolute-p src) src
2302 (expand-file-name src (file-name-directory
2303 (plist-get info :input-file)))))
2304 (caption-from
2305 (case (org-element-type element)
2306 (link (org-export-get-parent-element element))
2307 (t element)))
2308 (captions (org-e-odt-format-label caption-from info 'definition))
2309 (caption (car captions))
2310 (href
2311 (format "\n<draw:object %s xlink:href=\"%s\" xlink:type=\"simple\"/>"
2312 " xlink:show=\"embed\" xlink:actuate=\"onLoad\""
2313 (file-name-directory (org-e-odt-copy-formula-file full-src))))
2314 (embed-as (if caption 'paragraph 'character))
2315 width height)
2316 (cond
2317 ((eq embed-as 'character)
2318 (org-e-odt-format-entity "InlineFormula" href width height))
2320 (let* ((equation (org-e-odt-format-entity
2321 "CaptionedDisplayFormula" href width height captions))
2322 (label
2323 (let* ((org-e-odt-category-map-alist
2324 '(("__Table__" "Table" "value")
2325 ("__Figure__" "Illustration" "value")
2326 ("__MathFormula__" "Text" "math-label")
2327 ("__DvipngImage__" "Equation" "value")
2328 ("__Listing__" "Listing" "value"))))
2329 (car (org-e-odt-format-label caption-from info 'definition))))
2330 (formula-tree
2331 (org-element-adopt-elements
2332 (list 'table '(:type org :attr_odt (":style \"OrgEquation\"")))
2333 (org-element-adopt-elements
2334 (list 'table-row '(:type standard))
2335 (list 'table-cell nil "<c8>") (list 'table-cell nil "<c1>"))
2336 (org-element-adopt-elements
2337 (list 'table-row '(:type standard))
2338 (org-element-adopt-elements
2339 (list 'table-cell nil)
2340 (list 'export-block (list :type "ODT" :value equation)))
2341 (org-element-adopt-elements
2342 (list 'table-cell nil)
2343 (list 'export-block (list :type "ODT" :value label))))))
2344 (formula-info
2345 (org-export-collect-tree-properties
2346 formula-tree (org-export-get-environment 'e-odt))))
2347 (org-export-data formula-tree formula-info))))))
2349 (defun org-e-odt-copy-formula-file (src-file)
2350 "Returns the internal name of the file"
2351 (let* ((target-dir (format "Formula-%04d/"
2352 (incf org-e-odt-embedded-formulas-count)))
2353 (target-file (concat target-dir "content.xml")))
2354 ;; Create a directory for holding formula file. Also enter it in
2355 ;; to manifest.
2356 (make-directory (concat org-e-odt-zip-dir target-dir))
2357 (org-e-odt-create-manifest-file-entry
2358 "application/vnd.oasis.opendocument.formula" target-dir "1.2")
2359 ;; Copy over the formula file from user directory to zip
2360 ;; directory.
2361 (message "Embedding %s as %s ..." src-file target-file)
2362 (let ((case-fold-search nil))
2363 (cond
2364 ;; Case 1: Mathml.
2365 ((string-match "\\.\\(mathml\\|mml\\)\\'" src-file)
2366 (copy-file src-file (concat org-e-odt-zip-dir target-file) 'overwrite))
2367 ;; Case 2: OpenDocument formula.
2368 ((string-match "\\.odf\\'" src-file)
2369 (org-e-odt--zip-extract src-file "content.xml"
2370 (concat org-e-odt-zip-dir target-dir)))
2371 (t (error "%s is not a formula file" src-file))))
2372 ;; Enter the formula file in to manifest.
2373 (org-e-odt-create-manifest-file-entry "text/xml" target-file)
2374 target-file))
2376 ;;;; Targets
2378 (defun org-e-odt-format-entity (entity href width height &optional
2379 captions user-frame-params)
2380 (let* ((caption (car captions)) (short-caption (cdr captions))
2381 (entity-style (assoc-string entity org-e-odt-entity-frame-styles t))
2382 default-frame-params frame-params
2383 (--merge-frame-params
2384 (function
2385 (lambda (default-frame-params user-frame-params)
2386 (if (not user-frame-params) default-frame-params
2387 (assert (= (length default-frame-params) 3))
2388 (assert (= (length user-frame-params) 3))
2389 (loop for user-frame-param in user-frame-params
2390 for default-frame-param in default-frame-params
2391 collect (or user-frame-param default-frame-param)))))))
2392 (cond
2393 ((not caption)
2394 (setq default-frame-params (nth 2 entity-style))
2395 (setq frame-params (funcall --merge-frame-params
2396 default-frame-params user-frame-params))
2397 (apply 'org-e-odt--frame href width height frame-params))
2399 (setq default-frame-params (nth 3 entity-style))
2400 (setq frame-params (funcall --merge-frame-params
2401 default-frame-params user-frame-params))
2402 (apply 'org-e-odt--textbox
2403 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2404 "Illustration"
2405 (concat
2406 (apply 'org-e-odt--frame href width height
2407 (let ((entity-style-1 (copy-sequence
2408 (nth 2 entity-style))))
2409 (setcar (cdr entity-style-1)
2410 (concat
2411 (cadr entity-style-1)
2412 (and short-caption
2413 (format " draw:name=\"%s\" "
2414 short-caption))))
2415 entity-style-1))
2416 caption))
2417 width height frame-params)))))
2419 (defun org-e-odt-standalone-image-p (element info)
2420 "Test if ELEMENT is a standalone image for the purpose ODT export.
2421 INFO is a plist holding contextual information.
2423 Return non-nil, if ELEMENT is of type paragraph and it's sole
2424 content, save for whitespaces, is a link that qualifies as an
2425 inline image.
2427 Return non-nil, if ELEMENT is of type link and it's containing
2428 paragraph has no other content save for leading and trailing
2429 whitespaces.
2431 Return nil, otherwise.
2433 Bind `org-e-odt-standalone-image-predicate' to constrain
2434 paragraph further. For example, to check for only captioned
2435 standalone images, do the following.
2437 \(setq org-e-odt-standalone-image-predicate
2438 \(lambda \(paragraph\)
2439 \(org-element-property :caption paragraph\)\)\)
2441 (let ((--standalone-image-predicate
2442 (function (lambda (paragraph)
2443 (or (org-element-property :caption paragraph)
2444 (org-element-property :name paragraph)))))
2445 (paragraph (case (org-element-type element)
2446 (paragraph element)
2447 (link (and (org-export-inline-image-p
2448 element org-e-odt-inline-image-rules)
2449 (org-export-get-parent element)))
2450 (t nil))))
2451 (when paragraph
2452 (assert (eq (org-element-type paragraph) 'paragraph))
2453 (when (funcall --standalone-image-predicate paragraph)
2454 (let ((contents (org-element-contents paragraph)))
2455 (loop for x in contents
2456 with inline-image-count = 0
2457 always (cond
2458 ((eq (org-element-type x) 'plain-text)
2459 (not (org-string-nw-p x)))
2460 ((eq (org-element-type x) 'link)
2461 (when (org-export-inline-image-p
2462 x org-e-odt-inline-image-rules)
2463 (= (incf inline-image-count) 1)))
2464 (t nil))))))))
2467 (defun org-e-odt-get-previous-elements (blob info)
2468 (let ((parent (org-export-get-parent blob)))
2469 (cdr (memq blob (reverse (org-element-contents parent))))))
2471 (defun org-e-odt-resolve-numbered-paragraph (element info)
2472 (when (eq (org-element-type element) 'item)
2473 (let ((el element) ordinal)
2474 (while (eq (org-element-type el) 'item)
2475 (push (1+ (length (org-e-odt-get-previous-elements el info))) ordinal)
2476 (setq el (org-export-get-parent (org-export-get-parent el))))
2477 ordinal)))
2480 (defun org-e-odt-link (link desc info)
2481 "Transcode a LINK object from Org to ODT.
2483 DESC is the description part of the link, or the empty string.
2484 INFO is a plist holding contextual information. See
2485 `org-export-data'."
2486 (let* ((type (org-element-property :type link))
2487 (raw-path (org-element-property :path link))
2488 ;; Ensure DESC really exists, or set it to nil.
2489 (desc (and (not (string= desc "")) desc))
2490 (imagep (org-export-inline-image-p
2491 link org-e-odt-inline-image-rules))
2492 (path (cond
2493 ((member type '("http" "https" "ftp" "mailto"))
2494 (concat type ":" raw-path))
2495 ((string= type "file")
2496 (if (file-name-absolute-p raw-path)
2497 (concat "file://" (expand-file-name raw-path))
2498 (concat "file://" raw-path)))
2499 (t raw-path)))
2500 protocol)
2501 (cond
2502 ;; Image file.
2503 ((and (not desc) (org-export-inline-image-p
2504 link org-e-odt-inline-image-rules))
2505 (org-e-odt-link--inline-image link info))
2506 ;; Radio target: Transcode target's contents and use them as
2507 ;; link's description.
2508 ((string= type "radio")
2509 (let ((destination (org-export-resolve-radio-link link info)))
2510 (when destination
2511 (let ((desc (org-export-data (org-element-contents destination) info))
2512 (href (org-export-solidify-link-text path)))
2513 (format
2514 "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2515 href desc)))))
2516 ;; Links pointing to an headline: Find destination and build
2517 ;; appropriate referencing command.
2518 ((member type '("custom-id" "fuzzy" "id"))
2519 (let ((destination (if (string= type "fuzzy")
2520 (org-export-resolve-fuzzy-link link info)
2521 (org-export-resolve-id-link link info))))
2522 (case (org-element-type destination)
2523 ;; Fuzzy link points nowhere.
2524 ('nil
2525 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2526 "Emphasis" (or desc (org-export-data
2527 (org-element-property
2528 :raw-link link) info))))
2529 ;; Fuzzy link points to an invisible target.
2530 (keyword nil)
2531 ;; LINK points to an headline. Check if LINK should display
2532 ;; section numbers.
2533 (headline
2534 (let* ((headline-no (org-export-get-headline-number destination info))
2535 (label (format "sec-%s" (mapconcat 'number-to-string
2536 headline-no "-"))))
2537 (cond
2538 ;; Case 1: Headline is numbered and LINK has no
2539 ;; description or LINK's description matches headline's
2540 ;; title. Display section number.
2541 ((and (org-export-numbered-headline-p destination info)
2542 (or (not desc) (string= desc (org-element-property
2543 :raw-value destination))))
2544 (format
2545 "<text:bookmark-ref text:reference-format=\"chapter\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2546 label (mapconcat 'number-to-string headline-no ".")))
2547 ;; Case 2: Either the headline is un-numbered or
2548 ;; LINK has a custom description. Display LINK's
2549 ;; description or headline's title.
2551 (let ((desc (or desc (org-export-data
2552 (org-element-property :title destination)
2553 info))))
2554 (format
2555 "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2556 label desc))))))
2557 ;; Fuzzy link points to a target. Do as above.
2558 (target
2559 ;; Identify nearest meaningful container
2560 (let ((container
2561 (loop for parent in (org-export-get-genealogy destination)
2562 when
2563 (memq
2564 (org-element-type parent)
2565 '(footnote-definition footnote-reference headline item
2566 table))
2567 return parent)))
2568 ;; There is a meaningful container
2569 (when container
2570 (case (org-element-type container)
2571 ;; Container is item
2572 (item
2573 (format
2574 "<text:bookmark-ref text:reference-format=\"number-all-superior\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2575 (org-solidify-link-text path)
2577 (mapconcat 'number-to-string
2578 (org-e-odt-resolve-numbered-paragraph
2579 container info) ".")))))))
2580 (otherwise
2581 ;; (unless desc
2582 ;; (setq number (cond
2583 ;; ((org-e-odt-standalone-image-p destination info)
2584 ;; (org-export-get-ordinal
2585 ;; (assoc 'link (org-element-contents destination))
2586 ;; info 'link 'org-e-odt-standalone-image-p))
2587 ;; (t (org-export-get-ordinal destination info))))
2588 ;; (setq desc (when number
2589 ;; (if (atom number) (number-to-string number)
2590 ;; (mapconcat 'number-to-string number ".")))))
2592 (let ((label-reference
2593 (org-e-odt-format-label destination info 'reference)))
2594 (assert label-reference)
2595 label-reference)))))
2596 ;; Coderef: replace link with the reference name or the
2597 ;; equivalent line number.
2598 ((string= type "coderef")
2599 (let* ((line-no (format "%d" (org-export-resolve-coderef path info)))
2600 (href (concat "coderef-" path)))
2601 (format
2602 (org-export-get-coderef-format path desc)
2603 (format
2604 "<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2605 href line-no))))
2606 ;; Link type is handled by a special function.
2607 ((functionp (setq protocol (nth 2 (assoc type org-link-protocols))))
2608 (funcall protocol (org-link-unescape path) desc 'odt))
2609 ;; External link with a description part.
2610 ((and path desc)
2611 (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
2612 path desc))
2613 ;; External link without a description part.
2614 (path
2615 (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
2616 path path))
2617 ;; No path, only description. Try to do something useful.
2618 (t (format "<text:span text:style-name=\"%s\">%s</text:span>"
2619 "Emphasis" desc)))))
2622 ;;;; Paragraph
2624 (defun org-e-odt-paragraph (paragraph contents info)
2625 "Transcode a PARAGRAPH element from Org to ODT.
2626 CONTENTS is the contents of the paragraph, as a string. INFO is
2627 the plist used as a communication channel."
2628 (let* ((parent (org-export-get-parent paragraph))
2629 (parent-type (org-element-type parent))
2630 (style (case parent-type
2631 (quote-block "Quotations")
2632 (center-block "OrgCenter")
2633 (footnote-definition "Footnote")
2634 (t (or (org-element-property :style paragraph)
2635 "Text_20_body")))))
2636 ;; If this paragraph is a leading paragraph in an item and the
2637 ;; item has a checkbox, splice the checkbox and paragraph contents
2638 ;; together.
2639 (when (and (eq (org-element-type parent) 'item)
2640 (eq paragraph (car (org-element-contents parent))))
2641 (setq contents (concat (org-e-odt--checkbox parent) contents)))
2642 (assert style)
2643 (format "\n<text:p text:style-name=\"%s\">%s</text:p>" style contents)))
2646 ;;;; Plain List
2648 (defun org-e-odt-plain-list (plain-list contents info)
2649 "Transcode a PLAIN-LIST element from Org to ODT.
2650 CONTENTS is the contents of the list. INFO is a plist holding
2651 contextual information."
2652 (org-e-odt--wrap-label
2653 plain-list
2654 (format "\n<text:list text:style-name=\"%s\" %s>\n%s</text:list>"
2655 ;; Choose style based on list type.
2656 (case (org-element-property :type plain-list)
2657 (ordered "OrgNumberedList")
2658 (unordered "OrgBulletedList")
2659 (descriptive-1 "OrgDescriptionList")
2660 (descriptive-2 "OrgDescriptionList"))
2661 ;; If top-level list, re-start numbering. Otherwise,
2662 ;; continue numbering.
2663 (format "text:continue-numbering=\"%s\""
2664 (let* ((parent (org-export-get-parent plain-list)))
2665 (if (and parent (eq (org-element-type parent) 'item))
2666 "true" "false")))
2667 contents)))
2669 ;;;; Plain Text
2671 (defun org-e-odt-fill-tabs-and-spaces (line)
2672 (replace-regexp-in-string
2673 "\\([\t]\\|\\([ ]+\\)\\)"
2674 (lambda (s)
2675 (cond
2676 ((string= s "\t") "<text:tab/>")
2677 (t (let ((n (length s)))
2678 (cond
2679 ((= n 1) " ")
2680 ((> n 1) (concat " " (format "<text:s text:c=\"%d\"/>" (1- n))))
2681 (t ""))))))
2682 line))
2684 (defun org-e-odt-encode-plain-text (text &optional no-whitespace-filling)
2685 (mapc
2686 (lambda (pair)
2687 (setq text (replace-regexp-in-string (car pair) (cdr pair) text t t)))
2688 '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
2689 (if no-whitespace-filling text
2690 (org-e-odt-fill-tabs-and-spaces text)))
2692 (defun org-e-odt--quotation-marks (text info)
2693 "Export quotation marks depending on language conventions.
2694 TEXT is a string containing quotation marks to be replaced. INFO
2695 is a plist used as a communication channel."
2696 (mapc (lambda(l)
2697 (let ((start 0))
2698 (while (setq start (string-match (car l) text start))
2699 (let ((new-quote (concat (match-string 1 text) (cdr l))))
2700 (setq text (replace-match new-quote t t text))))))
2701 (cdr (or (assoc (plist-get info :language) org-e-odt-quotes)
2702 ;; Falls back on English.
2703 (assoc "en" org-e-odt-quotes))))
2704 text)
2706 (defun org-e-odt-plain-text (text info)
2707 "Transcode a TEXT string from Org to ODT.
2708 TEXT is the string to transcode. INFO is a plist holding
2709 contextual information."
2710 ;; Protect &, < and >.
2711 (setq text (org-e-odt-encode-plain-text text t))
2712 ;; Handle quotation marks
2713 (setq text (org-e-odt--quotation-marks text info))
2714 ;; Convert special strings.
2715 (when (plist-get info :with-special-strings)
2716 (mapc
2717 (lambda (pair)
2718 (setq text (replace-regexp-in-string (car pair) (cdr pair) text t nil)))
2719 org-e-odt-special-string-regexps))
2720 ;; Handle break preservation if required.
2721 (when (plist-get info :preserve-breaks)
2722 (setq text (replace-regexp-in-string
2723 "\\(\\\\\\\\\\)?[ \t]*\n" "<text:line-break/>\n" text t)))
2724 ;; Return value.
2725 text)
2728 ;;;; Planning
2730 (defun org-e-odt-planning (planning contents info)
2731 "Transcode a PLANNING element from Org to ODT.
2732 CONTENTS is nil. INFO is a plist used as a communication
2733 channel."
2734 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2735 "OrgTimestampWrapper"
2736 (concat
2737 (let ((closed (org-element-property :closed planning)))
2738 (when closed
2739 (concat
2740 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2741 "OrgTimestampKeyword" org-closed-string)
2742 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2743 "OrgTimestamp" (org-translate-time closed)))))
2744 (let ((deadline (org-element-property :deadline planning)))
2745 (when deadline
2746 (concat
2747 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2748 "OrgTimestampKeyword" org-deadline-string)
2749 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2750 "OrgTimestamp" (org-translate-time deadline)))))
2751 (let ((scheduled (org-element-property :scheduled planning)))
2752 (when scheduled
2753 (concat
2754 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2755 "OrgTimestampKeyword" org-scheduled-string)
2756 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2757 "OrgTimestamp" (org-translate-time scheduled))))))))
2760 ;;;; Property Drawer
2762 (defun org-e-odt-property-drawer (property-drawer contents info)
2763 "Transcode a PROPERTY-DRAWER element from Org to ODT.
2764 CONTENTS is nil. INFO is a plist holding contextual
2765 information."
2766 ;; The property drawer isn't exported but we want separating blank
2767 ;; lines nonetheless.
2771 ;;;; Quote Block
2773 (defun org-e-odt-quote-block (quote-block contents info)
2774 "Transcode a QUOTE-BLOCK element from Org to ODT.
2775 CONTENTS holds the contents of the block. INFO is a plist
2776 holding contextual information."
2777 (org-e-odt--wrap-label quote-block contents))
2780 ;;;; Quote Section
2782 (defun org-e-odt-quote-section (quote-section contents info)
2783 "Transcode a QUOTE-SECTION element from Org to ODT.
2784 CONTENTS is nil. INFO is a plist holding contextual information."
2785 (let ((value (org-remove-indentation
2786 (org-element-property :value quote-section))))
2787 (when value (org-e-odt-do-format-code value))))
2790 ;;;; Section
2792 (defun org-e-odt-format-section (text style &optional name)
2793 (let ((default-name (car (org-e-odt-add-automatic-style "Section"))))
2794 (format "\n<text:section text:style-name=\"%s\" %s>\n%s\n</text:section>"
2795 style
2796 (format "text:name=\"%s\"" (or name default-name))
2797 text)))
2800 (defun org-e-odt-section (section contents info) ; FIXME
2801 "Transcode a SECTION element from Org to ODT.
2802 CONTENTS holds the contents of the section. INFO is a plist
2803 holding contextual information."
2804 contents)
2806 ;;;; Radio Target
2808 (defun org-e-odt-radio-target (radio-target text info)
2809 "Transcode a RADIO-TARGET object from Org to ODT.
2810 TEXT is the text of the target. INFO is a plist holding
2811 contextual information."
2812 (org-e-odt--target
2813 text (org-export-solidify-link-text
2814 (org-element-property :value radio-target))))
2817 ;;;; Special Block
2819 (defun org-e-odt-special-block (special-block contents info)
2820 "Transcode a SPECIAL-BLOCK element from Org to ODT.
2821 CONTENTS holds the contents of the block. INFO is a plist
2822 holding contextual information."
2823 (let ((type (downcase (org-element-property :type special-block)))
2824 (attributes (org-export-read-attribute :attr_odt special-block)))
2825 (org-e-odt--wrap-label
2826 special-block
2827 (cond
2828 ;; Annotation.
2829 ((string= type "annotation")
2830 (let ((author (or (plist-get attributes :author)
2831 (let ((author (plist-get info :author)))
2832 (and author (org-export-data author info)))))
2833 (date (or (plist-get attributes :date)
2834 (plist-get info :date))))
2836 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2837 "Text_20_body"
2838 (format "<office:annotation>\n%s\n</office:annotation>"
2839 (concat
2840 (and author
2841 (format "<dc:creator>%s</dc:creator>" author))
2842 (and date
2843 (format "<dc:date>%s</dc:date>"
2844 (org-e-odt--date date)))
2845 contents)))))
2846 ;; Textbox.
2847 ((string= type "textbox")
2848 (let ((width (plist-get attributes :width))
2849 (height (plist-get attributes :height))
2850 (style (plist-get attributes :style))
2851 (extra (plist-get attributes :extra))
2852 (anchor (plist-get attributes :anchor)))
2853 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2854 "Text_20_body" (org-e-odt--textbox contents width height
2855 style extra anchor))))
2856 (t contents)))))
2859 ;;;; Src Block
2861 (defun org-e-odt-hfy-face-to-css (fn)
2862 "Create custom style for face FN.
2863 When FN is the default face, use it's foreground and background
2864 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
2865 use it's color attribute to create a character style whose name
2866 is obtained from FN. Currently all attributes of FN other than
2867 color are ignored.
2869 The style name for a face FN is derived using the following
2870 operations on the face name in that order - de-dash, CamelCase
2871 and prefix with \"OrgSrc\". For example,
2872 `font-lock-function-name-face' is associated with
2873 \"OrgSrcFontLockFunctionNameFace\"."
2874 (let* ((css-list (hfy-face-to-style fn))
2875 (style-name ((lambda (fn)
2876 (concat "OrgSrc"
2877 (mapconcat
2878 'capitalize (split-string
2879 (hfy-face-or-def-to-name fn) "-")
2880 ""))) fn))
2881 (color-val (cdr (assoc "color" css-list)))
2882 (background-color-val (cdr (assoc "background" css-list)))
2883 (style (and org-e-odt-create-custom-styles-for-srcblocks
2884 (cond
2885 ((eq fn 'default)
2886 (format org-e-odt-src-block-paragraph-format
2887 background-color-val color-val))
2889 (format
2891 <style:style style:name=\"%s\" style:family=\"text\">
2892 <style:text-properties fo:color=\"%s\"/>
2893 </style:style>" style-name color-val))))))
2894 (cons style-name style)))
2896 (defun org-e-odt-htmlfontify-string (line)
2897 (let* ((hfy-html-quote-regex "\\([<\"&> ]\\)")
2898 (hfy-html-quote-map '(("\"" "&quot;")
2899 ("<" "&lt;")
2900 ("&" "&amp;")
2901 (">" "&gt;")
2902 (" " "<text:s/>")
2903 (" " "<text:tab/>")))
2904 (hfy-face-to-css 'org-e-odt-hfy-face-to-css)
2905 (hfy-optimisations-1 (copy-sequence hfy-optimisations))
2906 (hfy-optimisations (add-to-list 'hfy-optimisations-1
2907 'body-text-only))
2908 (hfy-begin-span-handler
2909 (lambda (style text-block text-id text-begins-block-p)
2910 (insert (format "<text:span text:style-name=\"%s\">" style))))
2911 (hfy-end-span-handler (lambda nil (insert "</text:span>"))))
2912 (with-no-warnings (htmlfontify-string line))))
2914 (defun org-e-odt-do-format-code
2915 (code &optional lang refs retain-labels num-start)
2916 (let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
2917 (lang-mode (and lang (intern (format "%s-mode" lang))))
2918 (code-lines (org-split-string code "\n"))
2919 (code-length (length code-lines))
2920 (use-htmlfontify-p (and (functionp lang-mode)
2921 org-e-odt-fontify-srcblocks
2922 (require 'htmlfontify nil t)
2923 (fboundp 'htmlfontify-string)))
2924 (code (if (not use-htmlfontify-p) code
2925 (with-temp-buffer
2926 (insert code)
2927 (funcall lang-mode)
2928 (font-lock-fontify-buffer)
2929 (buffer-string))))
2930 (fontifier (if use-htmlfontify-p 'org-e-odt-htmlfontify-string
2931 'org-e-odt-encode-plain-text))
2932 (par-style (if use-htmlfontify-p "OrgSrcBlock"
2933 "OrgFixedWidthBlock"))
2934 (i 0))
2935 (assert (= code-length (length (org-split-string code "\n"))))
2936 (setq code
2937 (org-export-format-code
2938 code
2939 (lambda (loc line-num ref)
2940 (setq par-style
2941 (concat par-style (and (= (incf i) code-length) "LastLine")))
2943 (setq loc (concat loc (and ref retain-labels (format " (%s)" ref))))
2944 (setq loc (funcall fontifier loc))
2945 (when ref
2946 (setq loc (org-e-odt--target loc (concat "coderef-" ref))))
2947 (assert par-style)
2948 (setq loc (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2949 par-style loc))
2950 (if (not line-num) loc
2951 (format "\n<text:list-item>%s\n</text:list-item>" loc)))
2952 num-start refs))
2953 (cond
2954 ((not num-start) code)
2955 ((= num-start 0)
2956 (format
2957 "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
2958 " text:continue-numbering=\"false\"" code))
2960 (format
2961 "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
2962 " text:continue-numbering=\"true\"" code)))))
2964 (defun org-e-odt-format-code (element info)
2965 (let* ((lang (org-element-property :language element))
2966 ;; Extract code and references.
2967 (code-info (org-export-unravel-code element))
2968 (code (car code-info))
2969 (refs (cdr code-info))
2970 ;; Does the src block contain labels?
2971 (retain-labels (org-element-property :retain-labels element))
2972 ;; Does it have line numbers?
2973 (num-start (case (org-element-property :number-lines element)
2974 (continued (org-export-get-loc element info))
2975 (new 0))))
2976 (org-e-odt-do-format-code code lang refs retain-labels num-start)))
2978 (defun org-e-odt-src-block (src-block contents info)
2979 "Transcode a SRC-BLOCK element from Org to ODT.
2980 CONTENTS holds the contents of the item. INFO is a plist holding
2981 contextual information."
2982 (let* ((lang (org-element-property :language src-block))
2983 (caption (org-element-property :caption src-block))
2984 (short-caption (and (cdr caption)
2985 (org-export-data (cdr caption) info)))
2986 (caption (and (car caption) (org-export-data (car caption) info)))
2987 (label (org-element-property :name src-block))
2988 (attributes (org-export-read-attribute :attr_odt src-block)))
2989 ;; FIXME: Handle caption
2990 ;; caption-str (when caption)
2991 ;; (main (org-export-data (car caption) info))
2992 ;; (secondary (org-export-data (cdr caption) info))
2993 ;; (caption-str (org-e-odt--caption/label-string caption label info))
2994 (let* ((captions (org-e-odt-format-label src-block info 'definition))
2995 (caption (car captions)) (short-caption (cdr captions)))
2996 (concat
2997 (and caption
2998 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2999 "Listing" caption))
3000 (let ((--src-block (org-e-odt-format-code src-block info)))
3001 (if (not (plist-get attributes :textbox)) --src-block
3002 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3003 "Text_20_body"
3004 (org-e-odt--textbox --src-block nil nil nil))))))))
3007 ;;;; Statistics Cookie
3009 (defun org-e-odt-statistics-cookie (statistics-cookie contents info)
3010 "Transcode a STATISTICS-COOKIE object from Org to ODT.
3011 CONTENTS is nil. INFO is a plist holding contextual information."
3012 (let ((cookie-value (org-element-property :value statistics-cookie)))
3013 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3014 "OrgCode" cookie-value)))
3017 ;;;; Strike-Through
3019 (defun org-e-odt-strike-through (strike-through contents info)
3020 "Transcode STRIKE-THROUGH from Org to ODT.
3021 CONTENTS is the text with strike-through markup. INFO is a plist
3022 holding contextual information."
3023 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3024 "Strikethrough" contents))
3027 ;;;; Subscript
3029 (defun org-e-odt-subscript (subscript contents info)
3030 "Transcode a SUBSCRIPT object from Org to ODT.
3031 CONTENTS is the contents of the object. INFO is a plist holding
3032 contextual information."
3033 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3034 "OrgSubscript" contents))
3037 ;;;; Superscript
3039 (defun org-e-odt-superscript (superscript contents info)
3040 "Transcode a SUPERSCRIPT object from Org to ODT.
3041 CONTENTS is the contents of the object. INFO is a plist holding
3042 contextual information."
3043 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3044 "OrgSuperscript" contents))
3047 ;;;; Table Cell
3049 (defun org-e-odt-table-style-spec (element info)
3050 (let* ((table (org-export-get-parent-table element))
3051 (table-attributes (org-export-read-attribute :attr_odt table))
3052 (table-style (plist-get table-attributes :style)))
3053 (assoc table-style org-e-odt-table-styles)))
3055 (defun org-e-odt-get-table-cell-styles (table-cell info)
3056 "Retrieve styles applicable to a table cell.
3057 R and C are (zero-based) row and column numbers of the table
3058 cell. STYLE-SPEC is an entry in `org-e-odt-table-styles'
3059 applicable to the current table. It is `nil' if the table is not
3060 associated with any style attributes.
3062 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
3064 When STYLE-SPEC is nil, style the table cell the conventional way
3065 - choose cell borders based on row and column groupings and
3066 choose paragraph alignment based on `org-col-cookies' text
3067 property. See also
3068 `org-e-odt-get-paragraph-style-cookie-for-table-cell'.
3070 When STYLE-SPEC is non-nil, ignore the above cookie and return
3071 styles congruent with the ODF-1.2 specification."
3072 (let* ((table-cell-address (org-export-table-cell-address table-cell info))
3073 (r (car table-cell-address)) (c (cdr table-cell-address))
3074 (style-spec (org-e-odt-table-style-spec table-cell info))
3075 (table-dimensions (org-export-table-dimensions
3076 (org-export-get-parent-table table-cell)
3077 info)))
3078 (when style-spec
3079 ;; LibreOffice - particularly the Writer - honors neither table
3080 ;; templates nor custom table-cell styles. Inorder to retain
3081 ;; inter-operability with LibreOffice, only automatic styles are
3082 ;; used for styling of table-cells. The current implementation is
3083 ;; congruent with ODF-1.2 specification and hence is
3084 ;; future-compatible.
3086 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
3087 ;; which recognizes as many as 16 different cell types - is much
3088 ;; richer. Unfortunately it is NOT amenable to easy configuration
3089 ;; by hand.
3090 (let* ((template-name (nth 1 style-spec))
3091 (cell-style-selectors (nth 2 style-spec))
3092 (cell-type
3093 (cond
3094 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors))
3095 (= c 0)) "FirstColumn")
3096 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors))
3097 (= (1+ c) (cdr table-dimensions)))
3098 "LastColumn")
3099 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors))
3100 (= r 0)) "FirstRow")
3101 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors))
3102 (= (1+ r) (car table-dimensions)))
3103 "LastRow")
3104 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
3105 (= (% r 2) 1)) "EvenRow")
3106 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors))
3107 (= (% r 2) 0)) "OddRow")
3108 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
3109 (= (% c 2) 1)) "EvenColumn")
3110 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors))
3111 (= (% c 2) 0)) "OddColumn")
3112 (t ""))))
3113 (concat template-name cell-type)))))
3115 (defun org-e-odt-table-cell (table-cell contents info)
3116 "Transcode a TABLE-CELL element from Org to ODT.
3117 CONTENTS is nil. INFO is a plist used as a communication
3118 channel."
3119 (let* ((table-cell-address (org-export-table-cell-address table-cell info))
3120 (r (car table-cell-address))
3121 (c (cdr table-cell-address))
3122 (horiz-span (or (org-export-table-cell-width table-cell info) 0))
3123 (table-row (org-export-get-parent table-cell))
3124 (custom-style-prefix (org-e-odt-get-table-cell-styles
3125 table-cell info))
3126 (paragraph-style
3128 (and custom-style-prefix
3129 (format "%sTableParagraph" custom-style-prefix))
3130 (concat
3131 (cond
3132 ((and (= 1 (org-export-table-row-group table-row info))
3133 (org-export-table-has-header-p
3134 (org-export-get-parent-table table-row) info))
3135 "OrgTableHeading")
3136 ((let* ((table (org-export-get-parent-table table-cell))
3137 (table-attrs (org-export-read-attribute :attr_odt table))
3138 (table-header-columns (plist-get table-attrs
3139 :header-columns)))
3140 (<= c (cond ((wholenump table-header-columns)
3141 (- table-header-columns 1))
3142 (table-header-columns 0)
3143 (t -1))))
3144 "OrgTableHeading")
3145 (t "OrgTableContents"))
3146 (capitalize (symbol-name (org-export-table-cell-alignment
3147 table-cell info))))))
3148 (cell-style-name
3150 (and custom-style-prefix (format "%sTableCell"
3151 custom-style-prefix))
3152 (concat
3153 "OrgTblCell"
3154 (when (or (org-export-table-row-starts-rowgroup-p table-row info)
3155 (zerop r)) "T")
3156 (when (org-export-table-row-ends-rowgroup-p table-row info) "B")
3157 (when (and (org-export-table-cell-starts-colgroup-p table-cell info)
3158 (not (zerop c)) ) "L"))))
3159 (cell-attributes
3160 (concat
3161 (format " table:style-name=\"%s\"" cell-style-name)
3162 (and (> horiz-span 0)
3163 (format " table:number-columns-spanned=\"%d\""
3164 (1+ horiz-span))))))
3165 (unless contents (setq contents ""))
3166 (concat
3167 (assert paragraph-style)
3168 (format "\n<table:table-cell%s>\n%s\n</table:table-cell>"
3169 cell-attributes
3170 (let ((table-cell-contents (org-element-contents table-cell)))
3171 (if (memq (org-element-type (car table-cell-contents))
3172 org-element-all-elements)
3173 contents
3174 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3175 paragraph-style contents))))
3176 (let (s)
3177 (dotimes (i horiz-span s)
3178 (setq s (concat s "\n<table:covered-table-cell/>"))))
3179 "\n")))
3182 ;;;; Table Row
3184 (defun org-e-odt-table-row (table-row contents info)
3185 "Transcode a TABLE-ROW element from Org to ODT.
3186 CONTENTS is the contents of the row. INFO is a plist used as a
3187 communication channel."
3188 ;; Rules are ignored since table separators are deduced from
3189 ;; borders of the current row.
3190 (when (eq (org-element-property :type table-row) 'standard)
3191 (let* ((rowgroup-tags
3192 (if (and (= 1 (org-export-table-row-group table-row info))
3193 (org-export-table-has-header-p
3194 (org-export-get-parent-table table-row) info))
3195 ;; If the row belongs to the first rowgroup and the
3196 ;; table has more than one row groups, then this row
3197 ;; belongs to the header row group.
3198 '("\n<table:table-header-rows>" . "\n</table:table-header-rows>")
3199 ;; Otherwise, it belongs to non-header row group.
3200 '("\n<table:table-rows>" . "\n</table:table-rows>"))))
3201 (concat
3202 ;; Does this row begin a rowgroup?
3203 (when (org-export-table-row-starts-rowgroup-p table-row info)
3204 (car rowgroup-tags))
3205 ;; Actual table row
3206 (format "\n<table:table-row>\n%s\n</table:table-row>" contents)
3207 ;; Does this row end a rowgroup?
3208 (when (org-export-table-row-ends-rowgroup-p table-row info)
3209 (cdr rowgroup-tags))))))
3212 ;;;; Table
3214 (defun org-e-odt-table-first-row-data-cells (table info)
3215 (let ((table-row
3216 (org-element-map
3217 table 'table-row
3218 (lambda (row)
3219 (unless (eq (org-element-property :type row) 'rule) row))
3220 info 'first-match))
3221 (special-column-p (org-export-table-has-special-column-p table)))
3222 (if (not special-column-p) (org-element-contents table-row)
3223 (cdr (org-element-contents table-row)))))
3225 (defun org-e-odt--table (table contents info)
3226 "Transcode a TABLE element from Org to ODT.
3227 CONTENTS is the contents of the table. INFO is a plist holding
3228 contextual information."
3229 (case (org-element-property :type table)
3230 ;; Case 1: table.el doesn't support export to OD format. Strip
3231 ;; such tables from export.
3232 (table.el
3233 (prog1 nil
3234 (message
3235 (concat
3236 "(org-e-odt): Found table.el-type table in the source Org file."
3237 " table.el doesn't support export to ODT format."
3238 " Stripping the table from export."))))
3239 ;; Case 2: Native Org tables.
3240 (otherwise
3241 (let* ((captions (org-e-odt-format-label table info 'definition))
3242 (caption (car captions)) (short-caption (cdr captions))
3243 (attributes (org-export-read-attribute :attr_odt table))
3244 (custom-table-style (nth 1 (org-e-odt-table-style-spec table info)))
3245 (table-column-specs
3246 (function
3247 (lambda (table info)
3248 (let* ((table-style (or custom-table-style "OrgTable"))
3249 (column-style (format "%sColumn" table-style)))
3250 (mapconcat
3251 (lambda (table-cell)
3252 (let ((width (1+ (or (org-export-table-cell-width
3253 table-cell info) 0)))
3254 (s (format
3255 "\n<table:table-column table:style-name=\"%s\"/>"
3256 column-style))
3257 out)
3258 (dotimes (i width out) (setq out (concat s out)))))
3259 (org-e-odt-table-first-row-data-cells table info) "\n"))))))
3260 (concat
3261 ;; caption.
3262 (when caption
3263 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3264 "Table" caption))
3265 ;; begin table.
3266 (let* ((automatic-name
3267 (org-e-odt-add-automatic-style "Table" attributes)))
3268 (format
3269 "\n<table:table table:name=\"%s\" table:style-name=\"%s\">"
3270 (or short-caption (car automatic-name))
3271 (or custom-table-style (cdr automatic-name) "OrgTable")))
3272 ;; column specification.
3273 (funcall table-column-specs table info)
3274 ;; actual contents.
3275 "\n" contents
3276 ;; end table.
3277 "</table:table>")))))
3279 (defun org-e-odt-table (table contents info)
3280 "Transcode a TABLE element from Org to ODT.
3281 CONTENTS is the contents of the table. INFO is a plist holding
3282 contextual information.
3284 Use `org-e-odt--table' to typeset the table. Handle details
3285 pertaining to indentation here."
3286 (let* ((--get-previous-elements
3287 (function
3288 (lambda (blob info)
3289 (let ((parent (org-export-get-parent blob)))
3290 (cdr (memq blob (reverse (org-element-contents parent))))))))
3291 (--element-preceded-by-table-p
3292 (function
3293 (lambda (element info)
3294 (loop for el in (funcall --get-previous-elements element info)
3295 thereis (eq (org-element-type el) 'table)))))
3296 (--walk-list-genealogy-and-collect-tags
3297 (function
3298 (lambda (table info)
3299 (let* ((genealogy (org-export-get-genealogy table))
3300 (list-genealogy
3301 (when (eq (org-element-type (car genealogy)) 'item)
3302 (loop for el in genealogy
3303 when (memq (org-element-type el)
3304 '(item plain-list))
3305 collect el)))
3306 (llh-genealogy
3307 (apply 'nconc
3308 (loop for el in genealogy
3309 when (and (eq (org-element-type el) 'headline)
3310 (org-export-low-level-p el info))
3311 collect
3312 (list el
3313 (assq 'headline
3314 (org-element-contents
3315 (org-export-get-parent el)))))))
3316 parent-list)
3317 (nconc
3318 ;; Handle list genealogy.
3319 (loop for el in list-genealogy collect
3320 (case (org-element-type el)
3321 (plain-list
3322 (setq parent-list el)
3323 (cons "</text:list>"
3324 (format "\n<text:list text:style-name=\"%s\" %s>"
3325 (case (org-element-property :type el)
3326 (ordered "OrgNumberedList")
3327 (unordered "OrgBulletedList")
3328 (descriptive-1 "OrgDescriptionList")
3329 (descriptive-2 "OrgDescriptionList"))
3330 "text:continue-numbering=\"true\"")))
3331 (item
3332 (cond
3333 ((not parent-list)
3334 (if (funcall --element-preceded-by-table-p table info)
3335 '("</text:list-header>" . "<text:list-header>")
3336 '("</text:list-item>" . "<text:list-header>")))
3337 ((funcall --element-preceded-by-table-p
3338 parent-list info)
3339 '("</text:list-header>" . "<text:list-header>"))
3340 (t '("</text:list-item>" . "<text:list-item>"))))))
3341 ;; Handle low-level headlines.
3342 (loop for el in llh-genealogy
3343 with step = 'item collect
3344 (case step
3345 (plain-list
3346 (setq step 'item) ; Flip-flop
3347 (setq parent-list el)
3348 (cons "</text:list>"
3349 (format "\n<text:list text:style-name=\"%s\" %s>"
3350 (if (org-export-numbered-headline-p
3351 el info)
3352 "OrgNumberedList"
3353 "OrgBulletedList")
3354 "text:continue-numbering=\"true\"")))
3355 (item
3356 (setq step 'plain-list) ; Flip-flop
3357 (cond
3358 ((not parent-list)
3359 (if (funcall --element-preceded-by-table-p table info)
3360 '("</text:list-header>" . "<text:list-header>")
3361 '("</text:list-item>" . "<text:list-header>")))
3362 ((let ((section? (org-export-get-previous-element
3363 parent-list info)))
3364 (and section?
3365 (eq (org-element-type section?) 'section)
3366 (assq 'table (org-element-contents section?))))
3367 '("</text:list-header>" . "<text:list-header>"))
3369 '("</text:list-item>" . "<text:list-item>")))))))))))
3370 (close-open-tags (funcall --walk-list-genealogy-and-collect-tags
3371 table info)))
3372 ;; OpenDocument schema does not permit table to occur within a
3373 ;; list item.
3375 ;; One solution - the easiest and lightweight, in terms of
3376 ;; implementation - is to put the table in an indented text box
3377 ;; and make the text box part of the list-item. Unfortunately if
3378 ;; the table is big and spans multiple pages, the text box could
3379 ;; overflow. In this case, the following attribute will come
3380 ;; handy.
3382 ;; ,---- From OpenDocument-v1.1.pdf
3383 ;; | 15.27.28 Overflow behavior
3384 ;; |
3385 ;; | For text boxes contained within text document, the
3386 ;; | style:overflow-behavior property specifies the behavior of text
3387 ;; | boxes where the containing text does not fit into the text
3388 ;; | box.
3389 ;; |
3390 ;; | If the attribute's value is clip, the text that does not fit
3391 ;; | into the text box is not displayed.
3392 ;; |
3393 ;; | If the attribute value is auto-create-new-frame, a new frame
3394 ;; | will be created on the next page, with the same position and
3395 ;; | dimensions of the original frame.
3396 ;; |
3397 ;; | If the style:overflow-behavior property's value is
3398 ;; | auto-create-new-frame and the text box has a minimum width or
3399 ;; | height specified, then the text box will grow until the page
3400 ;; | bounds are reached before a new frame is created.
3401 ;; `----
3403 ;; Unfortunately, LibreOffice-3.4.6 doesn't honor
3404 ;; auto-create-new-frame property and always resorts to clipping
3405 ;; the text box. This results in table being truncated.
3407 ;; So we solve the problem the hard (and fun) way using list
3408 ;; continuations.
3410 ;; The problem only becomes more interesting if you take in to
3411 ;; account the following facts:
3413 ;; - Description lists are simulated as plain lists.
3414 ;; - Low-level headlines can be listified.
3415 ;; - In Org-mode, a table can occur not only as a regular list
3416 ;; item, but also within description lists and low-level
3417 ;; headlines.
3419 ;; See `org-e-odt-translate-description-lists' and
3420 ;; `org-e-odt-translate-low-level-headlines' for how this is
3421 ;; tackled.
3423 (concat "\n"
3424 ;; Discontinue the list.
3425 (mapconcat 'car close-open-tags "\n")
3426 ;; Put the table in an indented section.
3427 (let* ((table (org-e-odt--table table contents info))
3428 (level (/ (length (mapcar 'car close-open-tags)) 2))
3429 (style (format "OrgIndentedSection-Level-%d" level)))
3430 (when table (org-e-odt-format-section table style)))
3431 ;; Continue the list.
3432 (mapconcat 'cdr (nreverse close-open-tags) "\n"))))
3435 ;;;; Target
3437 (defun org-e-odt-target (target contents info)
3438 "Transcode a TARGET object from Org to ODT.
3439 CONTENTS is nil. INFO is a plist holding contextual
3440 information."
3441 (let ((value (org-element-property :value target)))
3442 (org-e-odt--target "" (org-export-solidify-link-text value))))
3445 ;;;; Timestamp
3447 (defun org-e-odt-timestamp (timestamp contents info)
3448 "Transcode a TIMESTAMP object from Org to ODT.
3449 CONTENTS is nil. INFO is a plist used as a communication
3450 channel."
3451 (let ((timestamp-1 (org-element-property :value timestamp))
3452 (timestamp-2 (org-element-property :range-end timestamp)))
3453 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3454 "OrgTimestampWrapper"
3455 (concat
3456 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3457 "OrgTimestamp" (org-translate-time timestamp-1))
3458 (and timestamp-2
3459 "&#x2013;"
3460 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3461 "OrgTimestamp" (org-translate-time timestamp-2)))))))
3464 ;;;; Underline
3466 (defun org-e-odt-underline (underline contents info)
3467 "Transcode UNDERLINE from Org to ODT.
3468 CONTENTS is the text with underline markup. INFO is a plist
3469 holding contextual information."
3470 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3471 "Underline" contents))
3474 ;;;; Verbatim
3476 (defun org-e-odt-verbatim (verbatim contents info)
3477 "Transcode a VERBATIM object from Org to ODT.
3478 CONTENTS is nil. INFO is a plist used as a communication
3479 channel."
3480 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3481 "OrgCode" (org-element-property :value verbatim)))
3484 ;;;; Verse Block
3486 (defun org-e-odt-verse-block (verse-block contents info)
3487 "Transcode a VERSE-BLOCK element from Org to ODT.
3488 CONTENTS is verse block contents. INFO is a plist holding
3489 contextual information."
3490 ;; Add line breaks to each line of verse.
3491 (setq contents (replace-regexp-in-string
3492 "\\(<text:line-break/>\\)?[ \t]*\n"
3493 "<text:line-break/>" contents))
3494 ;; Replace tabs and spaces.
3495 (setq contents (org-e-odt-fill-tabs-and-spaces contents))
3496 ;; Surround it in a verse environment.
3497 (org-e-odt--wrap-label
3498 verse-block
3499 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3500 "OrgVerse" contents)))
3504 ;;; Filters
3506 ;;;; Description lists
3508 ;; This translator is necessary to handle indented tables in a uniform
3509 ;; manner. See comment in `org-e-odt--table'.
3511 (defun org-e-odt--translate-description-lists (tree backend info)
3512 ;; OpenDocument has no notion of a description list. So simulate it
3513 ;; using plain lists. Description lists in the exported document
3514 ;; are typeset in the same manner as they are in a typical HTML
3515 ;; document.
3517 ;; Specifically, a description list like this:
3519 ;; ,----
3520 ;; | - term-1 :: definition-1
3521 ;; | - term-2 :: definition-2
3522 ;; `----
3524 ;; gets translated in to the following form:
3526 ;; ,----
3527 ;; | - term-1
3528 ;; | - definition-1
3529 ;; | - term-2
3530 ;; | - definition-2
3531 ;; `----
3533 ;; Further effect is achieved by fixing the OD styles as below:
3535 ;; 1. Set the :type property of the simulated lists to
3536 ;; `descriptive-1' and `descriptive-2'. Map these to list-styles
3537 ;; that has *no* bullets whatsoever.
3539 ;; 2. The paragraph containing the definition term is styled to be
3540 ;; in bold.
3542 (org-element-map
3543 tree 'plain-list
3544 (lambda (el)
3545 (when (equal (org-element-property :type el) 'descriptive)
3546 (org-element-set-element
3548 (apply 'org-element-adopt-elements
3549 (list 'plain-list (list :type 'descriptive-1))
3550 (mapcar
3551 (lambda (item)
3552 (org-element-adopt-elements
3553 (list 'item (list :checkbox (org-element-property
3554 :checkbox item)))
3555 (list 'paragraph (list :style "Text_20_body_20_bold")
3556 (or (org-element-property :tag item) "(no term)"))
3557 (org-element-adopt-elements
3558 (list 'plain-list (list :type 'descriptive-2))
3559 (apply 'org-element-adopt-elements
3560 (list 'item nil)
3561 (org-element-contents item)))))
3562 (org-element-contents el)))))
3563 nil)
3564 info)
3565 tree)
3567 ;;;; List tables
3569 ;; Lists that are marked with attribute `:list-table' are called as
3570 ;; list tables. They will be rendered as a table within the exported
3571 ;; document.
3573 ;; Consider an example. The following list table
3575 ;; #+attr_odt :list-table t
3576 ;; - Row 1
3577 ;; - 1.1
3578 ;; - 1.2
3579 ;; - 1.3
3580 ;; - Row 2
3581 ;; - 2.1
3582 ;; - 2.2
3583 ;; - 2.3
3585 ;; will be exported as though it were an Org table like the one show
3586 ;; below.
3588 ;; | Row 1 | 1.1 | 1.2 | 1.3 |
3589 ;; | Row 2 | 2.1 | 2.2 | 2.3 |
3591 ;; Note that org-tables are NOT multi-line and each line is mapped to
3592 ;; a unique row in the exported document. So if an exported table
3593 ;; needs to contain a single paragraph (with copious text) it needs to
3594 ;; be typed up in a single line. Editing such long lines using the
3595 ;; table editor will be a cumbersome task. Furthermore inclusion of
3596 ;; multi-paragraph text in a table cell is well-nigh impossible.
3598 ;; A LIST-TABLE circumvents above problems.
3600 ;; Note that in the example above the list items could be paragraphs
3601 ;; themselves and the list can be arbitrarily deep.
3603 ;; Inspired by following thread:
3604 ;; https://lists.gnu.org/archive/html/emacs-orgmode/2011-03/msg01101.html
3606 ;; Translate lists to tables
3608 (defun org-e-odt--translate-list-tables (tree backend info)
3609 (org-element-map
3610 tree 'plain-list
3611 (lambda (l1-list)
3612 (when (org-export-read-attribute :attr_odt l1-list :list-table)
3613 ;; Replace list with table.
3614 (org-element-set-element
3615 l1-list
3616 ;; Build replacement table.
3617 (apply 'org-element-adopt-elements
3618 (list 'table '(:type org :attr_odt (":style \"GriddedTable\"")))
3619 (org-element-map
3620 l1-list
3621 'item
3622 (lambda (l1-item)
3623 (let* ((l1-item-contents (org-element-contents l1-item))
3624 l1-item-leading-text l2-list)
3625 ;; Remove Level-2 list from the Level-item. It
3626 ;; will be subsequently attached as table-cells.
3627 (let ((cur l1-item-contents) prev)
3628 (while (and cur (not (eq (org-element-type (car cur))
3629 'plain-list)))
3630 (setq prev cur)
3631 (setq cur (cdr cur)))
3632 (when prev
3633 (setcdr prev nil)
3634 (setq l2-list (car cur)))
3635 (setq l1-item-leading-text l1-item-contents))
3636 ;; Level-1 items start a table row.
3637 (apply 'org-element-adopt-elements
3638 (list 'table-row (list :type 'standard))
3639 ;; Leading text of level-1 item define the
3640 ;; first table-cell.
3641 (apply 'org-element-adopt-elements
3642 (list 'table-cell nil)
3643 l1-item-leading-text)
3644 ;; Level-2 items define subsequent
3645 ;; table-cells of the row.
3646 (org-element-map
3647 l2-list
3648 'item
3649 (lambda (l2-item)
3650 (apply 'org-element-adopt-elements
3651 (list 'table-cell nil)
3652 (org-element-contents l2-item)))
3653 info nil 'item))))
3654 info nil 'item))))
3655 nil)
3656 info)
3657 tree)
3660 ;;; Interactive functions
3662 (defun org-e-odt-create-manifest-file-entry (&rest args)
3663 (push args org-e-odt-manifest-file-entries))
3665 (defun org-e-odt-write-manifest-file ()
3666 (make-directory (concat org-e-odt-zip-dir "META-INF"))
3667 (let ((manifest-file (concat org-e-odt-zip-dir "META-INF/manifest.xml")))
3668 (with-current-buffer
3669 (let ((nxml-auto-insert-xml-declaration-flag nil))
3670 (find-file-noselect manifest-file t))
3671 (insert
3672 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
3673 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
3674 (mapc
3675 (lambda (file-entry)
3676 (let* ((version (nth 2 file-entry))
3677 (extra (if (not version) ""
3678 (format " manifest:version=\"%s\"" version))))
3679 (insert
3680 (format org-e-odt-manifest-file-entry-tag
3681 (nth 0 file-entry) (nth 1 file-entry) extra))))
3682 org-e-odt-manifest-file-entries)
3683 (insert "\n</manifest:manifest>"))))
3685 (defmacro org-e-odt--export-wrap (out-file &rest body)
3686 `(let* ((--out-file ,out-file)
3687 (out-file-type (file-name-extension --out-file))
3688 (org-e-odt-xml-files '("META-INF/manifest.xml" "content.xml"
3689 "meta.xml" "styles.xml"))
3690 ;; Initialize workarea. All files that end up in the
3691 ;; exported get created here.
3692 (org-e-odt-zip-dir (file-name-as-directory
3693 (make-temp-file (format "%s-" out-file-type) t)))
3694 (org-e-odt-manifest-file-entries nil)
3695 (--cleanup-xml-buffers
3696 (function
3697 (lambda nil
3698 ;; Kill all XML buffers.
3699 (mapc (lambda (file)
3700 (let ((buf (get-file-buffer
3701 (concat org-e-odt-zip-dir file))))
3702 (when buf
3703 (set-buffer-modified-p nil)
3704 (kill-buffer buf))))
3705 org-e-odt-xml-files)
3706 ;; Delete temporary directory and also other embedded
3707 ;; files that get copied there.
3708 (delete-directory org-e-odt-zip-dir t)))))
3709 (org-condition-case-unless-debug err
3710 (progn
3711 (unless (executable-find "zip")
3712 ;; Not at all OSes ship with zip by default
3713 (error "Executable \"zip\" needed for creating OpenDocument files"))
3714 ;; Do export. This creates a bunch of xml files ready to be
3715 ;; saved and zipped.
3716 (progn ,@body)
3717 ;; Create a manifest entry for content.xml.
3718 (org-e-odt-create-manifest-file-entry "text/xml" "content.xml")
3719 ;; Write mimetype file
3720 (let* ((mimetypes
3721 '(("odt" . "application/vnd.oasis.opendocument.text")
3722 ("odf" . "application/vnd.oasis.opendocument.formula")))
3723 (mimetype (cdr (assoc-string out-file-type mimetypes t))))
3724 (unless mimetype
3725 (error "Unknown OpenDocument backend %S" out-file-type))
3726 (write-region mimetype nil (concat org-e-odt-zip-dir "mimetype"))
3727 (org-e-odt-create-manifest-file-entry mimetype "/" "1.2"))
3728 ;; Write out the manifest entries before zipping
3729 (org-e-odt-write-manifest-file)
3730 ;; Save all XML files.
3731 (mapc (lambda (file)
3732 (let ((buf (get-file-buffer (concat org-e-odt-zip-dir file))))
3733 (when buf
3734 (with-current-buffer buf
3735 ;; Prettify output if needed.
3736 (when org-e-odt-prettify-xml
3737 (indent-region (point-min) (point-max)))
3738 (save-buffer 0)))))
3739 org-e-odt-xml-files)
3740 ;; Run zip.
3741 (let* ((target --out-file)
3742 (target-name (file-name-nondirectory target))
3743 (target-dir (file-name-directory target))
3744 (cmds `(("zip" "-mX0" ,target-name "mimetype")
3745 ("zip" "-rmTq" ,target-name "."))))
3746 ;; If a file with same name as the desired output file
3747 ;; exists, remove it.
3748 (when (file-exists-p target)
3749 (delete-file target))
3750 ;; Zip up the xml files.
3751 (let ((coding-system-for-write 'no-conversion) exitcode err-string)
3752 (message "Creating ODT file...")
3753 ;; Switch temporarily to content.xml. This way Zip
3754 ;; process will inherit `org-e-odt-zip-dir' as the current
3755 ;; directory.
3756 (with-current-buffer
3757 (find-file-noselect (concat org-e-odt-zip-dir "content.xml") t)
3758 (mapc
3759 (lambda (cmd)
3760 (message "Running %s" (mapconcat 'identity cmd " "))
3761 (setq err-string
3762 (with-output-to-string
3763 (setq exitcode
3764 (apply 'call-process (car cmd)
3765 nil standard-output nil (cdr cmd)))))
3766 (or (zerop exitcode)
3767 (error (concat "Unable to create OpenDocument file."
3768 (format " Zip failed with error (%s)"
3769 err-string)))))
3770 cmds)
3771 ;; Zip file is now in the rightful place.
3772 (rename-file target-name target)))
3773 (message "Created %s" target)
3774 ;; Cleanup work directory and work files.
3775 (funcall --cleanup-xml-buffers)
3776 ;; Open the OpenDocument file in archive-mode for
3777 ;; examination.
3778 (find-file-noselect target t)
3779 ;; Return exported file.
3780 (cond
3781 ;; Case 1: Conversion desired on exported file. Run the
3782 ;; converter on the OpenDocument file. Return the
3783 ;; converted file.
3784 (org-e-odt-preferred-output-format
3785 (or (org-e-odt-convert target org-e-odt-preferred-output-format)
3786 target))
3787 ;; Case 2: No further conversion. Return exported
3788 ;; OpenDocument file.
3789 (t target))))
3790 (error
3791 ;; Cleanup work directory and work files.
3792 (funcall --cleanup-xml-buffers)
3793 (message "OpenDocument export failed: %s"
3794 (error-message-string err))))))
3797 ;;;; Export to OpenDocument formula
3799 ;;;###autoload
3800 (defun org-e-odt-export-as-odf (latex-frag &optional odf-file)
3801 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
3802 Use `org-create-math-formula' to convert LATEX-FRAG first to
3803 MathML. When invoked as an interactive command, use
3804 `org-latex-regexps' to infer LATEX-FRAG from currently active
3805 region. If no LaTeX fragments are found, prompt for it. Push
3806 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
3807 non-nil."
3808 (interactive
3809 `(,(let (frag)
3810 (setq frag (and (setq frag (and (region-active-p)
3811 (buffer-substring (region-beginning)
3812 (region-end))))
3813 (loop for e in org-latex-regexps
3814 thereis (when (string-match (nth 1 e) frag)
3815 (match-string (nth 2 e) frag)))))
3816 (read-string "LaTeX Fragment: " frag nil frag))
3817 ,(let ((odf-filename (expand-file-name
3818 (concat
3819 (file-name-sans-extension
3820 (or (file-name-nondirectory buffer-file-name)))
3821 "." "odf")
3822 (file-name-directory buffer-file-name))))
3823 (read-file-name "ODF filename: " nil odf-filename nil
3824 (file-name-nondirectory odf-filename)))))
3825 (let ((filename (or odf-file
3826 (expand-file-name
3827 (concat
3828 (file-name-sans-extension
3829 (or (file-name-nondirectory buffer-file-name)))
3830 "." "odf")
3831 (file-name-directory buffer-file-name)))))
3832 (org-e-odt--export-wrap
3833 filename
3834 (let* ((buffer (progn
3835 (require 'nxml-mode)
3836 (let ((nxml-auto-insert-xml-declaration-flag nil))
3837 (find-file-noselect (concat org-e-odt-zip-dir
3838 "content.xml") t))))
3839 (coding-system-for-write 'utf-8)
3840 (save-buffer-coding-system 'utf-8))
3841 (set-buffer buffer)
3842 (set-buffer-file-coding-system coding-system-for-write)
3843 (let ((mathml (org-create-math-formula latex-frag)))
3844 (unless mathml (error "No Math formula created"))
3845 (insert mathml)
3846 ;; Add MathML to kill ring, if needed.
3847 (when org-export-copy-to-kill-ring
3848 (org-kill-new (buffer-string))))))))
3850 ;;;###autoload
3851 (defun org-e-odt-export-as-odf-and-open ()
3852 "Export LaTeX fragment as OpenDocument formula and immediately open it.
3853 Use `org-e-odt-export-as-odf' to read LaTeX fragment and OpenDocument
3854 formula file."
3855 (interactive)
3856 (org-open-file (call-interactively 'org-e-odt-export-as-odf) 'system))
3859 ;;;; Export to OpenDocument Text
3861 ;;;###autoload
3862 (defun org-e-odt-export-to-odt
3863 (&optional subtreep visible-only body-only ext-plist pub-dir)
3864 "Export current buffer to a HTML file.
3866 If narrowing is active in the current buffer, only export its
3867 narrowed part.
3869 If a region is active, export that region.
3871 When optional argument SUBTREEP is non-nil, export the sub-tree
3872 at point, extracting information from the headline properties
3873 first.
3875 When optional argument VISIBLE-ONLY is non-nil, don't export
3876 contents of hidden elements.
3878 When optional argument BODY-ONLY is non-nil, only write code
3879 between \"\\begin{document}\" and \"\\end{document}\".
3881 EXT-PLIST, when provided, is a property list with external
3882 parameters overriding Org default settings, but still inferior to
3883 file-local settings.
3885 When optional argument PUB-DIR is set, use it as the publishing
3886 directory.
3888 Return output file's name."
3889 (interactive)
3890 (org-e-odt--export-wrap
3891 (org-export-output-file-name ".odt" subtreep pub-dir)
3892 (let* ((org-e-odt-embedded-images-count 0)
3893 (org-e-odt-embedded-formulas-count 0)
3894 (org-e-odt-automatic-styles nil)
3895 (org-e-odt-object-counters nil)
3896 ;; Let `htmlfontify' know that we are interested in collecting
3897 ;; styles.
3898 (hfy-user-sheet-assoc nil))
3899 ;; Initialize content.xml and kick-off the export process.
3900 (let ((out-buf (progn
3901 (require 'nxml-mode)
3902 (let ((nxml-auto-insert-xml-declaration-flag nil))
3903 (find-file-noselect
3904 (concat org-e-odt-zip-dir "content.xml") t)))))
3905 (org-export-to-buffer 'e-odt out-buf subtreep visible-only body-only)))))
3908 ;;;; Convert between OpenDocument and other formats
3910 (defun org-e-odt-reachable-p (in-fmt out-fmt)
3911 "Return non-nil if IN-FMT can be converted to OUT-FMT."
3912 (catch 'done
3913 (let ((reachable-formats (org-e-odt-do-reachable-formats in-fmt)))
3914 (dolist (e reachable-formats)
3915 (let ((out-fmt-spec (assoc out-fmt (cdr e))))
3916 (when out-fmt-spec
3917 (throw 'done (cons (car e) out-fmt-spec))))))))
3919 (defun org-e-odt-do-convert (in-file out-fmt &optional prefix-arg)
3920 "Workhorse routine for `org-e-odt-convert'."
3921 (require 'browse-url)
3922 (let* ((in-file (expand-file-name (or in-file buffer-file-name)))
3923 (dummy (or (file-readable-p in-file)
3924 (error "Cannot read %s" in-file)))
3925 (in-fmt (file-name-extension in-file))
3926 (out-fmt (or out-fmt (error "Output format unspecified")))
3927 (how (or (org-e-odt-reachable-p in-fmt out-fmt)
3928 (error "Cannot convert from %s format to %s format?"
3929 in-fmt out-fmt)))
3930 (convert-process (car how))
3931 (out-file (concat (file-name-sans-extension in-file) "."
3932 (nth 1 (or (cdr how) out-fmt))))
3933 (extra-options (or (nth 2 (cdr how)) ""))
3934 (out-dir (file-name-directory in-file))
3935 (cmd (format-spec convert-process
3936 `((?i . ,(shell-quote-argument in-file))
3937 (?I . ,(browse-url-file-url in-file))
3938 (?f . ,out-fmt)
3939 (?o . ,out-file)
3940 (?O . ,(browse-url-file-url out-file))
3941 (?d . , (shell-quote-argument out-dir))
3942 (?D . ,(browse-url-file-url out-dir))
3943 (?x . ,extra-options)))))
3944 (when (file-exists-p out-file)
3945 (delete-file out-file))
3947 (message "Executing %s" cmd)
3948 (let ((cmd-output (shell-command-to-string cmd)))
3949 (message "%s" cmd-output))
3951 (cond
3952 ((file-exists-p out-file)
3953 (message "Exported to %s" out-file)
3954 (when prefix-arg
3955 (message "Opening %s..." out-file)
3956 (org-open-file out-file 'system))
3957 out-file)
3959 (message "Export to %s failed" out-file)
3960 nil))))
3962 (defun org-e-odt-do-reachable-formats (in-fmt)
3963 "Return verbose info about formats to which IN-FMT can be converted.
3964 Return a list where each element is of the
3965 form (CONVERTER-PROCESS . OUTPUT-FMT-ALIST). See
3966 `org-e-odt-convert-processes' for CONVERTER-PROCESS and see
3967 `org-e-odt-convert-capabilities' for OUTPUT-FMT-ALIST."
3968 (let* ((converter
3969 (and org-e-odt-convert-process
3970 (cadr (assoc-string org-e-odt-convert-process
3971 org-e-odt-convert-processes t))))
3972 (capabilities
3973 (and org-e-odt-convert-process
3974 (cadr (assoc-string org-e-odt-convert-process
3975 org-e-odt-convert-processes t))
3976 org-e-odt-convert-capabilities))
3977 reachable-formats)
3978 (when converter
3979 (dolist (c capabilities)
3980 (when (member in-fmt (nth 1 c))
3981 (push (cons converter (nth 2 c)) reachable-formats))))
3982 reachable-formats))
3984 (defun org-e-odt-reachable-formats (in-fmt)
3985 "Return list of formats to which IN-FMT can be converted.
3986 The list of the form (OUTPUT-FMT-1 OUTPUT-FMT-2 ...)."
3987 (let (l)
3988 (mapc (lambda (e) (add-to-list 'l e))
3989 (apply 'append (mapcar
3990 (lambda (e) (mapcar 'car (cdr e)))
3991 (org-e-odt-do-reachable-formats in-fmt))))
3994 (defun org-e-odt-convert-read-params ()
3995 "Return IN-FILE and OUT-FMT params for `org-e-odt-do-convert'.
3996 This is a helper routine for interactive use."
3997 (let* ((input (if (featurep 'ido) 'ido-completing-read 'completing-read))
3998 (in-file (read-file-name "File to be converted: "
3999 nil buffer-file-name t))
4000 (in-fmt (file-name-extension in-file))
4001 (out-fmt-choices (org-e-odt-reachable-formats in-fmt))
4002 (out-fmt
4003 (or (and out-fmt-choices
4004 (funcall input "Output format: "
4005 out-fmt-choices nil nil nil))
4006 (error
4007 "No known converter or no known output formats for %s files"
4008 in-fmt))))
4009 (list in-file out-fmt)))
4011 ;;;###autoload
4012 (defun org-e-odt-convert (&optional in-file out-fmt prefix-arg)
4013 "Convert IN-FILE to format OUT-FMT using a command line converter.
4014 IN-FILE is the file to be converted. If unspecified, it defaults
4015 to variable `buffer-file-name'. OUT-FMT is the desired output
4016 format. Use `org-e-odt-convert-process' as the converter.
4017 If PREFIX-ARG is non-nil then the newly converted file is opened
4018 using `org-open-file'."
4019 (interactive
4020 (append (org-e-odt-convert-read-params) current-prefix-arg))
4021 (org-e-odt-do-convert in-file out-fmt prefix-arg))
4023 ;;; Library Initializations
4025 (mapc
4026 (lambda (desc)
4027 ;; Let Emacs open all OpenDocument files in archive mode
4028 (add-to-list 'auto-mode-alist
4029 (cons (concat "\\." (car desc) "\\'") 'archive-mode)))
4030 org-e-odt-file-extensions)
4032 (provide 'org-e-odt)
4034 ;;; org-e-odt.el ends here