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/>.
31 (require 'format-spec
)
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
))
87 :filters-alist
((:filter-parse-tree
88 .
(org-e-odt--translate-description-lists
89 org-e-odt--translate-list-tables
)))
92 ((?o
"As ODT file" org-e-odt-export-to-odt
)
93 (?O
"As ODT file and open"
95 (org-open-file (org-e-odt-export-to-odt s v b
) 'system
)))))
97 ((:odt-styles-file
"ODT_STYLES_FILE" nil nil t
)
98 (:LaTeX-fragments nil
"LaTeX" org-export-with-LaTeX-fragments
)))
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
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 '(("\\\\-" .
"­\\1") ; shy
133 ("---\\([^-]\\)" .
"—\\1") ; mdash
134 ("--\\([^-]\\)" .
"–\\1") ; ndash
135 ("\\.\\.\\." .
"…")) ; hellip
136 "Regular expressions for special string conversion.")
138 (defconst org-e-odt-schema-dir-list
140 (and org-e-odt-data-dir
141 (expand-file-name "./schema/" org-e-odt-data-dir
)) ; bail out
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
153 (and org-e-odt-data-dir
154 (expand-file-name "./styles/" org-e-odt-data-dir
)) ; bail out
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
170 (message "Debug (org-e-odt): Searching for OpenDocument styles files...")
171 (mapc (lambda (styles-dir)
173 (message "Debug (org-e-odt): Trying %s..." styles-dir
)
174 (when (and (file-readable-p
176 "OrgOdtContentTemplate.xml" styles-dir
))
179 "OrgOdtStyles.xml" styles-dir
)))
180 (message "Debug (org-e-odt): Using styles under %s"
182 (throw 'styles-dir styles-dir
))))
183 org-e-odt-styles-dir-list
)
186 (error "Error (org-e-odt): Cannot find factory styles files. Aborting."))
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
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\"/>
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:
233 \(\(OBJECT-NAME-A.1 OBJECT-PROPS-A.1\)
234 \(OBJECT-NAME-A.2 OBJECT-PROPS-A.2\) ...\)\)
236 \(\(OBJECT-NAME-B.1 OBJECT-PROPS-B.1\)
237 \(OBJECT-NAME-B.2 OBJECT-PROPS-B.2\) ...\)\)
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\"/>
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__"
271 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
272 ("OrgInlineImage" nil
"as-char"))
273 ("CaptionedParagraphImage" "__Figure__"
275 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
276 ("OrgImageCaptionFrame" nil
"paragraph"))
277 ("CaptionedPageImage" "__Figure__"
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
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
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
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
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"
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
384 :group
'org-export-e-odt
391 (defcustom org-e-odt-schema-dir
394 (message "Debug (org-e-odt): Searching for OpenDocument schema files...")
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"
403 (expand-file-name "od-schema-v1.2-cs01.rnc"
406 (expand-file-name "schemas.xml" schema-dir
)))
407 (message "Debug (org-e-odt): Using schema files under %s"
409 (throw 'schema-dir schema-dir
))))
410 org-e-odt-schema-dir-list
)
411 (message "Debug (org-e-odt): No OpenDocument schema files installed")
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."
432 (const :tag
"Not set" nil
)
433 (directory :tag
"Schema directory"))
434 :group
'org-export-e-odt
438 "Set `org-e-odt-schema-dir'.
439 Also add it to `rng-schema-locating-files'."
440 (let ((schema-dir value
))
444 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir
))
446 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir
))
448 (expand-file-name "schemas.xml" schema-dir
)))
451 (message "Error (org-e-odt): %s has no OpenDocument schema files"
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
))))))
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."
471 :group
'org-export-e-odt
474 (defcustom org-e-odt-styles-file nil
475 "Default styles file for use with ODT export.
476 Valid values are one of:
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
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
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
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")
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
524 ;;;; Document conversion
526 (defcustom org-e-odt-convert-processes
528 "soffice --headless --convert-to %f%x --outdir %d %i")
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
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
564 :type
'(choice :convert-widget
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
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")))
580 (("pdf" "pdf") ("odt" "odt") ("html" "html")))
582 ("ods" "ots" "xls" "csv" "xlsx")
583 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
584 ("xls" "xls") ("xlsx" "xlsx")))
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
623 See default setting of this variable for an typical
625 :group
'org-export-e-odt
629 (const :tag
"None" nil
)
630 (alist :tag
"Capabilities"
631 :key-type
(string :tag
"Document Class")
633 (group (repeat :tag
"Input formats" (string :tag
"Input format"))
634 (alist :tag
"Output formats"
635 :key-type
(string :tag
"Output format")
637 (group (string :tag
"Output file extension")
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
654 :type
'(choice :convert-widget
656 (apply 'widget-convert
(widget-type w
)
657 (eval (car (widget-get w
:args
)))))
658 `((const :tag
"None" nil
)
659 ,@(mapcar (lambda (c)
661 (org-e-odt-reachable-formats "odt")))))
663 (put 'org-e-odt-preferred-output-format
'safe-local-variable
'stringp
)
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.\"
683 :group
'org-export-e-odt
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.\"
707 \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo\)\)
709 \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
711 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)"
712 :group
'org-export-e-odt
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.\"
739 \(format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo\)\)
740 \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\)
742 \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)\)
743 \(format \(concat \"\\\\begin{center}\\n\"
745 \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\"
747 \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\"
749 \"\\\\end{minipage}}\"
750 \"\\\\end{center}\"\)
751 full-title contents\)\)"
752 :group
'org-export-e-odt
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
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."
780 :group
'org-export-e-odt
786 (defcustom org-e-odt-quotes
788 ("\\(\\s-\\|[[(]\\|^\\)\"" .
"« ")
789 ("\\(\\S-\\)\"" .
"» ")
790 ("\\(\\s-\\|(\\|^\\)'" .
"'"))
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
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 "))))
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
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
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."
847 :group
'org-export-e-odt
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
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 +
884 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
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\"
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\"
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
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
949 use-first-column-styles
950 use-last-column-styles
951 use-banding-rows-styles
952 use-banding-columns-styles
)
954 :value-type
(const :tag
"True" t
))))))
958 ;;; Internal functions
962 (defun org-e-odt--date (&optional org-ts fmt
)
965 (and (stringp org-ts
)
966 (string-match org-ts-regexp0 org-ts
)
968 (org-fix-decoded-time
969 (org-parse-time-string (match-string 0 org-ts
) t
)))))
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)))))))
978 (defun org-e-odt--frame (text width height style
&optional extra
982 (if width
(format " svg:width=\"%0.2fcm\"" width
) "")
983 (if height
(format " svg:height=\"%0.2fcm\"" height
) "")
985 (format " text:anchor-type=\"%s\"" (or anchor-type
"paragraph")))))
987 "\n<draw:frame draw:style-name=\"%s\"%s>\n%s\n</draw:frame>"
990 (let ((title (get-text-property 0 :title text
))
991 (desc (get-text-property 0 :description text
)))
993 (format "<svg:title>%s</svg:title>"
994 (org-e-odt-encode-plain-text title t
)))
996 (format "<svg:desc>%s</svg:desc>"
997 (org-e-odt-encode-plain-text desc t
)))))))))
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/")
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
)
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
)))))
1030 (lambda (size-in-pixels dpi
)
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
))
1040 (not (and user-height user-width
))
1043 (and (executable-find "identify")
1044 (let ((size-in-pixels
1045 (let ((dim (shell-command-to-string
1046 (format "identify -format \"%%w:%%h\" \"%s\""
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
)))
1053 (let ((size-in-pixels
1054 (ignore-errors ; Emacs could be in batch mode
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
))
1062 (error "Cannot determine Image size. Aborting ..."))))
1063 (width (car size
)) (height (cdr size
)))
1066 (setq width
(* width scale
) height
(* height scale
)))
1067 ((and user-height user-width
)
1068 (setq width user-width height user-height
))
1070 (setq width
(* user-height
(/ width height
)) height user-height
))
1072 (setq height
(* user-width
(/ height width
)) width user-width
))
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."
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
))
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
)))
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
)))))
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
))
1117 ;; convert attributes to a plist.
1118 (attr-plist (org-export-read-attribute :attr_odt attr-from
))
1119 ;; handle `:anchor', `:style' and `:attributes' properties.
1121 (car (assoc-string (plist-get attr-plist
:anchor
)
1122 '(("as-char") ("paragraph") ("page")) t
)))
1124 (and user-frame-anchor
(plist-get attr-plist
:style
)))
1126 (and user-frame-anchor
(plist-get attr-plist
:attributes
)))
1128 (list user-frame-style user-frame-attrs user-frame-anchor
))
1129 ;; (embed-as (or embed-as user-frame-anchor "paragraph"))
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
1138 (width (car size
)) (height (cdr size
))
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")
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
)
1157 (let* ((--quote-file-name
1158 ;; This is shamelessly stolen from `archive-zip-extract'.
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
)
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
1172 (setq exit-code
(archive-zip-extract archive member
))
1174 (unless (zerop exit-code
)
1175 (message command-output
)
1176 (error "Extraction failed"))))
1182 (defun org-e-odt--target (text id
)
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
))))
1191 (defun org-e-odt--textbox (text width height style
&optional
1194 (format "\n<draw:text-box %s>%s\n</draw:text-box>"
1195 (concat (format " fo:min-height=\"%0.2fcm\"" (or height
.2))
1197 (format " fo:min-width=\"%0.2fcm\"" (or width
.2))))
1199 width nil style extra anchor-type
))
1203 ;;;; Table of Contents
1205 (defun org-e-odt-begin-toc (index-title depth
)
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)))
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
""))
1227 </text:table-of-content-source>
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>
1235 (defun org-e-odt-end-toc ()
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
)
1247 (and org-export-with-section-numbers
1248 (concat section-number
". "))
1253 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1256 (setq text
(format "<text:span text:style-name=\"%s\">%s</text:span>"
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
))))
1268 (org-e-odt-begin-toc title depth
)
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>"
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
1293 (assert (stringp object-type
))
1294 (let* ((object (intern object-type
))
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
))
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 ""))
1319 ;; (concat (format "\\label{%s}\n" label) 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
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
) "")))
1338 ((and (not caption
) (not label
)) "")
1339 ((not caption
) (format "\\label{%s}\n" label
))
1340 ;; Option caption format with short name.
1342 (format "\\caption[%s]{%s%s}\n"
1343 (org-export-data (cdr caption
) info
)
1345 (org-export-data (car caption
) info
)))
1346 ;; Standard caption format.
1347 ;; (t (format "\\caption{%s%s}\n"
1349 ;; (org-export-data (car caption) info)))
1350 (t (org-export-data (car caption
) info
)))))
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 "[✓] ") ; CHECK MARK
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."
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
)))
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\">
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
)
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
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?"))))
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
)
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
))))
1432 ((and (stringp styles-file
) (file-exists-p styles-file
))
1433 (let ((styles-file-type (file-name-extension styles-file
)))
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
)
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
"")))
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))
1481 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
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
))))
1490 ;; Update content.xml.
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.
1515 "\n<text:sequence-decls>\n%s\n</text:sequence-decls>"
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.
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
)))
1545 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1546 "OrgTitle" (format "\n<text:title>%s</text:title>" title
))
1548 "\n<text:p text:style-name=\"OrgTitle\"/>"))
1550 ((and author
(not email
))
1553 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1555 (format "<text:initial-creator>%s</text:initial-creator>" author
))
1557 "\n<text:p text:style-name=\"OrgSubtitle\"/>"))
1562 "\n<text:p text:style-name=\"%s\">%s</text:p>"
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
)))
1569 "\n<text:p text:style-name=\"OrgSubtitle\"/>")))
1574 "\n<text:p text:style-name=\"%s\">%s</text:p>"
1577 "\n<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">%s</text:date>"
1579 "OrgDate" iso-date date
))
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
)
1586 (plist-get info
:headline-levels
)))))
1587 (when depth
(insert (or (org-e-odt-toc depth info
) ""))))
1591 (buffer-substring-no-properties (point-min) (point-max))))
1595 ;;; Transcode Functions
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>"
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
))
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
1622 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1623 "OrgTimestampWrapper"
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>"
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
))))))))
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
1641 (format "<text:span text:style-name=\"%s\">%s</text:span>"
1642 "OrgCode" (org-element-property :value code
)))
1647 ;; Comments are ignored.
1652 ;; Comment Blocks are ignored.
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
1665 ;; If there's no user defined function: simply
1666 ;; display contents of the drawer.
1668 (org-e-odt--wrap-label drawer output
)))
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
))
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)
1690 (org-element-property :utf-8 entity
))
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
)))
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
)))
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
))))
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
1743 (setq n
(format "%d" n
))
1744 (let ((id (concat "fn" n
))
1745 (note-class "footnote")
1746 (par-style "Footnote"))
1748 "<text:note text:id=\"%s\" text:note-class=\"%s\">%s</text:note>"
1751 (format "<text:note-citation>%s</text:note-citation>" n
)
1752 (format "<text:note-body>%s</text:note-body>" def
)))))))
1753 (--format-footnote-reference
1756 (setq n
(format "%d" n
))
1757 (let ((note-class "footnote")
1759 (ref-name (concat "fn" n
)))
1761 "<text:span text:style-name=\"%s\">%s</text:span>"
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
)))))))
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
)))
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
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>"
1785 (funcall --format-footnote-definition n def
))))))))
1790 (defun* org-e-odt-format-headline
1791 (todo todo-type priority text tags
1792 &key level section-number headline-label
&allow-other-keys
)
1797 (let ((style (if (member todo org-done-keywords
) "OrgDone" "OrgTodo")))
1798 (format "<text:span text:style-name=\"%s\">%s</text:span>"
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
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
)
1834 (lambda (todo todo-type priority text tags
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
)))
1864 (mapconcat (lambda (x)
1866 (let ((x (if (org-uuidgen-p x
) (concat "ID-" x
) x
)))
1868 "" (org-export-solidify-link-text x
)))))
1871 (anchored-title (org-e-odt--target full-text id
)))
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.
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
1890 (org-export-low-level-p parent info
))
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"
1897 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1899 (concat extra-targets anchored-title
))
1901 (if headline-has-table-p
1902 "</text:list-header>"
1903 "</text:list-item>")))
1904 (and (org-export-last-sibling-p headline info
)
1906 ;; Case 3. Standard headline. Export it as a section.
1910 "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\">%s</text:h>"
1911 (format "Heading_20_%s" level
)
1913 (concat extra-targets anchored-title
))
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
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
)))
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."
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
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
1974 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1978 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
1979 "OrgInlineTaskHeading"
1980 (org-e-odt-format-headline--wrap
1983 nil nil
"OrgInlineTaskFrame" " style:rel-width=\"100%\""))))))
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
))
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
)))
2006 (concat (org-e-odt--checkbox item
)
2007 (org-export-data tag info
))))))
2009 ((ordered unordered descriptive-1 descriptive-2
)
2010 (format "\n<text:list-item>\n%s\n%s"
2012 (let* ((--element-has-a-table-p
2014 (lambda (element info
)
2015 (loop for el in
(org-element-contents element
)
2016 thereis
(eq (org-element-type el
) 'table
))))))
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
)))))
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
)))
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
)))
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
)
2048 ;; At the moment, src blocks with a caption are wrapped
2049 ;; into a figure environment.
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
2072 (mathml "ltxmathml/")))
2073 (input-file (plist-get info
:input-file
))
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..."))))
2083 (org-format-latex cache-subdir cache-dir nil display-msg
2084 nil nil processing-type
)
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
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
))
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
2118 (org-e-odt-format-formula latex-environment info
))
2120 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2122 (org-e-odt-link--inline-image latex-environment info
)))
2123 (t (org-e-odt-do-format-code latex-frag
))))))
2129 ;; (when latex-frag ; FIXME
2130 ;; (setq href (org-propertize href :title "LaTeX Fragment"
2131 ;; :description latex-frag)))
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
)))
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
)))))
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")
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>"
2166 ;;;; Links :: Label references
2168 (defun org-e-odt-enumerate-element (element info
&optional predicate n
)
2169 (let* ((--numbered-parent-headline-at-<=-n
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
)
2179 (lambda (element scope info
&optional predicate
)
2182 (or scope
(plist-get info
:parse-tree
))
2183 (org-element-type element
)
2185 (and (or (not predicate
) (funcall predicate el info
))
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
))
2197 (mapconcat 'number-to-string
2198 (org-export-get-headline-number scope info
) "."))
2202 (number-to-string ordinal
))))
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
))
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
2221 ((eq (org-element-type element
) 'table
)
2223 ((org-e-odt-standalone-image-p element info
)
2225 ((member (org-element-type element
)
2226 '(latex-environment latex-fragment
))
2227 (let ((processing-type (plist-get info
:LaTeX-fragments
)))
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
)
2235 (t (error "Handle enumeration of %S" element
))))
2238 ((member (org-element-type element
)
2239 '(table latex-environment src-block
))
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
)))
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
))
2262 (cadr (assoc-string label-style org-e-odt-label-styles t
))
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
""))))
2271 (setq label
(org-export-solidify-link-text label
))
2272 (let* ((fmt (cddr (assoc-string label-style org-e-odt-label-styles t
)))
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
)
2278 (t (error "Unknow %S on label" op
)))))))
2280 ;;;; Links :: Math formula
2282 (defun org-e-odt-format-formula (element info
)
2284 ((eq (org-element-type element
) 'link
) ; FIXME
2285 (let* ((type (org-element-property :type element
))
2286 (raw-path (org-element-property :path element
)))
2288 ((file-name-absolute-p raw-path
)
2289 (expand-file-name 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
)))
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
)))))
2305 (case (org-element-type element
)
2306 (link (org-export-get-parent-element element
))
2308 (captions (org-e-odt-format-label caption-from info
'definition
))
2309 (caption (car captions
))
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
))
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
))
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
))))
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
))))))
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
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
2361 (message "Embedding %s as %s ..." src-file target-file
)
2362 (let ((case-fold-search nil
))
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
)
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
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
)))))))
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>"
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
)
2411 (cadr entity-style-1
)
2413 (format " draw:name=\"%s\" "
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
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
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
)
2447 (link (and (org-export-inline-image-p
2448 element org-e-odt-inline-image-rules
)
2449 (org-export-get-parent element
)))
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
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)))
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
))))
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
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
))
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
)))
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
)))
2511 (let ((desc (org-export-data (org-element-contents destination
) info
))
2512 (href (org-export-solidify-link-text path
)))
2514 "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
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.
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.
2531 ;; LINK points to an headline. Check if LINK should display
2534 (let* ((headline-no (org-export-get-headline-number destination info
))
2535 (label (format "sec-%s" (mapconcat 'number-to-string
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
))))
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
)
2555 "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
2557 ;; Fuzzy link points to a target. Do as above.
2559 ;; Identify nearest meaningful container
2561 (loop for parent in
(org-export-get-genealogy destination
)
2564 (org-element-type parent
)
2565 '(footnote-definition footnote-reference headline item
2568 ;; There is a meaningful container
2570 (case (org-element-type container
)
2571 ;; Container is item
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
) ".")))))))
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
)))
2602 (org-export-get-coderef-format path desc
)
2604 "<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
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.
2611 (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
2613 ;; External link without a description part.
2615 (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
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
)))))
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
)
2636 ;; If this paragraph is a leading paragraph in an item and the
2637 ;; item has a checkbox, splice the checkbox and paragraph contents
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
)))
2643 (format "\n<text:p text:style-name=\"%s\">%s</text:p>" style contents
)))
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
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
))
2671 (defun org-e-odt-fill-tabs-and-spaces (line)
2672 (replace-regexp-in-string
2673 "\\([\t]\\|\\([ ]+\\)\\)"
2676 ((string= s
"\t") "<text:tab/>")
2677 (t (let ((n (length s
)))
2680 ((> n
1) (concat " " (format "<text:s text:c=\"%d\"/>" (1- n
))))
2684 (defun org-e-odt-encode-plain-text (text &optional no-whitespace-filling
)
2687 (setq text
(replace-regexp-in-string (car pair
) (cdr pair
) text t t
)))
2688 '(("&" .
"&") ("<" .
"<") (">" .
">")))
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."
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
))))
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
)
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
)))
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
2734 (format "<text:span text:style-name=\"%s\">%s</text:span>"
2735 "OrgTimestampWrapper"
2737 (let ((closed (org-element-property :closed planning
)))
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
)))
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
)))
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
2766 ;; The property drawer isn't exported but we want separating blank
2767 ;; lines nonetheless.
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
))
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
))))
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>"
2796 (format "text:name=\"%s\"" (or name default-name
))
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."
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."
2813 text
(org-export-solidify-link-text
2814 (org-element-property :value radio-target
))))
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
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>"
2838 (format "<office:annotation>\n%s\n</office:annotation>"
2841 (format "<dc:creator>%s</dc:creator>" author
))
2843 (format "<dc:date>%s</dc:date>"
2844 (org-e-odt--date date
)))
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
))))
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
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)
2878 'capitalize
(split-string
2879 (hfy-face-or-def-to-name 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
2886 (format org-e-odt-src-block-paragraph-format
2887 background-color-val color-val
))
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 '(("\"" """)
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
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
2928 (font-lock-fontify-buffer)
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"))
2935 (assert (= code-length
(length (org-split-string code
"\n"))))
2937 (org-export-format-code
2939 (lambda (loc line-num ref
)
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
))
2946 (setq loc
(org-e-odt--target loc
(concat "coderef-" ref
))))
2948 (setq loc
(format "\n<text:p text:style-name=\"%s\">%s</text:p>"
2950 (if (not line-num
) loc
2951 (format "\n<text:list-item>%s\n</text:list-item>" loc
)))
2954 ((not num-start
) code
)
2957 "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
2958 " text:continue-numbering=\"false\"" code
))
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
))
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
)))
2998 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
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>"
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
)))
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
))
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
))
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
))
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
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
)
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
3090 (let* ((template-name (nth 1 style-spec
))
3091 (cell-style-selectors (nth 2 style-spec
))
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
)))
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
)))
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")
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
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
3128 (and custom-style-prefix
3129 (format "%sTableParagraph" custom-style-prefix
))
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
))
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
3140 (<= c
(cond ((wholenump table-header-columns
)
3141 (- table-header-columns
1))
3142 (table-header-columns 0)
3145 (t "OrgTableContents"))
3146 (capitalize (symbol-name (org-export-table-cell-alignment
3147 table-cell info
))))))
3150 (and custom-style-prefix
(format "%sTableCell"
3151 custom-style-prefix
))
3154 (when (or (org-export-table-row-starts-rowgroup-p table-row info
)
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"))))
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
""))
3167 (assert paragraph-style
)
3168 (format "\n<table:table-cell%s>\n%s\n</table:table-cell>"
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
)
3174 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3175 paragraph-style contents
))))
3177 (dotimes (i horiz-span s
)
3178 (setq s
(concat s
"\n<table:covered-table-cell/>"))))
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>"))))
3202 ;; Does this row begin a rowgroup?
3203 (when (org-export-table-row-starts-rowgroup-p table-row info
)
3204 (car rowgroup-tags
))
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
))))))
3214 (defun org-e-odt-table-first-row-data-cells (table info
)
3219 (unless (eq (org-element-property :type row
) 'rule
) row
))
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.
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.
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
)))
3247 (lambda (table info
)
3248 (let* ((table-style (or custom-table-style
"OrgTable"))
3249 (column-style (format "%sColumn" table-style
)))
3251 (lambda (table-cell)
3252 (let ((width (1+ (or (org-export-table-cell-width
3253 table-cell info
) 0)))
3255 "\n<table:table-column table:style-name=\"%s\"/>"
3258 (dotimes (i width out
) (setq out
(concat s out
)))))
3259 (org-e-odt-table-first-row-data-cells table info
) "\n"))))))
3263 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3266 (let* ((automatic-name
3267 (org-e-odt-add-automatic-style "Table" attributes
)))
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
)
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
3289 (let ((parent (org-export-get-parent blob
)))
3290 (cdr (memq blob
(reverse (org-element-contents parent
))))))))
3291 (--element-preceded-by-table-p
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
3298 (lambda (table info
)
3299 (let* ((genealogy (org-export-get-genealogy table
))
3301 (when (eq (org-element-type (car genealogy
)) 'item
)
3302 (loop for el in genealogy
3303 when
(memq (org-element-type el
)
3308 (loop for el in genealogy
3309 when
(and (eq (org-element-type el
) 'headline
)
3310 (org-export-low-level-p el info
))
3314 (org-element-contents
3315 (org-export-get-parent el
)))))))
3318 ;; Handle list genealogy.
3319 (loop for el in list-genealogy collect
3320 (case (org-element-type el
)
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\"")))
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
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
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
3354 "text:continue-numbering=\"true\"")))
3356 (setq step
'plain-list
) ; Flip-flop
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
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
3372 ;; OpenDocument schema does not permit table to occur within a
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
3382 ;; ,---- From OpenDocument-v1.1.pdf
3383 ;; | 15.27.28 Overflow behavior
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
3390 ;; | If the attribute's value is clip, the text that does not fit
3391 ;; | into the text box is not displayed.
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.
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.
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
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
3419 ;; See `org-e-odt-translate-description-lists' and
3420 ;; `org-e-odt-translate-low-level-headlines' for how this is
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"))))
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
3441 (let ((value (org-element-property :value target
)))
3442 (org-e-odt--target "" (org-export-solidify-link-text value
))))
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
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"
3456 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3457 "OrgTimestamp" (org-translate-time timestamp-1
))
3460 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3461 "OrgTimestamp" (org-translate-time timestamp-2
)))))))
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
))
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
3480 (format "<text:span text:style-name=\"%s\">%s</text:span>"
3481 "OrgCode" (org-element-property :value verbatim
)))
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
3499 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
3500 "OrgVerse" contents
)))
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
3517 ;; Specifically, a description list like this:
3520 ;; | - term-1 :: definition-1
3521 ;; | - term-2 :: definition-2
3524 ;; gets translated in to the following form:
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
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
))
3552 (org-element-adopt-elements
3553 (list 'item
(list :checkbox
(org-element-property
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
3561 (org-element-contents item
)))))
3562 (org-element-contents el
)))))
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
3573 ;; Consider an example. The following list table
3575 ;; #+attr_odt :list-table t
3585 ;; will be exported as though it were an Org table like the one show
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
)
3612 (when (org-export-read-attribute :attr_odt l1-list
:list-table
)
3613 ;; Replace list with table.
3614 (org-element-set-element
3616 ;; Build replacement table.
3617 (apply 'org-element-adopt-elements
3618 (list 'table
'(:type org
:attr_odt
(":style \"GriddedTable\"")))
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
))
3631 (setq cur
(cdr cur
)))
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.
3650 (apply 'org-element-adopt-elements
3651 (list 'table-cell nil
)
3652 (org-element-contents l2-item
)))
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
))
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")
3675 (lambda (file-entry)
3676 (let* ((version (nth 2 file-entry
))
3677 (extra (if (not version
) ""
3678 (format " manifest:version=\"%s\"" version
))))
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
3698 ;; Kill all XML buffers.
3699 (mapc (lambda (file)
3700 (let ((buf (get-file-buffer
3701 (concat org-e-odt-zip-dir file
))))
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
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.
3717 ;; Create a manifest entry for content.xml.
3718 (org-e-odt-create-manifest-file-entry "text/xml" "content.xml")
3719 ;; Write mimetype file
3721 '(("odt" .
"application/vnd.oasis.opendocument.text")
3722 ("odf" .
"application/vnd.oasis.opendocument.formula")))
3723 (mimetype (cdr (assoc-string out-file-type mimetypes t
))))
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
))))
3734 (with-current-buffer buf
3735 ;; Prettify output if needed.
3736 (when org-e-odt-prettify-xml
3737 (indent-region (point-min) (point-max)))
3739 org-e-odt-xml-files
)
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
3756 (with-current-buffer
3757 (find-file-noselect (concat org-e-odt-zip-dir
"content.xml") t
)
3760 (message "Running %s" (mapconcat 'identity cmd
" "))
3762 (with-output-to-string
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)"
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
3778 (find-file-noselect target t
)
3779 ;; Return exported file.
3781 ;; Case 1: Conversion desired on exported file. Run the
3782 ;; converter on the OpenDocument file. Return the
3784 (org-e-odt-preferred-output-format
3785 (or (org-e-odt-convert target org-e-odt-preferred-output-format
)
3787 ;; Case 2: No further conversion. Return exported
3788 ;; OpenDocument file.
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
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
3810 (setq frag
(and (setq frag
(and (region-active-p)
3811 (buffer-substring (region-beginning)
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
3819 (file-name-sans-extension
3820 (or (file-name-nondirectory buffer-file-name
)))
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
3828 (file-name-sans-extension
3829 (or (file-name-nondirectory buffer-file-name
)))
3831 (file-name-directory buffer-file-name
)))))
3832 (org-e-odt--export-wrap
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
))
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"))
3846 ;; Add MathML to kill ring, if needed.
3847 (when org-export-copy-to-kill-ring
3848 (org-kill-new (buffer-string))))))))
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
3856 (org-open-file (call-interactively 'org-e-odt-export-as-odf
) 'system
))
3859 ;;;; Export to OpenDocument Text
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
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
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
3888 Return output file's name."
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
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
))
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."
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
))))
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?"
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
))
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
))
3952 ((file-exists-p out-file
)
3953 (message "Exported to %s" out-file
)
3955 (message "Opening %s..." out-file
)
3956 (org-open-file out-file
'system
))
3959 (message "Export to %s failed" out-file
)
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."
3969 (and org-e-odt-convert-process
3970 (cadr (assoc-string org-e-odt-convert-process
3971 org-e-odt-convert-processes t
))))
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
))
3979 (dolist (c capabilities
)
3980 (when (member in-fmt
(nth 1 c
))
3981 (push (cons converter
(nth 2 c
)) 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 ...)."
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
))
4003 (or (and out-fmt-choices
4004 (funcall input
"Output format: "
4005 out-fmt-choices nil nil nil
))
4007 "No known converter or no known output formats for %s files"
4009 (list in-file out-fmt
)))
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'."
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
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