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