1 ;;; org-odt.el --- OpenDocumentText export for Org-mode
3 ;; Copyright (C) 2010-2011 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 (yet) part of GNU Emacs.
10 ;; However, it is distributed under the same license.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
33 (defgroup org-export-odt nil
34 "Options specific for ODT export of Org-mode files."
38 (defun org-odt-end-export ()
39 (org-odt-fixup-label-references)
41 ;; remove empty paragraphs
42 (goto-char (point-min))
43 (while (re-search-forward
44 "<text:p\\( text:style-name=\"Text_20_body\"\\)?>[ \r\n\t]*</text:p>"
47 (goto-char (point-min))
49 ;; Convert whitespace place holders
50 (goto-char (point-min))
52 (while (setq beg
(next-single-property-change (point) 'org-whitespace
))
53 (setq n
(get-text-property beg
'org-whitespace
)
54 end
(next-single-property-change beg
'org-whitespace
))
56 (delete-region beg end
)
57 (insert (format "<span style=\"visibility:hidden;\">%s</span>"
58 (make-string n ?x
)))))
60 ;; Remove empty lines at the beginning of the file.
61 (goto-char (point-min))
62 (when (looking-at "\\s-+\n") (replace-match ""))
64 ;; Remove display properties
65 (remove-text-properties (point-min) (point-max) '(display t
)))
67 (defvar org-odt-suppress-xref nil
)
68 (defconst org-export-odt-special-string-regexps
69 '(("\\\\-" .
"­\\1") ; shy
70 ("---\\([^-]\\)" .
"—\\1") ; mdash
71 ("--\\([^-]\\)" .
"–\\1") ; ndash
72 ("\\.\\.\\." .
"…")) ; hellip
73 "Regular expressions for special string conversion.")
75 (defconst org-odt-lib-dir
(file-name-directory load-file-name
))
76 (defconst org-odt-styles-dir
77 (let* ((styles-dir1 (expand-file-name "../etc/styles/" org-odt-lib-dir
)) ; git
78 (styles-dir2 (expand-file-name "./etc/styles/" org-odt-lib-dir
)) ; elpa
79 (styles-dir3 (expand-file-name "./etc/org/" data-directory
)) ; system
82 (mapc (lambda (styles-dir)
83 (when (and (file-readable-p
85 "OrgOdtContentTemplate.xml" styles-dir
))
88 "OrgOdtStyles.xml" styles-dir
)))
89 (throw 'styles-dir styles-dir
)))
90 (list styles-dir1 styles-dir2 styles-dir3
))
93 (error "Cannot find factory styles file. Check package dir layout"))
95 "Directory that holds auxiliary XML files used by the ODT exporter.
97 This directory contains the following XML files -
98 \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\". These
99 XML files are used as the default values of
100 `org-export-odt-styles-file' and
101 `org-export-odt-content-template-file'.
103 The default value of this variable varies depending on the
104 version of org in use. Note that the user could be using org
105 from one of: org's own private git repository, GNU ELPA tar or
108 (defcustom org-export-odt-schema-dir
109 (let ((schema-dir (expand-file-name
110 "../contrib/odt/etc/schema/" org-odt-lib-dir
)))
111 (if (and (file-readable-p
112 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir
))
114 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir
))
116 (expand-file-name "schemas.xml" schema-dir
)))
118 (prog1 nil
(message "Unable to locate OpenDocument schema files."))))
119 "Directory that contains OpenDocument schema files.
121 This directory contains:
122 1. rnc files for OpenDocument schema
123 2. a \"schemas.xml\" file that specifies locating rules needed
124 for auto validation of OpenDocument XML files.
126 Use the customize interface to set this variable. This ensures
127 that `rng-schema-locating-files' is updated and auto-validation
128 of OpenDocument XML takes place based on the value
129 `rng-nxml-auto-validate-flag'.
131 The default value of this variable varies depending on the
132 version of org in use. The OASIS schema files are available only
133 in the org's private git repository. It is *not* bundled with
134 GNU ELPA tar or standard Emacs distribution."
136 (const :tag
"Not set" nil
)
137 (directory :tag
"Schema directory"))
138 :group
'org-export-odt
141 "Set `org-export-odt-schema-dir'.
142 Also add it to `rng-schema-locating-files'."
143 (let ((schema-dir value
))
147 (expand-file-name "od-manifest-schema-v1.2-cs01.rnc" schema-dir
))
149 (expand-file-name "od-schema-v1.2-cs01.rnc" schema-dir
))
151 (expand-file-name "schemas.xml" schema-dir
)))
154 (message "Warning (org-odt): Unable to locate OpenDocument schema files.")))))
155 (when org-export-odt-schema-dir
156 (eval-after-load 'rng-loc
157 '(add-to-list 'rng-schema-locating-files
158 (expand-file-name "schemas.xml"
159 org-export-odt-schema-dir
))))))
161 (defvar org-odt-file-extensions
162 '(("odt" .
"OpenDocument Text")
163 ("ott" .
"OpenDocument Text Template")
164 ("odm" .
"OpenDocument Master Document")
165 ("ods" .
"OpenDocument Spreadsheet")
166 ("ots" .
"OpenDocument Spreadsheet Template")
167 ("odg" .
"OpenDocument Drawing (Graphics)")
168 ("otg" .
"OpenDocument Drawing Template")
169 ("odp" .
"OpenDocument Presentation")
170 ("otp" .
"OpenDocument Presentation Template")
171 ("odi" .
"OpenDocument Image")
172 ("odf" .
"OpenDocument Formula")
173 ("odc" .
"OpenDocument Chart")))
177 ;; Let Org open all OpenDocument files using system-registered app
178 (add-to-list 'org-file-apps
179 (cons (concat "\\." (car desc
) "\\'") 'system
))
180 ;; Let Emacs open all OpenDocument files in archive mode
181 (add-to-list 'auto-mode-alist
182 (cons (concat "\\." (car desc
) "\\'") 'archive-mode
)))
183 org-odt-file-extensions
)
185 ;; register the odt exporter with the pre-processor
186 (add-to-list 'org-export-backends
'odt
)
188 ;; register the odt exporter with org-lparse library
189 (org-lparse-register-backend 'odt
)
191 (defun org-odt-unload-function ()
192 (org-lparse-unregister-backend 'odt
)
193 (remove-hook 'org-export-preprocess-after-blockquote-hook
194 'org-export-odt-preprocess-latex-fragments
)
197 (defcustom org-export-odt-content-template-file nil
198 "Template file for \"content.xml\".
199 The exporter embeds the exported content just before
200 \"</office:text>\" element.
202 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
203 under `org-odt-styles-dir' is used."
205 :group
'org-export-odt
)
207 (defcustom org-export-odt-styles-file nil
208 "Default styles file for use with ODT export.
209 Valid values are one of:
211 2. path to a styles.xml file
212 3. path to a *.odt or a *.ott file
213 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
216 In case of option 1, an in-built styles.xml is used. See
217 `org-odt-styles-dir' for more information.
219 In case of option 3, the specified file is unzipped and the
220 styles.xml embedded therein is used.
222 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
223 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
224 generated odt file. Use relative path for specifying the
225 FILE-MEMBERS. styles.xml must be specified as one of the
228 Use options 1, 2 or 3 only if styles.xml alone suffices for
229 achieving the desired formatting. Use option 4, if the styles.xml
230 references additional files like header and footer images for
231 achieving the desired formattting.
233 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
234 a per-file basis. For example,
236 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
237 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
238 :group
'org-export-odt
241 (const :tag
"Factory settings" nil
)
242 (file :must-match t
:tag
"styles.xml")
243 (file :must-match t
:tag
"ODT or OTT file")
244 (list :tag
"ODT or OTT file + Members"
245 (file :must-match t
:tag
"ODF Text or Text Template file")
247 (file :tag
" Member" "styles.xml")
248 (repeat (file :tag
"Member"))))))
250 (eval-after-load 'org-exp
251 '(add-to-list 'org-export-inbuffer-options-extra
252 '("ODT_STYLES_FILE" :odt-styles-file
)))
254 (defconst org-export-odt-tmpdir-prefix
"%s-")
255 (defconst org-export-odt-bookmark-prefix
"OrgXref.")
257 (defvar org-export-odt-embed-images t
258 "Should the images be copied in to the odt file or just linked?")
260 (defvar org-export-odt-inline-images
'maybe
) ; counterpart of
261 ; `org-export-html-inline-images'
263 (defcustom org-export-odt-inline-image-extensions
264 '("png" "jpeg" "jpg" "gif")
265 "Extensions of image files that can be inlined into HTML."
266 :type
'(repeat (string :tag
"Extension"))
267 :group
'org-export-odt
)
269 (defcustom org-export-odt-pixels-per-inch display-pixels-per-inch
270 ;; FIXME add docstring
273 :group
'org-export-odt
)
275 (defcustom org-export-odt-create-custom-styles-for-srcblocks t
276 "Whether custom styles for colorized source blocks be automatically created.
277 When this option is turned on, the exporter creates custom styles
278 for source blocks based on the advice of `htmlfontify'. Creation
279 of custom styles happen as part of `org-odt-hfy-face-to-css'.
281 When this option is turned off exporter does not create such
284 Use the latter option if you do not want the custom styles to be
285 based on your current display settings. It is necessary that the
286 styles.xml already contains needed styles for colorizing to work.
288 This variable is effective only if
289 `org-export-odt-fontify-srcblocks' is turned on."
290 :group
'org-export-odt
293 (defvar org-export-odt-default-org-styles-alist
294 '((paragraph .
((default .
"Text_20_body")
295 (fixedwidth .
"OrgFixedWidthBlock")
297 (quote .
"Quotations")
298 (blockquote .
"Quotations")
299 (center .
"OrgCenter")
303 (subtitle .
"OrgSubtitle")
304 (footnote .
"Footnote")
305 (src .
"OrgSrcBlock")
306 (illustration .
"Illustration")
308 (definition-term .
"Text_20_body_20_bold")
309 (horizontal-line .
"Horizontal_20_Line")))
310 (character .
((bold .
"Bold")
311 (emphasis .
"Emphasis")
313 (verbatim .
"OrgCode")
314 (strike .
"Strikethrough")
315 (underline .
"Underline")
316 (subscript .
"OrgSubscript")
317 (superscript .
"OrgSuperscript")))
318 (list .
((ordered .
"OrgNumberedList")
319 (unordered .
"OrgBulletedList")
320 (description .
"OrgDescriptionList"))))
321 "Default styles for various entities.")
323 (defvar org-export-odt-org-styles-alist org-export-odt-default-org-styles-alist
)
324 (defun org-odt-get-style-name-for-entity (category &optional entity
)
325 (let ((entity (or entity
'default
)))
327 (cdr (assoc entity
(cdr (assoc category
328 org-export-odt-org-styles-alist
))))
329 (cdr (assoc entity
(cdr (assoc category
330 org-export-odt-default-org-styles-alist
))))
331 (error "Cannot determine style name for entity %s of type %s"
334 (defcustom org-export-odt-preferred-output-format nil
335 "Automatically post-process to this format after exporting to \"odt\".
336 Interactive commands `org-export-as-odt' and
337 `org-export-as-odt-and-open' export first to \"odt\" format and
338 then use `org-export-odt-convert-process' to convert the
339 resulting document to this format. During customization of this
340 variable, the list of valid values are populated based on
341 `org-export-odt-convert-capabilities'."
342 :group
'org-export-odt
343 :type
'(choice :convert-widget
345 (apply 'widget-convert
(widget-type w
)
346 (eval (car (widget-get w
:args
)))))
347 `((const :tag
"None" nil
)
348 ,@(mapcar (lambda (c)
350 (org-lparse-reachable-formats "odt")))))
353 (defun org-export-as-odt-and-open (arg)
354 "Export the outline as ODT and immediately open it with a browser.
355 If there is an active region, export only the region.
356 The prefix ARG specifies how many levels of the outline should become
357 headlines. The default is 3. Lower levels will become bulleted lists."
360 (or org-export-odt-preferred-output-format
"odt") "odt" arg
))
363 (defun org-export-as-odt-batch ()
364 "Call the function `org-lparse-batch'.
365 This function can be used in batch processing as:
367 --load=$HOME/lib/emacs/org.el
368 --eval \"(setq org-export-headline-levels 2)\"
369 --visit=MyFile --funcall org-export-as-odt-batch"
370 (org-lparse-batch "odt"))
373 (defun org-export-as-odt-to-buffer (arg)
374 "Call `org-lparse-odt` with output to a temporary buffer.
375 No file is created. The prefix ARG is passed through to `org-lparse-to-buffer'."
377 (org-lparse-to-buffer "odt" arg
))
380 (defun org-replace-region-by-odt (beg end
)
381 "Assume the current region has org-mode syntax, and convert it to ODT.
382 This can be used in any buffer. For example, you could write an
383 itemized list in org-mode syntax in an ODT buffer and then use this
384 command to convert it."
386 (org-replace-region-by "odt" beg end
))
389 (defun org-export-region-as-odt (beg end
&optional body-only buffer
)
390 "Convert region from BEG to END in org-mode buffer to ODT.
391 If prefix arg BODY-ONLY is set, omit file header, footer, and table of
392 contents, and only produce the region of converted text, useful for
393 cut-and-paste operations.
394 If BUFFER is a buffer or a string, use/create that buffer as a target
395 of the converted ODT. If BUFFER is the symbol `string', return the
396 produced ODT as a string and leave not buffer behind. For example,
397 a Lisp program could call this function in the following way:
399 (setq odt (org-export-region-as-odt beg end t 'string))
401 When called interactively, the output buffer is selected, and shown
402 in a window. A non-interactive call will only return the buffer."
404 (org-lparse-region "odt" beg end body-only buffer
))
406 ;;; org-export-as-odt
408 (defun org-export-as-odt (arg &optional hidden ext-plist
409 to-buffer body-only pub-dir
)
410 "Export the outline as a OpenDocumentText file.
411 If there is an active region, export only the region. The prefix
412 ARG specifies how many levels of the outline should become
413 headlines. The default is 3. Lower levels will become bulleted
414 lists. HIDDEN is obsolete and does nothing.
415 EXT-PLIST is a property list with external parameters overriding
416 org-mode's default settings, but still inferior to file-local
417 settings. When TO-BUFFER is non-nil, create a buffer with that
418 name and export to that buffer. If TO-BUFFER is the symbol
419 `string', don't leave any buffer behind but just return the
420 resulting XML as a string. When BODY-ONLY is set, don't produce
421 the file header and footer, simply return the content of
422 <body>...</body>, without even the body tags themselves. When
423 PUB-DIR is set, use this as the publishing directory."
425 (org-lparse (or org-export-odt-preferred-output-format
"odt")
426 "odt" arg hidden ext-plist to-buffer body-only pub-dir
))
428 (defvar org-odt-entity-control-callbacks-alist
430 .
(org-odt-begin-export org-odt-end-export
))
432 .
(org-odt-begin-document-content org-odt-end-document-content
))
434 .
(org-odt-begin-document-body org-odt-end-document-body
))
436 .
(org-odt-begin-toc org-odt-end-toc
))
438 .
(org-odt-begin-environment org-odt-end-environment
))
440 .
(org-odt-begin-footnote-definition org-odt-end-footnote-definition
))
442 .
(org-odt-begin-table org-odt-end-table
))
444 .
(org-odt-begin-table-rowgroup org-odt-end-table-rowgroup
))
446 .
(org-odt-begin-list org-odt-end-list
))
448 .
(org-odt-begin-list-item org-odt-end-list-item
))
450 .
(org-odt-begin-outline org-odt-end-outline
))
452 .
(org-odt-begin-outline-text org-odt-end-outline-text
))
454 .
(org-odt-begin-paragraph org-odt-end-paragraph
)))
457 (defvar org-odt-entity-format-callbacks-alist
458 `((EXTRA-TARGETS . org-lparse-format-extra-targets
)
459 (ORG-TAGS . org-lparse-format-org-tags
)
460 (SECTION-NUMBER . org-lparse-format-section-number
)
461 (HEADLINE . org-odt-format-headline
)
462 (TOC-ENTRY . org-odt-format-toc-entry
)
463 (TOC-ITEM . org-odt-format-toc-item
)
464 (TAGS . org-odt-format-tags
)
465 (SPACES . org-odt-format-spaces
)
466 (TABS . org-odt-format-tabs
)
467 (LINE-BREAK . org-odt-format-line-break
)
468 (FONTIFY . org-odt-format-fontify
)
469 (TODO . org-lparse-format-todo
)
470 (LINK . org-odt-format-link
)
471 (INLINE-IMAGE . org-odt-format-inline-image
)
472 (ORG-LINK . org-odt-format-org-link
)
473 (HEADING . org-odt-format-heading
)
474 (ANCHOR . org-odt-format-anchor
)
475 (TABLE . org-lparse-format-table
)
476 (TABLE-ROW . org-odt-format-table-row
)
477 (TABLE-CELL . org-odt-format-table-cell
)
478 (FOOTNOTES-SECTION . ignore
)
479 (FOOTNOTE-REFERENCE . org-odt-format-footnote-reference
)
480 (HORIZONTAL-LINE . org-odt-format-horizontal-line
)
481 (COMMENT . org-odt-format-comment
)
482 (LINE . org-odt-format-line
)
483 (ORG-ENTITY . org-odt-format-org-entity
))
487 ;;;_. control callbacks
489 (defun org-odt-begin-office-body ()
491 (insert-file-contents
492 (or org-export-odt-content-template-file
493 (expand-file-name "OrgOdtContentTemplate.xml"
494 org-odt-styles-dir
)))
495 (goto-char (point-min))
496 (re-search-forward "</office:text>" nil nil
)
497 (delete-region (match-beginning 0) (point-max)))
499 ;; Following variable is let bound when `org-do-lparse' is in
500 ;; progress. See org-html.el.
501 (defvar org-lparse-toc
)
502 (defun org-odt-format-toc ()
503 (if (not org-lparse-toc
) "" (concat "\n" org-lparse-toc
"\n")))
505 (defun org-odt-format-preamble (opt-plist)
506 (let* ((title (plist-get opt-plist
:title
))
507 (author (plist-get opt-plist
:author
))
508 (date (plist-get opt-plist
:date
))
509 (iso-date (org-odt-format-date date
))
510 (date (org-odt-format-date date
"%d %b %Y"))
511 (email (plist-get opt-plist
:email
))
512 ;; switch on or off above vars based on user settings
513 (author (and (plist-get opt-plist
:author-info
) (or author email
)))
514 (email (and (plist-get opt-plist
:email-info
) email
))
515 (date (and (plist-get opt-plist
:time-stamp-file
) date
)))
520 (org-odt-format-stylized-paragraph
521 'title
(org-odt-format-tags
522 '("<text:title>" .
"</text:title>") title
))
524 "<text:p text:style-name=\"OrgTitle\"/>"))
526 ((and author
(not email
))
529 (org-odt-format-stylized-paragraph
532 '("<text:initial-creator>" .
"</text:initial-creator>")
535 "<text:p text:style-name=\"OrgSubtitle\"/>"))
539 (org-odt-format-stylized-paragraph
543 '("<text:initial-creator>" .
"</text:initial-creator>")
544 author
) (concat "mailto:" email
)))
546 "<text:p text:style-name=\"OrgSubtitle\"/>")))
550 (org-odt-format-stylized-paragraph
553 '("<text:date style:data-style-name=\"%s\" text:date-value=\"%s\">"
554 .
"</text:date>") date
"N75" iso-date
))
556 "<text:p text:style-name=\"OrgSubtitle\"/>"))
558 (org-odt-format-toc))))
560 (defun org-odt-begin-document-body (opt-plist)
561 (org-odt-begin-office-body)
562 (insert (org-odt-format-preamble opt-plist
)))
564 (defvar org-lparse-body-only
) ; let bound during org-do-lparse
565 (defvar org-lparse-to-buffer
) ; let bound during org-do-lparse
566 (defun org-odt-end-document-body (opt-plist)
567 (unless org-lparse-body-only
568 (org-lparse-insert-tag "</office:text>")
569 (org-lparse-insert-tag "</office:body>")))
571 (defun org-odt-begin-document-content (opt-plist)
574 (defun org-odt-end-document-content ()
575 (org-lparse-insert-tag "</office:document-content>"))
577 (defun org-odt-begin-outline (level1 snumber title tags
578 target extra-targets class
)
580 'HEADING
(org-lparse-format
581 'HEADLINE title extra-targets tags snumber level1
)
584 (defun org-odt-end-outline ()
587 (defun org-odt-begin-outline-text (level1 snumber class
)
590 (defun org-odt-end-outline-text ()
593 (defun org-odt-begin-paragraph (&optional style
)
594 (org-lparse-insert-tag
595 "<text:p%s>" (org-odt-get-extra-attrs-for-paragraph-style style
)))
597 (defun org-odt-end-paragraph ()
598 (org-lparse-insert-tag "</text:p>"))
600 (defun org-odt-get-extra-attrs-for-paragraph-style (style)
604 ((stringp style
) style
)
605 ((symbolp style
) (org-odt-get-style-name-for-entity
608 (error "Don't know how to handle paragraph style %s" style
))
609 (format " text:style-name=\"%s\"" style-name
)))
611 (defun org-odt-format-stylized-paragraph (style text
)
613 '("<text:p%s>" .
"</text:p>") text
614 (org-odt-get-extra-attrs-for-paragraph-style style
)))
616 (defvar org-lparse-opt-plist
) ; bound during org-do-lparse
617 (defun org-odt-format-author (&optional author
)
618 (when (setq author
(or author
(plist-get org-lparse-opt-plist
:author
)))
619 (org-odt-format-tags '("<dc:creator>" .
"</dc:creator>") author
)))
621 (defun org-odt-format-date (&optional org-ts fmt
)
624 (and (stringp org-ts
)
625 (string-match org-ts-regexp0 org-ts
)
627 (org-fix-decoded-time
628 (org-parse-time-string (match-string 0 org-ts
) t
)))))
631 (fmt (format-time-string fmt time
))
632 (t (setq date
(format-time-string "%Y-%m-%dT%H:%M:%S%z" time
))
633 (format "%s:%s" (substring date
0 -
2) (substring date -
2)))))))
635 (defun org-odt-begin-annotation (&optional author date
)
636 (org-lparse-insert-tag "<office:annotation>")
637 (when (setq author
(org-odt-format-author author
))
639 (insert (org-odt-format-tags
640 '("<dc:date>" .
"</dc:date>")
642 (or date
(plist-get org-lparse-opt-plist
:date
)))))
643 (org-lparse-begin-paragraph))
645 (defun org-odt-end-annotation ()
646 (org-lparse-insert-tag "</office:annotation>"))
648 (defun org-odt-begin-environment (style env-options-plist
)
651 (org-lparse-stash-save-paragraph-state)
652 (org-odt-begin-annotation (plist-get env-options-plist
'author
)
653 (plist-get env-options-plist
'date
)))
654 ((blockquote verse center quote
)
655 (org-lparse-begin-paragraph style
)
658 (org-lparse-end-paragraph)
660 (t (error "Unknown environment %s" style
))))
662 (defun org-odt-end-environment (style env-options-plist
)
665 (org-lparse-end-paragraph)
666 (org-odt-end-annotation)
667 (org-lparse-stash-pop-paragraph-state))
668 ((blockquote verse center quote
)
669 (org-lparse-end-paragraph)
672 (org-lparse-begin-paragraph)
674 (t (error "Unknown environment %s" style
))))
676 (defvar org-lparse-list-level
) ; dynamically bound in org-do-lparse
677 (defun org-odt-begin-list (ltype)
678 (setq ltype
(or (org-lparse-html-list-type-to-canonical-list-type ltype
)
680 (let* ((style-name (org-odt-get-style-name-for-entity 'list ltype
))
681 (extra (concat (when (= org-lparse-list-level
1)
682 " text:continue-numbering=\"false\"")
684 (format " text:style-name=\"%s\"" style-name
)))))
686 ((ordered unordered description
)
687 (org-lparse-end-paragraph)
688 (org-lparse-insert-tag "<text:list%s>" extra
))
689 (t (error "Unknown list type: %s" ltype
)))))
691 (defun org-odt-end-list (ltype)
692 (setq ltype
(or (org-lparse-html-list-type-to-canonical-list-type ltype
)
695 (org-lparse-insert-tag "</text:list>")
696 (error "Unknown list type: %s" ltype
)))
698 (defun org-odt-begin-list-item (ltype &optional arg headline
)
699 (setq ltype
(or (org-lparse-html-list-type-to-canonical-list-type ltype
)
703 (assert (not headline
) t
)
704 (let* ((counter arg
) (extra ""))
705 (org-lparse-insert-tag "<text:list-item>")
706 (org-lparse-begin-paragraph)))
708 (let* ((id arg
) (extra ""))
709 (org-lparse-insert-tag "<text:list-item>")
710 (org-lparse-begin-paragraph)
711 (insert (if headline
(org-odt-format-target headline id
)
712 (org-odt-format-bookmark "" id
)))))
714 (assert (not headline
) t
)
715 (let ((term (or arg
"(no term)")))
718 '("<text:list-item>" .
"</text:list-item>")
719 (org-odt-format-stylized-paragraph 'definition-term term
)))
720 (org-lparse-begin-list-item 'unordered
)
721 (org-lparse-begin-list 'description
)
722 (org-lparse-begin-list-item 'unordered
)))
723 (t (error "Unknown list type"))))
725 (defun org-odt-end-list-item (ltype)
726 (setq ltype
(or (org-lparse-html-list-type-to-canonical-list-type ltype
)
730 (org-lparse-insert-tag "</text:list-item>"))
732 (org-lparse-end-list-item-1)
733 (org-lparse-end-list 'description
)
734 (org-lparse-end-list-item-1))
735 (t (error "Unknown list type"))))
737 ;; Following variables are let bound when table emission is in
738 ;; progress. See org-lparse.el.
739 (defvar org-lparse-table-begin-marker
)
740 (defvar org-lparse-table-ncols
)
741 (defvar org-lparse-table-rowgrp-open
)
742 (defvar org-lparse-table-rownum
)
743 (defvar org-lparse-table-cur-rowgrp-is-hdr
)
744 (defvar org-lparse-table-is-styled
)
745 (defvar org-lparse-table-rowgrp-info
)
746 (defvar org-lparse-table-colalign-vector
)
748 (defvar org-odt-table-style nil
749 "Table style specified by \"#+ATTR_ODT: <style-name>\" line.
750 This is set during `org-odt-begin-table'.")
752 (defvar org-odt-table-style-spec nil
753 "Entry for `org-odt-table-style' in `org-export-odt-table-styles'.")
755 (defcustom org-export-odt-table-styles
756 '(("OrgEquation" "OrgEquation"
757 ((use-first-column-styles . t
)
758 (use-last-column-styles . t
))))
759 "Specify how Table Styles should be derived from a Table Template.
760 This is a list where each element is of the
761 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
763 TABLE-STYLE-NAME is the style associated with the table through
764 `org-odt-table-style'.
766 TABLE-TEMPLATE-NAME is a set of - upto 9 - automatic
767 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
768 below) that is included in
769 `org-export-odt-content-template-file'.
771 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
773 PARAGRAPH-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
775 TABLE-CELL-TYPE := \"FirstRow\" | \"LastColumn\" |
776 \"FirstRow\" | \"LastRow\" |
777 \"EvenRow\" | \"OddRow\" |
778 \"EvenColumn\" | \"OddColumn\" | \"\"
779 where \"+\" above denotes string concatenation.
781 TABLE-CELL-OPTIONS is an alist where each element is of the
782 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
783 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles' |
784 `use-last-row-styles' |
785 `use-first-column-styles' |
786 `use-last-column-styles' |
787 `use-banding-rows-styles' |
788 `use-banding-columns-styles' |
789 `use-first-row-styles'
790 ON-OR-OFF := `t' | `nil'
792 For example, with the following configuration
794 \(setq org-export-odt-table-styles
795 '\(\(\"TableWithHeaderRowsAndColumns\" \"Custom\"
796 \(\(use-first-row-styles . t\)
797 \(use-first-column-styles . t\)\)\)
798 \(\"TableWithHeaderColumns\" \"Custom\"
799 \(\(use-first-column-styles . t\)\)\)\)\)
801 1. A table associated with \"TableWithHeaderRowsAndColumns\"
802 style will use the following table-cell styles -
803 \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
804 \"CustomTableCell\" and the following paragraph styles
805 \"CustomFirstRowTableParagraph\",
806 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
809 2. A table associated with \"TableWithHeaderColumns\" style will
810 use the following table-cell styles -
811 \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
812 following paragraph styles
813 \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
816 Note that TABLE-TEMPLATE-NAME corresponds to the
817 \"<table:table-template>\" elements contained within
818 \"<office:styles>\". The entries (TABLE-STYLE-NAME
819 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
820 \"table:template-name\" and \"table:use-first-row-styles\" etc
821 attributes of \"<table:table>\" element. Refer ODF-1.2
822 specification for more information. Also consult the
823 implementation filed under `org-odt-get-table-cell-styles'.
825 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
826 formatting of numbered display equations. Do not delete this
827 style from the list."
828 :group
'org-export-odt
830 (const :tag
"None" nil
)
831 (repeat :tag
"Table Styles"
832 (list :tag
"Table Style Specification"
833 (string :tag
"Table Style Name")
834 (string :tag
"Table Template Name")
835 (alist :options
(use-first-row-styles
837 use-first-column-styles
838 use-last-column-styles
839 use-banding-rows-styles
840 use-banding-columns-styles
)
842 :value-type
(const :tag
"True" t
))))))
844 (defun org-odt-begin-table (caption label attributes
)
845 (setq org-odt-table-style attributes
)
846 (setq org-odt-table-style-spec
847 (assoc org-odt-table-style org-export-odt-table-styles
))
850 (org-odt-format-stylized-paragraph
851 'table
(org-odt-format-entity-caption label caption
"__Table__"))))
852 (org-lparse-insert-tag
853 "<table:table table:name=\"%s\" table:style-name=\"%s\">"
854 (or label
"") (or (nth 1 org-odt-table-style-spec
) "OrgTable"))
855 (setq org-lparse-table-begin-marker
(point)))
857 (defvar org-lparse-table-colalign-info
)
858 (defun org-odt-end-table ()
859 (goto-char org-lparse-table-begin-marker
)
860 (loop for level from
0 below org-lparse-table-ncols
861 do
(let* ((col-cookie (and org-lparse-table-is-styled
862 (cdr (assoc (1+ level
)
863 org-lparse-table-colalign-info
))))
864 (extra-columns (or (nth 1 col-cookie
) 0)))
865 (dotimes (i (1+ extra-columns
))
868 "<table:table-column table:style-name=\"%sColumn\"/>"
869 "" (or (nth 1 org-odt-table-style-spec
) "OrgTable"))))
871 ;; fill style attributes for table cells
872 (when org-lparse-table-is-styled
873 (while (re-search-forward "@@\\(table-cell:p\\|table-cell:style-name\\)@@\\([0-9]+\\)@@\\([0-9]+\\)@@" nil t
)
874 (let* ((spec (match-string 1))
875 (r (string-to-number (match-string 2)))
876 (c (string-to-number (match-string 3)))
877 (cell-styles (org-odt-get-table-cell-styles
878 r c org-odt-table-style-spec
))
879 (table-cell-style (car cell-styles
))
880 (table-cell-paragraph-style (cdr cell-styles
)))
882 ((equal spec
"table-cell:p")
883 (replace-match table-cell-paragraph-style t t
))
884 ((equal spec
"table-cell:style-name")
885 (replace-match table-cell-style t t
))))))
886 (goto-char (point-max))
887 (org-lparse-insert-tag "</table:table>"))
889 (defun org-odt-begin-table-rowgroup (&optional is-header-row
)
890 (when org-lparse-table-rowgrp-open
891 (org-lparse-end 'TABLE-ROWGROUP
))
892 (org-lparse-insert-tag (if is-header-row
893 "<table:table-header-rows>"
894 "<table:table-rows>"))
895 (setq org-lparse-table-rowgrp-open t
)
896 (setq org-lparse-table-cur-rowgrp-is-hdr is-header-row
))
898 (defun org-odt-end-table-rowgroup ()
899 (when org-lparse-table-rowgrp-open
900 (setq org-lparse-table-rowgrp-open nil
)
901 (org-lparse-insert-tag
902 (if org-lparse-table-cur-rowgrp-is-hdr
903 "</table:table-header-rows>" "</table:table-rows>"))))
905 (defun org-odt-format-table-row (row)
907 '("<table:table-row>" .
"</table:table-row>") row
))
909 (defun org-odt-get-table-cell-styles (r c
&optional style-spec
)
910 "Retrieve styles applicable to a table cell.
911 R and C are (zero-based) row and column numbers of the table
912 cell. STYLE-SPEC is an entry in `org-export-odt-table-styles'
913 applicable to the current table. It is `nil' if the table is not
914 associated with any style attributes.
916 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
918 When STYLE-SPEC is nil, style the table cell the conventional way
919 - choose cell borders based on row and column groupings and
920 choose paragraph alignment based on `org-col-cookies' text
922 `org-odt-get-paragraph-style-cookie-for-table-cell'.
924 When STYLE-SPEC is non-nil, ignore the above cookie and return
925 styles congruent with the ODF-1.2 specification."
929 ;; LibreOffice - particularly the Writer - honors neither table
930 ;; templates nor custom table-cell styles. Inorder to retain
931 ;; inter-operability with LibreOffice, only automatic styles are
932 ;; used for styling of table-cells. The current implementation is
933 ;; congruent with ODF-1.2 specification and hence is
934 ;; future-compatible.
936 ;; Additional Note: LibreOffice's AutoFormat facility for tables -
937 ;; which recognizes as many as 16 different cell types - is much
938 ;; richer. Unfortunately it is NOT amenable to easy configuration
941 (let* ((template-name (nth 1 style-spec
))
942 (cell-style-selectors (nth 2 style-spec
))
945 ((and (cdr (assoc 'use-first-column-styles cell-style-selectors
))
946 (= c
0)) "FirstColumn")
947 ((and (cdr (assoc 'use-last-column-styles cell-style-selectors
))
948 (= c
(1- org-lparse-table-ncols
))) "LastColumn")
949 ((and (cdr (assoc 'use-first-row-styles cell-style-selectors
))
951 ((and (cdr (assoc 'use-last-row-styles cell-style-selectors
))
952 (= r org-lparse-table-rownum
))
954 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors
))
955 (= (% r
2) 1)) "EvenRow")
956 ((and (cdr (assoc 'use-banding-rows-styles cell-style-selectors
))
957 (= (% r
2) 0)) "OddRow")
958 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors
))
959 (= (% c
2) 1)) "EvenColumn")
960 ((and (cdr (assoc 'use-banding-columns-styles cell-style-selectors
))
961 (= (% c
2) 0)) "OddColumn")
964 (concat template-name cell-type
"TableCell")
965 (concat template-name cell-type
"TableParagraph"))))
972 ((eq (cdr (assoc r org-lparse-table-rowgrp-info
)) :start
) "T")
974 (when (= r org-lparse-table-rownum
) "B")
977 ((or (memq (nth c org-table-colgroup-info
) '(:start
:startend
))
978 (memq (nth (1- c
) org-table-colgroup-info
) '(:end
:startend
))) "L")
980 (capitalize (aref org-lparse-table-colalign-vector c
))))))
982 (defun org-odt-get-paragraph-style-cookie-for-table-cell (r c
)
984 (and (not org-odt-table-style-spec
)
986 (org-lparse-table-cur-rowgrp-is-hdr "OrgTableHeading")
987 ((and (= c
0) (org-lparse-get 'TABLE-FIRST-COLUMN-AS-LABELS
))
989 (t "OrgTableContents")))
990 (and org-lparse-table-is-styled
991 (format "@@table-cell:p@@%03d@@%03d@@" r c
))))
993 (defun org-odt-get-style-name-cookie-for-table-cell (r c
)
994 (when org-lparse-table-is-styled
995 (format "@@table-cell:style-name@@%03d@@%03d@@" r c
)))
997 (defun org-odt-format-table-cell (data r c horiz-span
)
999 (let* ((paragraph-style-cookie
1000 (org-odt-get-paragraph-style-cookie-for-table-cell r c
))
1002 (org-odt-get-style-name-cookie-for-table-cell r c
))
1003 (extra (and style-name-cookie
1004 (format " table:style-name=\"%s\"" style-name-cookie
)))
1005 (extra (concat extra
1006 (and (> horiz-span
0)
1007 (format " table:number-columns-spanned=\"%d\""
1008 (1+ horiz-span
))))))
1009 (org-odt-format-tags
1010 '("<table:table-cell%s>" .
"</table:table-cell>")
1011 (if org-lparse-list-table-p data
1012 (org-odt-format-stylized-paragraph paragraph-style-cookie data
)) extra
))
1014 (dotimes (i horiz-span
)
1015 (setq s
(concat s
"\n<table:covered-table-cell/>"))) s
)
1018 (defun org-odt-begin-footnote-definition (n)
1019 (org-lparse-begin-paragraph 'footnote
))
1021 (defun org-odt-end-footnote-definition (n)
1022 (org-lparse-end-paragraph))
1024 (defun org-odt-begin-toc (lang-specific-heading max-level
)
1027 <text:table-of-content text:style-name=\"Sect2\" text:protected=\"true\" text:name=\"Table of Contents1\">
1028 <text:table-of-content-source text:outline-level=\"%d\">
1029 <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
1030 " max-level lang-specific-heading
))
1031 (loop for level from
1 upto
10
1034 <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
1035 <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
1036 <text:index-entry-chapter/>
1037 <text:index-entry-text/>
1038 <text:index-entry-link-end/>
1039 </text:table-of-content-entry-template>
1044 </text:table-of-content-source>
1047 <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
1048 <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
1050 " lang-specific-heading
)))
1052 (defun org-odt-end-toc ()
1055 </text:table-of-content>
1058 (defun org-odt-format-toc-entry (snumber todo headline tags href
)
1059 (setq headline
(concat
1060 (and org-export-with-section-numbers
1061 (concat snumber
". "))
1065 (org-lparse-format 'SPACES
3)
1066 (org-lparse-format 'FONTIFY tags
"tag")))))
1068 (setq headline
(org-lparse-format 'FONTIFY headline
"todo")))
1070 (let ((org-odt-suppress-xref t
))
1071 (org-odt-format-link headline
(concat "#" href
))))
1073 (defun org-odt-format-toc-item (toc-entry level org-last-level
)
1074 (let ((style (format "Contents_20_%d"
1075 (+ level
(or (org-lparse-get 'TOPLEVEL-HLEVEL
) 1) -
1))))
1076 (insert "\n" (org-odt-format-stylized-paragraph style toc-entry
) "\n")))
1078 ;; Following variable is let bound during 'ORG-LINK callback. See
1080 (defvar org-lparse-link-description-is-image nil
)
1081 (defun org-odt-format-link (desc href
&optional attr
)
1083 ((and (= (string-to-char href
) ?
#) (not org-odt-suppress-xref
))
1084 (setq href
(concat org-export-odt-bookmark-prefix
(substring href
1)))
1085 (let ((xref-format "text"))
1086 (when (numberp desc
)
1087 (setq desc
(format "%d" desc
) xref-format
"number"))
1088 (org-odt-format-tags
1089 '("<text:bookmark-ref text:reference-format=\"%s\" text:ref-name=\"%s\">" .
1090 "</text:bookmark-ref>")
1091 desc xref-format href
)))
1092 (org-lparse-link-description-is-image
1093 (org-odt-format-tags
1094 '("<draw:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" .
"</draw:a>")
1095 desc href
(or attr
"")))
1097 (org-odt-format-tags
1098 '("<text:a xlink:type=\"simple\" xlink:href=\"%s\" %s>" .
"</text:a>")
1099 desc href
(or attr
"")))))
1101 (defun org-odt-format-spaces (n)
1105 " " (org-odt-format-tags "<text:s text:c=\"%d\"/>" "" (1- n
))))
1108 (defun org-odt-format-tabs (&optional n
)
1109 (let ((tab "<text:tab/>")
1113 (defun org-odt-format-line-break ()
1114 (org-odt-format-tags "<text:line-break/>" ""))
1116 (defun org-odt-format-horizontal-line ()
1117 (org-odt-format-stylized-paragraph 'horizontal-line
""))
1119 (defun org-odt-encode-plain-text (line &optional no-whitespace-filling
)
1120 (setq line
(org-xml-encode-plain-text line
))
1121 (if no-whitespace-filling line
1122 (org-odt-fill-tabs-and-spaces line
)))
1124 (defun org-odt-format-line (line)
1125 (case org-lparse-dyn-current-environment
1127 (org-odt-format-stylized-paragraph
1128 'fixedwidth
(org-odt-encode-plain-text line
)) "\n"))
1129 (t (concat line
"\n"))))
1131 (defun org-odt-format-comment (fmt &rest args
)
1132 (let ((comment (apply 'format fmt args
)))
1133 (format "\n<!-- %s -->\n" comment
)))
1135 (defun org-odt-format-org-entity (wd)
1136 (org-entity-get-representation wd
'utf8
))
1138 (defun org-odt-fill-tabs-and-spaces (line)
1139 (replace-regexp-in-string
1140 "\\([\t]\\|\\([ ]+\\)\\)" (lambda (s)
1142 ((string= s
"\t") (org-odt-format-tabs))
1143 (t (org-odt-format-spaces (length s
))))) line
))
1145 (defcustom org-export-odt-fontify-srcblocks t
1146 "Specify whether or not source blocks need to be fontified.
1147 Turn this option on if you want to colorize the source code
1148 blocks in the exported file. For colorization to work, you need
1149 to make available an enhanced version of `htmlfontify' library."
1151 :group
'org-export-odt
)
1153 (defun org-odt-format-source-line-with-line-number-and-label
1154 (line rpllbl num fontifier par-style
)
1156 (let ((keep-label (not (numberp rpllbl
)))
1157 (ref (org-find-text-property-in-string 'org-coderef line
)))
1158 (setq line
(concat line
(and keep-label ref
(format "(%s)" ref
))))
1159 (setq line
(funcall fontifier line
))
1161 (setq line
(org-odt-format-target line
(concat "coderef-" ref
))))
1162 (setq line
(org-odt-format-stylized-paragraph par-style line
))
1164 (org-odt-format-tags '("<text:list-item>" .
"</text:list-item>") line
))))
1166 (defun org-odt-format-source-code-or-example-plain
1167 (lines lang caption textareap cols rows num cont rpllbl fmt
)
1168 "Format source or example blocks much like fixedwidth blocks.
1169 Use this when `org-export-odt-fontify-srcblocks' option is turned
1171 (let* ((lines (org-split-string lines
"[\r\n]"))
1172 (line-count (length lines
))
1177 (org-odt-format-source-line-with-line-number-and-label
1178 line rpllbl num
'org-odt-encode-plain-text
1179 (if (= i line-count
) "OrgFixedWidthBlockLastLine"
1180 "OrgFixedWidthBlock")))
1183 (defvar org-src-block-paragraph-format
1184 "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
1185 <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
1186 <style:background-image/>
1187 </style:paragraph-properties>
1188 <style:text-properties fo:color=\"%s\"/>
1190 "Custom paragraph style for colorized source and example blocks.
1191 This style is much the same as that of \"OrgFixedWidthBlock\"
1192 except that the foreground and background colors are set
1193 according to the default face identified by the `htmlfontify'.")
1195 (defvar hfy-optimisations
)
1196 (declare-function hfy-face-to-style
"htmlfontify" (fn))
1197 (declare-function hfy-face-or-def-to-name
"htmlfontify" (fn))
1199 (defun org-odt-hfy-face-to-css (fn)
1200 "Create custom style for face FN.
1201 When FN is the default face, use it's foreground and background
1202 properties to create \"OrgSrcBlock\" paragraph style. Otherwise
1203 use it's color attribute to create a character style whose name
1204 is obtained from FN. Currently all attributes of FN other than
1207 The style name for a face FN is derived using the following
1208 operations on the face name in that order - de-dash, CamelCase
1209 and prefix with \"OrgSrc\". For example,
1210 `font-lock-function-name-face' is associated with
1211 \"OrgSrcFontLockFunctionNameFace\"."
1212 (let* ((css-list (hfy-face-to-style fn
))
1213 (style-name ((lambda (fn)
1216 'capitalize
(split-string
1217 (hfy-face-or-def-to-name fn
) "-")
1219 (color-val (cdr (assoc "color" css-list
)))
1220 (background-color-val (cdr (assoc "background" css-list
)))
1221 (style (and org-export-odt-create-custom-styles-for-srcblocks
1224 (format org-src-block-paragraph-format
1225 background-color-val color-val
))
1229 <style:style style:name=\"%s\" style:family=\"text\">
1230 <style:text-properties fo:color=\"%s\"/>
1231 </style:style>" style-name color-val
))))))
1232 (cons style-name style
)))
1234 (defun org-odt-insert-custom-styles-for-srcblocks (styles)
1235 "Save STYLES used for colorizing of source blocks.
1236 Update styles.xml with styles that were collected as part of
1237 `org-odt-hfy-face-to-css' callbacks."
1239 (with-current-buffer
1240 (find-file-noselect (expand-file-name "styles.xml") t
)
1241 (goto-char (point-min))
1242 (when (re-search-forward "</office:styles>" nil t
)
1243 (goto-char (match-beginning 0))
1244 (insert "\n<!-- Org Htmlfontify Styles -->\n" styles
"\n")))))
1246 (defun org-odt-format-source-code-or-example-colored
1247 (lines lang caption textareap cols rows num cont rpllbl fmt
)
1248 "Format source or example blocks using `htmlfontify-string'.
1249 Use this routine when `org-export-odt-fontify-srcblocks' option
1251 (let* ((lang-m (and lang
(or (cdr (assoc lang org-src-lang-modes
)) lang
)))
1252 (mode (and lang-m
(intern (concat (if (symbolp lang-m
)
1253 (symbol-name lang-m
)
1255 (org-inhibit-startup t
)
1256 (org-startup-folded nil
)
1257 (lines (with-temp-buffer
1259 (if (functionp mode
) (funcall mode
) (fundamental-mode))
1260 (font-lock-fontify-buffer)
1262 (hfy-html-quote-regex "\\([<\"&> ]\\)")
1263 (hfy-html-quote-map '(("\"" """)
1268 (" " "<text:tab/>")))
1269 (hfy-face-to-css 'org-odt-hfy-face-to-css
)
1270 (hfy-optimisations-1 (copy-seq hfy-optimisations
))
1271 (hfy-optimisations (add-to-list 'hfy-optimisations-1
1273 (hfy-begin-span-handler
1274 (lambda (style text-block text-id text-begins-block-p
)
1275 (insert (format "<text:span text:style-name=\"%s\">" style
))))
1276 (hfy-end-span-handler (lambda nil
(insert "</text:span>"))))
1277 (when (fboundp 'htmlfontify-string
)
1278 (let* ((lines (org-split-string lines
"[\r\n]"))
1279 (line-count (length lines
))
1284 (org-odt-format-source-line-with-line-number-and-label
1285 line rpllbl num
'htmlfontify-string
1286 (if (= i line-count
) "OrgSrcBlockLastLine" "OrgSrcBlock")))
1289 (defun org-odt-format-source-code-or-example (lines lang caption textareap
1292 "Format source or example blocks for export.
1293 Use `org-odt-format-source-code-or-example-plain' or
1294 `org-odt-format-source-code-or-example-colored' depending on the
1295 value of `org-export-odt-fontify-srcblocks."
1296 (setq lines
(org-export-number-lines
1297 lines
0 0 num cont rpllbl fmt
'preprocess
)
1299 (or (and org-export-odt-fontify-srcblocks
1300 (or (featurep 'htmlfontify
)
1301 ;; htmlfontify.el was introduced in Emacs 23.2
1302 ;; So load it with some caution
1303 (require 'htmlfontify nil t
))
1304 (fboundp 'htmlfontify-string
)
1305 'org-odt-format-source-code-or-example-colored
)
1306 'org-odt-format-source-code-or-example-plain
)
1307 lines lang caption textareap cols rows num cont rpllbl fmt
))
1309 (let ((extra (format " text:continue-numbering=\"%s\""
1310 (if cont
"true" "false"))))
1311 (org-odt-format-tags
1312 '("<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>"
1313 .
"</text:list>") lines extra
))))
1315 (defun org-odt-remap-stylenames (style-name)
1317 (cdr (assoc style-name
'(("timestamp-wrapper" .
"OrgTimestampWrapper")
1318 ("timestamp" .
"OrgTimestamp")
1319 ("timestamp-kwd" .
"OrgTimestampKeyword")
1321 ("todo" .
"OrgTodo")
1322 ("done" .
"OrgDone")
1323 ("target" .
"OrgTarget"))))
1326 (defun org-odt-format-fontify (text style
&optional id
)
1330 (org-odt-remap-stylenames style
))
1332 (org-odt-get-style-name-for-entity 'character style
))
1334 (assert (< 1 (length style
)))
1335 (let ((parent-style (pop style
)))
1336 (mapconcat (lambda (s)
1337 ;; (assert (stringp s) t)
1338 (org-odt-remap-stylenames s
)) style
"")
1339 (org-odt-remap-stylenames parent-style
)))
1340 (t (error "Don't how to handle style %s" style
)))))
1341 (org-odt-format-tags
1342 '("<text:span text:style-name=\"%s\">" .
"</text:span>")
1345 (defun org-odt-relocate-relative-path (path dir
)
1346 (if (file-name-absolute-p path
) path
1347 (file-relative-name (expand-file-name path dir
)
1348 (expand-file-name "eyecandy" dir
))))
1350 (defun org-odt-format-inline-image (thefile)
1351 (let* ((thelink (if (file-name-absolute-p thefile
) thefile
1352 (org-xml-format-href
1353 (org-odt-relocate-relative-path
1354 thefile org-current-export-file
))))
1356 (org-odt-format-tags
1357 "<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1358 (if org-export-odt-embed-images
1359 (org-odt-copy-image-file thefile
) thelink
))))
1360 (org-export-odt-format-image thefile href
)))
1362 (defun org-export-odt-format-formula (src href
&optional embed-as
)
1363 "Create image tag with source and attributes."
1365 (let* ((caption (org-find-text-property-in-string 'org-caption src
))
1366 (caption (and caption
(org-xml-format-desc caption
)))
1367 (label (org-find-text-property-in-string 'org-label src
))
1368 (latex-frag (org-find-text-property-in-string 'org-latex-src src
))
1369 (embed-as (or embed-as
1371 (org-find-text-property-in-string
1372 'org-latex-src-embed-type src
))
1373 (if (or caption label
) 'paragraph
'character
)))
1376 (setq href
(org-propertize href
:title
"LaTeX Fragment"
1377 :description latex-frag
)))
1379 ((eq embed-as
'character
)
1380 (org-odt-format-entity "InlineFormula" href width height
))
1382 (org-lparse-end-paragraph)
1383 (org-lparse-insert-list-table
1384 `((,(org-odt-format-entity
1385 (if caption
"CaptionedDisplayFormula" "DisplayFormula")
1386 href width height caption nil
)
1388 (org-odt-format-entity-caption label nil
"__MathFormula__"))))
1389 nil nil nil
"OrgEquation" nil
'((1 "c" 8) (2 "c" 1)))
1390 (throw 'nextline nil
))))))
1392 (defvar org-odt-embedded-formulas-count
0)
1393 (defun org-odt-copy-formula-file (path)
1394 "Returns the internal name of the file"
1395 (let* ((src-file (expand-file-name
1396 path
(file-name-directory org-current-export-file
)))
1397 (target-dir (format "Formula-%04d/"
1398 (incf org-odt-embedded-formulas-count
)))
1399 (target-file (concat target-dir
"content.xml")))
1400 (when (not org-lparse-to-buffer
)
1401 (message "Embedding %s as %s ..."
1402 (substring-no-properties path
) target-file
)
1404 (make-directory target-dir
)
1405 (org-odt-create-manifest-file-entry
1406 "application/vnd.oasis.opendocument.formula" target-dir
"1.2")
1408 (case (org-odt-is-formula-link-p src-file
)
1410 (copy-file src-file target-file
'overwrite
))
1412 (org-odt-zip-extract-one src-file
"content.xml" target-dir
))
1414 (error "%s is not a formula file" src-file
)))
1416 (org-odt-create-manifest-file-entry "text/xml" target-file
))
1419 (defun org-odt-format-inline-formula (thefile)
1420 (let* ((thelink (if (file-name-absolute-p thefile
) thefile
1421 (org-xml-format-href
1422 (org-odt-relocate-relative-path
1423 thefile org-current-export-file
))))
1425 (org-odt-format-tags
1426 "<draw:object xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>" ""
1427 (file-name-directory (org-odt-copy-formula-file thefile
)))))
1428 (org-export-odt-format-formula thefile href
)))
1430 (defun org-odt-is-formula-link-p (file)
1431 (let ((case-fold-search nil
))
1433 ((string-match "\\.\\(mathml\\|mml\\)\\'" file
)
1435 ((string-match "\\.odf\\'" file
)
1438 (defun org-odt-format-org-link (opt-plist type-1 path fragment desc attr
1440 "Make a OpenDocument link.
1441 OPT-PLIST is an options list.
1442 TYPE-1 is the device-type of the link (THIS://foo.html).
1443 PATH is the path of the link (http://THIS#location).
1444 FRAGMENT is the fragment part of the link, if any (foo.html#THIS).
1445 DESC is the link description, if any.
1446 ATTR is a string of other attributes of the a element."
1447 (declare (special org-lparse-par-open
))
1449 (let* ((may-inline-p
1450 (and (member type-1
'("http" "https" "file"))
1451 (org-lparse-should-inline-p path descp
)
1453 (type (if (equal type-1
"id") "file" type-1
))
1457 ;; check for inlined images
1458 ((and (member type
'("file"))
1461 filename org-export-odt-inline-image-extensions
)
1462 (or (eq t org-export-odt-inline-images
)
1463 (and org-export-odt-inline-images
(not descp
))))
1464 (org-odt-format-inline-image thefile
))
1465 ;; check for embedded formulas
1466 ((and (member type
'("file"))
1468 (org-odt-is-formula-link-p filename
)
1470 (org-odt-format-inline-formula thefile
))
1471 ((string= type
"coderef")
1472 (let* ((ref fragment
)
1473 (lineno-or-ref (cdr (assoc ref org-export-code-refs
)))
1474 (desc (and descp desc
))
1475 (org-odt-suppress-xref nil
)
1476 (href (org-xml-format-href (concat "#coderef-" ref
))))
1478 ((and (numberp lineno-or-ref
) (not desc
))
1479 (org-odt-format-link lineno-or-ref href
))
1480 ((and (numberp lineno-or-ref
) desc
1481 (string-match (regexp-quote (concat "(" ref
")")) desc
))
1482 (format (replace-match "%s" t t desc
)
1483 (org-odt-format-link lineno-or-ref href
)))
1486 (if (and desc
(string-match
1487 (regexp-quote (concat "(" ref
")"))
1489 (replace-match "%s" t t desc
)
1492 (org-odt-format-link (org-xml-format-desc desc
) href
)))))
1494 (when (string= type
"file")
1497 ((file-name-absolute-p path
)
1498 (concat "file://" (expand-file-name path
)))
1499 (t (org-odt-relocate-relative-path
1500 thefile org-current-export-file
)))))
1502 (when (and (member type
'("" "http" "https" "file")) fragment
)
1503 (setq thefile
(concat thefile
"#" fragment
)))
1505 (setq thefile
(org-xml-format-href thefile
))
1507 (when (not (member type
'("" "file")))
1508 (setq thefile
(concat type
":" thefile
)))
1510 (let ((org-odt-suppress-xref nil
))
1511 (org-odt-format-link
1512 (org-xml-format-desc desc
) thefile attr
)))))))
1514 (defun org-odt-format-heading (text level
&optional id
)
1515 (let* ((text (if id
(org-odt-format-target text id
) text
)))
1516 (org-odt-format-tags
1517 '("<text:h text:style-name=\"Heading_20_%s\" text:outline-level=\"%s\">" .
1518 "</text:h>") text level level
)))
1520 (defun org-odt-format-headline (title extra-targets tags
1521 &optional snumber level
)
1523 (org-lparse-format 'EXTRA-TARGETS extra-targets
)
1525 ;; No need to generate section numbers. They are auto-generated by
1528 ;; (concat (org-lparse-format 'SECTION-NUMBER snumber level) " ")
1530 (and tags
(concat (org-lparse-format 'SPACES
3)
1531 (org-lparse-format 'ORG-TAGS tags
)))))
1533 (defun org-odt-format-anchor (text name
&optional class
)
1534 (org-odt-format-target text name
))
1536 (defun org-odt-format-bookmark (text id
)
1538 (org-odt-format-tags "<text:bookmark text:name=\"%s\"/>" text id
)
1541 (defun org-odt-format-target (text id
)
1542 (let ((name (concat org-export-odt-bookmark-prefix id
)))
1544 (and id
(org-odt-format-tags
1545 "<text:bookmark-start text:name=\"%s\"/>" "" name
))
1546 (org-odt-format-bookmark text id
)
1547 (and id
(org-odt-format-tags
1548 "<text:bookmark-end text:name=\"%s\"/>" "" name
)))))
1550 (defun org-odt-format-footnote (n def
)
1551 (let ((id (concat "fn" n
))
1552 (note-class "footnote")
1553 (par-style "Footnote"))
1554 (org-odt-format-tags
1555 '("<text:note text:id=\"%s\" text:note-class=\"%s\">" .
1558 (org-odt-format-tags
1559 '("<text:note-citation>" .
"</text:note-citation>")
1561 (org-odt-format-tags
1562 '("<text:note-body>" .
"</text:note-body>")
1566 (defun org-odt-format-footnote-reference (n def refcnt
)
1568 (org-odt-format-footnote n def
)
1569 (org-odt-format-footnote-ref n
)))
1571 (defun org-odt-format-footnote-ref (n)
1572 (let ((note-class "footnote")
1574 (ref-name (concat "fn" n
)))
1575 (org-odt-format-tags
1576 '("<text:span text:style-name=\"%s\">" .
"</text:span>")
1577 (org-odt-format-tags
1578 '("<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">" .
"</text:note-ref>")
1579 n note-class ref-format ref-name
)
1582 (defun org-odt-get-image-name (file-name)
1586 (concat (sha1 file-name
) "." (file-name-extension file-name
)) "Pictures")))
1588 (defun org-export-odt-format-image (src href
&optional embed-as
)
1589 "Create image tag with source and attributes."
1591 (let* ((caption (org-find-text-property-in-string 'org-caption src
))
1592 (caption (and caption
(org-xml-format-desc caption
)))
1593 (attr (org-find-text-property-in-string 'org-attributes src
))
1594 (label (org-find-text-property-in-string 'org-label src
))
1595 (latex-frag (org-find-text-property-in-string
1596 'org-latex-src src
))
1597 (category (and latex-frag
"__DvipngImage__"))
1598 (embed-as (or embed-as
1600 (or (org-find-text-property-in-string
1601 'org-latex-src-embed-type src
) 'character
)
1603 (attr-plist (org-lparse-get-block-params attr
))
1604 (size (org-odt-image-size-from-file
1605 src
(plist-get attr-plist
:width
)
1606 (plist-get attr-plist
:height
)
1607 (plist-get attr-plist
:scale
) nil embed-as
))
1608 (width (car size
)) (height (cdr size
)))
1610 (setq href
(org-propertize href
:title
"LaTeX Fragment"
1611 :description latex-frag
)))
1613 ((not (or caption label
))
1615 (paragraph (org-odt-format-entity "DisplayImage" href width height
))
1616 (character (org-odt-format-entity "InlineImage" href width height
))
1617 (t (error "Unknown value for embed-as %S" embed-as
))))
1619 (org-odt-format-entity
1620 "CaptionedDisplayImage" href width height caption label category
))))))
1622 (defun org-odt-format-object-description (title description
)
1623 (concat (and title
(org-odt-format-tags
1624 '("<svg:title>" .
"</svg:title>")
1625 (org-odt-encode-plain-text title t
)))
1626 (and description
(org-odt-format-tags
1627 '("<svg:desc>" .
"</svg:desc>")
1628 (org-odt-encode-plain-text description t
)))))
1630 (defun org-odt-format-frame (text width height style
&optional
1634 (if width
(format " svg:width=\"%0.2fcm\"" width
) "")
1635 (if height
(format " svg:height=\"%0.2fcm\"" height
) "")
1637 (format " text:anchor-type=\"%s\"" (or anchor-type
"paragraph")))))
1638 (org-odt-format-tags
1639 '("<draw:frame draw:style-name=\"%s\"%s>" .
"</draw:frame>")
1640 (concat text
(org-odt-format-object-description
1641 (get-text-property 0 :title text
)
1642 (get-text-property 0 :description text
)))
1643 style frame-attrs
)))
1645 (defun org-odt-format-textbox (text width height style
&optional
1647 (org-odt-format-frame
1648 (org-odt-format-tags
1649 '("<draw:text-box %s>" .
"</draw:text-box>")
1650 text
(concat (format " fo:min-height=\"%0.2fcm\"" (or height
.2))
1651 (format " fo:min-width=\"%0.2fcm\"" (or width
.2))))
1652 width nil style extra anchor-type
))
1654 (defun org-odt-format-inlinetask (heading content
1655 &optional todo priority tags
)
1656 (org-odt-format-stylized-paragraph
1657 nil
(org-odt-format-textbox
1658 (concat (org-odt-format-stylized-paragraph
1659 "OrgInlineTaskHeading"
1661 'HEADLINE
(concat (org-lparse-format-todo todo
) " " heading
)
1663 content
) nil nil
"OrgInlineTaskFrame" " style:rel-width=\"100%\"")))
1665 (defvar org-odt-entity-frame-styles
1666 '(("InlineImage" "__Figure__" ("OrgInlineImage" nil
"as-char"))
1667 ("DisplayImage" "__Figure__" ("OrgDisplayImage" nil
"paragraph"))
1668 ("CaptionedDisplayImage" "__Figure__"
1669 ("OrgCaptionedImage"
1670 " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
1671 ("OrgImageCaptionFrame"))
1672 ("InlineFormula" "__MathFormula__" ("OrgInlineFormula" nil
"as-char"))
1673 ("DisplayFormula" "__MathFormula__" ("OrgDisplayFormula" nil
"as-char"))
1674 ("CaptionedDisplayFormula" "__MathFormula__"
1675 ("OrgCaptionedFormula" nil
"paragraph")
1676 ("OrgFormulaCaptionFrame" nil
"as-char"))))
1678 (defun org-odt-format-entity (entity href width height
1679 &optional caption label category
)
1680 (let* ((entity-style (assoc entity org-odt-entity-frame-styles
))
1681 (entity-frame (apply 'org-odt-format-frame
1682 href width height
(nth 2 entity-style
))))
1683 (if (not (or caption label
)) entity-frame
1684 (apply 'org-odt-format-textbox
1685 (org-odt-format-stylized-paragraph
1687 (concat entity-frame
1688 (org-odt-format-entity-caption
1689 label caption
(or category
(nth 1 entity-style
)))))
1690 width height
(nth 3 entity-style
)))))
1692 (defvar org-odt-embedded-images-count
0)
1693 (defun org-odt-copy-image-file (path)
1694 "Returns the internal name of the file"
1695 (let* ((image-type (file-name-extension path
))
1696 (media-type (format "image/%s" image-type
))
1697 (src-file (expand-file-name
1698 path
(file-name-directory org-current-export-file
)))
1699 (target-dir "Images/")
1701 (format "%s%04d.%s" target-dir
1702 (incf org-odt-embedded-images-count
) image-type
)))
1703 (when (not org-lparse-to-buffer
)
1704 (message "Embedding %s as %s ..."
1705 (substring-no-properties path
) target-file
)
1707 (when (= 1 org-odt-embedded-images-count
)
1708 (make-directory target-dir
)
1709 (org-odt-create-manifest-file-entry "" target-dir
))
1711 (copy-file src-file target-file
'overwrite
)
1712 (org-odt-create-manifest-file-entry media-type target-file
))
1715 (defvar org-export-odt-image-size-probe-method
1716 '(emacs imagemagick force
)
1717 "Ordered list of methods by for determining size of an embedded
1720 (defvar org-export-odt-default-image-sizes-alist
1721 '(("character" .
(5 .
0.4))
1722 ("paragraph" .
(5 .
5)))
1723 "Hardcoded image dimensions one for each of the anchor
1726 ;; A4 page size is 21.0 by 29.7 cms
1727 ;; The default page settings has 2cm margin on each of the sides. So
1728 ;; the effective text area is 17.0 by 25.7 cm
1729 (defvar org-export-odt-max-image-size
'(17.0 .
20.0)
1730 "Limiting dimensions for an embedded image.")
1732 (defun org-odt-do-image-size (probe-method file
&optional dpi anchor-type
)
1733 (setq dpi
(or dpi org-export-odt-pixels-per-inch
))
1734 (setq anchor-type
(or anchor-type
"paragraph"))
1735 (flet ((size-in-cms (size-in-pixels)
1736 (flet ((pixels-to-cms (pixels)
1737 (let* ((cms-per-inch 2.54)
1738 (inches (/ pixels dpi
)))
1739 (* cms-per-inch inches
))))
1741 (cons (pixels-to-cms (car size-in-pixels
))
1742 (pixels-to-cms (cdr size-in-pixels
)))))))
1745 (size-in-cms (ignore-errors (image-size (create-image file
) 'pixels
))))
1748 (let ((dim (shell-command-to-string
1749 (format "identify -format \"%%w:%%h\" \"%s\"" file
))))
1750 (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim
)
1751 (cons (string-to-number (match-string 1 dim
))
1752 (string-to-number (match-string 2 dim
)))))))
1754 (cdr (assoc-string anchor-type
1755 org-export-odt-default-image-sizes-alist
))))))
1757 (defun org-odt-image-size-from-file (file &optional user-width
1758 user-height scale dpi embed-as
)
1759 (unless (file-name-absolute-p file
)
1760 (setq file
(expand-file-name
1761 file
(file-name-directory org-current-export-file
))))
1762 (let* (size width height
)
1763 (unless (and user-height user-width
)
1764 (loop for probe-method in org-export-odt-image-size-probe-method
1766 do
(setq size
(org-odt-do-image-size
1767 probe-method file dpi embed-as
)))
1768 (or size
(error "Cannot determine Image size. Aborting ..."))
1769 (setq width
(car size
) height
(cdr size
)))
1772 (setq width
(* width scale
) height
(* height scale
)))
1773 ((and user-height user-width
)
1774 (setq width user-width height user-height
))
1776 (setq width
(* user-height
(/ width height
)) height user-height
))
1778 (setq height
(* user-width
(/ height width
)) width user-width
))
1780 ;; ensure that an embedded image fits comfortably within a page
1781 (let ((max-width (car org-export-odt-max-image-size
))
1782 (max-height (cdr org-export-odt-max-image-size
)))
1783 (when (or (> width max-width
) (> height max-height
))
1784 (let* ((scale1 (/ max-width width
))
1785 (scale2 (/ max-height height
))
1786 (scale (min scale1 scale2
)))
1787 (setq width
(* scale width
) height
(* scale height
)))))
1788 (cons width height
)))
1790 (defvar org-odt-entity-labels-alist nil
1791 "Associate Labels with the Labelled entities.
1792 Each element of the alist is of the form (LABEL-NAME
1793 CATEGORY-NAME SEQNO LABEL-STYLE-NAME). LABEL-NAME is same as
1794 that specified by \"#+LABEL: ...\" line. CATEGORY-NAME is the
1795 type of the entity that LABEL-NAME is attached to. CATEGORY-NAME
1796 can be one of \"Table\", \"Figure\" or \"Equation\". SEQNO is
1797 the unique number assigned to the referenced entity on a
1798 per-CATEGORY basis. It is generated sequentially and is 1-based.
1799 LABEL-STYLE-NAME is a key `org-odt-label-styles'.
1801 See `org-odt-add-label-definition' and
1802 `org-odt-fixup-label-references'.")
1804 (defvar org-odt-entity-counts-plist nil
1805 "Plist of running counters of SEQNOs for each of the CATEGORY-NAMEs.
1806 See `org-odt-entity-labels-alist' for known CATEGORY-NAMEs.")
1808 (defvar org-odt-label-styles
1809 '(("text" "(%n)" "text" "(%n)")
1810 ("category-and-value" "%e %n%c" "category-and-value" "%e %n"))
1811 "Specify how labels are applied and referenced.
1812 This is an alist where each element is of the
1813 form (LABEL-STYLE-NAME LABEL-ATTACH-FMT LABEL-REF-MODE
1816 LABEL-ATTACH-FMT controls how labels and captions are attached to
1817 an entity. It may contain following specifiers - %e, %n and %c.
1818 %e is replaced with the CATEGORY-NAME. %n is replaced with
1819 \"<text:sequence ...> SEQNO </text:sequence>\". %c is replaced
1820 with CAPTION. See `org-odt-format-label-definition'.
1822 LABEL-REF-MODE and LABEL-REF-FMT controls how label references
1823 are generated. The following XML is generated for a label
1824 reference - \"<text:sequence-ref
1825 text:reference-format=\"LABEL-REF-MODE\" ...> LABEL-REF-FMT
1826 </text:sequence-ref>\". LABEL-REF-FMT may contain following
1827 specifiers - %e and %n. %e is replaced with the CATEGORY-NAME.
1828 %n is replaced with SEQNO. See
1829 `org-odt-format-label-reference'.")
1831 (defvar org-odt-category-map-alist
1832 '(("__Table__" "Table" "category-and-value")
1833 ("__Figure__" "Figure" "category-and-value")
1834 ("__MathFormula__" "Equation" "text")
1835 ("__DvipngImage__" "Equation" "category-and-value"))
1836 "Map a CATEGORY-HANDLE to CATEGORY-NAME and LABEL-STYLE.
1837 This is an alist where each element is of the form
1838 \\(CATEGORY-HANDLE CATEGORY-NAME LABEL-STYLE\\). CATEGORY_HANDLE
1839 could either be one of the internal handles (as seen above) or be
1840 derived from the \"#+LABEL:<label-name>\" specification. See
1841 `org-export-odt-get-category-from-label'. CATEGORY-NAME and
1842 LABEL-STYLE are used for generating ODT labels. See
1843 `org-odt-label-styles'.")
1845 (defvar org-export-odt-user-categories
1846 '("Illustration" "Table" "Text" "Drawing" "Equation" "Figure"))
1848 (defvar org-export-odt-get-category-from-label nil
1849 "Should category of label be inferred from label itself.
1850 When this option is non-nil, a label is parsed in to two
1851 component parts delimited by a \":\" (colon) as shown here -
1852 #+LABEL:[CATEGORY-HANDLE:]EXTRA. The CATEGORY-HANDLE is mapped
1853 to a CATEGORY-NAME and LABEL-STYLE using
1854 `org-odt-category-map-alist'. (If no such map is provided and
1855 CATEGORY-NAME is set to CATEGORY-HANDLE and LABEL-STYLE is set to
1856 \"category-and-value\"). If CATEGORY-NAME so obtained is listed
1857 under `org-export-odt-user-categories' then the user specified
1858 styles are used. Otherwise styles as determined by the internal
1859 CATEGORY-HANDLE is used. See
1860 `org-odt-get-label-category-and-style' for details.")
1862 (defun org-odt-get-label-category-and-style (label default-category
)
1863 "See `org-export-odt-get-category-from-label'."
1864 (let ((default-category-map
1865 (assoc default-category org-odt-category-map-alist
))
1866 user-category user-category-map category
)
1868 ((not org-export-odt-get-category-from-label
)
1869 default-category-map
)
1870 ((not (setq user-category
1872 (and (string-match "\\`\\(.*\\):.+" label
)
1873 (match-string 1 label
)))))
1874 default-category-map
)
1876 (setq user-category-map
1877 (or (assoc user-category org-odt-category-map-alist
)
1878 (list nil user-category
"category-and-value"))
1879 category
(nth 1 user-category-map
))
1880 (if (member category org-export-odt-user-categories
)
1882 default-category-map
)))))
1884 (defun org-odt-add-label-definition (label default-category
)
1885 "Create an entry in `org-odt-entity-labels-alist' and return it."
1886 (setq label
(substring-no-properties label
))
1887 (let* ((label-props (org-odt-get-label-category-and-style
1888 label default-category
))
1889 (category (nth 1 label-props
))
1891 (label-style (nth 2 label-props
))
1892 (sequence-var (intern (mapconcat
1894 (org-split-string counter
) "-")))
1895 (seqno (1+ (or (plist-get org-odt-entity-counts-plist sequence-var
)
1897 (label-props (list label category seqno label-style
)))
1898 (setq org-odt-entity-counts-plist
1899 (plist-put org-odt-entity-counts-plist sequence-var seqno
))
1900 (push label-props org-odt-entity-labels-alist
)
1903 (defun org-odt-format-label-definition (caption label category seqno label-style
)
1906 (cadr (assoc-string label-style org-odt-label-styles t
))
1908 (?n .
,(org-odt-format-tags
1909 '("<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">" .
"</text:sequence>")
1910 (format "%d" seqno
) label category category
))
1911 (?c .
,(or (and caption
(concat ": " caption
)) "")))))
1913 (defun org-odt-format-label-reference (label category seqno label-style
)
1916 (let* ((fmt (cddr (assoc-string label-style org-odt-label-styles t
)))
1919 (org-odt-format-tags
1920 '("<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">"
1921 .
"</text:sequence-ref>")
1922 (format-spec fmt2
`((?e .
,category
)
1923 (?n .
,(format "%d" seqno
)))) fmt1 label
))))
1925 (defun org-odt-fixup-label-references ()
1926 (goto-char (point-min))
1927 (while (re-search-forward
1928 "<text:sequence-ref text:ref-name=\"\\([^\"]+\\)\">[ \t\n]*</text:sequence-ref>"
1930 (let* ((label (match-string 1))
1931 (label-def (assoc label org-odt-entity-labels-alist
))
1933 (apply 'org-odt-format-label-reference label-def
))))
1934 (if rpl
(replace-match rpl t t
)
1936 (format "Unable to resolve reference to label \"%s\"" label
))))))
1938 (defun org-odt-format-entity-caption (label caption category
)
1940 (apply 'org-odt-format-label-definition
1941 caption
(org-odt-add-label-definition label category
)))
1944 (defun org-odt-format-tags (tag text
&rest args
)
1945 (let ((prefix (when org-lparse-encode-pending
"@"))
1946 (suffix (when org-lparse-encode-pending
"@")))
1947 (apply 'org-lparse-format-tags tag text prefix suffix args
)))
1949 (defvar org-odt-manifest-file-entries nil
)
1950 (defun org-odt-init-outfile (filename)
1951 (unless (executable-find "zip")
1952 ;; Not at all OSes ship with zip by default
1953 (error "Executable \"zip\" needed for creating OpenDocument files"))
1955 (let* ((outdir (make-temp-file
1956 (format org-export-odt-tmpdir-prefix org-lparse-backend
) t
))
1957 (content-file (expand-file-name "content.xml" outdir
)))
1960 (with-current-buffer (find-file-noselect content-file t
))
1963 (setq org-odt-manifest-file-entries nil
1964 org-odt-embedded-images-count
0
1965 org-odt-embedded-formulas-count
0
1966 org-odt-entity-labels-alist nil
1967 org-odt-entity-counts-plist nil
)
1970 (defcustom org-export-odt-prettify-xml nil
1971 "Specify whether or not the xml output should be prettified.
1972 When this option is turned on, `indent-region' is run on all
1973 component xml buffers before they are saved. Turn this off for
1974 regular use. Turn this on if you need to examine the xml
1976 :group
'org-export-odt
1979 (defvar hfy-user-sheet-assoc
) ; bound during org-do-lparse
1980 (defun org-odt-save-as-outfile (target opt-plist
)
1982 (org-odt-update-meta-file opt-plist
)
1984 ;; write styles file
1985 (when (equal org-lparse-backend
'odt
)
1986 (org-odt-update-styles-file opt-plist
))
1988 ;; create mimetype file
1989 (let ((mimetype (org-odt-write-mimetype-file org-lparse-backend
)))
1990 (org-odt-create-manifest-file-entry mimetype
"/" "1.2"))
1992 ;; create a manifest entry for content.xml
1993 (org-odt-create-manifest-file-entry "text/xml" "content.xml")
1995 ;; write out the manifest entries before zipping
1996 (org-odt-write-manifest-file)
1998 (let ((xml-files '("mimetype" "META-INF/manifest.xml" "content.xml"
2000 (zipdir default-directory
))
2001 (when (equal org-lparse-backend
'odt
)
2002 (push "styles.xml" xml-files
))
2003 (message "Switching to directory %s" (expand-file-name zipdir
))
2005 ;; save all xml files
2006 (mapc (lambda (file)
2007 (with-current-buffer
2008 (find-file-noselect (expand-file-name file
) t
)
2009 ;; prettify output if needed
2010 (when org-export-odt-prettify-xml
2011 (indent-region (point-min) (point-max)))
2015 (let* ((target-name (file-name-nondirectory target
))
2016 (target-dir (file-name-directory target
))
2017 (cmds `(("zip" "-mX0" ,target-name
"mimetype")
2018 ("zip" "-rmTq" ,target-name
"."))))
2019 (when (file-exists-p target
)
2020 ;; FIXME: If the file is locked this throws a cryptic error
2021 (delete-file target
))
2023 (let ((coding-system-for-write 'no-conversion
) exitcode err-string
)
2024 (message "Creating odt file...")
2027 (message "Running %s" (mapconcat 'identity cmd
" "))
2029 (with-output-to-string
2031 (apply 'call-process
(car cmd
)
2032 nil standard-output nil
(cdr cmd
)))))
2033 (or (zerop exitcode
)
2034 (ignore (message "%s" err-string
))
2035 (error "Unable to create odt file (%S)" exitcode
)))
2038 ;; move the file from outdir to target-dir
2039 (rename-file target-name target-dir
)
2041 ;; kill all xml buffers
2042 (mapc (lambda (file)
2044 (find-file-noselect (expand-file-name file zipdir
) t
)))
2047 (delete-directory zipdir
)))
2048 (message "Created %s" target
)
2049 (set-buffer (find-file-noselect target t
)))
2051 (defconst org-odt-manifest-file-entry-tag
2053 <manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
2055 (defun org-odt-create-manifest-file-entry (&rest args
)
2056 (push args org-odt-manifest-file-entries
))
2058 (defun org-odt-write-manifest-file ()
2059 (make-directory "META-INF")
2060 (let ((manifest-file (expand-file-name "META-INF/manifest.xml")))
2061 (with-current-buffer
2062 (find-file-noselect manifest-file t
)
2064 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
2065 <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
2067 (lambda (file-entry)
2068 (let* ((version (nth 2 file-entry
))
2070 (format " manifest:version=\"%s\"" version
)
2073 (format org-odt-manifest-file-entry-tag
2074 (nth 0 file-entry
) (nth 1 file-entry
) extra
))))
2075 org-odt-manifest-file-entries
)
2076 (insert "\n</manifest:manifest>"))))
2078 (defun org-odt-update-meta-file (opt-plist)
2079 (let ((date (org-odt-format-date (plist-get opt-plist
:date
)))
2080 (author (or (plist-get opt-plist
:author
) ""))
2081 (email (plist-get opt-plist
:email
))
2082 (keywords (plist-get opt-plist
:keywords
))
2083 (description (plist-get opt-plist
:description
))
2084 (title (plist-get opt-plist
:title
)))
2087 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
2088 <office:document-meta
2089 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
2090 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2091 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
2092 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
2093 xmlns:ooo=\"http://openoffice.org/2004/office\"
2094 office:version=\"1.2\">
2096 (org-odt-format-author)
2097 (org-odt-format-tags
2098 '("\n<meta:initial-creator>" .
"</meta:initial-creator>") author
)
2099 (org-odt-format-tags '("\n<dc:date>" .
"</dc:date>") date
)
2100 (org-odt-format-tags
2101 '("\n<meta:creation-date>" .
"</meta:creation-date>") date
)
2102 (org-odt-format-tags '("\n<meta:generator>" .
"</meta:generator>")
2103 (when org-export-creator-info
2104 (format "Org-%s/Emacs-%s"
2105 org-version emacs-version
)))
2106 (org-odt-format-tags '("\n<meta:keyword>" .
"</meta:keyword>") keywords
)
2107 (org-odt-format-tags '("\n<dc:subject>" .
"</dc:subject>") description
)
2108 (org-odt-format-tags '("\n<dc:title>" .
"</dc:title>") title
)
2110 " </office:meta>" "</office:document-meta>")
2111 nil
(expand-file-name "meta.xml")))
2113 ;; create a manifest entry for meta.xml
2114 (org-odt-create-manifest-file-entry "text/xml" "meta.xml"))
2116 (defun org-odt-update-styles-file (opt-plist)
2117 ;; write styles file
2118 (let ((styles-file (plist-get opt-plist
:odt-styles-file
)))
2119 (org-odt-copy-styles-file (and styles-file
2120 (read (org-trim styles-file
)))))
2122 ;; Update styles.xml - take care of outline numbering
2123 (with-current-buffer
2124 (find-file-noselect (expand-file-name "styles.xml") t
)
2125 ;; Don't make automatic backup of styles.xml file. This setting
2126 ;; prevents the backedup styles.xml file from being zipped in to
2127 ;; odt file. This is more of a hackish fix. Better alternative
2128 ;; would be to fix the zip command so that the output odt file
2129 ;; includes only the needed files and excludes any auto-generated
2130 ;; extra files like backups and auto-saves etc etc. Note that
2131 ;; currently the zip command zips up the entire temp directory so
2132 ;; that any auto-generated files created under the hood ends up in
2133 ;; the resulting odt file.
2134 (set (make-local-variable 'backup-inhibited
) t
)
2136 ;; Import local setting of `org-export-with-section-numbers'
2137 (org-lparse-bind-local-variables opt-plist
)
2138 (org-odt-configure-outline-numbering
2139 (if org-export-with-section-numbers org-export-headline-levels
0)))
2141 ;; Write custom stlyes for source blocks
2142 (org-odt-insert-custom-styles-for-srcblocks
2145 (format " %s\n" (cddr style
)))
2146 hfy-user-sheet-assoc
"")))
2148 (defun org-odt-write-mimetype-file (format)
2149 ;; create mimetype file
2152 (odt "application/vnd.oasis.opendocument.text")
2153 (odf "application/vnd.oasis.opendocument.formula")
2154 (t (error "Unknown OpenDocument backend %S" org-lparse-backend
)))))
2155 (write-region mimetype nil
(expand-file-name "mimetype"))
2158 (defun org-odt-finalize-outfile ()
2159 (org-odt-delete-empty-paragraphs))
2161 (defun org-odt-delete-empty-paragraphs ()
2162 (goto-char (point-min))
2163 (let ((open "<text:p[^>]*>")
2164 (close "</text:p>"))
2165 (while (re-search-forward (format "%s[ \r\n\t]*%s" open close
) nil t
)
2166 (replace-match ""))))
2168 (defcustom org-export-odt-convert-processes
2169 '(("BasicODConverter"
2170 ("soffice" "-norestore" "-invisible" "-headless"
2171 "\"macro:///BasicODConverter.Main.Convert(%I,%f,%O)\""))
2173 ("unoconv" "-f" "%f" "-o" "%d" "%i")))
2174 "Specify a list of document converters and their usage.
2175 The converters in this list are offered as choices while
2176 customizing `org-export-odt-convert-process'.
2178 This variable is an alist where each element is of the
2179 form (CONVERTER-NAME CONVERTER-PROCESS). CONVERTER-NAME is name
2180 of the converter. CONVERTER-PROCESS specifies the command-line
2181 syntax of the converter and is of the form (CONVERTER-PROGRAM
2182 ARG1 ARG2 ...). CONVERTER-PROGRAM is the name of the executable.
2183 ARG1, ARG2 etc are command line options that are passed to
2184 CONVERTER-PROGRAM. Format specifiers can be used in the ARGs and
2185 they are interpreted as below:
2187 %i input file name in full
2188 %I input file name as a URL
2189 %f format of the output file
2190 %o output file name in full
2191 %O output file name as a URL
2192 %d output dir in full
2193 %D output dir as a URL."
2194 :group
'org-export-odt
2197 (const :tag
"None" nil
)
2198 (alist :tag
"Converters"
2199 :key-type
(string :tag
"Converter Name")
2200 :value-type
(group (cons (string :tag
"Executable")
2201 (repeat (string :tag
"Command line args")))))))
2203 (defcustom org-export-odt-convert-process nil
2204 "Use this converter to convert from \"odt\" format to other formats.
2205 During customization, the list of converter names are populated
2206 from `org-export-odt-convert-processes'."
2207 :group
'org-export-odt
2208 :type
'(choice :convert-widget
2210 (apply 'widget-convert
(widget-type w
)
2211 (eval (car (widget-get w
:args
)))))
2212 `((const :tag
"None" nil
)
2213 ,@(mapcar (lambda (c)
2214 `(const :tag
,(car c
) ,(car c
)))
2215 org-export-odt-convert-processes
))))
2217 (defcustom org-export-odt-convert-capabilities
2219 ("odt" "ott" "doc" "rtf")
2220 (("pdf" "pdf") ("odt" "odt") ("xhtml" "html") ("rtf" "rtf")
2221 ("ott" "ott") ("doc" "doc") ("ooxml" "xml") ("html" "html")))
2223 ("html" "xhtml") (("pdf" "pdf") ("odt" "txt") ("html" "html")))
2225 ("ods" "ots" "xls" "csv")
2226 (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv")
2227 ("ods" "ods") ("xls" "xls") ("xhtml" "xhtml") ("ooxml" "xml")))
2230 (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("xhtml" "xml")
2231 ("otp" "otp") ("ppt" "ppt") ("odg" "odg") ("html" "html"))))
2232 "Specify input and output formats of `org-export-odt-convert-process'.
2233 More correctly, specify the set of input and output formats that
2234 the user is actually interested in.
2236 This variable is an alist where each element is of the
2237 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
2238 INPUT-FMT-LIST is a list of INPUT-FMTs. OUTPUT-FMT-ALIST is an
2239 alist where each element is of the form (OUTPUT-FMT
2240 OUTPUT-FILE-EXTENSION).
2242 The variable is interpreted as follows:
2243 `org-export-odt-convert-process' can take any document that is in
2244 INPUT-FMT-LIST and produce any document that is in the
2245 OUTPUT-FMT-LIST. A document converted to OUTPUT-FMT will have
2246 OUTPUT-FILE-EXTENSION as the file name extension. OUTPUT-FMT
2247 serves dual purposes:
2248 - It is used for populating completion candidates during
2249 `org-export-odt-convert' commands.
2250 - It is used as the value of \"%f\" specifier in
2251 `org-export-odt-convert-process'.
2253 DOCUMENT-CLASS is used to group a set of file formats in
2254 INPUT-FMT-LIST in to a single class.
2256 Note that this variable inherently captures how LibreOffice based
2257 converters work. LibreOffice maps documents of various formats
2258 to classes like Text, Web, Spreadsheet, Presentation etc and
2259 allow document of a given class (irrespective of it's source
2260 format) to be converted to any of the export formats associated
2263 See default setting of this variable for an typical
2265 :group
'org-export-odt
2268 (const :tag
"None" nil
)
2269 (alist :key-type
(string :tag
"Document Class")
2271 (group (repeat :tag
"Input formats" (string :tag
"Input format"))
2272 (alist :tag
"Output formats"
2273 :key-type
(string :tag
"Output format")
2275 (group (string :tag
"Output file extension")))))))
2277 (declare-function org-create-math-formula
"org"
2278 (latex-frag &optional mathml-file
))
2281 (defun org-export-odt-convert (&optional in-file out-fmt prefix-arg
)
2282 "Convert IN-FILE to format OUT-FMT using a command line converter.
2283 IN-FILE is the file to be converted. If unspecified, it defaults
2284 to variable `buffer-file-name'. OUT-FMT is the desired output
2285 format. Use `org-export-odt-convert-process' as the converter.
2286 If PREFIX-ARG is non-nil then the newly converted file is opened
2287 using `org-open-file'."
2289 (append (org-lparse-convert-read-params) current-prefix-arg
))
2290 (org-lparse-do-convert in-file out-fmt prefix-arg
))
2292 (defun org-odt-get (what &optional opt-plist
)
2295 (EXPORT-DIR (org-export-directory :html opt-plist
))
2296 (FILE-NAME-EXTENSION "odt")
2297 (EXPORT-BUFFER-NAME "*Org ODT Export*")
2298 (ENTITY-CONTROL org-odt-entity-control-callbacks-alist
)
2299 (ENTITY-FORMAT org-odt-entity-format-callbacks-alist
)
2300 (INIT-METHOD 'org-odt-init-outfile
)
2301 (FINAL-METHOD 'org-odt-finalize-outfile
)
2302 (SAVE-METHOD 'org-odt-save-as-outfile
)
2304 (and org-export-odt-convert-process
2305 (cadr (assoc-string org-export-odt-convert-process
2306 org-export-odt-convert-processes t
))))
2307 (CONVERT-CAPABILITIES
2308 (and org-export-odt-convert-process
2309 (cadr (assoc-string org-export-odt-convert-process
2310 org-export-odt-convert-processes t
))
2311 org-export-odt-convert-capabilities
))
2313 (SPECIAL-STRING-REGEXPS org-export-odt-special-string-regexps
)
2314 (INLINE-IMAGES 'maybe
)
2315 (INLINE-IMAGE-EXTENSIONS '("png" "jpeg" "jpg" "gif" "svg"))
2316 (PLAIN-TEXT-MAP '(("&" .
"&") ("<" .
"<") (">" .
">")))
2317 (TABLE-FIRST-COLUMN-AS-LABELS nil
)
2318 (FOOTNOTE-SEPARATOR (org-lparse-format 'FONTIFY
"," 'superscript
))
2319 (CODING-SYSTEM-FOR-WRITE 'utf-8
)
2320 (CODING-SYSTEM-FOR-SAVE 'utf-8
)
2321 (t (error "Unknown property: %s" what
))))
2323 (defvar org-lparse-latex-fragment-fallback
) ; set by org-do-lparse
2324 (defun org-export-odt-do-preprocess-latex-fragments ()
2325 "Convert LaTeX fragments to images."
2326 (let* ((latex-frag-opt (plist-get org-lparse-opt-plist
:LaTeX-fragments
))
2327 (latex-frag-opt ; massage the options
2328 (or (and (member latex-frag-opt
'(mathjax t
))
2329 (not (and (fboundp 'org-format-latex-mathml-available-p
)
2330 (org-format-latex-mathml-available-p)))
2331 (prog1 org-lparse-latex-fragment-fallback
2334 "LaTeX to MathML converter not available. "
2335 (format "Using %S instead."
2336 org-lparse-latex-fragment-fallback
)))))
2338 cache-dir display-msg
)
2340 ((eq latex-frag-opt
'dvipng
)
2341 (setq cache-dir
"ltxpng/")
2342 (setq display-msg
"Creating LaTeX image %s"))
2343 ((member latex-frag-opt
'(mathjax t
))
2344 (setq latex-frag-opt
'mathml
)
2345 (setq cache-dir
"ltxmathml/")
2346 (setq display-msg
"Creating MathML formula %s")))
2347 (when (and org-current-export-file
)
2349 (concat cache-dir
(file-name-sans-extension
2350 (file-name-nondirectory org-current-export-file
)))
2351 org-current-export-dir nil display-msg
2352 nil nil latex-frag-opt
))))
2354 (defadvice org-format-latex-as-mathml
2355 (after org-odt-protect-latex-fragment activate
)
2356 "Encode LaTeX fragment as XML.
2357 Do this when translation to MathML fails."
2358 (when (or (not (> (length ad-return-value
) 0))
2359 (get-text-property 0 'org-protected ad-return-value
))
2360 (setq ad-return-value
2361 (org-propertize (org-odt-encode-plain-text (ad-get-arg 0))
2362 'org-protected t
))))
2364 (defun org-export-odt-preprocess-latex-fragments ()
2365 (when (equal org-export-current-backend
'odt
)
2366 (org-export-odt-do-preprocess-latex-fragments)))
2368 (defun org-export-odt-preprocess-label-references ()
2369 (goto-char (point-min))
2370 (let (label label-components category value pretty-label
)
2371 (while (re-search-forward "\\\\ref{\\([^{}\n]+\\)}" nil t
)
2372 (org-if-unprotected-at (match-beginning 1)
2374 (let ((org-lparse-encode-pending t
)
2375 (label (match-string 1)))
2376 ;; markup generated below is mostly an eye-candy. At
2377 ;; pre-processing stage, there is no information on which
2378 ;; entity a label reference points to. The actual markup
2379 ;; is generated as part of `org-odt-fixup-label-references'
2380 ;; which gets called at the fag end of export. By this
2381 ;; time we would have seen and collected all the label
2382 ;; definitions in `org-odt-entity-labels-alist'.
2383 (org-odt-format-tags
2384 '("<text:sequence-ref text:ref-name=\"%s\">" .
2385 "</text:sequence-ref>")
2386 "" (org-add-props label
'(org-protected t
)))) t t
)))))
2388 ;; process latex fragments as part of
2389 ;; `org-export-preprocess-after-blockquote-hook'. Note that this hook
2390 ;; is the one that is closest and well before the call to
2391 ;; `org-export-attach-captions-and-attributes' in
2392 ;; `org-export-preprocess-stirng'. The above arrangement permits
2393 ;; captions, labels and attributes to be attached to png images
2394 ;; generated out of latex equations.
2395 (add-hook 'org-export-preprocess-after-blockquote-hook
2396 'org-export-odt-preprocess-latex-fragments
)
2398 (defun org-export-odt-preprocess (parameters)
2399 (org-export-odt-preprocess-label-references))
2401 (declare-function archive-zip-extract
"arc-mode.el" (archive name
))
2402 (defun org-odt-zip-extract-one (archive member
&optional target
)
2404 (let* ((target (or target default-directory
))
2405 (archive (expand-file-name archive
))
2406 (archive-zip-extract
2407 (list "unzip" "-qq" "-o" "-d" target
))
2408 exit-code command-output
)
2409 (setq command-output
2411 (setq exit-code
(archive-zip-extract archive member
))
2413 (unless (zerop exit-code
)
2414 (message command-output
)
2415 (error "Extraction failed"))))
2417 (defun org-odt-zip-extract (archive members
&optional target
)
2418 (when (atom members
) (setq members
(list members
)))
2419 (mapc (lambda (member)
2420 (org-odt-zip-extract-one archive member target
))
2423 (defun org-odt-copy-styles-file (&optional styles-file
)
2424 ;; Non-availability of styles.xml is not a critical error. For now
2425 ;; throw an error purely for aesthetic reasons.
2426 (setq styles-file
(or styles-file
2427 org-export-odt-styles-file
2428 (expand-file-name "OrgOdtStyles.xml"
2430 (error "org-odt: Missing styles file?")))
2432 ((listp styles-file
)
2433 (let ((archive (nth 0 styles-file
))
2434 (members (nth 1 styles-file
)))
2435 (org-odt-zip-extract archive members
)
2438 (when (org-file-image-p member
)
2439 (let* ((image-type (file-name-extension member
))
2440 (media-type (format "image/%s" image-type
)))
2441 (org-odt-create-manifest-file-entry media-type member
))))
2443 ((and (stringp styles-file
) (file-exists-p styles-file
))
2444 (let ((styles-file-type (file-name-extension styles-file
)))
2446 ((string= styles-file-type
"xml")
2447 (copy-file styles-file
"styles.xml" t
))
2448 ((member styles-file-type
'("odt" "ott"))
2449 (org-odt-zip-extract styles-file
"styles.xml")))))
2451 (error (format "Invalid specification of styles.xml file: %S"
2452 org-export-odt-styles-file
))))
2454 ;; create a manifest entry for styles.xml
2455 (org-odt-create-manifest-file-entry "text/xml" "styles.xml"))
2457 (defvar org-export-odt-factory-settings
2458 "d4328fb9d1b6cb211d4320ff546829f26700dc5e"
2459 "SHA1 hash of OrgOdtStyles.xml.")
2461 (defun org-odt-configure-outline-numbering (level)
2462 "Outline numbering is retained only upto LEVEL.
2463 To disable outline numbering pass a LEVEL of 0."
2464 (goto-char (point-min))
2466 "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
2468 "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
2469 (while (re-search-forward regex nil t
)
2470 (when (> (string-to-number (match-string 2)) level
)
2471 (replace-match replacement t nil
))))
2475 (defun org-export-as-odf (latex-frag &optional odf-file
)
2476 "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
2477 Use `org-create-math-formula' to convert LATEX-FRAG first to
2478 MathML. When invoked as an interactive command, use
2479 `org-latex-regexps' to infer LATEX-FRAG from currently active
2480 region. If no LaTeX fragments are found, prompt for it. Push
2481 MathML source to kill ring, if `org-export-copy-to-kill-ring' is
2485 (setq frag
(and (setq frag
(and (region-active-p)
2486 (buffer-substring (region-beginning)
2488 (loop for e in org-latex-regexps
2489 thereis
(when (string-match (nth 1 e
) frag
)
2490 (match-string (nth 2 e
) frag
)))))
2491 (read-string "LaTeX Fragment: " frag nil frag
))
2492 ,(let ((odf-filename (expand-file-name
2494 (file-name-sans-extension
2495 (or (file-name-nondirectory buffer-file-name
)))
2497 (file-name-directory buffer-file-name
))))
2498 (message "default val is %s" odf-filename
)
2499 (read-file-name "ODF filename: " nil odf-filename nil
2500 (file-name-nondirectory odf-filename
)))))
2501 (let* ((org-lparse-backend 'odf
)
2502 org-lparse-opt-plist
2503 (filename (or odf-file
2506 (file-name-sans-extension
2507 (or (file-name-nondirectory buffer-file-name
)))
2509 (file-name-directory buffer-file-name
))))
2510 (buffer (find-file-noselect (org-odt-init-outfile filename
)))
2511 (coding-system-for-write 'utf-8
)
2512 (save-buffer-coding-system 'utf-8
))
2514 (set-buffer-file-coding-system coding-system-for-write
)
2515 (let ((mathml (org-create-math-formula latex-frag
)))
2516 (unless mathml
(error "No Math formula created"))
2518 (or (org-export-push-to-kill-ring
2519 (upcase (symbol-name org-lparse-backend
)))
2520 (message "Exporting... done")))
2521 (org-odt-save-as-outfile filename nil
)))
2524 (defun org-export-as-odf-and-open ()
2525 "Export LaTeX fragment as OpenDocument formula and immediately open it.
2526 Use `org-export-as-odf' to read LaTeX fragment and OpenDocument
2529 (org-lparse-and-open
2530 nil nil nil
(call-interactively 'org-export-as-odf
)))
2534 ;;; org-odt.el ends here