dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

ox-odt.el (163415B)


      1 ;;; ox-odt.el --- OpenDocument Text Exporter for Org Mode -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2010-2023 Free Software Foundation, Inc.
      4 
      5 ;; Author: Jambunathan K <kjambunathan at gmail dot com>
      6 ;; Keywords: outlines, hypermedia, calendar, wp
      7 ;; URL: https://orgmode.org
      8 
      9 ;; This file is part of GNU Emacs.
     10 
     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.
     15 
     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.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;;; Code:
     27 
     28 (require 'org-macs)
     29 (org-assert-version)
     30 
     31 (require 'cl-lib)
     32 (require 'format-spec)
     33 (require 'org-compat)
     34 (require 'org-macs)
     35 (require 'ox)
     36 (require 'table nil 'noerror)
     37 
     38 (declare-function org-at-heading-p "org" (&optional _))
     39 (declare-function org-back-to-heading "org" (&optional invisible-ok))
     40 (declare-function org-next-visible-heading "org" (arg))
     41 
     42 ;;; Define Back-End
     43 
     44 (org-export-define-backend 'odt
     45   '((bold . org-odt-bold)
     46     (center-block . org-odt-center-block)
     47     (clock . org-odt-clock)
     48     (code . org-odt-code)
     49     (drawer . org-odt-drawer)
     50     (dynamic-block . org-odt-dynamic-block)
     51     (entity . org-odt-entity)
     52     (example-block . org-odt-example-block)
     53     (export-block . org-odt-export-block)
     54     (export-snippet . org-odt-export-snippet)
     55     (fixed-width . org-odt-fixed-width)
     56     (footnote-definition . org-odt-footnote-definition)
     57     (footnote-reference . org-odt-footnote-reference)
     58     (headline . org-odt-headline)
     59     (horizontal-rule . org-odt-horizontal-rule)
     60     (inline-src-block . org-odt-inline-src-block)
     61     (inlinetask . org-odt-inlinetask)
     62     (italic . org-odt-italic)
     63     (item . org-odt-item)
     64     (keyword . org-odt-keyword)
     65     (latex-environment . org-odt-latex-environment)
     66     (latex-fragment . org-odt-latex-fragment)
     67     (line-break . org-odt-line-break)
     68     (link . org-odt-link)
     69     (node-property . org-odt-node-property)
     70     (paragraph . org-odt-paragraph)
     71     (plain-list . org-odt-plain-list)
     72     (plain-text . org-odt-plain-text)
     73     (planning . org-odt-planning)
     74     (property-drawer . org-odt-property-drawer)
     75     (quote-block . org-odt-quote-block)
     76     (radio-target . org-odt-radio-target)
     77     (section . org-odt-section)
     78     (special-block . org-odt-special-block)
     79     (src-block . org-odt-src-block)
     80     (statistics-cookie . org-odt-statistics-cookie)
     81     (strike-through . org-odt-strike-through)
     82     (subscript . org-odt-subscript)
     83     (superscript . org-odt-superscript)
     84     (table . org-odt-table)
     85     (table-cell . org-odt-table-cell)
     86     (table-row . org-odt-table-row)
     87     (target . org-odt-target)
     88     (template . org-odt-template)
     89     (timestamp . org-odt-timestamp)
     90     (underline . org-odt-underline)
     91     (verbatim . org-odt-verbatim)
     92     (verse-block . org-odt-verse-block))
     93   :filters-alist '((:filter-parse-tree
     94 		    . (org-odt--translate-latex-fragments
     95 		       org-odt--translate-description-lists
     96 		       org-odt--translate-list-tables
     97 		       org-odt--translate-image-links)))
     98   :menu-entry
     99   '(?o "Export to ODT"
    100        ((?o "As ODT file" org-odt-export-to-odt)
    101 	(?O "As ODT file and open"
    102 	    (lambda (a s v b)
    103 	      (if a (org-odt-export-to-odt t s v)
    104 		(org-open-file (org-odt-export-to-odt nil s v) 'system))))))
    105   :options-alist
    106   '((:odt-styles-file "ODT_STYLES_FILE" nil org-odt-styles-file t)
    107     (:description "DESCRIPTION" nil nil newline)
    108     (:keywords "KEYWORDS" nil nil space)
    109     (:subtitle "SUBTITLE" nil nil parse)
    110     ;; Other variables.
    111     (:odt-content-template-file nil nil org-odt-content-template-file)
    112     (:odt-display-outline-level nil nil org-odt-display-outline-level)
    113     (:odt-fontify-srcblocks nil nil org-odt-fontify-srcblocks)
    114     (:odt-format-drawer-function nil nil org-odt-format-drawer-function)
    115     (:odt-format-headline-function nil nil org-odt-format-headline-function)
    116     (:odt-format-inlinetask-function nil nil org-odt-format-inlinetask-function)
    117     (:odt-inline-formula-rules nil nil org-odt-inline-formula-rules)
    118     (:odt-inline-image-rules nil nil org-odt-inline-image-rules)
    119     (:odt-pixels-per-inch nil nil org-odt-pixels-per-inch)
    120     (:odt-table-styles nil nil org-odt-table-styles)
    121     (:odt-use-date-fields nil nil org-odt-use-date-fields)
    122     ;; Redefine regular option.
    123     (:with-latex nil "tex" org-odt-with-latex)
    124     ;; Retrieve LaTeX header for fragments.
    125     (:latex-header "LATEX_HEADER" nil nil newline)))
    126 
    127 
    128 ;;; Dependencies
    129 
    130 ;;; Hooks
    131 
    132 ;;; Function and Dynamically Scoped Variables Declarations
    133 
    134 (declare-function hfy-face-to-style "htmlfontify" (fn))
    135 (declare-function hfy-face-or-def-to-name "htmlfontify" (fn))
    136 (declare-function archive-zip-extract "arc-mode" (archive name))
    137 (declare-function org-create-math-formula "org" (latex-frag &optional mathml-file))
    138 (declare-function browse-url-file-url "browse-url" (file))
    139 
    140 (defvar nxml-auto-insert-xml-declaration-flag) ; nxml-mode.el
    141 (defvar archive-zip-extract)		       ; arc-mode.el
    142 (defvar hfy-end-span-handler)		       ; htmlfontify.el
    143 (defvar hfy-begin-span-handler)		       ; htmlfontify.el
    144 (defvar hfy-face-to-css)		       ; htmlfontify.el
    145 (defvar hfy-html-quote-map)		       ; htmlfontify.el
    146 (defvar hfy-html-quote-regex)		       ; htmlfontify.el
    147 
    148 
    149 ;;; Internal Variables
    150 
    151 (defconst org-odt-lib-dir
    152   (file-name-directory (or load-file-name (buffer-file-name)))
    153   "Location of ODT exporter.
    154 Use this to infer values of `org-odt-styles-dir' and
    155 `org-odt-schema-dir'.")
    156 
    157 (defvar org-odt-data-dir (expand-file-name "../../etc/" org-odt-lib-dir)
    158   "Data directory for ODT exporter.
    159 Use this to infer values of `org-odt-styles-dir' and
    160 `org-odt-schema-dir'.")
    161 
    162 (defconst org-odt-special-string-regexps
    163   '(("\\\\-" . "&#x00ad;\\1")		; shy
    164     ("---\\([^-]\\)" . "&#x2014;\\1")	; mdash
    165     ("--\\([^-]\\)" . "&#x2013;\\1")	; ndash
    166     ("\\.\\.\\." . "&#x2026;"))		; hellip
    167   "Regular expressions for special string conversion.")
    168 
    169 (defconst org-odt-schema-dir-list
    170   (list (expand-file-name "./schema/" org-odt-data-dir))
    171   "List of directories to search for OpenDocument schema files.
    172 Use this list to set the default value of `org-odt-schema-dir'.
    173 The entries in this list are populated heuristically based on the
    174 values of `org-odt-lib-dir' and `org-odt-data-dir'.")
    175 
    176 (defconst org-odt-styles-dir-list
    177   (list
    178    (and org-odt-data-dir
    179 	(expand-file-name "./styles/" org-odt-data-dir)) ; bail out
    180    (expand-file-name "./styles/" org-odt-data-dir)
    181    (expand-file-name "../etc/styles/" org-odt-lib-dir) ; git
    182    (expand-file-name "./etc/styles/" org-odt-lib-dir)  ; elpa
    183    (expand-file-name "./org/" data-directory)	       ; system
    184    )
    185   "List of directories to search for OpenDocument styles files.
    186 See `org-odt-styles-dir'.  The entries in this list are populated
    187 heuristically based on the values of `org-odt-lib-dir' and
    188 `org-odt-data-dir'.")
    189 
    190 (defconst org-odt-styles-dir
    191   (let ((styles-dir
    192 	 (cl-find-if
    193 	  (lambda (dir)
    194 	    (and dir
    195 		 (file-readable-p
    196 		  (expand-file-name "OrgOdtContentTemplate.xml" dir))
    197 		 (file-readable-p (expand-file-name "OrgOdtStyles.xml" dir))))
    198 	  org-odt-styles-dir-list)))
    199     (unless styles-dir
    200       (error "Error (ox-odt): Cannot find factory styles files, aborting"))
    201     styles-dir)
    202   "Directory that holds auxiliary XML files used by the ODT exporter.
    203 
    204 This directory contains the following XML files -
    205  \"OrgOdtStyles.xml\" and \"OrgOdtContentTemplate.xml\".  These
    206  XML files are used as the default values of
    207  `org-odt-styles-file' and `org-odt-content-template-file'.
    208 
    209 The default value of this variable varies depending on the
    210 version of Org in use and is initialized from
    211 `org-odt-styles-dir-list'.  Note that the user could be using Org
    212 from one of: Org own private git repository, GNU ELPA tar or
    213 standard Emacs.")
    214 
    215 (defconst org-odt-bookmark-prefix "OrgXref.")
    216 
    217 (defconst org-odt-manifest-file-entry-tag
    218   "\n<manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s\"%s/>")
    219 
    220 (defconst org-odt-file-extensions
    221   '(("odt" . "OpenDocument Text")
    222     ("ott" . "OpenDocument Text Template")
    223     ("odm" . "OpenDocument Master Document")
    224     ("ods" . "OpenDocument Spreadsheet")
    225     ("ots" . "OpenDocument Spreadsheet Template")
    226     ("odg" . "OpenDocument Drawing (Graphics)")
    227     ("otg" . "OpenDocument Drawing Template")
    228     ("odp" . "OpenDocument Presentation")
    229     ("otp" . "OpenDocument Presentation Template")
    230     ("odi" . "OpenDocument Image")
    231     ("odf" . "OpenDocument Formula")
    232     ("odc" . "OpenDocument Chart")))
    233 
    234 (defconst org-odt-table-style-format
    235   "
    236 <style:style style:name=\"%s\" style:family=\"table\">
    237   <style:table-properties style:rel-width=\"%s%%\" fo:margin-top=\"0cm\" fo:margin-bottom=\"0.20cm\" table:align=\"center\"/>
    238 </style:style>
    239 "
    240   "Template for auto-generated Table styles.")
    241 
    242 (defvar org-odt-automatic-styles '()
    243   "Registry of automatic styles for various OBJECT-TYPEs.
    244 The variable has the following form:
    245  ((OBJECT-TYPE-A
    246    ((OBJECT-NAME-A.1 OBJECT-PROPS-A.1)
    247     (OBJECT-NAME-A.2 OBJECT-PROPS-A.2) ...))
    248   (OBJECT-TYPE-B
    249    ((OBJECT-NAME-B.1 OBJECT-PROPS-B.1)
    250     (OBJECT-NAME-B.2 OBJECT-PROPS-B.2) ...))
    251   ...).
    252 
    253 OBJECT-TYPEs could be \"Section\", \"Table\", \"Figure\" etc.
    254 OBJECT-PROPS is (typically) a plist created by passing
    255 \"#+ATTR_ODT: \" option to `org-odt-parse-block-attributes'.
    256 
    257 Use `org-odt-add-automatic-style' to add update this variable.'")
    258 
    259 (defvar org-odt-object-counters nil
    260   "Running counters for various OBJECT-TYPEs.
    261 Use this to generate automatic names and style-names.  See
    262 `org-odt-add-automatic-style'.")
    263 
    264 (defvar org-odt-src-block-paragraph-format
    265   "<style:style style:name=\"OrgSrcBlock\" style:family=\"paragraph\" style:parent-style-name=\"Preformatted_20_Text\">
    266    <style:paragraph-properties fo:background-color=\"%s\" fo:padding=\"0.049cm\" fo:border=\"0.51pt solid #000000\" style:shadow=\"none\">
    267     <style:background-image/>
    268    </style:paragraph-properties>
    269    <style:text-properties fo:color=\"%s\"/>
    270   </style:style>"
    271   "Custom paragraph style for colorized source and example blocks.
    272 This style is much the same as that of \"OrgFixedWidthBlock\"
    273 except that the foreground and background colors are set
    274 according to the default face identified by the `htmlfontify'.")
    275 
    276 (defvar hfy-optimizations)
    277 (defvar org-odt-embedded-formulas-count 0)
    278 (defvar org-odt-embedded-images-count 0)
    279 (defvar org-odt-image-size-probe-method
    280   (append (and (executable-find "identify") '(imagemagick)) ; See Bug#10675
    281 	  '(emacs fixed))
    282   "Ordered list of methods for determining image sizes.")
    283 
    284 (defvar org-odt-default-image-sizes-alist
    285   '(("as-char" . (5 . 0.4))
    286     ("paragraph" . (5 . 5)))
    287   "Hardcoded image dimensions one for each of the anchor methods.")
    288 
    289 ;; A4 page size is 21.0 by 29.7 cms
    290 ;; The default page settings has 2cm margin on each of the sides. So
    291 ;; the effective text area is 17.0 by 25.7 cm
    292 (defvar org-odt-max-image-size '(17.0 . 20.0)
    293   "Limiting dimensions for an embedded image.")
    294 
    295 (defconst org-odt-label-styles
    296   '(("math-formula" "%c" "text" "(%n)")
    297     ("math-label" "(%n)" "text" "(%n)")
    298     ("category-and-value" "%e %n: %c" "category-and-value" "%e %n")
    299     ("value" "%e %n: %c" "value" "%n"))
    300   "Specify how labels are applied and referenced.
    301 
    302 This is an alist where each element is of the form:
    303 
    304   (STYLE-NAME ATTACH-FMT REF-MODE REF-FMT)
    305 
    306 ATTACH-FMT controls how labels and captions are attached to an
    307 entity.  It may contain following specifiers - %e and %c.  %e is
    308 replaced with the CATEGORY-NAME.  %n is replaced with
    309 \"<text:sequence ...> SEQNO </text:sequence>\".  %c is replaced
    310 with CAPTION.
    311 
    312 REF-MODE and REF-FMT controls how label references are generated.
    313 The following XML is generated for a label reference -
    314 \"<text:sequence-ref text:reference-format=\"REF-MODE\" ...>
    315 REF-FMT </text:sequence-ref>\".  REF-FMT may contain following
    316 specifiers - %e and %n.  %e is replaced with the CATEGORY-NAME.
    317 %n is replaced with SEQNO.
    318 
    319 See also `org-odt-format-label'.")
    320 
    321 (defvar org-odt-category-map-alist
    322   '(("__Table__" "Table" "value" "Table" org-odt--enumerable-p)
    323     ("__Figure__" "Illustration" "value" "Figure" org-odt--enumerable-image-p)
    324     ("__MathFormula__" "Text" "math-formula" "Equation" org-odt--enumerable-formula-p)
    325     ("__DvipngImage__" "Equation" "value" "Equation" org-odt--enumerable-latex-image-p)
    326     ("__Listing__" "Listing" "value" "Listing" org-odt--enumerable-p))
    327   "Map a CATEGORY-HANDLE to OD-VARIABLE and LABEL-STYLE.
    328 
    329 This is a list where each entry is of the form:
    330 
    331   (CATEGORY-HANDLE OD-VARIABLE LABEL-STYLE CATEGORY-NAME ENUMERATOR-PREDICATE)
    332 
    333 CATEGORY_HANDLE identifies the captionable entity in question.
    334 
    335 OD-VARIABLE is the OpenDocument sequence counter associated with
    336 the entity.  These counters are declared within
    337 \"<text:sequence-decls>...</text:sequence-decls>\" block of
    338 `org-odt-content-template-file'.
    339 
    340 LABEL-STYLE is a key into `org-odt-label-styles' and specifies
    341 how a given entity should be captioned and referenced.
    342 
    343 CATEGORY-NAME is used for qualifying captions on export.
    344 
    345 ENUMERATOR-PREDICATE is used for assigning a sequence number to
    346 the entity.  See `org-odt--enumerate'.")
    347 
    348 (defvar org-odt-manifest-file-entries nil)
    349 (defvar hfy-user-sheet-assoc)
    350 
    351 (defvar org-odt-zip-dir nil
    352   "Temporary work directory for OpenDocument exporter.")
    353 
    354 
    355 
    356 ;;; User Configuration Variables
    357 
    358 (defgroup org-export-odt nil
    359   "Options for exporting Org mode files to ODT."
    360   :tag "Org Export ODT"
    361   :group 'org-export)
    362 
    363 
    364 ;;;; Debugging
    365 
    366 (defcustom org-odt-prettify-xml nil
    367   "Specify whether or not the xml output should be prettified.
    368 When this option is turned on, `indent-region' is run on all
    369 component xml buffers before they are saved.  Turn this off for
    370 regular use.  Turn this on if you need to examine the xml
    371 visually."
    372   :version "24.1"
    373   :type 'boolean)
    374 
    375 
    376 ;;;; Document schema
    377 
    378 (require 'rng-loc)
    379 (defcustom org-odt-schema-dir
    380   (cl-find-if
    381    (lambda (dir)
    382      (and dir
    383 	  (file-expand-wildcards
    384 	   (expand-file-name "od-manifest-schema*.rnc" dir))
    385 	  (file-expand-wildcards (expand-file-name "od-schema*.rnc" dir))
    386 	  (file-readable-p (expand-file-name "schemas.xml" dir))))
    387    org-odt-schema-dir-list)
    388   "Directory that contains OpenDocument schema files.
    389 
    390 This directory contains:
    391 1. rnc files for OpenDocument schema
    392 2. a \"schemas.xml\" file that specifies locating rules needed
    393    for auto validation of OpenDocument XML files.
    394 
    395 Use the customize interface to set this variable.  This ensures
    396 that `rng-schema-locating-files' is updated and auto-validation
    397 of OpenDocument XML takes place based on the value
    398 `rng-nxml-auto-validate-flag'.
    399 
    400 The default value of this variable varies depending on the
    401 version of org in use and is initialized from
    402 `org-odt-schema-dir-list'.  The OASIS schema files are available
    403 only in the org's private git repository.  It is *not* bundled
    404 with GNU ELPA tar or standard Emacs distribution."
    405   :type '(choice
    406 	  (const :tag "Not set" nil)
    407 	  (directory :tag "Schema directory"))
    408   :version "24.1"
    409   :set
    410   (lambda (var value)
    411     "Set `org-odt-schema-dir'.
    412 Also add it to `rng-schema-locating-files'."
    413     (let ((schema-dir value))
    414       (set-default-toplevel-value var
    415 	   (if (and
    416 		(file-expand-wildcards
    417 		 (expand-file-name "od-manifest-schema*.rnc" schema-dir))
    418 		(file-expand-wildcards
    419 		 (expand-file-name "od-schema*.rnc" schema-dir))
    420 		(file-readable-p
    421 		 (expand-file-name "schemas.xml" schema-dir)))
    422 	       schema-dir
    423 	     (when value
    424 	       (message "Error (ox-odt): %s has no OpenDocument schema files"
    425 			value))
    426 	     nil)))
    427     (when org-odt-schema-dir
    428       (eval-after-load 'rng-loc
    429 	'(add-to-list 'rng-schema-locating-files
    430 		      (expand-file-name "schemas.xml"
    431 					org-odt-schema-dir))))))
    432 
    433 
    434 ;;;; Document styles
    435 
    436 (defcustom org-odt-content-template-file nil
    437   "Template file for \"content.xml\".
    438 The exporter embeds the exported content just before
    439 \"</office:text>\" element.
    440 
    441 If unspecified, the file named \"OrgOdtContentTemplate.xml\"
    442 under `org-odt-styles-dir' is used."
    443   :type '(choice (const nil)
    444 		 (file))
    445   :version "24.3")
    446 
    447 (defcustom org-odt-styles-file nil
    448   "Default styles file for use with ODT export.
    449 Valid values are one of:
    450 1. nil
    451 2. path to a styles.xml file
    452 3. path to a *.odt or a *.ott file
    453 4. list of the form (ODT-OR-OTT-FILE (FILE-MEMBER-1 FILE-MEMBER-2
    454 ...))
    455 
    456 In case of option 1, an in-built styles.xml is used.  See
    457 `org-odt-styles-dir' for more information.
    458 
    459 In case of option 3, the specified file is unzipped and the
    460 styles.xml embedded therein is used.
    461 
    462 In case of option 4, the specified ODT-OR-OTT-FILE is unzipped
    463 and FILE-MEMBER-1, FILE-MEMBER-2 etc are copied in to the
    464 generated odt file.  Use relative path for specifying the
    465 FILE-MEMBERS.  styles.xml must be specified as one of the
    466 FILE-MEMBERS.
    467 
    468 Use options 1, 2 or 3 only if styles.xml alone suffices for
    469 achieving the desired formatting.  Use option 4, if the styles.xml
    470 references additional files like header and footer images for
    471 achieving the desired formatting.
    472 
    473 Use \"#+ODT_STYLES_FILE: ...\" directive to set this variable on
    474 a per-file basis.  For example,
    475 
    476 #+ODT_STYLES_FILE: \"/path/to/styles.xml\" or
    477 #+ODT_STYLES_FILE: (\"/path/to/file.ott\" (\"styles.xml\" \"image/hdr.png\"))."
    478   :version "24.1"
    479   :type
    480   '(choice
    481     (const :tag "Factory settings" nil)
    482     (file :must-match t :tag "styles.xml")
    483     (file :must-match t :tag "ODT or OTT file")
    484     (list :tag "ODT or OTT file + Members"
    485 	  (file :must-match t :tag "ODF Text or Text Template file")
    486 	  (cons :tag "Members"
    487 		(file :tag "	Member" "styles.xml")
    488 		(repeat (file :tag "Member"))))))
    489 
    490 (defcustom org-odt-display-outline-level 2
    491   "Outline levels considered for enumerating captioned entities."
    492   :version "24.4"
    493   :package-version '(Org . "8.0")
    494   :type 'integer)
    495 
    496 ;;;; Document conversion
    497 
    498 (defcustom org-odt-convert-processes
    499   '(("LibreOffice"
    500      "soffice --headless --convert-to %f%x --outdir %d %i")
    501     ("unoconv"
    502      "unoconv -f %f -o %d %i"))
    503   "Specify a list of document converters and their usage.
    504 The converters in this list are offered as choices while
    505 customizing `org-odt-convert-process'.
    506 
    507 This variable is a list where each element is of the
    508 form (CONVERTER-NAME CONVERTER-CMD).  CONVERTER-NAME is the name
    509 of the converter.  CONVERTER-CMD is the shell command for the
    510 converter and can contain format specifiers.  These format
    511 specifiers are interpreted as below:
    512 
    513 %i input file name in full
    514 %I input file name as a URL
    515 %f format of the output file
    516 %o output file name in full
    517 %O output file name as a URL
    518 %d output dir in full
    519 %D output dir as a URL.
    520 %x extra options as set in `org-odt-convert-capabilities'."
    521   :version "24.1"
    522   :type
    523   '(choice
    524     (const :tag "None" nil)
    525     (alist :tag "Converters"
    526 	   :key-type (string :tag "Converter Name")
    527 	   :value-type (group (string :tag "Command line")))))
    528 
    529 (defcustom org-odt-convert-process "LibreOffice"
    530   "Use this converter to convert from \"odt\" format to other formats.
    531 During customization, the list of converter names are populated
    532 from `org-odt-convert-processes'."
    533   :version "24.1"
    534   :type '(choice :convert-widget
    535 		 (lambda (w)
    536 		   (apply 'widget-convert (widget-type w)
    537 			  (eval (car (widget-get w :args)))))
    538 		 `((const :tag "None" nil)
    539 		   ,@(mapcar (lambda (c)
    540 			       `(const :tag ,(car c) ,(car c)))
    541 			     org-odt-convert-processes))))
    542 
    543 (defcustom org-odt-convert-capabilities
    544   '(("Text"
    545      ("odt" "ott" "doc" "rtf" "docx")
    546      (("pdf" "pdf") ("odt" "odt") ("rtf" "rtf") ("ott" "ott")
    547       ("doc" "doc" ":\"MS Word 97\"") ("docx" "docx") ("html" "html")))
    548     ("Web"
    549      ("html")
    550      (("pdf" "pdf") ("odt" "odt") ("html" "html")))
    551     ("Spreadsheet"
    552      ("ods" "ots" "xls" "csv" "xlsx")
    553      (("pdf" "pdf") ("ots" "ots") ("html" "html") ("csv" "csv") ("ods" "ods")
    554       ("xls" "xls") ("xlsx" "xlsx")))
    555     ("Presentation"
    556      ("odp" "otp" "ppt" "pptx")
    557      (("pdf" "pdf") ("swf" "swf") ("odp" "odp") ("otp" "otp") ("ppt" "ppt")
    558       ("pptx" "pptx") ("odg" "odg"))))
    559   "Specify input and output formats of `org-odt-convert-process'.
    560 More correctly, specify the set of input and output formats that
    561 the user is actually interested in.
    562 
    563 This variable is an alist where each element is of the
    564 form (DOCUMENT-CLASS INPUT-FMT-LIST OUTPUT-FMT-ALIST).
    565 INPUT-FMT-LIST is a list of INPUT-FMTs.  OUTPUT-FMT-ALIST is an
    566 alist where each element is of the form (OUTPUT-FMT
    567 OUTPUT-FILE-EXTENSION EXTRA-OPTIONS).
    568 
    569 The variable is interpreted as follows:
    570 `org-odt-convert-process' can take any document that is in
    571 INPUT-FMT-LIST and produce any document that is in the
    572 OUTPUT-FMT-LIST.  A document converted to OUTPUT-FMT will have
    573 OUTPUT-FILE-EXTENSION as the file name extension.  OUTPUT-FMT
    574 serves dual purposes:
    575 - It is used for populating completion candidates during
    576   `org-odt-convert' commands.
    577 - It is used as the value of \"%f\" specifier in
    578   `org-odt-convert-process'.
    579 
    580 EXTRA-OPTIONS is used as the value of \"%x\" specifier in
    581 `org-odt-convert-process'.
    582 
    583 DOCUMENT-CLASS is used to group a set of file formats in
    584 INPUT-FMT-LIST in to a single class.
    585 
    586 Note that this variable inherently captures how LibreOffice based
    587 converters work.  LibreOffice maps documents of various formats
    588 to classes like Text, Web, Spreadsheet, Presentation etc and
    589 allow document of a given class (irrespective of its source
    590 format) to be converted to any of the export formats associated
    591 with that class.
    592 
    593 See default setting of this variable for a typical configuration."
    594   :version "24.1"
    595   :type
    596   '(choice
    597     (const :tag "None" nil)
    598     (alist :tag "Capabilities"
    599 	   :key-type (string :tag "Document Class")
    600 	   :value-type
    601 	   (group (repeat :tag "Input formats" (string :tag "Input format"))
    602 		  (alist :tag "Output formats"
    603 			 :key-type (string :tag "Output format")
    604 			 :value-type
    605 			 (group (string :tag "Output file extension")
    606 				(choice
    607 				 (const :tag "None" nil)
    608 				 (string :tag "Extra options"))))))))
    609 
    610 (defcustom org-odt-preferred-output-format nil
    611   "Automatically post-process to this format after exporting to \"odt\".
    612 Command `org-odt-export-to-odt' exports first to \"odt\" format
    613 and then uses `org-odt-convert-process' to convert the
    614 resulting document to this format.  During customization of this
    615 variable, the list of valid values are populated based on
    616 `org-odt-convert-capabilities'.
    617 
    618 You can set this option on per-file basis using file local
    619 values.  See Info node `(emacs) File Variables'."
    620   :version "24.1"
    621   :type '(choice :convert-widget
    622 		 (lambda (w)
    623 		   (apply 'widget-convert (widget-type w)
    624 			  (eval (car (widget-get w :args)))))
    625 		 `((const :tag "None" nil)
    626 		   ,@(mapcar (lambda (c)
    627 			       `(const :tag ,c ,c))
    628 			     (org-odt-reachable-formats "odt")))))
    629 ;;;###autoload
    630 (put 'org-odt-preferred-output-format 'safe-local-variable 'stringp)
    631 
    632 
    633 ;;;; Drawers
    634 
    635 (defcustom org-odt-format-drawer-function (lambda (_name contents) contents)
    636   "Function called to format a drawer in ODT code.
    637 
    638 The function must accept two parameters:
    639   NAME      the drawer name, like \"LOGBOOK\"
    640   CONTENTS  the contents of the drawer.
    641 
    642 The function should return the string to be exported.
    643 
    644 The default value simply returns the value of CONTENTS."
    645   :version "26.1"
    646   :package-version '(Org . "8.3")
    647   :type 'function)
    648 
    649 
    650 ;;;; Headline
    651 
    652 (defcustom org-odt-format-headline-function
    653   'org-odt-format-headline-default-function
    654   "Function to format headline text.
    655 
    656 This function will be called with 5 arguments:
    657 TODO      the todo keyword (string or nil).
    658 TODO-TYPE the type of todo (symbol: `todo', `done', nil)
    659 PRIORITY  the priority of the headline (integer or nil)
    660 TEXT      the main headline text (string).
    661 TAGS      the tags string, separated with colons (string or nil).
    662 
    663 The function result will be used as headline text."
    664   :version "26.1"
    665   :package-version '(Org . "8.3")
    666   :type 'function)
    667 
    668 
    669 ;;;; Inlinetasks
    670 
    671 (defcustom org-odt-format-inlinetask-function
    672   'org-odt-format-inlinetask-default-function
    673   "Function called to format an inlinetask in ODT code.
    674 
    675 The function must accept six parameters:
    676   TODO      the todo keyword, as a string
    677   TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
    678   PRIORITY  the inlinetask priority, as a string
    679   NAME      the inlinetask name, as a string.
    680   TAGS      the inlinetask tags, as a string.
    681   CONTENTS  the contents of the inlinetask, as a string.
    682 
    683 The function should return the string to be exported."
    684   :version "26.1"
    685   :package-version '(Org . "8.3")
    686   :type 'function)
    687 
    688 
    689 ;;;; LaTeX
    690 
    691 (defcustom org-odt-with-latex org-export-with-latex
    692   "Non-nil means process LaTeX math snippets.
    693 
    694 When set, the exporter will process LaTeX environments and
    695 fragments.
    696 
    697 This option can also be set with the +OPTIONS line,
    698 e.g. \"tex:mathjax\".  Allowed values are:
    699 
    700 nil            Ignore math snippets.
    701 `verbatim'     Keep everything in verbatim
    702 `dvipng'       Process the LaTeX fragments to images.  This will also
    703                include processing of non-math environments.
    704 `imagemagick'  Convert the LaTeX fragments to pdf files and use
    705                imagemagick to convert pdf files to png files.
    706 `mathjax'      Do MathJax preprocessing and arrange for MathJax.js to
    707                be loaded.
    708 
    709 Any other symbol is a synonym for `mathjax'."
    710   :version "24.4"
    711   :package-version '(Org . "8.0")
    712   :type '(choice
    713 	  (const :tag "Do not process math in any way" nil)
    714 	  (const :tag "Leave math verbatim" verbatim)
    715 	  (const :tag "Use dvipng to make images" dvipng)
    716 	  (const :tag "Use imagemagick to make images" imagemagick)
    717 	  (other :tag "Use MathJax to display math" mathjax)))
    718 
    719 
    720 ;;;; Links
    721 
    722 (defcustom org-odt-inline-formula-rules
    723   '(("file" . "\\.\\(mathml\\|mml\\|odf\\)\\'"))
    724   "Rules characterizing formula files that can be inlined into ODT.
    725 
    726 A rule consists in an association whose key is the type of link
    727 to consider, and value is a regexp that will be matched against
    728 link's path."
    729   :version "24.4"
    730   :package-version '(Org . "8.0")
    731   :type '(alist :key-type (string :tag "Type")
    732 		:value-type (regexp :tag "Path")))
    733 
    734 (defcustom org-odt-inline-image-rules
    735   `(("file" . ,(regexp-opt '(".jpeg" ".jpg" ".png" ".gif" ".svg"))))
    736   "Rules characterizing image files that can be inlined into ODT.
    737 
    738 A rule consists in an association whose key is the type of link
    739 to consider, and value is a regexp that will be matched against
    740 link's path."
    741   :version "26.1"
    742   :package-version '(Org . "8.3")
    743   :type '(alist :key-type (string :tag "Type")
    744 		:value-type (regexp :tag "Path")))
    745 
    746 (defcustom org-odt-pixels-per-inch 96.0
    747   "Scaling factor for converting images pixels to inches.
    748 Use this for sizing of embedded images.  See Info node `(org)
    749 Images in ODT export' for more information."
    750   :type 'float
    751   :version "24.4"
    752   :package-version '(Org . "8.1"))
    753 
    754 
    755 ;;;; Src Block
    756 
    757 (defcustom org-odt-create-custom-styles-for-srcblocks t
    758   "Whether custom styles for colorized source blocks be automatically created.
    759 When this option is turned on, the exporter creates custom styles
    760 for source blocks based on the advice of `htmlfontify'.  Creation
    761 of custom styles happen as part of `org-odt-hfy-face-to-css'.
    762 
    763 When this option is turned off exporter does not create such
    764 styles.
    765 
    766 Use the latter option if you do not want the custom styles to be
    767 based on your current display settings.  It is necessary that the
    768 styles.xml already contains needed styles for colorizing to work.
    769 
    770 This variable is effective only if `org-odt-fontify-srcblocks' is
    771 turned on."
    772   :version "24.1"
    773   :type 'boolean)
    774 
    775 (defcustom org-odt-fontify-srcblocks t
    776   "Specify whether or not source blocks need to be fontified.
    777 Turn this option on if you want to colorize the source code
    778 blocks in the exported file.  For colorization to work, you need
    779 to make available an enhanced version of `htmlfontify' library."
    780   :type 'boolean
    781   :version "24.1")
    782 
    783 
    784 ;;;; Table
    785 
    786 (defcustom org-odt-table-styles
    787   '(("OrgEquation" "OrgEquation"
    788      ((use-first-column-styles . t)
    789       (use-last-column-styles . t)))
    790     ("TableWithHeaderRowAndColumn" "Custom"
    791      ((use-first-row-styles . t)
    792       (use-first-column-styles . t)))
    793     ("TableWithFirstRowandLastRow" "Custom"
    794      ((use-first-row-styles . t)
    795       (use-last-row-styles . t)))
    796     ("GriddedTable" "Custom" nil))
    797   "Specify how Table Styles should be derived from a Table Template.
    798 This is a list where each element is of the
    799 form (TABLE-STYLE-NAME TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS).
    800 
    801 TABLE-STYLE-NAME is the style associated with the table through
    802 \"#+ATTR_ODT: :style TABLE-STYLE-NAME\" line.
    803 
    804 TABLE-TEMPLATE-NAME is a set of - up to 9 - automatic
    805 TABLE-CELL-STYLE-NAMEs and PARAGRAPH-STYLE-NAMEs (as defined
    806 below) that is included in `org-odt-content-template-file'.
    807 
    808 TABLE-CELL-STYLE-NAME := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
    809                          \"TableCell\"
    810 PARAGRAPH-STYLE-NAME  := TABLE-TEMPLATE-NAME + TABLE-CELL-TYPE +
    811                          \"TableParagraph\"
    812 TABLE-CELL-TYPE       := \"FirstRow\"   | \"LastColumn\" |
    813                          \"FirstRow\"   | \"LastRow\"    |
    814                          \"EvenRow\"    | \"OddRow\"     |
    815                          \"EvenColumn\" | \"OddColumn\"  | \"\"
    816 where \"+\" above denotes string concatenation.
    817 
    818 TABLE-CELL-OPTIONS is an alist where each element is of the
    819 form (TABLE-CELL-STYLE-SELECTOR . ON-OR-OFF).
    820 TABLE-CELL-STYLE-SELECTOR := `use-first-row-styles'       |
    821                              `use-last-row-styles'        |
    822                              `use-first-column-styles'    |
    823                              `use-last-column-styles'     |
    824                              `use-banding-rows-styles'    |
    825                              `use-banding-columns-styles' |
    826                              `use-first-row-styles'
    827 ON-OR-OFF                 := t | nil
    828 
    829 For example, with the following configuration
    830 
    831 \(setq org-odt-table-styles
    832       \\='((\"TableWithHeaderRowsAndColumns\" \"Custom\"
    833          ((use-first-row-styles . t)
    834           (use-first-column-styles . t)))
    835         (\"TableWithHeaderColumns\" \"Custom\"
    836          ((use-first-column-styles . t)))))
    837 
    838 1. A table associated with \"TableWithHeaderRowsAndColumns\"
    839    style will use the following table-cell styles -
    840    \"CustomFirstRowTableCell\", \"CustomFirstColumnTableCell\",
    841    \"CustomTableCell\" and the following paragraph styles
    842    \"CustomFirstRowTableParagraph\",
    843    \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
    844    as appropriate.
    845 
    846 2. A table associated with \"TableWithHeaderColumns\" style will
    847    use the following table-cell styles -
    848    \"CustomFirstColumnTableCell\", \"CustomTableCell\" and the
    849    following paragraph styles
    850    \"CustomFirstColumnTableParagraph\", \"CustomTableParagraph\"
    851    as appropriate..
    852 
    853 Note that TABLE-TEMPLATE-NAME corresponds to the
    854 \"<table:table-template>\" elements contained within
    855 \"<office:styles>\".  The entries (TABLE-STYLE-NAME
    856 TABLE-TEMPLATE-NAME TABLE-CELL-OPTIONS) correspond to
    857 \"table:template-name\" and \"table:use-first-row-styles\" etc
    858 attributes of \"<table:table>\" element.  Refer ODF-1.2
    859 specification for more information.  Also consult the
    860 implementation filed under `org-odt-get-table-cell-styles'.
    861 
    862 The TABLE-STYLE-NAME \"OrgEquation\" is used internally for
    863 formatting of numbered display equations.  Do not delete this
    864 style from the list."
    865   :version "24.1"
    866   :type '(choice
    867           (const :tag "None" nil)
    868           (repeat :tag "Table Styles"
    869                   (list :tag "Table Style Specification"
    870 			(string :tag "Table Style Name")
    871 			(string  :tag "Table Template Name")
    872 			(alist :options (use-first-row-styles
    873 					 use-last-row-styles
    874 					 use-first-column-styles
    875 					 use-last-column-styles
    876 					 use-banding-rows-styles
    877 					 use-banding-columns-styles)
    878 			       :key-type symbol
    879 			       :value-type (const :tag "True" t))))))
    880 
    881 ;;;; Timestamps
    882 
    883 (defcustom org-odt-use-date-fields nil
    884   "Non-nil, if timestamps should be exported as date fields.
    885 
    886 When nil, export timestamps as plain text.
    887 
    888 When non-nil, map `org-time-stamp-custom-formats' to a pair of
    889 OpenDocument date-styles with names \"OrgDate1\" and \"OrgDate2\"
    890 respectively.  A timestamp with no time component is formatted
    891 with style \"OrgDate1\" while one with explicit hour and minutes
    892 is formatted with style \"OrgDate2\".
    893 
    894 This feature is experimental.  Most (but not all) of the common
    895 %-specifiers in `format-time-string' are supported.
    896 Specifically, locale-dependent specifiers like \"%c\", \"%x\" are
    897 formatted as canonical Org timestamps.  For finer control, avoid
    898 these %-specifiers.
    899 
    900 Textual specifiers like \"%b\", \"%h\", \"%B\", \"%a\", \"%A\"
    901 etc., are displayed by the application in the default language
    902 and country specified in `org-odt-styles-file'.  Note that the
    903 default styles file uses language \"en\" and country \"GB\".  You
    904 can localize the week day and month strings in the exported
    905 document by setting the default language and country either using
    906 the application UI or through a custom styles file.
    907 
    908 See `org-odt--build-date-styles' for implementation details."
    909   :version "24.4"
    910   :package-version '(Org . "8.0")
    911   :type 'boolean)
    912 
    913 
    914 
    915 ;;; Internal functions
    916 
    917 ;;;; Date
    918 
    919 (defun org-odt--format-timestamp (timestamp &optional end iso-date-p)
    920   (let* ((format-timestamp
    921 	  (lambda (timestamp format &optional end utc)
    922 	    (if timestamp
    923 		(org-format-timestamp timestamp format end utc)
    924 	      (format-time-string format nil utc))))
    925 	 (has-time-p (or (not timestamp)
    926 			 (org-timestamp-has-time-p timestamp)))
    927 	 (iso-date (let ((format (if has-time-p "%Y-%m-%dT%H:%M:%S"
    928 				   "%Y-%m-%d")))
    929 		     (funcall format-timestamp timestamp format end))))
    930     (if iso-date-p iso-date
    931       (let* ((style (if has-time-p "OrgDate2" "OrgDate1"))
    932 	     ;; LibreOffice does not care about end goes as content
    933 	     ;; within the "<text:date>...</text:date>" field.  The
    934 	     ;; displayed date is automagically corrected to match the
    935 	     ;; format requested by "style:data-style-name" attribute.  So
    936 	     ;; don't bother about formatting the date contents to be
    937 	     ;; compatible with "OrgDate1" and "OrgDateTime" styles.  A
    938 	     ;; simple Org-style date should suffice.
    939 	     (date (let ((format (org-time-stamp-format
    940                                   has-time-p 'no-brackets 'custom)))
    941 		     (funcall format-timestamp timestamp format end)))
    942 	     (repeater (let ((repeater-type (org-element-property
    943 					     :repeater-type timestamp))
    944 			     (repeater-value (org-element-property
    945 					      :repeater-value timestamp))
    946 			     (repeater-unit (org-element-property
    947 					     :repeater-unit timestamp)))
    948 			 (concat
    949 			  (cl-case repeater-type
    950 			    (catchup "++") (restart ".+") (cumulate "+"))
    951 			  (when repeater-value
    952 			    (number-to-string repeater-value))
    953 			  (cl-case repeater-unit
    954 			    (hour "h") (day "d") (week "w") (month "m")
    955 			    (year "y"))))))
    956 	(concat
    957 	 (format "<text:date text:date-value=\"%s\" style:data-style-name=\"%s\" text:fixed=\"true\">%s</text:date>"
    958 		 iso-date style date)
    959 	 (and (not (string= repeater ""))  " ")
    960 	 repeater)))))
    961 
    962 ;;;; Frame
    963 
    964 (defun org-odt--frame (text width height style &optional extra
    965 			    anchor-type &rest title-and-desc)
    966   (let ((frame-attrs
    967 	 (concat
    968 	  (if width (format " svg:width=\"%0.2fcm\"" width) "")
    969 	  (if height (format " svg:height=\"%0.2fcm\"" height) "")
    970 	  extra
    971 	  (format " text:anchor-type=\"%s\"" (or anchor-type "paragraph"))
    972 	  (format " draw:name=\"%s\""
    973 		  (car (org-odt-add-automatic-style "Frame"))))))
    974     (format
    975      "\n<draw:frame draw:style-name=\"%s\"%s>\n%s\n</draw:frame>"
    976      style frame-attrs
    977      (concat text
    978 	     (let ((title (car title-and-desc))
    979 		   (desc (cadr title-and-desc)))
    980 	       (concat (when title
    981 			 (format "<svg:title>%s</svg:title>"
    982 				 (org-odt--encode-plain-text title t)))
    983 		       (when desc
    984 			 (format "<svg:desc>%s</svg:desc>"
    985 				 (org-odt--encode-plain-text desc t)))))))))
    986 
    987 
    988 ;;;; Library wrappers
    989 
    990 (defun org-odt--zip-extract (archive members target)
    991   (when (atom members) (setq members (list members)))
    992   (require 'arc-mode)
    993   (dolist (member members)
    994     (let* ((--quote-file-name
    995 	    ;; This is shamelessly stolen from `archive-zip-extract'.
    996 	    (lambda (name)
    997 	      (if (or (not (memq system-type '(windows-nt ms-dos)))
    998 		      (and (boundp 'w32-quote-process-args)
    999 			   (null w32-quote-process-args)))
   1000 		  (shell-quote-argument name)
   1001 		name)))
   1002 	   (target (funcall --quote-file-name target))
   1003 	   (archive (expand-file-name archive))
   1004 	   (archive-zip-extract
   1005 	    (list "unzip" "-qq" "-o" "-d" target))
   1006 	   exit-code command-output)
   1007       (setq command-output
   1008 	    (with-temp-buffer
   1009 	      (setq exit-code (archive-zip-extract archive member))
   1010 	      (buffer-string)))
   1011       (unless (zerop exit-code)
   1012 	(message command-output)
   1013 	(error "Extraction failed")))))
   1014 
   1015 ;;;; Target
   1016 
   1017 (defun org-odt--target (text id)
   1018   (if (not id) text
   1019     (concat
   1020      (format "\n<text:bookmark-start text:name=\"OrgXref.%s\"/>" id)
   1021      (format "\n<text:bookmark text:name=\"%s\"/>" id) text
   1022      (format "\n<text:bookmark-end text:name=\"OrgXref.%s\"/>" id))))
   1023 
   1024 ;;;; Textbox
   1025 
   1026 (defun org-odt--textbox (text width height style &optional
   1027 			      extra anchor-type)
   1028   (org-odt--frame
   1029    (format "\n<draw:text-box %s>%s\n</draw:text-box>"
   1030 	   (concat (format " fo:min-height=\"%0.2fcm\"" (or height .2))
   1031 		   (and (not width)
   1032 			(format " fo:min-width=\"%0.2fcm\"" (or width .2))))
   1033 	   text)
   1034    width nil style extra anchor-type))
   1035 
   1036 
   1037 
   1038 ;;;; Table of Contents
   1039 
   1040 (defun org-odt--format-toc (title entries depth)
   1041   "Return a table of contents.
   1042 TITLE is the title of the table, as a string, or nil.  ENTRIES is
   1043 the contents of the table, as a string.  DEPTH is an integer
   1044 specifying the depth of the table."
   1045   (concat
   1046    "
   1047 <text:table-of-content text:style-name=\"OrgIndexSection\" text:protected=\"true\" text:name=\"Table of Contents\">\n"
   1048    (format "  <text:table-of-content-source text:outline-level=\"%d\">" depth)
   1049    (and title
   1050 	(format "
   1051     <text:index-title-template text:style-name=\"Contents_20_Heading\">%s</text:index-title-template>
   1052 "
   1053 		title))
   1054 
   1055    (let ((levels (number-sequence 1 10)))
   1056      (mapconcat
   1057       (lambda (level)
   1058 	(format
   1059 	 "
   1060       <text:table-of-content-entry-template text:outline-level=\"%d\" text:style-name=\"Contents_20_%d\">
   1061        <text:index-entry-link-start text:style-name=\"Internet_20_link\"/>
   1062        <text:index-entry-chapter/>
   1063        <text:index-entry-text/>
   1064        <text:index-entry-link-end/>
   1065       </text:table-of-content-entry-template>\n"
   1066 	 level level)) levels ""))
   1067    "
   1068   </text:table-of-content-source>
   1069   <text:index-body>"
   1070    (and title
   1071 	(format "
   1072     <text:index-title text:style-name=\"Sect1\" text:name=\"Table of Contents1_Head\">
   1073       <text:p text:style-name=\"Contents_20_Heading\">%s</text:p>
   1074     </text:index-title>\n"
   1075 		title))
   1076    entries
   1077    "
   1078   </text:index-body>
   1079 </text:table-of-content>"))
   1080 
   1081 (cl-defun org-odt-format-toc-headline
   1082     (todo _todo-type priority text tags
   1083 	  &key _level section-number headline-label &allow-other-keys)
   1084   (format "<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
   1085 	  headline-label
   1086 	  (concat
   1087 	   ;; Section number.
   1088 	   (and section-number (concat section-number ". "))
   1089 	   ;; Todo.
   1090 	   (when todo
   1091 	     (let ((style (if (member todo org-done-keywords)
   1092 			      "OrgDone" "OrgTodo")))
   1093 	       (format "<text:span text:style-name=\"%s\">%s</text:span> "
   1094 		       style todo)))
   1095 	   (when priority
   1096 	     (let* ((style (format "OrgPriority-%s" priority))
   1097 		    (priority (format "[#%c]" priority)))
   1098 	       (format "<text:span text:style-name=\"%s\">%s</text:span> "
   1099 		       style priority)))
   1100 	   ;; Title.
   1101 	   text
   1102 	   ;; Tags.
   1103 	   (when tags
   1104 	     (concat
   1105 	      (format " <text:span text:style-name=\"%s\">[%s]</text:span>"
   1106 		      "OrgTags"
   1107 		      (mapconcat
   1108 		       (lambda (tag)
   1109 			 (format
   1110 			  "<text:span text:style-name=\"%s\">%s</text:span>"
   1111 			  "OrgTag" tag)) tags " : ")))))))
   1112 
   1113 (defun org-odt-toc (depth info &optional scope)
   1114   "Build a table of contents.
   1115 DEPTH is an integer specifying the depth of the table.  INFO is
   1116 a plist containing current export properties.  Optional argument
   1117 SCOPE, when non-nil, defines the scope of the table.  Return the
   1118 table of contents as a string, or nil."
   1119   (cl-assert (wholenump depth))
   1120   ;; When a headline is marked as a radio target, as in the example below:
   1121   ;;
   1122   ;; ** <<<Some Heading>>>
   1123   ;;    Some text.
   1124   ;;
   1125   ;; suppress generation of radio targets.  i.e., Radio targets are to
   1126   ;; be marked as targets within /document body/ and *not* within
   1127   ;; /TOC/, as otherwise there will be duplicated anchors one in TOC
   1128   ;; and one in the document body.
   1129   ;;
   1130   ;; Likewise, links, footnote references and regular targets are also
   1131   ;; suppressed.
   1132   (let* ((headlines (org-export-collect-headlines info depth scope))
   1133 	 (backend (org-export-toc-entry-backend
   1134 		      (org-export-backend-name (plist-get info :back-end)))))
   1135     (when headlines
   1136       (org-odt--format-toc
   1137        (and (not scope) (org-export-translate "Table of Contents" :utf-8 info))
   1138        (mapconcat
   1139 	(lambda (headline)
   1140 	  (let* ((entry (org-odt-format-headline--wrap
   1141 			 headline backend info 'org-odt-format-toc-headline))
   1142 		 (level (org-export-get-relative-level headline info))
   1143 		 (style (format "Contents_20_%d" level)))
   1144 	    (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1145 		    style entry)))
   1146 	headlines "\n")
   1147        depth))))
   1148 
   1149 
   1150 ;;;; Document styles
   1151 
   1152 (defun org-odt-add-automatic-style (object-type &optional object-props)
   1153   "Create an automatic style of type OBJECT-TYPE with param OBJECT-PROPS.
   1154 OBJECT-PROPS is (typically) a plist created by passing
   1155 \"#+ATTR_ODT: \" option of the object in question to
   1156 `org-odt-parse-block-attributes'.
   1157 
   1158 Use `org-odt-object-counters' to generate an automatic
   1159 OBJECT-NAME and STYLE-NAME.  If OBJECT-PROPS is non-nil, add a
   1160 new entry in `org-odt-automatic-styles'.  Return (OBJECT-NAME
   1161 . STYLE-NAME)."
   1162   (cl-assert (stringp object-type))
   1163   (let* ((object (intern object-type))
   1164 	 (seqvar object)
   1165 	 (seqno (1+ (or (plist-get org-odt-object-counters seqvar) 0)))
   1166 	 (object-name (format "%s%d" object-type seqno)) style-name)
   1167     (setq org-odt-object-counters
   1168 	  (plist-put org-odt-object-counters seqvar seqno))
   1169     (when object-props
   1170       (setq style-name (format "Org%s" object-name))
   1171       (setq org-odt-automatic-styles
   1172 	    (plist-put org-odt-automatic-styles object
   1173 		       (append (list (list style-name object-props))
   1174 			       (plist-get org-odt-automatic-styles object)))))
   1175     (cons object-name style-name)))
   1176 
   1177 ;;;; Checkbox
   1178 
   1179 (defun org-odt--checkbox (item)
   1180   "Return check-box string associated to ITEM."
   1181   (let ((checkbox (org-element-property :checkbox item)))
   1182     (if (not checkbox) ""
   1183       (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1184 	      "OrgCode" (cl-case checkbox
   1185 			  (on "[&#x2713;] ") ; CHECK MARK
   1186 			  (off "[ ] ")
   1187 			  (trans "[-] "))))))
   1188 
   1189 ;;; Template
   1190 
   1191 (defun org-odt--build-date-styles (fmt style)
   1192   ;; In LibreOffice 3.4.6, there doesn't seem to be a convenient way
   1193   ;; to modify the date fields.  A date could be modified by
   1194   ;; offsetting in days.  That's about it.  Also, date and time may
   1195   ;; have to be emitted as two fields - a date field and a time field
   1196   ;; - separately.
   1197 
   1198   ;; One can add Form Controls to date and time fields so that they
   1199   ;; can be easily modified.  But then, the exported document will
   1200   ;; become tightly coupled with LibreOffice and may not function
   1201   ;; properly with other OpenDocument applications.
   1202 
   1203   ;; I have a strange feeling that Date styles are a bit flaky at the
   1204   ;; moment.
   1205 
   1206   ;; The feature is experimental.
   1207   (when (and fmt style)
   1208     (let* ((fmt-alist
   1209 	    '(("%A" . "<number:day-of-week number:style=\"long\"/>")
   1210 	      ("%B" . "<number:month number:textual=\"true\" number:style=\"long\"/>")
   1211 	      ("%H" . "<number:hours number:style=\"long\"/>")
   1212 	      ("%M" . "<number:minutes number:style=\"long\"/>")
   1213 	      ("%S" . "<number:seconds number:style=\"long\"/>")
   1214 	      ("%V" . "<number:week-of-year/>")
   1215 	      ("%Y" . "<number:year number:style=\"long\"/>")
   1216 	      ("%a" . "<number:day-of-week number:style=\"short\"/>")
   1217 	      ("%b" . "<number:month number:textual=\"true\" number:style=\"short\"/>")
   1218 	      ("%d" . "<number:day number:style=\"long\"/>")
   1219 	      ("%e" . "<number:day number:style=\"short\"/>")
   1220 	      ("%h" . "<number:month number:textual=\"true\" number:style=\"short\"/>")
   1221 	      ("%k" . "<number:hours number:style=\"short\"/>")
   1222 	      ("%m" . "<number:month number:style=\"long\"/>")
   1223 	      ("%p" . "<number:am-pm/>")
   1224 	      ("%y" . "<number:year number:style=\"short\"/>")))
   1225 	   (case-fold-search nil)
   1226 	   (re (mapconcat 'identity (mapcar 'car fmt-alist) "\\|"))
   1227 	   match rpl (start 0) (filler-beg 0) filler-end filler output)
   1228       (dolist (pair
   1229 	       '(("\\(?:%[[:digit:]]*N\\)" . "") ; strip ns, us and ns
   1230 		 ("%C" . "Y")		; replace century with year
   1231 		 ("%D" . "%m/%d/%y")
   1232 		 ("%G" . "Y")	      ; year corresponding to iso week
   1233 		 ("%I" . "%H")	      ; hour on a 12-hour clock
   1234 		 ("%R" . "%H:%M")
   1235 		 ("%T" . "%H:%M:%S")
   1236 		 ("%U\\|%W" . "%V")   ; week no. starting on Sun./Mon.
   1237 		 ("%Z" . "")	      ; time zone name
   1238 		 ("%c" . "%Y-%M-%d %a %H:%M" ) ; locale's date and time format
   1239 		 ("%g" . "%y")
   1240 		 ("%X" . "%x" )		; locale's pref. time format
   1241 		 ("%j" . "")		; day of the year
   1242 		 ("%l" . "%k")		; like %I blank-padded
   1243 		 ("%s" . "") ; no. of secs since 1970-01-01 00:00:00 +0000
   1244 		 ("%n" . "<text:line-break/>")
   1245 		 ("%r" . "%I:%M:%S %p")
   1246 		 ("%t" . "<text:tab/>")
   1247 		 ("%u\\|%w" . "") ; numeric day of week - Mon (1-7), Sun(0-6)
   1248 		 ("%x" . "%Y-%M-%d %a")	; locale's pref. time format
   1249 		 ("%z" . "")		; time zone in numeric form
   1250 		 ))
   1251 	(setq fmt (replace-regexp-in-string (car pair) (cdr pair) fmt t t)))
   1252       (while (string-match re fmt start)
   1253 	(setq match (match-string 0 fmt))
   1254 	(setq rpl (assoc-default match fmt-alist))
   1255 	(setq start (match-end 0))
   1256 	(setq filler-end (match-beginning 0))
   1257 	(setq filler (substring fmt (prog1 filler-beg
   1258 				      (setq filler-beg (match-end 0)))
   1259 				filler-end))
   1260 	(setq filler (and (not (string= filler ""))
   1261 			  (format "<number:text>%s</number:text>"
   1262 				  (org-odt--encode-plain-text filler))))
   1263 	(setq output (concat output "\n" filler "\n" rpl)))
   1264       (setq filler (substring fmt filler-beg))
   1265       (unless (string= filler "")
   1266 	(setq output (concat output
   1267 			     (format "\n<number:text>%s</number:text>"
   1268 				     (org-odt--encode-plain-text filler)))))
   1269       (format "\n<number:date-style style:name=\"%s\" %s>%s\n</number:date-style>"
   1270 	      style
   1271 	      (concat " number:automatic-order=\"true\""
   1272 		      " number:format-source=\"fixed\"")
   1273 	      output ))))
   1274 
   1275 (defun org-odt-template (contents info)
   1276   "Return complete document string after ODT conversion.
   1277 CONTENTS is the transcoded contents string.  RAW-DATA is the
   1278 original parsed data.  INFO is a plist holding export options."
   1279   ;; Write meta file.
   1280   (let ((title (org-export-data (plist-get info :title) info))
   1281 	(subtitle (org-export-data (plist-get info :subtitle) info))
   1282 	(author (let ((author (plist-get info :author)))
   1283 		  (if (not author) "" (org-export-data author info))))
   1284 	(keywords (or (plist-get info :keywords) ""))
   1285 	(description (or (plist-get info :description) "")))
   1286     (write-region
   1287      (concat
   1288       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
   1289      <office:document-meta
   1290          xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
   1291          xmlns:xlink=\"http://www.w3.org/1999/xlink\"
   1292          xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
   1293          xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
   1294          xmlns:ooo=\"http://openoffice.org/2004/office\"
   1295          office:version=\"1.2\">
   1296        <office:meta>\n"
   1297       (format "<dc:creator>%s</dc:creator>\n" author)
   1298       (format "<meta:initial-creator>%s</meta:initial-creator>\n" author)
   1299       ;; Date, if required.
   1300       (when (plist-get info :with-date)
   1301 	;; Check if DATE is specified as an Org-timestamp.  If yes,
   1302 	;; include it as meta information.  Otherwise, just use
   1303 	;; today's date.
   1304 	(let* ((date (let ((date (plist-get info :date)))
   1305 		       (and (not (cdr date))
   1306 			    (eq (org-element-type (car date)) 'timestamp)
   1307 			    (car date)))))
   1308 	  (let ((iso-date (org-odt--format-timestamp date nil 'iso-date)))
   1309 	    (concat
   1310 	     (format "<dc:date>%s</dc:date>\n" iso-date)
   1311 	     (format "<meta:creation-date>%s</meta:creation-date>\n"
   1312 		     iso-date)))))
   1313       (format "<meta:generator>%s</meta:generator>\n"
   1314 	      (plist-get info :creator))
   1315       (format "<meta:keyword>%s</meta:keyword>\n" keywords)
   1316       (format "<dc:subject>%s</dc:subject>\n" description)
   1317       (format "<dc:title>%s</dc:title>\n" title)
   1318       (when (org-string-nw-p subtitle)
   1319 	(format
   1320 	 "<meta:user-defined meta:name=\"subtitle\">%s</meta:user-defined>\n"
   1321 	 subtitle))
   1322       "\n"
   1323       "  </office:meta>\n" "</office:document-meta>")
   1324      nil (concat org-odt-zip-dir "meta.xml"))
   1325     ;; Add meta.xml in to manifest.
   1326     (org-odt-create-manifest-file-entry "text/xml" "meta.xml"))
   1327 
   1328   ;; Update styles file.
   1329   ;; Copy styles.xml.  Also dump htmlfontify styles, if there is any.
   1330   ;; Write styles file.
   1331   (let* ((styles-file
   1332 	  (pcase (plist-get info :odt-styles-file)
   1333 	    (`nil (expand-file-name "OrgOdtStyles.xml" org-odt-styles-dir))
   1334 	    ((and s (pred (string-match-p "\\`(.*)\\'")))
   1335 	     (condition-case nil
   1336 		 (read s)
   1337 	       (error (user-error "Invalid styles file specification: %S" s))))
   1338 	    (filename (org-strip-quotes filename)))))
   1339     (cond
   1340      ;; Non-availability of styles.xml is not a critical error.  For
   1341      ;; now, throw an error.
   1342      ((null styles-file) (error "Missing styles file"))
   1343      ((listp styles-file)
   1344       (let ((archive (nth 0 styles-file))
   1345 	    (members (nth 1 styles-file)))
   1346 	(org-odt--zip-extract archive members org-odt-zip-dir)
   1347 	(dolist (member members)
   1348 	  (when (org-file-image-p member)
   1349 	    (let* ((image-type (file-name-extension member))
   1350 		   (media-type (format "image/%s" image-type)))
   1351 	      (org-odt-create-manifest-file-entry media-type member))))))
   1352      ((file-exists-p styles-file)
   1353       (let ((styles-file-type (file-name-extension styles-file)))
   1354 	(cond
   1355 	 ((string= styles-file-type "xml")
   1356 	  (copy-file styles-file (concat org-odt-zip-dir "styles.xml") t))
   1357 	 ((member styles-file-type '("odt" "ott"))
   1358 	  (org-odt--zip-extract styles-file "styles.xml" org-odt-zip-dir)))))
   1359      (t
   1360       (error "Invalid specification of styles.xml file: %S"
   1361 	     (plist-get info :odt-styles-file))))
   1362 
   1363     ;; create a manifest entry for styles.xml
   1364     (org-odt-create-manifest-file-entry "text/xml" "styles.xml")
   1365     ;; Ensure we have write permissions to this file.
   1366     (set-file-modes (concat org-odt-zip-dir "styles.xml") #o600)
   1367 
   1368     ;; FIXME: Who is opening an empty styles.xml before this point?
   1369     (with-current-buffer
   1370 	(find-file-noselect (concat org-odt-zip-dir "styles.xml") t)
   1371       (revert-buffer t t)
   1372 
   1373       ;; Write custom styles for source blocks
   1374       ;; Save STYLES used for colorizing of source blocks.
   1375       ;; Update styles.xml with styles that were collected as part of
   1376       ;; `org-odt-hfy-face-to-css' callbacks.
   1377       (let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
   1378 			       hfy-user-sheet-assoc "")))
   1379 	(when styles
   1380 	  (goto-char (point-min))
   1381 	  (when (re-search-forward "</office:styles>" nil t)
   1382 	    (goto-char (match-beginning 0))
   1383 	    (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
   1384 
   1385       ;; Update styles.xml - take care of outline numbering
   1386 
   1387       ;; Don't make automatic backup of styles.xml file. This setting
   1388       ;; prevents the backed-up styles.xml file from being zipped in to
   1389       ;; odt file. This is more of a hackish fix. Better alternative
   1390       ;; would be to fix the zip command so that the output odt file
   1391       ;; includes only the needed files and excludes any auto-generated
   1392       ;; extra files like backups and auto-saves etc etc. Note that
   1393       ;; currently the zip command zips up the entire temp directory so
   1394       ;; that any auto-generated files created under the hood ends up in
   1395       ;; the resulting odt file.
   1396       (setq-local backup-inhibited t)
   1397 
   1398       ;; Outline numbering is retained only up to LEVEL.
   1399       ;; To disable outline numbering pass a LEVEL of 0.
   1400 
   1401       (goto-char (point-min))
   1402       (let ((regex
   1403 	     "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
   1404 	    (replacement
   1405 	     "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
   1406 	(while (re-search-forward regex nil t)
   1407 	  (unless (let ((sec-num (plist-get info :section-numbers))
   1408 			(level (string-to-number (match-string 2))))
   1409 		    (if (wholenump sec-num) (<= level sec-num) sec-num))
   1410 	    (replace-match replacement t nil))))
   1411       (save-buffer 0)))
   1412   ;; Update content.xml.
   1413 
   1414   (let* ( ;; `org-display-custom-times' should be accessed right
   1415 	 ;; within the context of the Org buffer.  So obtain its
   1416 	 ;; value before moving on to temp-buffer context down below.
   1417 	 (custom-time-fmts
   1418 	  (if org-display-custom-times
   1419               (cons (org-time-stamp-format
   1420                      nil 'no-brackets 'custom)
   1421                     (org-time-stamp-format
   1422                      'with-time 'no-brackets 'custom))
   1423 	    '("%Y-%M-%d %a" . "%Y-%M-%d %a %H:%M"))))
   1424     (with-temp-buffer
   1425       (insert-file-contents
   1426        (or (plist-get info :odt-content-template-file)
   1427 	   (expand-file-name "OrgOdtContentTemplate.xml"
   1428 			     org-odt-styles-dir)))
   1429       ;; Write automatic styles.
   1430       ;; - Position the cursor.
   1431       (goto-char (point-min))
   1432       (re-search-forward "  </office:automatic-styles>" nil t)
   1433       (goto-char (match-beginning 0))
   1434       ;; - Dump automatic table styles.
   1435       (cl-loop for (style-name props) in
   1436 	       (plist-get org-odt-automatic-styles 'Table) do
   1437 	       (when (setq props (or (plist-get props :rel-width) "96"))
   1438 		 (insert (format org-odt-table-style-format style-name props))))
   1439       ;; - Dump date-styles.
   1440       (when (plist-get info :odt-use-date-fields)
   1441 	(insert (org-odt--build-date-styles (car custom-time-fmts)
   1442 					    "OrgDate1")
   1443 		(org-odt--build-date-styles (cdr custom-time-fmts)
   1444 					    "OrgDate2")))
   1445       ;; Update display level.
   1446       ;; - Remove existing sequence decls.  Also position the cursor.
   1447       (goto-char (point-min))
   1448       (when (re-search-forward "<text:sequence-decls" nil t)
   1449 	(delete-region (match-beginning 0)
   1450 		       (re-search-forward "</text:sequence-decls>" nil nil)))
   1451       ;; Update sequence decls according to user preference.
   1452       (insert
   1453        (format
   1454 	"\n<text:sequence-decls>\n%s\n</text:sequence-decls>"
   1455 	(mapconcat
   1456 	 (lambda (x)
   1457 	   (format
   1458 	    "<text:sequence-decl text:display-outline-level=\"%d\" text:name=\"%s\"/>"
   1459 	    (plist-get info :odt-display-outline-level)
   1460 	    (nth 1 x)))
   1461 	 org-odt-category-map-alist "\n")))
   1462       ;; Position the cursor to document body.
   1463       (goto-char (point-min))
   1464       (re-search-forward "</office:text>" nil nil)
   1465       (goto-char (match-beginning 0))
   1466 
   1467       ;; Preamble - Title, Author, Date etc.
   1468       (insert
   1469        (let* ((title (and (plist-get info :with-title)
   1470 			  (org-export-data (plist-get info :title) info)))
   1471 	      (subtitle (when title
   1472 			  (org-export-data (plist-get info :subtitle) info)))
   1473 	      (author (and (plist-get info :with-author)
   1474 			   (let ((auth (plist-get info :author)))
   1475 			     (and auth (org-export-data auth info)))))
   1476 	      (email (plist-get info :email))
   1477 	      ;; Switch on or off above vars based on user settings
   1478 	      (author (and (plist-get info :with-author) (or author email)))
   1479 	      (email (and (plist-get info :with-email) email)))
   1480 	 (concat
   1481 	  ;; Title.
   1482 	  (when (org-string-nw-p title)
   1483 	    (concat
   1484 	     (format "\n<text:p text:style-name=\"%s\">%s</text:p>\n"
   1485 		     "OrgTitle" (format "\n<text:title>%s</text:title>" title))
   1486 	     ;; Separator.
   1487 	     "\n<text:p text:style-name=\"OrgTitle\"/>\n"
   1488 	     ;; Subtitle.
   1489 	     (when (org-string-nw-p subtitle)
   1490 	       (concat
   1491 		(format "<text:p text:style-name=\"OrgSubtitle\">\n%s\n</text:p>\n"
   1492 			(concat
   1493 			 "<text:user-defined style:data-style-name=\"N0\" text:name=\"subtitle\">\n"
   1494 			 subtitle
   1495 			 "</text:user-defined>\n"))
   1496 		;; Separator.
   1497 		"<text:p text:style-name=\"OrgSubtitle\"/>\n"))))
   1498 	  (cond
   1499 	   ((and author (not email))
   1500 	    ;; Author only.
   1501 	    (concat
   1502 	     (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1503 		     "OrgSubtitle"
   1504 		     (format "<text:initial-creator>%s</text:initial-creator>" author))
   1505 	     ;; Separator.
   1506 	     "\n<text:p text:style-name=\"OrgSubtitle\"/>"))
   1507 	   ((and author email)
   1508 	    ;; Author and E-mail.
   1509 	    (concat
   1510 	     (format
   1511 	      "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1512 	      "OrgSubtitle"
   1513 	      (format
   1514 	       "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
   1515 	       (concat "mailto:" email)
   1516 	       (format "<text:initial-creator>%s</text:initial-creator>" author)))
   1517 	     ;; Separator.
   1518 	     "\n<text:p text:style-name=\"OrgSubtitle\"/>")))
   1519 	  ;; Date, if required.
   1520 	  (when (plist-get info :with-date)
   1521 	    (let* ((date (plist-get info :date))
   1522 		   ;; Check if DATE is specified as a timestamp.
   1523 		   (timestamp (and (not (cdr date))
   1524 				   (eq (org-element-type (car date)) 'timestamp)
   1525 				   (car date))))
   1526 	      (when date
   1527 		(concat
   1528 		 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1529 			 "OrgSubtitle"
   1530 			 (if (and (plist-get info :odt-use-date-fields) timestamp)
   1531 			     (org-odt--format-timestamp (car date))
   1532 			   (org-export-data date info)))
   1533 		 ;; Separator
   1534 		 "<text:p text:style-name=\"OrgSubtitle\"/>")))))))
   1535       ;; Table of Contents
   1536       (let* ((with-toc (plist-get info :with-toc))
   1537 	     (depth (and with-toc (if (wholenump with-toc)
   1538 				      with-toc
   1539 				    (plist-get info :headline-levels)))))
   1540 	(when depth (insert (or (org-odt-toc depth info) ""))))
   1541       ;; Contents.
   1542       (insert contents)
   1543       ;; Return contents.
   1544       (buffer-substring-no-properties (point-min) (point-max)))))
   1545 
   1546 
   1547 
   1548 ;;; Transcode Functions
   1549 
   1550 ;;;; Bold
   1551 
   1552 (defun org-odt-bold (_bold contents _info)
   1553   "Transcode BOLD from Org to ODT.
   1554 CONTENTS is the text with bold markup.  INFO is a plist holding
   1555 contextual information."
   1556   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1557 	  "Bold" contents))
   1558 
   1559 
   1560 ;;;; Center Block
   1561 
   1562 (defun org-odt-center-block (_center-block contents _info)
   1563   "Transcode a CENTER-BLOCK element from Org to ODT.
   1564 CONTENTS holds the contents of the center block.  INFO is a plist
   1565 holding contextual information."
   1566   contents)
   1567 
   1568 
   1569 ;;;; Clock
   1570 
   1571 (defun org-odt-clock (clock contents info)
   1572   "Transcode a CLOCK element from Org to ODT.
   1573 CONTENTS is nil.  INFO is a plist used as a communication
   1574 channel."
   1575   (let ((timestamp (org-element-property :value clock))
   1576 	(duration (org-element-property :duration clock)))
   1577     (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1578 	    (if (eq (org-element-type (org-export-get-next-element clock info))
   1579 		    'clock) "OrgClock" "OrgClockLastLine")
   1580 	    (concat
   1581 	     (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1582 		     "OrgClockKeyword" org-clock-string)
   1583 	     (org-odt-timestamp timestamp contents info)
   1584 	     (and duration (format " (%s)" duration))))))
   1585 
   1586 
   1587 ;;;; Code
   1588 
   1589 (defun org-odt-code (code _contents _info)
   1590   "Transcode a CODE object from Org to ODT.
   1591 CONTENTS is nil.  INFO is a plist used as a communication
   1592 channel."
   1593   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1594 	  "OrgCode" (org-odt--encode-plain-text
   1595 		     (org-element-property :value code))))
   1596 
   1597 
   1598 ;;;; Drawer
   1599 
   1600 (defun org-odt-drawer (drawer contents info)
   1601   "Transcode a DRAWER element from Org to ODT.
   1602 CONTENTS holds the contents of the block.  INFO is a plist
   1603 holding contextual information."
   1604   (let* ((name (org-element-property :drawer-name drawer))
   1605 	 (output (funcall (plist-get info :odt-format-drawer-function)
   1606 			  name contents)))
   1607     output))
   1608 
   1609 
   1610 ;;;; Dynamic Block
   1611 
   1612 (defun org-odt-dynamic-block (_dynamic-block contents _info)
   1613   "Transcode a DYNAMIC-BLOCK element from Org to ODT.
   1614 CONTENTS holds the contents of the block.  INFO is a plist
   1615 holding contextual information.  See `org-export-data'."
   1616   contents)
   1617 
   1618 
   1619 ;;;; Entity
   1620 
   1621 (defun org-odt-entity (entity _contents _info)
   1622   "Transcode an ENTITY object from Org to ODT.
   1623 CONTENTS are the definition itself.  INFO is a plist holding
   1624 contextual information."
   1625   (org-element-property :utf-8 entity))
   1626 
   1627 
   1628 ;;;; Example Block
   1629 
   1630 (defun org-odt-example-block (example-block _contents info)
   1631   "Transcode a EXAMPLE-BLOCK element from Org to ODT.
   1632 CONTENTS is nil.  INFO is a plist holding contextual information."
   1633   (org-odt-format-code example-block info))
   1634 
   1635 
   1636 ;;;; Export Snippet
   1637 
   1638 (defun org-odt-export-snippet (export-snippet _contents _info)
   1639   "Transcode a EXPORT-SNIPPET object from Org to ODT.
   1640 CONTENTS is nil.  INFO is a plist holding contextual information."
   1641   (when (eq (org-export-snippet-backend export-snippet) 'odt)
   1642     (org-element-property :value export-snippet)))
   1643 
   1644 
   1645 ;;;; Export Block
   1646 
   1647 (defun org-odt-export-block (export-block _contents _info)
   1648   "Transcode a EXPORT-BLOCK element from Org to ODT.
   1649 CONTENTS is nil.  INFO is a plist holding contextual information."
   1650   (when (string= (org-element-property :type export-block) "ODT")
   1651     (org-remove-indentation (org-element-property :value export-block))))
   1652 
   1653 
   1654 ;;;; Fixed Width
   1655 
   1656 (defun org-odt-fixed-width (fixed-width _contents info)
   1657   "Transcode a FIXED-WIDTH element from Org to ODT.
   1658 CONTENTS is nil.  INFO is a plist holding contextual information."
   1659   (org-odt-do-format-code (org-element-property :value fixed-width) info))
   1660 
   1661 
   1662 ;;;; Footnote Definition
   1663 
   1664 ;; Footnote Definitions are ignored.
   1665 
   1666 
   1667 ;;;; Footnote Reference
   1668 
   1669 (defun org-odt-footnote-reference (footnote-reference _contents info)
   1670   "Transcode a FOOTNOTE-REFERENCE element from Org to ODT.
   1671 CONTENTS is nil.  INFO is a plist holding contextual information."
   1672   (let ((--format-footnote-definition
   1673 	 (lambda (n def)
   1674 	   (setq n (format "%d" n))
   1675 	   (let ((id (concat  "fn" n))
   1676 		 (note-class "footnote"))
   1677 	     (format
   1678 	      "<text:note text:id=\"%s\" text:note-class=\"%s\">%s</text:note>"
   1679 	      id note-class
   1680 	      (concat
   1681 	       (format "<text:note-citation>%s</text:note-citation>" n)
   1682 	       (format "<text:note-body>%s</text:note-body>" def))))))
   1683 	(--format-footnote-reference
   1684 	 (lambda (n)
   1685 	   (setq n (format "%d" n))
   1686 	   (let ((note-class "footnote")
   1687 		 (ref-format "text")
   1688 		 (ref-name (concat "fn" n)))
   1689 	     (format
   1690 	      "<text:span text:style-name=\"%s\">%s</text:span>"
   1691 	      "OrgSuperscript"
   1692 	      (format "<text:note-ref text:note-class=\"%s\" text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:note-ref>"
   1693 		      note-class ref-format ref-name n))))))
   1694     (concat
   1695      ;; Insert separator between two footnotes in a row.
   1696      (let ((prev (org-export-get-previous-element footnote-reference info)))
   1697        (and (eq (org-element-type prev) 'footnote-reference)
   1698 	    (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1699 		    "OrgSuperscript" ",")))
   1700      ;; Transcode footnote reference.
   1701      (let ((n (org-export-get-footnote-number footnote-reference info nil t)))
   1702        (cond
   1703 	((not
   1704 	  (org-export-footnote-first-reference-p footnote-reference info nil t))
   1705 	 (funcall --format-footnote-reference n))
   1706 	(t
   1707 	 (let* ((raw (org-export-get-footnote-definition
   1708 		      footnote-reference info))
   1709 		(def
   1710 		 (let ((def (org-trim
   1711 			     (org-export-data-with-backend
   1712 			      raw
   1713 			      (org-export-create-backend
   1714 			       :parent 'odt
   1715 			       :transcoders
   1716 			       '((paragraph . (lambda (p c i)
   1717 						(org-odt--format-paragraph
   1718 						 p c i
   1719 						 "Footnote"
   1720 						 "OrgFootnoteCenter"
   1721 						 "OrgFootnoteQuotations")))))
   1722 			      info))))
   1723 		   ;; Inline definitions are secondary strings.  We
   1724 		   ;; need to wrap them within a paragraph.
   1725 		   (if (eq (org-element-class (car (org-element-contents raw)))
   1726 			   'element)
   1727 		       def
   1728 		     (format
   1729 		      "\n<text:p text:style-name=\"Footnote\">%s</text:p>"
   1730 		      def)))))
   1731 	   (funcall --format-footnote-definition n def))))))))
   1732 
   1733 
   1734 ;;;; Headline
   1735 
   1736 (defun org-odt-format-headline--wrap (headline backend info
   1737 					       &optional format-function
   1738 					       &rest extra-keys)
   1739   "Transcode a HEADLINE element using BACKEND.
   1740 INFO is a plist holding contextual information."
   1741   (setq backend (or backend (plist-get info :back-end)))
   1742   (let* ((level (+ (org-export-get-relative-level headline info)))
   1743 	 (headline-number (org-export-get-headline-number headline info))
   1744 	 (section-number (and (org-export-numbered-headline-p headline info)
   1745 			      (mapconcat 'number-to-string
   1746 					 headline-number ".")))
   1747 	 (todo (and (plist-get info :with-todo-keywords)
   1748 		    (let ((todo (org-element-property :todo-keyword headline)))
   1749 		      (and todo
   1750 			   (org-export-data-with-backend todo backend info)))))
   1751 	 (todo-type (and todo (org-element-property :todo-type headline)))
   1752 	 (priority (and (plist-get info :with-priority)
   1753 			(org-element-property :priority headline)))
   1754 	 (text (org-export-data-with-backend
   1755 		(org-element-property :title headline) backend info))
   1756 	 (tags (and (plist-get info :with-tags)
   1757 		    (org-export-get-tags headline info)))
   1758 	 (headline-label (org-export-get-reference headline info))
   1759 	 (format-function
   1760 	  (if (functionp format-function) format-function
   1761 	    (cl-function
   1762 	     (lambda (todo todo-type priority text tags
   1763 		           &key _level _section-number _headline-label
   1764 		           &allow-other-keys)
   1765 	       (funcall (plist-get info :odt-format-headline-function)
   1766 			todo todo-type priority text tags))))))
   1767     (apply format-function
   1768 	   todo todo-type priority text tags
   1769 	   :headline-label headline-label
   1770 	   :level level
   1771 	   :section-number section-number extra-keys)))
   1772 
   1773 (defun org-odt-headline (headline contents info)
   1774   "Transcode a HEADLINE element from Org to ODT.
   1775 CONTENTS holds the contents of the headline.  INFO is a plist
   1776 holding contextual information."
   1777   ;; Case 1: This is a footnote section: ignore it.
   1778   (unless (org-element-property :footnote-section-p headline)
   1779     (let* ((full-text (org-odt-format-headline--wrap headline nil info))
   1780 	   ;; Get level relative to current parsed data.
   1781 	   (level (org-export-get-relative-level headline info))
   1782 	   (numbered (org-export-numbered-headline-p headline info))
   1783 	   ;; Get canonical label for the headline.
   1784 	   (id (org-export-get-reference headline info))
   1785 	   ;; Extra targets.
   1786 	   (extra-targets
   1787 	    (let ((id (org-element-property :ID headline)))
   1788 	      (if id (org-odt--target "" (concat "ID-" id)) "")))
   1789 	   ;; Title.
   1790 	   (anchored-title (org-odt--target full-text id)))
   1791       (cond
   1792        ;; Case 2. This is a deep sub-tree: export it as a list item.
   1793        ;;         Also export as items headlines for which no section
   1794        ;;         format has been found.
   1795        ((org-export-low-level-p headline info)
   1796 	;; Build the real contents of the sub-tree.
   1797 	(concat
   1798 	 (and (org-export-first-sibling-p headline info)
   1799 	      (format "\n<text:list text:style-name=\"%s\" %s>"
   1800 		      ;; Choose style based on list type.
   1801 		      (if numbered "OrgNumberedList" "OrgBulletedList")
   1802 		      ;; If top-level list, re-start numbering.  Otherwise,
   1803 		      ;; continue numbering.
   1804 		      (format "text:continue-numbering=\"%s\""
   1805 			      (let* ((parent (org-export-get-parent-headline
   1806 					      headline)))
   1807 				(if (and parent
   1808 					 (org-export-low-level-p parent info))
   1809 				    "true" "false")))))
   1810 	 (let ((headline-has-table-p
   1811 		(let ((section (assq 'section (org-element-contents headline))))
   1812 		  (assq 'table (and section (org-element-contents section))))))
   1813 	   (format "\n<text:list-item>\n%s\n%s"
   1814 		   (concat
   1815 		    (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1816 			    "Text_20_body"
   1817 			    (concat extra-targets anchored-title))
   1818 		    contents)
   1819 		   (if headline-has-table-p
   1820 		       "</text:list-header>"
   1821 		     "</text:list-item>")))
   1822 	 (and (org-export-last-sibling-p headline info)
   1823 	      "</text:list>")))
   1824        ;; Case 3. Standard headline.  Export it as a section.
   1825        (t
   1826 	(concat
   1827 	 (format
   1828 	  "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\" text:is-list-header=\"%s\">%s</text:h>"
   1829 	  (format "Heading_20_%s%s"
   1830 		  level (if numbered "" "_unnumbered"))
   1831 	  level
   1832 	  (if numbered "false" "true")
   1833 	  (concat extra-targets anchored-title))
   1834 	 contents))))))
   1835 
   1836 (defun org-odt-format-headline-default-function
   1837     (todo todo-type priority text tags)
   1838   "Default format function for a headline.
   1839 See `org-odt-format-headline-function' for details."
   1840   (concat
   1841    ;; Todo.
   1842    (when todo
   1843      (let ((style (if (eq todo-type 'done) "OrgDone" "OrgTodo")))
   1844        (format "<text:span text:style-name=\"%s\">%s</text:span> " style todo)))
   1845    (when priority
   1846      (let* ((style (format "OrgPriority-%c" priority))
   1847 	    (priority (format "[#%c]" priority)))
   1848        (format "<text:span text:style-name=\"%s\">%s</text:span> "
   1849 	       style priority)))
   1850    ;; Title.
   1851    text
   1852    ;; Tags.
   1853    (when tags
   1854      (concat
   1855       "<text:tab/>"
   1856       (format "<text:span text:style-name=\"%s\">[%s]</text:span>"
   1857 	      "OrgTags" (mapconcat
   1858 			 (lambda (tag)
   1859 			   (format
   1860 			    "<text:span text:style-name=\"%s\">%s</text:span>"
   1861 			    "OrgTag" tag)) tags " : "))))))
   1862 
   1863 
   1864 ;;;; Horizontal Rule
   1865 
   1866 (defun org-odt-horizontal-rule (_horizontal-rule _contents _info)
   1867   "Transcode an HORIZONTAL-RULE  object from Org to ODT.
   1868 CONTENTS is nil.  INFO is a plist holding contextual information."
   1869   (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1870 	  "Horizontal_20_Line" ""))
   1871 
   1872 
   1873 ;;;; Inline Babel Call
   1874 
   1875 ;; Inline Babel Calls are ignored.
   1876 
   1877 
   1878 ;;;; Inline Src Block
   1879 
   1880 (defun org-odt--find-verb-separator (s)
   1881   "Return a character not used in string S.
   1882 This is used to choose a separator for constructs like \\verb."
   1883   (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
   1884     (cl-loop for c across ll
   1885 	     when (not (string-match (regexp-quote (char-to-string c)) s))
   1886 	     return (char-to-string c))))
   1887 
   1888 (defun org-odt-inline-src-block (_inline-src-block _contents _info)
   1889   "Transcode an INLINE-SRC-BLOCK element from Org to ODT.
   1890 CONTENTS holds the contents of the item.  INFO is a plist holding
   1891 contextual information."
   1892   (error "FIXME"))
   1893 
   1894 
   1895 ;;;; Inlinetask
   1896 
   1897 (defun org-odt-inlinetask (inlinetask contents info)
   1898   "Transcode an INLINETASK element from Org to ODT.
   1899 CONTENTS holds the contents of the block.  INFO is a plist
   1900 holding contextual information."
   1901   (let* ((todo
   1902 	  (and (plist-get info :with-todo-keywords)
   1903 	       (let ((todo (org-element-property :todo-keyword inlinetask)))
   1904 		 (and todo (org-export-data todo info)))))
   1905 	 (todo-type (and todo (org-element-property :todo-type inlinetask)))
   1906 	 (priority (and (plist-get info :with-priority)
   1907 			(org-element-property :priority inlinetask)))
   1908 	 (text (org-export-data (org-element-property :title inlinetask) info))
   1909 	 (tags (and (plist-get info :with-tags)
   1910 		    (org-export-get-tags inlinetask info))))
   1911     (funcall (plist-get info :odt-format-inlinetask-function)
   1912 	     todo todo-type priority text tags contents)))
   1913 
   1914 (defun org-odt-format-inlinetask-default-function
   1915     (todo todo-type priority name tags contents)
   1916   "Default format function for inlinetasks.
   1917 See `org-odt-format-inlinetask-function' for details."
   1918   (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1919 	  "Text_20_body"
   1920 	  (org-odt--textbox
   1921 	   (concat
   1922 	    (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   1923 		    "OrgInlineTaskHeading"
   1924 		    (org-odt-format-headline-default-function
   1925 		     todo todo-type priority name tags))
   1926 	    contents)
   1927 	   nil nil "OrgInlineTaskFrame" " style:rel-width=\"100%\"")))
   1928 
   1929 ;;;; Italic
   1930 
   1931 (defun org-odt-italic (_italic contents _info)
   1932   "Transcode ITALIC from Org to ODT.
   1933 CONTENTS is the text with italic markup.  INFO is a plist holding
   1934 contextual information."
   1935   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   1936 	  "Emphasis" contents))
   1937 
   1938 
   1939 ;;;; Item
   1940 
   1941 (defun org-odt-item (item contents info)
   1942   "Transcode an ITEM element from Org to ODT.
   1943 CONTENTS holds the contents of the item.  INFO is a plist holding
   1944 contextual information."
   1945   (let* ((plain-list (org-export-get-parent item))
   1946 	 (count (org-element-property :counter item))
   1947 	 (type (org-element-property :type plain-list)))
   1948     (unless (memq type '(ordered unordered descriptive-1 descriptive-2))
   1949       (error "Unknown list type: %S" type))
   1950     (format "\n<text:list-item%s>\n%s\n%s"
   1951 	    (if count (format " text:start-value=\"%s\"" count) "")
   1952 	    contents
   1953 	    (if (org-element-map item 'table #'identity info 'first-match)
   1954 		"</text:list-header>"
   1955 	      "</text:list-item>"))))
   1956 
   1957 ;;;; Keyword
   1958 
   1959 (defun org-odt-keyword (keyword _contents info)
   1960   "Transcode a KEYWORD element from Org to ODT.
   1961 CONTENTS is nil.  INFO is a plist holding contextual
   1962 information."
   1963   (let ((key (org-element-property :key keyword))
   1964 	(value (org-element-property :value keyword)))
   1965     (cond
   1966      ((string= key "ODT") value)
   1967      ((string= key "INDEX")
   1968       ;; FIXME
   1969       (ignore))
   1970      ((string= key "TOC")
   1971       (let ((case-fold-search t))
   1972 	(cond
   1973 	 ((string-match-p "\\<headlines\\>" value)
   1974 	  (let ((depth (or (and (string-match "\\<[0-9]+\\>" value)
   1975 				(string-to-number (match-string 0 value)))
   1976 			   (plist-get info :headline-levels)))
   1977 		(scope
   1978 		 (cond
   1979 		  ((string-match ":target +\\(\".+?\"\\|\\S-+\\)" value) ;link
   1980 		   (org-export-resolve-link
   1981 		    (org-strip-quotes (match-string 1 value)) info))
   1982 		  ((string-match-p "\\<local\\>" value) keyword)))) ;local
   1983 	    (org-odt-toc depth info scope)))
   1984 	 ((string-match-p "tables\\|figures\\|listings" value)
   1985 	  ;; FIXME
   1986 	  (ignore))))))))
   1987 
   1988 
   1989 ;;;; Latex Environment
   1990 
   1991 ;; (eval-after-load 'ox-odt '(ad-deactivate 'org-format-latex-as-mathml))
   1992 ;; (advice-add 'org-format-latex-as-mathml	; FIXME
   1993 ;;   :around #'org--odt-protect-latex-fragment)
   1994 ;; (defun org--odt-protect-latex-fragment (orig-fun latex-frag &rest args)
   1995 ;;   "Encode LaTeX fragment as XML.
   1996 ;; Do this when translation to MathML fails."
   1997 ;;   (let ((retval (apply orig-fun latex-frag args)))
   1998 ;;     (if (> (length retval) 0)
   1999 ;;         retval
   2000 ;;       (org-odt--encode-plain-text latex-frag))))
   2001 
   2002 (defun org-odt-latex-environment (latex-environment _contents info)
   2003   "Transcode a LATEX-ENVIRONMENT element from Org to ODT.
   2004 CONTENTS is nil.  INFO is a plist holding contextual information."
   2005   (let* ((latex-frag (org-remove-indentation
   2006 		      (org-element-property :value latex-environment))))
   2007     (org-odt-do-format-code latex-frag info)))
   2008 
   2009 
   2010 ;;;; Latex Fragment
   2011 
   2012 ;; (when latex-frag			; FIXME
   2013 ;; 	(setq href (propertize href :title "LaTeX Fragment"
   2014 ;; 				   :description latex-frag)))
   2015 ;; handle verbatim
   2016 ;; provide descriptions
   2017 
   2018 (defun org-odt-latex-fragment (latex-fragment _contents _info)
   2019   "Transcode a LATEX-FRAGMENT object from Org to ODT.
   2020 CONTENTS is nil.  INFO is a plist holding contextual information."
   2021   (let ((latex-frag (org-element-property :value latex-fragment)))
   2022     (format "<text:span text:style-name=\"%s\">%s</text:span>"
   2023 	    "OrgCode" (org-odt--encode-plain-text latex-frag t))))
   2024 
   2025 
   2026 ;;;; Line Break
   2027 
   2028 (defun org-odt-line-break (_line-break _contents _info)
   2029   "Transcode a LINE-BREAK object from Org to ODT.
   2030 CONTENTS is nil.  INFO is a plist holding contextual information."
   2031   "<text:line-break/>")
   2032 
   2033 
   2034 ;;;; Link
   2035 
   2036 ;;;; Links :: Label references
   2037 
   2038 (defun org-odt--enumerate (element info &optional predicate n)
   2039   (when predicate (cl-assert (funcall predicate element info)))
   2040   (let* ((--numbered-parent-headline-at-<=-n
   2041 	  (lambda (element n info)
   2042 	    (cl-loop for x in (org-element-lineage element)
   2043 		     thereis (and (eq (org-element-type x) 'headline)
   2044 				  (<= (org-export-get-relative-level x info) n)
   2045 				  (org-export-numbered-headline-p x info)
   2046 				  x))))
   2047 	 (--enumerate
   2048 	  (lambda (element scope info &optional predicate)
   2049 	    (let ((counter 0))
   2050 	      (org-element-map (or scope (plist-get info :parse-tree))
   2051 		  (org-element-type element)
   2052 		(lambda (el)
   2053 		  (and (or (not predicate) (funcall predicate el info))
   2054 		       (cl-incf counter)
   2055 		       (eq element el)
   2056 		       counter))
   2057 		info 'first-match))))
   2058 	 (scope (funcall --numbered-parent-headline-at-<=-n
   2059 			 element
   2060 			 (or n (plist-get info :odt-display-outline-level))
   2061 			 info))
   2062 	 (ordinal (funcall --enumerate element scope info predicate))
   2063 	 (tag
   2064 	  (concat
   2065 	   ;; Section number.
   2066 	   (and scope
   2067 		(mapconcat 'number-to-string
   2068 			   (org-export-get-headline-number scope info) "."))
   2069 	   ;; Separator.
   2070 	   (and scope ".")
   2071 	   ;; Ordinal.
   2072 	   (number-to-string ordinal))))
   2073     tag))
   2074 
   2075 (defun org-odt-format-label (element info op)
   2076   "Return a label for ELEMENT.
   2077 
   2078 ELEMENT is a `link', `table', `src-block' or `paragraph' type
   2079 element.  INFO is a plist used as a communication channel.  OP is
   2080 either `definition' or `reference', depending on the purpose of
   2081 the generated string.
   2082 
   2083 Return value is a string if OP is set to `reference' or a cons
   2084 cell like CAPTION . SHORT-CAPTION) where CAPTION and
   2085 SHORT-CAPTION are strings."
   2086   (cl-assert (memq (org-element-type element) '(link table src-block paragraph)))
   2087   (let* ((element-or-parent
   2088 	  (cl-case (org-element-type element)
   2089 	    (link (org-export-get-parent-element element))
   2090 	    (t element)))
   2091 	 ;; Get label and caption.
   2092 	 (label (and (or (org-element-property :name element)
   2093 			 (org-element-property :name element-or-parent))
   2094 		     (org-export-get-reference element-or-parent info)))
   2095 	 (caption (let ((c (org-export-get-caption element-or-parent)))
   2096 		    (and c (org-export-data c info))))
   2097 	 ;; FIXME: We don't use short-caption for now
   2098 	 ;; (short-caption nil)
   2099 	 )
   2100     (when (or label caption)
   2101       (let* ((default-category
   2102 	       (cl-case (org-element-type element)
   2103 		 (table "__Table__")
   2104 		 (src-block "__Listing__")
   2105 		 ((link paragraph)
   2106 		  (cond
   2107 		   ((org-odt--enumerable-latex-image-p element info)
   2108 		    "__DvipngImage__")
   2109 		   ((org-odt--enumerable-image-p element info)
   2110 		    "__Figure__")
   2111 		   ((org-odt--enumerable-formula-p element info)
   2112 		    "__MathFormula__")
   2113 		   (t (error "Don't know how to format label for link: %S"
   2114 			     element))))
   2115 		 (t (error "Don't know how to format label for element type: %s"
   2116 			   (org-element-type element)))))
   2117 	     seqno)
   2118 	(cl-assert default-category)
   2119 	(pcase-let
   2120 	    ((`(,counter ,label-style ,category ,predicate)
   2121 	      (assoc-default default-category org-odt-category-map-alist)))
   2122 	  ;; Compute sequence number of the element.
   2123 	  (setq seqno (org-odt--enumerate element info predicate))
   2124 	  ;; Localize category string.
   2125 	  (setq category (org-export-translate category :utf-8 info))
   2126 	  (cl-case op
   2127 	    ;; Case 1: Handle Label definition.
   2128 	    (definition
   2129 	      (cons
   2130 	       (concat
   2131 		;; Sneak in a bookmark.  The bookmark is used when the
   2132 		;; labeled element is referenced with a link that
   2133 		;; provides its own description.
   2134 		(format "\n<text:bookmark text:name=\"%s\"/>" label)
   2135 		;; Label definition: Typically formatted as below:
   2136 		;;     CATEGORY SEQ-NO: LONG CAPTION
   2137 		;; with translation for correct punctuation.
   2138 		(format-spec
   2139 		 (org-export-translate
   2140 		  (cadr (assoc-string label-style org-odt-label-styles t))
   2141 		  :utf-8 info)
   2142 		 `((?e . ,category)
   2143 		   (?n . ,(format
   2144 			   "<text:sequence text:ref-name=\"%s\" text:name=\"%s\" text:formula=\"ooow:%s+1\" style:num-format=\"1\">%s</text:sequence>"
   2145 			   label counter counter seqno))
   2146 		   (?c . ,(or caption "")))))
   2147 	       nil)) ;; short-caption
   2148 	    ;; Case 2: Handle Label reference.
   2149 	    (reference
   2150 	     (let* ((fmt (cddr (assoc-string label-style org-odt-label-styles t)))
   2151 		    (fmt1 (car fmt))
   2152 		    (fmt2 (cadr fmt)))
   2153 	       (format "<text:sequence-ref text:reference-format=\"%s\" text:ref-name=\"%s\">%s</text:sequence-ref>"
   2154 		       fmt1
   2155 		       label
   2156 		       (format-spec fmt2 `((?e . ,category) (?n . ,seqno))))))
   2157 	    (t (error "Unknown %S on label" op))))))))
   2158 
   2159 
   2160 ;;;; Links :: Inline Images
   2161 
   2162 (defun org-odt--copy-image-file (path)
   2163   "Return the internal name of the file."
   2164   (let* ((image-type (file-name-extension path))
   2165 	 (media-type (format "image/%s" image-type))
   2166 	 (target-dir "Images/")
   2167 	 (target-file
   2168 	  (format "%s%04d.%s" target-dir
   2169 		  (cl-incf org-odt-embedded-images-count) image-type)))
   2170     (message "Embedding %s as %s..."
   2171 	     (substring-no-properties path) target-file)
   2172 
   2173     (when (= 1 org-odt-embedded-images-count)
   2174       (make-directory (concat org-odt-zip-dir target-dir))
   2175       (org-odt-create-manifest-file-entry "" target-dir))
   2176 
   2177     (copy-file path (concat org-odt-zip-dir target-file) 'overwrite)
   2178     (org-odt-create-manifest-file-entry media-type target-file)
   2179     target-file))
   2180 
   2181 ;; For --without-x builds.
   2182 (declare-function clear-image-cache "image.c" (&optional filter))
   2183 (declare-function image-size "image.c" (spec &optional pixels frame))
   2184 
   2185 (defun org-odt--image-size
   2186     (file info &optional user-width user-height scale dpi embed-as)
   2187   (let* ((--pixels-to-cms
   2188           (lambda (pixels dpi)
   2189             (let ((cms-per-inch 2.54)
   2190                   (inches (/ pixels dpi)))
   2191               (* cms-per-inch inches))))
   2192 	 (--size-in-cms
   2193 	  (lambda (size-in-pixels dpi)
   2194 	    (and size-in-pixels
   2195 		 (cons (funcall --pixels-to-cms (car size-in-pixels) dpi)
   2196 		       (funcall --pixels-to-cms (cdr size-in-pixels) dpi)))))
   2197 	 (dpi (or dpi (plist-get info :odt-pixels-per-inch)))
   2198 	 (anchor-type (or embed-as "paragraph"))
   2199 	 (user-width (and (not scale) user-width))
   2200 	 (user-height (and (not scale) user-height))
   2201 	 (size
   2202 	  (and
   2203 	   (not (and user-height user-width))
   2204 	   (or
   2205 	    ;; Use Imagemagick.
   2206 	    (and (executable-find "identify")
   2207 		 (let ((size-in-pixels
   2208 			(let ((dim (shell-command-to-string
   2209 				    (format "identify -format \"%%w:%%h\" \"%s\""
   2210 					    file))))
   2211 			  (when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
   2212 			    (cons (string-to-number (match-string 1 dim))
   2213 				  (string-to-number (match-string 2 dim)))))))
   2214 		   (funcall --size-in-cms size-in-pixels dpi)))
   2215 	    ;; Use Emacs.
   2216 	    (let ((size-in-pixels
   2217 		   (ignore-errors	; Emacs could be in batch mode
   2218 		     (clear-image-cache)
   2219 		     (image-size (create-image file) 'pixels))))
   2220 	      (funcall --size-in-cms size-in-pixels dpi))
   2221 	    ;; Use hard-coded values.
   2222 	    (cdr (assoc-string anchor-type
   2223 			       org-odt-default-image-sizes-alist))
   2224 	    ;; Error out.
   2225 	    (error "Cannot determine image size, aborting"))))
   2226 	 (width (car size)) (height (cdr size)))
   2227     (cond
   2228      (scale
   2229       (setq width (* width scale) height (* height scale)))
   2230      ((and user-height user-width)
   2231       (setq width user-width height user-height))
   2232      (user-height
   2233       (setq width (* user-height (/ width height)) height user-height))
   2234      (user-width
   2235       (setq height (* user-width (/ height width)) width user-width))
   2236      (t (ignore)))
   2237     ;; ensure that an embedded image fits comfortably within a page
   2238     (let ((max-width (car org-odt-max-image-size))
   2239 	  (max-height (cdr org-odt-max-image-size)))
   2240       (when (or (> width max-width) (> height max-height))
   2241 	(let* ((scale1 (/ max-width width))
   2242 	       (scale2 (/ max-height height))
   2243 	       (scale (min scale1 scale2)))
   2244 	  (setq width (* scale width) height (* scale height)))))
   2245     (cons width height)))
   2246 
   2247 (defun org-odt-link--inline-image (element info)
   2248   "Return ODT code for an inline image.
   2249 LINK is the link pointing to the inline image.  INFO is a plist
   2250 used as a communication channel."
   2251   (cl-assert (eq (org-element-type element) 'link))
   2252   (let* ((src (let* ((type (org-element-property :type element))
   2253 		     (raw-path (org-element-property :path element)))
   2254 		(cond ((member type '("http" "https"))
   2255 		       (concat type ":" raw-path))
   2256 		      ((file-name-absolute-p raw-path)
   2257 		       (expand-file-name raw-path))
   2258 		      (t raw-path))))
   2259 	 (src-expanded (if (file-name-absolute-p src) src
   2260 			 (expand-file-name src (file-name-directory
   2261 						(plist-get info :input-file)))))
   2262 	 (href (format
   2263 		"\n<draw:image xlink:href=\"%s\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"/>"
   2264 		(org-odt--copy-image-file src-expanded)))
   2265 	 ;; Extract attributes from #+ATTR_ODT line.
   2266 	 (attr-from (cl-case (org-element-type element)
   2267 		      (link (org-export-get-parent-element element))
   2268 		      (t element)))
   2269 	 ;; Convert attributes to a plist.
   2270 	 (attr-plist (org-export-read-attribute :attr_odt attr-from))
   2271 	 ;; Handle `:anchor', `:style' and `:attributes' properties.
   2272 	 (user-frame-anchor
   2273 	  (car (assoc-string (plist-get attr-plist :anchor)
   2274 			     '(("as-char") ("paragraph") ("page")) t)))
   2275 	 (user-frame-style
   2276 	  (and user-frame-anchor (plist-get attr-plist :style)))
   2277 	 (user-frame-attrs
   2278 	  (and user-frame-anchor (plist-get attr-plist :attributes)))
   2279 	 (user-frame-params
   2280 	  (list user-frame-style user-frame-attrs user-frame-anchor))
   2281 	 ;; (embed-as (or embed-as user-frame-anchor "paragraph"))
   2282 	 ;;
   2283 	 ;; Handle `:width', `:height' and `:scale' properties.  Read
   2284 	 ;; them as numbers since we need them for computations.
   2285 	 (size (org-odt--image-size
   2286 		src-expanded info
   2287 		(let ((width (plist-get attr-plist :width)))
   2288 		  (and width (read width)))
   2289 		(let ((length (plist-get attr-plist :length)))
   2290 		  (and length (read length)))
   2291 		(let ((scale (plist-get attr-plist :scale)))
   2292 		  (and scale (read scale)))
   2293 		nil			; embed-as
   2294 		"paragraph"		; FIXME
   2295 		))
   2296 	 (width (car size)) (height (cdr size))
   2297 	 (standalone-link-p (org-odt--standalone-link-p element info))
   2298 	 (embed-as (if standalone-link-p "paragraph" "as-char"))
   2299 	 (captions (org-odt-format-label element info 'definition))
   2300 	 (caption (car captions))
   2301 	 (entity (concat (and caption "Captioned") embed-as "Image"))
   2302 	 ;; Check if this link was created by LaTeX-to-PNG converter.
   2303 	 (replaces (org-element-property
   2304 		    :replaces (if (not standalone-link-p) element
   2305 				(org-export-get-parent-element element))))
   2306 	 ;; If yes, note down the type of the element - LaTeX Fragment
   2307 	 ;; or LaTeX environment.  It will go in to frame title.
   2308 	 (title (and replaces (capitalize
   2309 			       (symbol-name (org-element-type replaces)))))
   2310 
   2311 	 ;; If yes, note down its contents.  It will go in to frame
   2312 	 ;; description.  This quite useful for debugging.
   2313 	 (desc (and replaces (org-element-property :value replaces))))
   2314     (org-odt--render-image/formula entity href width height
   2315 				   captions user-frame-params title desc)))
   2316 
   2317 
   2318 ;;;; Links :: Math formula
   2319 
   2320 (defun org-odt-link--inline-formula (element info)
   2321   (let* ((src (let ((raw-path (org-element-property :path element)))
   2322 		(cond
   2323 		 ((file-name-absolute-p raw-path)
   2324 		  (expand-file-name raw-path))
   2325 		 (t raw-path))))
   2326 	 (src-expanded (if (file-name-absolute-p src) src
   2327 			 (expand-file-name src (file-name-directory
   2328 						(plist-get info :input-file)))))
   2329 	 (href
   2330 	  (format
   2331 	   "\n<draw:object %s xlink:href=\"%s\" xlink:type=\"simple\"/>"
   2332 	   " xlink:show=\"embed\" xlink:actuate=\"onLoad\""
   2333 	   (file-name-directory (org-odt--copy-formula-file src-expanded))))
   2334 	 (standalone-link-p (org-odt--standalone-link-p element info))
   2335 	 (embed-as (if standalone-link-p 'paragraph 'character))
   2336 	 (captions (org-odt-format-label element info 'definition))
   2337 	 ;; Check if this link was created by LaTeX-to-MathML
   2338 	 ;; converter.
   2339 	 (replaces (org-element-property
   2340 		    :replaces (if (not standalone-link-p) element
   2341 				(org-export-get-parent-element element))))
   2342 	 ;; If yes, note down the type of the element - LaTeX Fragment
   2343 	 ;; or LaTeX environment.  It will go in to frame title.
   2344 	 (title (and replaces (capitalize
   2345 			       (symbol-name (org-element-type replaces)))))
   2346 
   2347 	 ;; If yes, note down its contents.  It will go in to frame
   2348 	 ;; description.  This quite useful for debugging.
   2349 	 (desc (and replaces (org-element-property :value replaces)))
   2350 	 ) ;; width height
   2351     (cond
   2352      ((eq embed-as 'character)
   2353       (org-odt--render-image/formula "InlineFormula" href nil nil ;; width height
   2354 				     nil nil title desc))
   2355      (t
   2356       (let* ((equation (org-odt--render-image/formula
   2357 			"CaptionedDisplayFormula" href nil nil ;; width height
   2358 			captions nil title desc))
   2359 	     (label
   2360 	      (let* ((org-odt-category-map-alist
   2361 		      '(("__MathFormula__" "Text" "math-label" "Equation"
   2362 			 org-odt--enumerable-formula-p))))
   2363 		(car (org-odt-format-label element info 'definition)))))
   2364 	(concat equation "<text:tab/>" label))))))
   2365 
   2366 (defun org-odt--copy-formula-file (src-file)
   2367   "Return the internal name of the file."
   2368   (let* ((target-dir (format "Formula-%04d/"
   2369 			     (cl-incf org-odt-embedded-formulas-count)))
   2370 	 (target-file (concat target-dir "content.xml")))
   2371     ;; Create a directory for holding formula file.  Also enter it in
   2372     ;; to manifest.
   2373     (make-directory (concat org-odt-zip-dir target-dir))
   2374     (org-odt-create-manifest-file-entry
   2375      "application/vnd.oasis.opendocument.formula" target-dir "1.2")
   2376     ;; Copy over the formula file from user directory to zip
   2377     ;; directory.
   2378     (message "Embedding %s as %s..." src-file target-file)
   2379     (let ((ext (file-name-extension src-file)))
   2380       (cond
   2381        ;; Case 1: Mathml.
   2382        ((member ext '("mathml" "mml"))
   2383 	(copy-file src-file (concat org-odt-zip-dir target-file) 'overwrite))
   2384        ;; Case 2: OpenDocument formula.
   2385        ((string= ext "odf")
   2386 	(org-odt--zip-extract src-file "content.xml"
   2387 			      (concat org-odt-zip-dir target-dir)))
   2388        (t (error "%s is not a formula file" src-file))))
   2389     ;; Enter the formula file in to manifest.
   2390     (org-odt-create-manifest-file-entry "text/xml" target-file)
   2391     target-file))
   2392 
   2393 ;;;; Targets
   2394 
   2395 (defun org-odt--render-image/formula (cfg-key href width height &optional
   2396 					      captions user-frame-params
   2397 					      &rest title-and-desc)
   2398   (let* ((frame-cfg-alist
   2399 	  ;; Each element of this alist is of the form (CFG-HANDLE
   2400 	  ;; INNER-FRAME-PARAMS OUTER-FRAME-PARAMS).
   2401 
   2402 	  ;; CFG-HANDLE is the key to the alist.
   2403 
   2404 	  ;; INNER-FRAME-PARAMS and OUTER-FRAME-PARAMS specify the
   2405 	  ;; frame params for INNER-FRAME and OUTER-FRAME
   2406 	  ;; respectively.  See below.
   2407 
   2408 	  ;; Configurations that are meant to be applied to
   2409 	  ;; non-captioned image/formula specifies no
   2410 	  ;; OUTER-FRAME-PARAMS.
   2411 
   2412 	  ;; TERMINOLOGY
   2413 	  ;; ===========
   2414 	  ;; INNER-FRAME :: Frame that directly surrounds an
   2415 	  ;;                image/formula.
   2416 
   2417 	  ;; OUTER-FRAME :: Frame that encloses the INNER-FRAME.  This
   2418 	  ;;                frame also contains the caption, if any.
   2419 
   2420 	  ;; FRAME-PARAMS :: List of the form (FRAME-STYLE-NAME
   2421 	  ;;                 FRAME-ATTRIBUTES FRAME-ANCHOR).  Note
   2422 	  ;;                 that these are the last three arguments
   2423 	  ;;                 to `org-odt--frame'.
   2424 
   2425 	  ;; Note that an un-captioned image/formula requires just an
   2426 	  ;; INNER-FRAME, while a captioned image/formula requires
   2427 	  ;; both an INNER and an OUTER-FRAME.
   2428 	  '(("As-CharImage" ("OrgInlineImage" nil "as-char"))
   2429 	    ("ParagraphImage" ("OrgDisplayImage" nil "paragraph"))
   2430 	    ("PageImage" ("OrgPageImage" nil "page"))
   2431 	    ("CaptionedAs-CharImage"
   2432 	     ("OrgCaptionedImage"
   2433 	      " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
   2434 	     ("OrgInlineImage" nil "as-char"))
   2435 	    ("CaptionedParagraphImage"
   2436 	     ("OrgCaptionedImage"
   2437 	      " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
   2438 	     ("OrgImageCaptionFrame" nil "paragraph"))
   2439 	    ("CaptionedPageImage"
   2440 	     ("OrgCaptionedImage"
   2441 	      " style:rel-width=\"100%\" style:rel-height=\"scale\"" "paragraph")
   2442 	     ("OrgPageImageCaptionFrame" nil "page"))
   2443 	    ("InlineFormula" ("OrgInlineFormula" nil "as-char"))
   2444 	    ("DisplayFormula" ("OrgDisplayFormula" nil "as-char"))
   2445 	    ("CaptionedDisplayFormula"
   2446 	     ("OrgCaptionedFormula" nil "paragraph")
   2447 	     ("OrgFormulaCaptionFrame" nil "paragraph"))))
   2448 	 (caption (car captions)) (short-caption (cdr captions))
   2449 	 ;; Retrieve inner and outer frame params, from configuration.
   2450 	 (frame-cfg (assoc-string cfg-key frame-cfg-alist t))
   2451 	 (inner (nth 1 frame-cfg))
   2452 	 (outer (nth 2 frame-cfg))
   2453 	 ;; User-specified frame params (from #+ATTR_ODT spec)
   2454 	 (user user-frame-params)
   2455 	 (--merge-frame-params (lambda (default user)
   2456 				 "Merge default and user frame params."
   2457 				 (if (not user) default
   2458 				   (cl-assert (= (length default) 3))
   2459 				   (cl-assert (= (length user) 3))
   2460 				   (cl-loop for u in user
   2461 					    for d in default
   2462 					    collect (or u d))))))
   2463     (cond
   2464      ;; Case 1: Image/Formula has no caption.
   2465      ;;         There is only one frame, one that surrounds the image
   2466      ;;         or formula.
   2467      ((not caption)
   2468       ;; Merge user frame params with that from configuration.
   2469       (setq inner (funcall --merge-frame-params inner user))
   2470       (apply 'org-odt--frame href width height
   2471 	     (append inner title-and-desc)))
   2472      ;; Case 2: Image/Formula is captioned or labeled.
   2473      ;;         There are two frames: The inner one surrounds the
   2474      ;;         image or formula.  The outer one contains the
   2475      ;;         caption/sequence number.
   2476      (t
   2477       ;; Merge user frame params with outer frame params.
   2478       (setq outer (funcall --merge-frame-params outer user))
   2479       ;; Short caption, if specified, goes as part of inner frame.
   2480       (setq inner (let ((frame-params (copy-sequence inner)))
   2481 		    (setcar (cdr frame-params)
   2482 			    (concat
   2483 			     (cadr frame-params)
   2484 			     (when short-caption
   2485 			       (format " draw:name=\"%s\" " short-caption))))
   2486 		    frame-params))
   2487       (apply 'org-odt--textbox
   2488 	     (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   2489 		     "Illustration"
   2490 		     (concat
   2491 		      (apply 'org-odt--frame href width height
   2492 			     (append inner title-and-desc))
   2493 		      caption))
   2494 	     width height outer)))))
   2495 
   2496 (defun org-odt--enumerable-p (element _info)
   2497   ;; Element should have a caption or label.
   2498   (or (org-element-property :caption element)
   2499       (org-element-property :name element)))
   2500 
   2501 (defun org-odt--enumerable-image-p (element info)
   2502   (org-odt--standalone-link-p
   2503    element info
   2504    ;; Paragraph should have a caption or label.  It SHOULD NOT be a
   2505    ;; replacement element. (i.e., It SHOULD NOT be a result of LaTeX
   2506    ;; processing.)
   2507    (lambda (p)
   2508      (and (not (org-element-property :replaces p))
   2509 	  (or (org-element-property :caption p)
   2510 	      (org-element-property :name p))))
   2511    ;; Link should point to an image file.
   2512    (lambda (l)
   2513      (cl-assert (eq (org-element-type l) 'link))
   2514      (org-export-inline-image-p l (plist-get info :odt-inline-image-rules)))))
   2515 
   2516 (defun org-odt--enumerable-latex-image-p (element info)
   2517   (org-odt--standalone-link-p
   2518    element info
   2519    ;; Paragraph should have a caption or label.  It SHOULD also be a
   2520    ;; replacement element. (i.e., It SHOULD be a result of LaTeX
   2521    ;; processing.)
   2522    (lambda (p)
   2523      (and (org-element-property :replaces p)
   2524 	  (or (org-element-property :caption p)
   2525 	      (org-element-property :name p))))
   2526    ;; Link should point to an image file.
   2527    (lambda (l)
   2528      (cl-assert (eq (org-element-type l) 'link))
   2529      (org-export-inline-image-p l (plist-get info :odt-inline-image-rules)))))
   2530 
   2531 (defun org-odt--enumerable-formula-p (element info)
   2532   (org-odt--standalone-link-p
   2533    element info
   2534    ;; Paragraph should have a caption or label.
   2535    (lambda (p)
   2536      (or (org-element-property :caption p)
   2537 	 (org-element-property :name p)))
   2538    ;; Link should point to a MathML or ODF file.
   2539    (lambda (l)
   2540      (cl-assert (eq (org-element-type l) 'link))
   2541      (org-export-inline-image-p l (plist-get info :odt-inline-formula-rules)))))
   2542 
   2543 (defun org-odt--standalone-link-p (element _info &optional
   2544 					   paragraph-predicate
   2545 					   link-predicate)
   2546   "Test if ELEMENT is a standalone link for the purpose ODT export.
   2547 INFO is a plist holding contextual information.
   2548 
   2549 Return non-nil, if ELEMENT is of type paragraph satisfying
   2550 PARAGRAPH-PREDICATE and its sole content, save for whitespaces,
   2551 is a link that satisfies LINK-PREDICATE.
   2552 
   2553 Return non-nil, if ELEMENT is of type link satisfying
   2554 LINK-PREDICATE and its containing paragraph satisfies
   2555 PARAGRAPH-PREDICATE in addition to having no other content save for
   2556 leading and trailing whitespaces.
   2557 
   2558 Return nil, otherwise."
   2559   (let ((p (cl-case (org-element-type element)
   2560 	     (paragraph element)
   2561 	     (link (and (or (not link-predicate)
   2562 			    (funcall link-predicate element))
   2563 			(org-export-get-parent element)))
   2564 	     (t nil))))
   2565     (when (and p (eq (org-element-type p) 'paragraph))
   2566       (when (or (not paragraph-predicate)
   2567 		(funcall paragraph-predicate p))
   2568 	(let ((contents (org-element-contents p)))
   2569 	  (cl-loop for x in contents
   2570 		   with inline-image-count = 0
   2571 		   always (cl-case (org-element-type x)
   2572 			    (plain-text
   2573 			     (not (org-string-nw-p x)))
   2574 			    (link
   2575 			     (and (or (not link-predicate)
   2576 				      (funcall link-predicate x))
   2577 				  (= (cl-incf inline-image-count) 1)))
   2578 			    (t nil))))))))
   2579 
   2580 (defun org-odt-link--infer-description (destination info)
   2581   ;; DESTINATION is a headline or an element (like paragraph,
   2582   ;; verse-block etc) to which a "#+NAME: label" can be attached.
   2583 
   2584   ;; Note that labels that are attached to captioned entities - inline
   2585   ;; images, math formulae and tables - get resolved as part of
   2586   ;; `org-odt-format-label' and `org-odt--enumerate'.
   2587 
   2588   ;; Create a cross-reference to DESTINATION but make best-efforts to
   2589   ;; create a *meaningful* description.  Check item numbers, section
   2590   ;; number and section title in that order.
   2591 
   2592   ;; NOTE: Counterpart of `org-export-get-ordinal'.
   2593   ;; FIXME: Handle footnote-definition footnote-reference?
   2594   (let* ((genealogy (org-element-lineage destination))
   2595 	 (data (reverse genealogy))
   2596 	 (label (let ((type (org-element-type destination)))
   2597 		  (if (memq type '(headline target))
   2598 		      (org-export-get-reference destination info)
   2599 		    (error "FIXME: Unable to resolve %S" destination)))))
   2600     (or
   2601      (let* ( ;; Locate top-level list.
   2602 	    (top-level-list
   2603 	     (cl-loop for x on data
   2604 		      when (eq (org-element-type (car x)) 'plain-list)
   2605 		      return x))
   2606 	    ;; Get list item nos.
   2607 	    (item-numbers
   2608 	     (cl-loop for (plain-list item . rest) on top-level-list by #'cddr
   2609 		      until (not (eq (org-element-type plain-list) 'plain-list))
   2610 		      collect (when (eq (org-element-property :type
   2611 							      plain-list)
   2612 					'ordered)
   2613 				(1+ (length (org-export-get-previous-element
   2614 					     item info t))))))
   2615 	    ;; Locate top-most listified headline.
   2616 	    (listified-headlines
   2617 	     (cl-loop for x on data
   2618 		      when (and (eq (org-element-type (car x)) 'headline)
   2619 				(org-export-low-level-p (car x) info))
   2620 		      return x))
   2621 	    ;; Get listified headline numbers.
   2622 	    (listified-headline-nos
   2623 	     (cl-loop for el in listified-headlines
   2624 		      when (eq (org-element-type el) 'headline)
   2625 		      collect (when (org-export-numbered-headline-p el info)
   2626 				(1+ (length (org-export-get-previous-element
   2627 					     el info t)))))))
   2628        ;; Combine item numbers from both the listified headlines and
   2629        ;; regular list items.
   2630 
   2631        ;; Case 1: Check if all the parents of list item are numbered.
   2632        ;; If yes, link to the item proper.
   2633        (let ((item-numbers (append listified-headline-nos item-numbers)))
   2634 	 (when (and item-numbers (not (memq nil item-numbers)))
   2635 	   (format "<text:bookmark-ref text:reference-format=\"number-all-superior\" text:ref-name=\"%s\">%s</text:bookmark-ref>"
   2636 		   label
   2637 		   (mapconcat (lambda (n) (if (not n) " "
   2638 				            (concat (number-to-string n) ".")))
   2639 			      item-numbers "")))))
   2640      ;; Case 2: Locate a regular and numbered headline in the
   2641      ;; hierarchy.  Display its section number.
   2642      (let ((headline
   2643 	    (and
   2644 	     ;; Test if destination is a numbered headline.
   2645 	     (org-export-numbered-headline-p destination info)
   2646 	     (cl-loop for el in (cons destination genealogy)
   2647 		      when (and (eq (org-element-type el) 'headline)
   2648 				(not (org-export-low-level-p el info))
   2649 				(org-export-numbered-headline-p el info))
   2650 		      return el))))
   2651        ;; We found one.
   2652        (when headline
   2653 	 (format "<text:bookmark-ref text:reference-format=\"chapter\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
   2654 		 label
   2655 		 (mapconcat 'number-to-string (org-export-get-headline-number
   2656 					       headline info) "."))))
   2657      ;; Case 4: Locate a regular headline in the hierarchy.  Display
   2658      ;; its title.
   2659      (let ((headline (cl-loop for el in (cons destination genealogy)
   2660 			      when (and (eq (org-element-type el) 'headline)
   2661 					(not (org-export-low-level-p el info)))
   2662 			      return el)))
   2663        ;; We found one.
   2664        (when headline
   2665 	 (format "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
   2666 		 label
   2667 		 (let ((title (org-element-property :title headline)))
   2668 		   (org-export-data title info)))))
   2669      (error "FIXME?"))))
   2670 
   2671 (defun org-odt-link (link desc info)
   2672   "Transcode a LINK object from Org to ODT.
   2673 
   2674 DESC is the description part of the link, or the empty string.
   2675 INFO is a plist holding contextual information.  See
   2676 `org-export-data'."
   2677   (let* ((type (org-element-property :type link))
   2678 	 (raw-path (org-element-property :path link))
   2679 	 ;; Ensure DESC really exists, or set it to nil.
   2680 	 (desc (and (not (string= desc "")) desc))
   2681 	 (imagep (org-export-inline-image-p
   2682 		  link (plist-get info :odt-inline-image-rules)))
   2683 	 (path (cond
   2684 		((member type '("http" "https" "ftp" "mailto"))
   2685 		 (concat type ":" raw-path))
   2686 		((string= type "file")
   2687                  (let ((path-uri (org-export-file-uri raw-path)))
   2688                    (if (string-prefix-p "file://" path-uri)
   2689                        path-uri
   2690                      ;; Otherwise, it is a relative path.
   2691                      ;; OpenOffice treats base directory inside the odt
   2692                      ;; archive.  The directory containing the odt file
   2693                      ;; is "../".
   2694                      (concat "../" path-uri))))
   2695 		(t raw-path)))
   2696 	 ;; Convert & to &amp; for correct XML representation
   2697 	 (path (replace-regexp-in-string "&" "&amp;" path)))
   2698     (cond
   2699      ;; Link type is handled by a special function.
   2700      ((org-export-custom-protocol-maybe link desc 'odt info))
   2701      ;; Image file.
   2702      ((and (not desc) imagep) (org-odt-link--inline-image link info))
   2703      ;; Formula file.
   2704      ((and (not desc)
   2705 	   (org-export-inline-image-p
   2706 	    link (plist-get info :odt-inline-formula-rules)))
   2707       (org-odt-link--inline-formula link info))
   2708      ;; Radio target: Transcode target's contents and use them as
   2709      ;; link's description.
   2710      ((string= type "radio")
   2711       (let ((destination (org-export-resolve-radio-link link info)))
   2712 	(if (not destination) desc
   2713 	  (format
   2714 	   "<text:bookmark-ref text:reference-format=\"text\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
   2715 	   (org-export-get-reference destination info)
   2716 	   desc))))
   2717      ;; Links pointing to a headline: Find destination and build
   2718      ;; appropriate referencing command.
   2719      ((member type '("custom-id" "fuzzy" "id"))
   2720       (let ((destination (if (string= type "fuzzy")
   2721 			     (org-export-resolve-fuzzy-link link info)
   2722 			   (org-export-resolve-id-link link info))))
   2723 	(cl-case (org-element-type destination)
   2724 	  ;; Fuzzy link points to a headline.  If there's
   2725 	  ;; a description, create a hyperlink.  Otherwise, try to
   2726 	  ;; provide a meaningful description.
   2727 	  (headline
   2728 	   (if (not desc) (org-odt-link--infer-description destination info)
   2729 	     (let ((label
   2730 		    (or (and (string= type "custom-id")
   2731 			     (org-element-property :CUSTOM_ID destination))
   2732 			(org-export-get-reference destination info))))
   2733 	       (format
   2734 		"<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
   2735 		label desc))))
   2736 	  ;; Fuzzy link points to a target.  If there's a description,
   2737 	  ;; create a hyperlink.  Otherwise, try to provide
   2738 	  ;; a meaningful description.
   2739 	  (target
   2740 	   (format "<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
   2741 		   (org-export-get-reference destination info)
   2742 		   (or desc (org-export-get-ordinal destination info))))
   2743           ;; Link to a file, corresponding to string return value of
   2744           ;; `org-export-resolve-id-link'.  Export it is file link.
   2745           (plain-text
   2746            (let ((file-link (org-element-copy link)))
   2747              (org-element-put-property file-link :type "file")
   2748              (org-element-put-property file-link :path destination)
   2749              (org-element-put-property
   2750               file-link
   2751               :raw-link (format "file:%s" destination))
   2752              (org-odt-link file-link desc info)))
   2753 	  ;; Fuzzy link points to some element (e.g., an inline image,
   2754 	  ;; a math formula or a table).
   2755 	  (otherwise
   2756 	   (let ((label-reference
   2757 		  (ignore-errors
   2758 		    (org-odt-format-label destination info 'reference))))
   2759 	     (cond
   2760 	      ((not label-reference)
   2761 	       (org-odt-link--infer-description destination info))
   2762 	      ;; LINK has no description.  Create
   2763 	      ;; a cross-reference showing entity's sequence
   2764 	      ;; number.
   2765 	      ((not desc) label-reference)
   2766 	      ;; LINK has description.  Insert a hyperlink with
   2767 	      ;; user-provided description.
   2768 	      (t
   2769 	       (format
   2770 		"<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
   2771 		(org-export-get-reference destination info)
   2772 		desc))))))))
   2773      ;; Coderef: replace link with the reference name or the
   2774      ;; equivalent line number.
   2775      ((string= type "coderef")
   2776       (let* ((line-no (format "%d" (org-export-resolve-coderef path info)))
   2777 	     (href (concat "coderef-" path)))
   2778 	(format
   2779 	 (org-export-get-coderef-format path desc)
   2780 	 (format
   2781 	  "<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
   2782 	  href line-no))))
   2783      ;; External link with a description part.
   2784      ((and path desc)
   2785       (let ((link-contents (org-element-contents link)))
   2786 	;; Check if description is a link to an inline image.
   2787 	(if (and (not (cdr link-contents))
   2788 		 (let ((desc-element (car link-contents)))
   2789 		   (and (eq (org-element-type desc-element) 'link)
   2790 			(org-export-inline-image-p
   2791 			 desc-element
   2792 			 (plist-get info :odt-inline-image-rules)))))
   2793 	    ;; Format link as a clickable image.
   2794 	    (format "\n<draw:a xlink:type=\"simple\" xlink:href=\"%s\">\n%s\n</draw:a>"
   2795 		    path desc)
   2796 	  ;; Otherwise, format it as a regular link.
   2797 	  (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
   2798 		  path desc))))
   2799      ;; External link without a description part.
   2800      (path
   2801       (format "<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
   2802 	      path path))
   2803      ;; No path, only description.  Try to do something useful.
   2804      (t (format "<text:span text:style-name=\"%s\">%s</text:span>"
   2805 		"Emphasis" desc)))))
   2806 
   2807 
   2808 ;;;; Node Property
   2809 
   2810 (defun org-odt-node-property (node-property _contents _info)
   2811   "Transcode a NODE-PROPERTY element from Org to ODT.
   2812 CONTENTS is nil.  INFO is a plist holding contextual
   2813 information."
   2814   (org-odt--encode-plain-text
   2815    (format "%s:%s"
   2816 	   (org-element-property :key node-property)
   2817 	   (let ((value (org-element-property :value node-property)))
   2818 	     (if value (concat " " value) "")))))
   2819 
   2820 ;;;; Paragraph
   2821 
   2822 (defun org-odt--paragraph-style (paragraph)
   2823   "Return style of PARAGRAPH.
   2824 Style is a symbol among `quoted', `centered' and nil."
   2825   (let ((up paragraph))
   2826     (while (and (setq up (org-element-property :parent up))
   2827 		(not (memq (org-element-type up)
   2828 			   '(center-block quote-block section)))))
   2829     (cl-case (org-element-type up)
   2830       (center-block 'centered)
   2831       (quote-block 'quoted))))
   2832 
   2833 (defun org-odt--format-paragraph (paragraph contents info default center quote)
   2834   "Format paragraph according to given styles.
   2835 PARAGRAPH is a paragraph type element.  CONTENTS is the
   2836 transcoded contents of that paragraph, as a string.  INFO is
   2837 a plist used as a communication channel.  DEFAULT, CENTER and
   2838 QUOTE are, respectively, style to use when paragraph belongs to
   2839 no special environment, a center block, or a quote block."
   2840   (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   2841 	  (cl-case (org-odt--paragraph-style paragraph)
   2842 	    (quoted quote)
   2843 	    (centered center)
   2844 	    (otherwise default))
   2845 	  ;; If PARAGRAPH is a leading paragraph in an item that has
   2846 	  ;; a checkbox, splice checkbox and paragraph contents
   2847 	  ;; together.
   2848 	  (concat (let ((parent (org-element-property :parent paragraph)))
   2849 		    (and (eq (org-element-type parent) 'item)
   2850 			 (not (org-export-get-previous-element paragraph info))
   2851 			 (org-odt--checkbox parent)))
   2852 		  contents)))
   2853 
   2854 (defun org-odt-paragraph (paragraph contents info)
   2855   "Transcode a PARAGRAPH element from Org to ODT.
   2856 CONTENTS is the contents of the paragraph, as a string.  INFO is
   2857 the plist used as a communication channel."
   2858   (org-odt--format-paragraph
   2859    paragraph contents info
   2860    (or (org-element-property :style paragraph) "Text_20_body")
   2861    "OrgCenter"
   2862    "Quotations"))
   2863 
   2864 
   2865 ;;;; Plain List
   2866 
   2867 (defun org-odt-plain-list (plain-list contents _info)
   2868   "Transcode a PLAIN-LIST element from Org to ODT.
   2869 CONTENTS is the contents of the list.  INFO is a plist holding
   2870 contextual information."
   2871   (format "\n<text:list text:style-name=\"%s\" %s>\n%s</text:list>"
   2872 	  ;; Choose style based on list type.
   2873 	  (cl-case (org-element-property :type plain-list)
   2874 	    (ordered "OrgNumberedList")
   2875 	    (unordered "OrgBulletedList")
   2876 	    (descriptive-1 "OrgDescriptionList")
   2877 	    (descriptive-2 "OrgDescriptionList"))
   2878 	  ;; If top-level list, re-start numbering.  Otherwise,
   2879 	  ;; continue numbering.
   2880 	  (format "text:continue-numbering=\"%s\""
   2881 		  (let* ((parent (org-export-get-parent plain-list)))
   2882 		    (if (and parent (eq (org-element-type parent) 'item))
   2883 			"true" "false")))
   2884 	  contents))
   2885 
   2886 ;;;; Plain Text
   2887 
   2888 (defun org-odt--encode-tabs-and-spaces (line)
   2889   (replace-regexp-in-string
   2890    "\\(\t\\| \\{2,\\}\\)"
   2891    (lambda (s)
   2892      (if (string= s "\t") "<text:tab/>"
   2893        (format " <text:s text:c=\"%d\"/>" (1- (length s)))))
   2894    line))
   2895 
   2896 (defun org-odt--encode-plain-text (text &optional no-whitespace-filling)
   2897   (dolist (pair '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
   2898     (setq text (replace-regexp-in-string (car pair) (cdr pair) text t t)))
   2899   (if no-whitespace-filling text
   2900     (org-odt--encode-tabs-and-spaces text)))
   2901 
   2902 (defun org-odt-plain-text (text info)
   2903   "Transcode a TEXT string from Org to ODT.
   2904 TEXT is the string to transcode.  INFO is a plist holding
   2905 contextual information."
   2906   (let ((output text))
   2907     ;; Protect &, < and >.
   2908     (setq output (org-odt--encode-plain-text output t))
   2909     ;; Handle smart quotes.  Be sure to provide original string since
   2910     ;; OUTPUT may have been modified.
   2911     (when (plist-get info :with-smart-quotes)
   2912       (setq output (org-export-activate-smart-quotes output :utf-8 info text)))
   2913     ;; Convert special strings.
   2914     (when (plist-get info :with-special-strings)
   2915       (dolist (pair org-odt-special-string-regexps)
   2916 	(setq output
   2917 	      (replace-regexp-in-string (car pair) (cdr pair) output t nil))))
   2918     ;; Handle break preservation if required.
   2919     (if (plist-get info :preserve-breaks)
   2920         (setq output (replace-regexp-in-string
   2921 		      "\\(\\\\\\\\\\)?[ \t]*\n" "<text:line-break/>" output t))
   2922       ;; OpenDocument schema recognizes newlines as spaces, which may
   2923       ;; not be desired in scripts that do not separate words with
   2924       ;; spaces (for example, Han script).  `fill-region' is able to
   2925       ;; handle such situations.
   2926       ;; FIXME: The unnecessary spaced may still remain when a newline
   2927       ;; is at a boundary between Org objects (e.g. italics markup
   2928       ;; followed by newline).
   2929       (setq output
   2930             (with-temp-buffer
   2931               (insert output)
   2932               (save-match-data
   2933                 (let ((leading (and (string-match (rx bos (1+ blank)) output)
   2934                                     (match-string 0 output)))
   2935                       (trailing (and (string-match (rx (1+ blank) eos) output)
   2936                                      (match-string 0 output))))
   2937                   ;; Unfill, retaining leading/trailing space.
   2938                   (let ((fill-column (point-max)))
   2939                     (fill-region (point-min) (point-max)))
   2940                   (concat leading (buffer-string) trailing))))))
   2941     ;; Return value.
   2942     output))
   2943 
   2944 
   2945 ;;;; Planning
   2946 
   2947 (defun org-odt-planning (planning contents info)
   2948   "Transcode a PLANNING element from Org to ODT.
   2949 CONTENTS is nil.  INFO is a plist used as a communication
   2950 channel."
   2951   (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   2952 	  "OrgPlanning"
   2953 	  (concat
   2954 	   (let ((closed (org-element-property :closed planning)))
   2955 	     (when closed
   2956 	       (concat
   2957 		(format "<text:span text:style-name=\"%s\">%s</text:span>"
   2958 			"OrgClosedKeyword" org-closed-string)
   2959 		(org-odt-timestamp closed contents info))))
   2960 	   (let ((deadline (org-element-property :deadline planning)))
   2961 	     (when deadline
   2962 	       (concat
   2963 		(format "<text:span text:style-name=\"%s\">%s</text:span>"
   2964 			"OrgDeadlineKeyword" org-deadline-string)
   2965 		(org-odt-timestamp deadline contents info))))
   2966 	   (let ((scheduled (org-element-property :scheduled planning)))
   2967 	     (when scheduled
   2968 	       (concat
   2969 		(format "<text:span text:style-name=\"%s\">%s</text:span>"
   2970 			"OrgScheduledKeyword" org-scheduled-string)
   2971 		(org-odt-timestamp scheduled contents info)))))))
   2972 
   2973 
   2974 ;;;; Property Drawer
   2975 
   2976 (defun org-odt-property-drawer (_property-drawer contents _info)
   2977   "Transcode a PROPERTY-DRAWER element from Org to ODT.
   2978 CONTENTS holds the contents of the drawer.  INFO is a plist
   2979 holding contextual information."
   2980   (and (org-string-nw-p contents)
   2981        (format "<text:p text:style-name=\"OrgFixedWidthBlock\">%s</text:p>"
   2982 	       contents)))
   2983 
   2984 
   2985 ;;;; Quote Block
   2986 
   2987 (defun org-odt-quote-block (_quote-block contents _info)
   2988   "Transcode a QUOTE-BLOCK element from Org to ODT.
   2989 CONTENTS holds the contents of the block.  INFO is a plist
   2990 holding contextual information."
   2991   contents)
   2992 
   2993 
   2994 ;;;; Section
   2995 
   2996 (defun org-odt-format-section (text style &optional name)
   2997   (let ((default-name (car (org-odt-add-automatic-style "Section"))))
   2998     (format "\n<text:section text:style-name=\"%s\" %s>\n%s\n</text:section>"
   2999 	    style
   3000 	    (format "text:name=\"%s\"" (or name default-name))
   3001 	    text)))
   3002 
   3003 
   3004 (defun org-odt-section (_section contents _info) ; FIXME
   3005   "Transcode a SECTION element from Org to ODT.
   3006 CONTENTS holds the contents of the section.  INFO is a plist
   3007 holding contextual information."
   3008   contents)
   3009 
   3010 ;;;; Radio Target
   3011 
   3012 (defun org-odt-radio-target (radio-target text info)
   3013   "Transcode a RADIO-TARGET object from Org to ODT.
   3014 TEXT is the text of the target.  INFO is a plist holding
   3015 contextual information."
   3016   (org-odt--target text (org-export-get-reference radio-target info)))
   3017 
   3018 
   3019 ;;;; Special Block
   3020 
   3021 (defun org-odt-special-block (special-block contents info)
   3022   "Transcode a SPECIAL-BLOCK element from Org to ODT.
   3023 CONTENTS holds the contents of the block.  INFO is a plist
   3024 holding contextual information."
   3025   (let ((type (org-element-property :type special-block))
   3026 	(attributes (org-export-read-attribute :attr_odt special-block)))
   3027     (cond
   3028      ;; Annotation.
   3029      ((string= type "annotation")
   3030       (let* ((author (or (plist-get attributes :author)
   3031 			 (let ((author (plist-get info :author)))
   3032 			   (and author (org-export-data author info)))))
   3033 	     (date (or (plist-get attributes :date)
   3034 		       ;; FIXME: Is `car' right thing to do below?
   3035 		       (car (plist-get info :date)))))
   3036 	(format "\n<text:p>%s</text:p>"
   3037 		(format "<office:annotation>\n%s\n</office:annotation>"
   3038 			(concat
   3039 			 (and author
   3040 			      (format "<dc:creator>%s</dc:creator>" author))
   3041 			 (and date
   3042 			      (format "<dc:date>%s</dc:date>"
   3043 				      (org-odt--format-timestamp date nil 'iso-date)))
   3044 			 contents)))))
   3045      ;; Textbox.
   3046      ((string= type "textbox")
   3047       (let ((width (plist-get attributes :width))
   3048 	    (height (plist-get attributes :height))
   3049 	    (style (plist-get attributes :style))
   3050 	    (extra (plist-get attributes :extra))
   3051 	    (anchor (plist-get attributes :anchor)))
   3052 	(format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3053 		"Text_20_body" (org-odt--textbox contents width height
   3054 						 style extra anchor))))
   3055      (t contents))))
   3056 
   3057 
   3058 ;;;; Src Block
   3059 
   3060 (defun org-odt-hfy-face-to-css (fn)
   3061   "Create custom style for face FN.
   3062 When FN is the default face, use its foreground and background
   3063 properties to create \"OrgSrcBlock\" paragraph style.  Otherwise
   3064 use its color attribute to create a character style whose name
   3065 is obtained from FN.  Currently all attributes of FN other than
   3066 color are ignored.
   3067 
   3068 The style name for a face FN is derived using the following
   3069 operations on the face name in that order - de-dash, CamelCase
   3070 and prefix with \"OrgSrc\".  For example,
   3071 `font-lock-function-name-face' is associated with
   3072 \"OrgSrcFontLockFunctionNameFace\"."
   3073   (let* ((css-list (hfy-face-to-style fn))
   3074 	 (style-name (concat "OrgSrc"
   3075                              (mapconcat
   3076                               'capitalize (split-string
   3077                                            (hfy-face-or-def-to-name fn) "-")
   3078                               "")))
   3079 	 (color-val (cdr (assoc "color" css-list)))
   3080 	 (background-color-val (cdr (assoc "background" css-list)))
   3081 	 (style (and org-odt-create-custom-styles-for-srcblocks
   3082 		     (cond
   3083 		      ((eq fn 'default)
   3084 		       (format org-odt-src-block-paragraph-format
   3085 			       background-color-val color-val))
   3086 		      (t
   3087 		       (format
   3088 			"
   3089 <style:style style:name=\"%s\" style:family=\"text\">
   3090   <style:text-properties fo:color=\"%s\"/>
   3091  </style:style>" style-name color-val))))))
   3092     (cons style-name style)))
   3093 
   3094 (defun org-odt-htmlfontify-string (line)
   3095   (let* ((hfy-html-quote-regex "\\([<\"&> \t]\\)")
   3096 	 (hfy-html-quote-map '(("\"" "&quot;")
   3097 			       ("<" "&lt;")
   3098 			       ("&" "&amp;")
   3099 			       (">" "&gt;")
   3100 			       (" " "<text:s/>")
   3101 			       ("\t" "<text:tab/>")))
   3102 	 (hfy-face-to-css 'org-odt-hfy-face-to-css)
   3103 	 (hfy-optimizations-1 (copy-sequence hfy-optimizations))
   3104 	 (hfy-optimizations (cl-pushnew 'body-text-only hfy-optimizations-1))
   3105 	 (hfy-begin-span-handler
   3106 	  (lambda (style _text-block _text-id _text-begins-block-p)
   3107 	    (insert (format "<text:span text:style-name=\"%s\">" style))))
   3108 	 (hfy-end-span-handler (lambda () (insert "</text:span>"))))
   3109     (with-no-warnings (htmlfontify-string line))))
   3110 
   3111 (defun org-odt-do-format-code
   3112     (code info &optional lang refs retain-labels num-start)
   3113   (let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
   3114 	 (lang-mode (and lang (intern (format "%s-mode" lang))))
   3115 	 (code-lines (org-split-string code "\n"))
   3116 	 (code-length (length code-lines))
   3117 	 (use-htmlfontify-p (and (functionp lang-mode)
   3118 				 (plist-get info :odt-fontify-srcblocks)
   3119 				 (require 'htmlfontify nil t)
   3120 				 (fboundp 'htmlfontify-string)))
   3121 	 (code (if (not use-htmlfontify-p) code
   3122 		 (with-temp-buffer
   3123 		   (insert code)
   3124 		   (funcall lang-mode)
   3125                    (font-lock-ensure)
   3126 		   (buffer-string))))
   3127 	 (fontifier (if use-htmlfontify-p 'org-odt-htmlfontify-string
   3128 		      'org-odt--encode-plain-text))
   3129 	 (par-style (if use-htmlfontify-p "OrgSrcBlock"
   3130 		      "OrgFixedWidthBlock"))
   3131 	 (i 0))
   3132     (cl-assert (= code-length (length (org-split-string code "\n"))))
   3133     (setq code
   3134 	  (org-export-format-code
   3135 	   code
   3136 	   (lambda (loc line-num ref)
   3137 	     (setq par-style
   3138 		   (concat par-style (and (= (cl-incf i) code-length)
   3139 					  "LastLine")))
   3140 
   3141 	     (setq loc (concat loc (and ref retain-labels (format " (%s)" ref))))
   3142 	     (setq loc (funcall fontifier loc))
   3143 	     (when ref
   3144 	       (setq loc (org-odt--target loc (concat "coderef-" ref))))
   3145 	     (cl-assert par-style)
   3146 	     (setq loc (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3147 			       par-style loc))
   3148 	     (if (not line-num) loc
   3149 	       (format "\n<text:list-item>%s\n</text:list-item>" loc)))
   3150 	   num-start refs))
   3151     (cond
   3152      ((not num-start) code)
   3153      ((= num-start 0)
   3154       (format
   3155        "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
   3156        " text:continue-numbering=\"false\"" code))
   3157      (t
   3158       (format
   3159        "\n<text:list text:style-name=\"OrgSrcBlockNumberedLine\"%s>%s</text:list>"
   3160        " text:continue-numbering=\"true\"" code)))))
   3161 
   3162 (defun org-odt-format-code (element info)
   3163   (let* ((lang (org-element-property :language element))
   3164 	 ;; Extract code and references.
   3165 	 (code-info (org-export-unravel-code element))
   3166 	 (code (car code-info))
   3167 	 (refs (cdr code-info))
   3168 	 ;; Does the source block contain labels?
   3169 	 (retain-labels (org-element-property :retain-labels element))
   3170 	 ;; Does it have line numbers?
   3171 	 (num-start (org-export-get-loc element info)))
   3172     (org-odt-do-format-code code info lang refs retain-labels num-start)))
   3173 
   3174 (defun org-odt-src-block (src-block _contents info)
   3175   "Transcode a SRC-BLOCK element from Org to ODT.
   3176 CONTENTS holds the contents of the item.  INFO is a plist holding
   3177 contextual information."
   3178   (let* ((attributes (org-export-read-attribute :attr_odt src-block))
   3179 	 (caption (car (org-odt-format-label src-block info 'definition))))
   3180     (concat
   3181      (and caption
   3182 	  (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3183 		  "Listing" caption))
   3184      (let ((--src-block (org-odt-format-code src-block info)))
   3185        (if (not (plist-get attributes :textbox)) --src-block
   3186 	 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3187 		 "Text_20_body"
   3188 		 (org-odt--textbox --src-block nil nil nil)))))))
   3189 
   3190 
   3191 ;;;; Statistics Cookie
   3192 
   3193 (defun org-odt-statistics-cookie (statistics-cookie _contents _info)
   3194   "Transcode a STATISTICS-COOKIE object from Org to ODT.
   3195 CONTENTS is nil.  INFO is a plist holding contextual information."
   3196   (let ((cookie-value (org-element-property :value statistics-cookie)))
   3197     (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3198 	    "OrgCode" cookie-value)))
   3199 
   3200 
   3201 ;;;; Strike-Through
   3202 
   3203 (defun org-odt-strike-through (_strike-through contents _info)
   3204   "Transcode STRIKE-THROUGH from Org to ODT.
   3205 CONTENTS is the text with strike-through markup.  INFO is a plist
   3206 holding contextual information."
   3207   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3208 	  "Strikethrough" contents))
   3209 
   3210 
   3211 ;;;; Subscript
   3212 
   3213 (defun org-odt-subscript (_subscript contents _info)
   3214   "Transcode a SUBSCRIPT object from Org to ODT.
   3215 CONTENTS is the contents of the object.  INFO is a plist holding
   3216 contextual information."
   3217   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3218 	  "OrgSubscript" contents))
   3219 
   3220 
   3221 ;;;; Superscript
   3222 
   3223 (defun org-odt-superscript (_superscript contents _info)
   3224   "Transcode a SUPERSCRIPT object from Org to ODT.
   3225 CONTENTS is the contents of the object.  INFO is a plist holding
   3226 contextual information."
   3227   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3228 	  "OrgSuperscript" contents))
   3229 
   3230 
   3231 ;;;; Table Cell
   3232 
   3233 (defun org-odt-table-style-spec (element info)
   3234   (let* ((table (org-export-get-parent-table element))
   3235 	 (table-attributes (org-export-read-attribute :attr_odt table))
   3236 	 (table-style (plist-get table-attributes :style)))
   3237     (assoc table-style (plist-get info :odt-table-styles))))
   3238 
   3239 (defun org-odt-get-table-cell-styles (table-cell info)
   3240   "Retrieve styles applicable to a table cell.
   3241 R and C are (zero-based) row and column numbers of the table
   3242 cell.  STYLE-SPEC is an entry in `org-odt-table-styles'
   3243 applicable to the current table.  It is nil if the table is not
   3244 associated with any style attributes.
   3245 
   3246 Return a cons of (TABLE-CELL-STYLE-NAME . PARAGRAPH-STYLE-NAME).
   3247 
   3248 When STYLE-SPEC is nil, style the table cell the conventional way
   3249 - choose cell borders based on row and column groupings and
   3250 choose paragraph alignment based on `org-col-cookies' text
   3251 property.  See also `org-odt-table-style-spec'.
   3252 
   3253 When STYLE-SPEC is non-nil, ignore the above cookie and return
   3254 styles congruent with the ODF-1.2 specification."
   3255   (let* ((table-cell-address (org-export-table-cell-address table-cell info))
   3256 	 (r (car table-cell-address)) (c (cdr table-cell-address))
   3257 	 (style-spec (org-odt-table-style-spec table-cell info))
   3258 	 (table-dimensions (org-export-table-dimensions
   3259 			    (org-export-get-parent-table table-cell)
   3260 			    info)))
   3261     (when style-spec
   3262       ;; LibreOffice - particularly the Writer - honors neither table
   3263       ;; templates nor custom table-cell styles.  Inorder to retain
   3264       ;; interoperability with LibreOffice, only automatic styles are
   3265       ;; used for styling of table-cells.  The current implementation is
   3266       ;; congruent with ODF-1.2 specification and hence is
   3267       ;; future-compatible.
   3268 
   3269       ;; Additional Note: LibreOffice's AutoFormat facility for tables -
   3270       ;; which recognizes as many as 16 different cell types - is much
   3271       ;; richer. Unfortunately it is NOT amenable to easy configuration
   3272       ;; by hand.
   3273       (let* ((template-name (nth 1 style-spec))
   3274 	     (cell-style-selectors (nth 2 style-spec))
   3275 	     (cell-type
   3276 	      (cond
   3277 	       ((and (cdr (assq 'use-first-column-styles cell-style-selectors))
   3278 		     (= c 0)) "FirstColumn")
   3279 	       ((and (cdr (assq 'use-last-column-styles cell-style-selectors))
   3280 		     (= (1+ c) (cdr table-dimensions)))
   3281 		"LastColumn")
   3282 	       ((and (cdr (assq 'use-first-row-styles cell-style-selectors))
   3283 		     (= r 0)) "FirstRow")
   3284 	       ((and (cdr (assq 'use-last-row-styles cell-style-selectors))
   3285 		     (= (1+ r) (car table-dimensions)))
   3286 		"LastRow")
   3287 	       ((and (cdr (assq 'use-banding-rows-styles cell-style-selectors))
   3288 		     (= (% r 2) 1)) "EvenRow")
   3289 	       ((and (cdr (assq 'use-banding-rows-styles cell-style-selectors))
   3290 		     (= (% r 2) 0)) "OddRow")
   3291 	       ((and (cdr (assq 'use-banding-columns-styles cell-style-selectors))
   3292 		     (= (% c 2) 1)) "EvenColumn")
   3293 	       ((and (cdr (assq 'use-banding-columns-styles cell-style-selectors))
   3294 		     (= (% c 2) 0)) "OddColumn")
   3295 	       (t ""))))
   3296 	(concat template-name cell-type)))))
   3297 
   3298 (defun org-odt-table-cell (table-cell contents info)
   3299   "Transcode a TABLE-CELL element from Org to ODT.
   3300 CONTENTS is nil.  INFO is a plist used as a communication
   3301 channel."
   3302   (let* ((table-cell-address (org-export-table-cell-address table-cell info))
   3303 	 (r (car table-cell-address))
   3304 	 (c (cdr table-cell-address))
   3305 	 (horiz-span (or (org-export-table-cell-width table-cell info) 0))
   3306 	 (table-row (org-export-get-parent table-cell))
   3307 	 (custom-style-prefix (org-odt-get-table-cell-styles
   3308 			       table-cell info))
   3309 	 (paragraph-style
   3310 	  (or
   3311 	   (and custom-style-prefix
   3312 		(format "%sTableParagraph" custom-style-prefix))
   3313 	   (concat
   3314 	    (cond
   3315 	     ((and (= 1 (org-export-table-row-group table-row info))
   3316 		   (org-export-table-has-header-p
   3317 		    (org-export-get-parent-table table-row) info))
   3318 	      "OrgTableHeading")
   3319 	     ((let* ((table (org-export-get-parent-table table-cell))
   3320 		     (table-attrs (org-export-read-attribute :attr_odt table))
   3321 		     (table-header-columns
   3322 		      (let ((cols (plist-get table-attrs :header-columns)))
   3323 			(and cols (read cols)))))
   3324 		(<= c (cond ((wholenump table-header-columns)
   3325 			     (- table-header-columns 1))
   3326 			    (table-header-columns 0)
   3327 			    (t -1))))
   3328 	      "OrgTableHeading")
   3329 	     (t "OrgTableContents"))
   3330 	    (capitalize (symbol-name (org-export-table-cell-alignment
   3331 				      table-cell info))))))
   3332 	 (cell-style-name
   3333 	  (or
   3334 	   (and custom-style-prefix (format "%sTableCell"
   3335 					    custom-style-prefix))
   3336 	   (concat
   3337 	    "OrgTblCell"
   3338 	    (when (or (org-export-table-row-starts-rowgroup-p table-row info)
   3339 		      (zerop r)) "T")
   3340 	    (when (org-export-table-row-ends-rowgroup-p table-row info) "B")
   3341 	    (when (and (org-export-table-cell-starts-colgroup-p table-cell info)
   3342 		       (not (zerop c)) ) "L"))))
   3343 	 (cell-attributes
   3344 	  (concat
   3345 	   (format " table:style-name=\"%s\"" cell-style-name)
   3346 	   (and (> horiz-span 0)
   3347 		(format " table:number-columns-spanned=\"%d\""
   3348 			(1+ horiz-span))))))
   3349     (unless contents (setq contents ""))
   3350     (concat
   3351      (cl-assert paragraph-style)
   3352      (format "\n<table:table-cell%s>\n%s\n</table:table-cell>"
   3353 	     cell-attributes
   3354 	     (let ((table-cell-contents (org-element-contents table-cell)))
   3355 	       (if (eq (org-element-class (car table-cell-contents)) 'element)
   3356 		   contents
   3357 		 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3358 			 paragraph-style contents))))
   3359      (let (s)
   3360        (dotimes (_ horiz-span s)
   3361 	 (setq s (concat s "\n<table:covered-table-cell/>"))))
   3362      "\n")))
   3363 
   3364 
   3365 ;;;; Table Row
   3366 
   3367 (defun org-odt-table-row (table-row contents info)
   3368   "Transcode a TABLE-ROW element from Org to ODT.
   3369 CONTENTS is the contents of the row.  INFO is a plist used as a
   3370 communication channel."
   3371   ;; Rules are ignored since table separators are deduced from
   3372   ;; borders of the current row.
   3373   (when (eq (org-element-property :type table-row) 'standard)
   3374     (let* ((rowgroup-tags
   3375 	    (if (and (= 1 (org-export-table-row-group table-row info))
   3376 		     (org-export-table-has-header-p
   3377 		      (org-export-get-parent-table table-row) info))
   3378 		;; If the row belongs to the first rowgroup and the
   3379 		;; table has more than one row groups, then this row
   3380 		;; belongs to the header row group.
   3381 		'("\n<table:table-header-rows>" . "\n</table:table-header-rows>")
   3382 	      ;; Otherwise, it belongs to non-header row group.
   3383 	      '("\n<table:table-rows>" . "\n</table:table-rows>"))))
   3384       (concat
   3385        ;; Does this row begin a rowgroup?
   3386        (when (org-export-table-row-starts-rowgroup-p table-row info)
   3387 	 (car rowgroup-tags))
   3388        ;; Actual table row
   3389        (format "\n<table:table-row>\n%s\n</table:table-row>" contents)
   3390        ;; Does this row end a rowgroup?
   3391        (when (org-export-table-row-ends-rowgroup-p table-row info)
   3392 	 (cdr rowgroup-tags))))))
   3393 
   3394 
   3395 ;;;; Table
   3396 
   3397 (defun org-odt-table-first-row-data-cells (table info)
   3398   (let ((table-row
   3399 	 (org-element-map table 'table-row
   3400 	   (lambda (row)
   3401 	     (unless (eq (org-element-property :type row) 'rule) row))
   3402 	   info 'first-match))
   3403 	(special-column-p (org-export-table-has-special-column-p table)))
   3404     (if (not special-column-p) (org-element-contents table-row)
   3405       (cdr (org-element-contents table-row)))))
   3406 
   3407 (defun org-odt--table (table contents info)
   3408   "Transcode a TABLE element from Org to ODT.
   3409 CONTENTS is the contents of the table.  INFO is a plist holding
   3410 contextual information."
   3411   (cl-case (org-element-property :type table)
   3412     ;; Case 1: table.el doesn't support export to OD format.  Strip
   3413     ;; such tables from export.
   3414     (table.el
   3415      (prog1 nil
   3416        (message
   3417 	(concat
   3418 	 "(ox-odt): Found table.el-type table in the source Org file."
   3419 	 "  table.el doesn't support export to ODT format."
   3420 	 "  Stripping the table from export."))))
   3421     ;; Case 2: Native Org tables.
   3422     (otherwise
   3423      (let* ((captions (org-odt-format-label table info 'definition))
   3424 	    (caption (car captions)) (short-caption (cdr captions))
   3425 	    (attributes (org-export-read-attribute :attr_odt table))
   3426 	    (custom-table-style (nth 1 (org-odt-table-style-spec table info)))
   3427 	    (table-column-specs
   3428 	     (lambda (table info)
   3429 	       (let* ((table-style (or custom-table-style "OrgTable"))
   3430 		      (column-style (format "%sColumn" table-style)))
   3431 		 (mapconcat
   3432 		  (lambda (table-cell)
   3433 		    (let ((width (1+ (or (org-export-table-cell-width
   3434 					  table-cell info) 0)))
   3435 			  (s (format
   3436 			      "\n<table:table-column table:style-name=\"%s\"/>"
   3437 			      column-style))
   3438 			  out)
   3439 		      (dotimes (_ width out) (setq out (concat s out)))))
   3440 		  (org-odt-table-first-row-data-cells table info) "\n")))))
   3441        (concat
   3442 	;; caption.
   3443 	(when caption
   3444 	  (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
   3445 		  "Table" caption))
   3446 	;; begin table.
   3447 	(let* ((automatic-name
   3448 		(org-odt-add-automatic-style "Table" attributes)))
   3449 	  (format
   3450 	   "\n<table:table table:style-name=\"%s\"%s>"
   3451 	   (or custom-table-style (cdr automatic-name) "OrgTable")
   3452 	   (concat (when short-caption
   3453 		     (format " table:name=\"%s\"" short-caption)))))
   3454 	;; column specification.
   3455 	(funcall table-column-specs table info)
   3456 	;; actual contents.
   3457 	"\n" contents
   3458 	;; end table.
   3459 	"</table:table>")))))
   3460 
   3461 (defun org-odt-table (table contents info)
   3462   "Transcode a TABLE element from Org to ODT.
   3463 CONTENTS is the contents of the table.  INFO is a plist holding
   3464 contextual information.
   3465 
   3466 Use `org-odt--table' to typeset the table.  Handle details
   3467 pertaining to indentation here."
   3468   (let* ((--element-preceded-by-table-p
   3469 	  (lambda (element info)
   3470 	    (cl-loop for el in (org-export-get-previous-element element info t)
   3471 		     thereis (eq (org-element-type el) 'table))))
   3472 	 (--walk-list-genealogy-and-collect-tags
   3473 	  (lambda (table info)
   3474 	    (let* ((genealogy (org-element-lineage table))
   3475 		   (list-genealogy
   3476 		    (when (eq (org-element-type (car genealogy)) 'item)
   3477 		      (cl-loop for el in genealogy
   3478 			       when (memq (org-element-type el)
   3479 					  '(item plain-list))
   3480 			       collect el)))
   3481 		   (llh-genealogy
   3482 		    (apply #'nconc
   3483 			   (cl-loop
   3484 			    for el in genealogy
   3485 			    when (and (eq (org-element-type el) 'headline)
   3486 				      (org-export-low-level-p el info))
   3487 			    collect
   3488 			    (list el
   3489 				  (assq 'headline
   3490 					(org-element-contents
   3491 					 (org-export-get-parent el)))))))
   3492 		   parent-list)
   3493 	      (nconc
   3494 	       ;; Handle list genealogy.
   3495 	       (cl-loop
   3496 		for el in list-genealogy collect
   3497 		(cl-case (org-element-type el)
   3498 		  (plain-list
   3499 		   (setq parent-list el)
   3500 		   (cons "</text:list>"
   3501 			 (format "\n<text:list text:style-name=\"%s\" %s>"
   3502 				 (cl-case (org-element-property :type el)
   3503 				   (ordered "OrgNumberedList")
   3504 				   (unordered "OrgBulletedList")
   3505 				   (descriptive-1 "OrgDescriptionList")
   3506 				   (descriptive-2 "OrgDescriptionList"))
   3507 				 "text:continue-numbering=\"true\"")))
   3508 		  (item
   3509 		   (cond
   3510 		    ((not parent-list)
   3511 		     (if (funcall --element-preceded-by-table-p table info)
   3512 			 '("</text:list-header>" . "<text:list-header>")
   3513 		       '("</text:list-item>" . "<text:list-header>")))
   3514 		    ((funcall --element-preceded-by-table-p
   3515 			      parent-list info)
   3516 		     '("</text:list-header>" . "<text:list-header>"))
   3517 		    (t '("</text:list-item>" . "<text:list-item>"))))))
   3518 	       ;; Handle low-level headlines.
   3519 	       (cl-loop for el in llh-genealogy
   3520 			with step = 'item collect
   3521 			(cl-case step
   3522 			  (plain-list
   3523 			   (setq step 'item) ; Flip-flop
   3524 			   (setq parent-list el)
   3525 			   (cons "</text:list>"
   3526 				 (format "\n<text:list text:style-name=\"%s\" %s>"
   3527 					 (if (org-export-numbered-headline-p
   3528 					      el info)
   3529 					     "OrgNumberedList"
   3530 					   "OrgBulletedList")
   3531 					 "text:continue-numbering=\"true\"")))
   3532 			  (item
   3533 			   (setq step 'plain-list) ; Flip-flop
   3534 			   (cond
   3535 			    ((not parent-list)
   3536 			     (if (funcall --element-preceded-by-table-p table info)
   3537 				 '("</text:list-header>" . "<text:list-header>")
   3538 			       '("</text:list-item>" . "<text:list-header>")))
   3539 			    ((let ((section? (org-export-get-previous-element
   3540 					      parent-list info)))
   3541 			       (and section?
   3542 				    (eq (org-element-type section?) 'section)
   3543 				    (assq 'table (org-element-contents section?))))
   3544 			     '("</text:list-header>" . "<text:list-header>"))
   3545 			    (t
   3546 			     '("</text:list-item>" . "<text:list-item>"))))))))))
   3547 	 (close-open-tags (funcall --walk-list-genealogy-and-collect-tags
   3548 				   table info)))
   3549     ;; OpenDocument schema does not permit table to occur within a
   3550     ;; list item.
   3551 
   3552     ;; One solution - the easiest and lightweight, in terms of
   3553     ;; implementation - is to put the table in an indented text box
   3554     ;; and make the text box part of the list-item.  Unfortunately if
   3555     ;; the table is big and spans multiple pages, the text box could
   3556     ;; overflow.  In this case, the following attribute will come
   3557     ;; handy.
   3558 
   3559     ;; ,---- From OpenDocument-v1.1.pdf
   3560     ;; | 15.27.28 Overflow behavior
   3561     ;; |
   3562     ;; | For text boxes contained within text document, the
   3563     ;; | style:overflow-behavior property specifies the behavior of text
   3564     ;; | boxes where the containing text does not fit into the text
   3565     ;; | box.
   3566     ;; |
   3567     ;; | If the attribute's value is clip, the text that does not fit
   3568     ;; | into the text box is not displayed.
   3569     ;; |
   3570     ;; | If the attribute value is auto-create-new-frame, a new frame
   3571     ;; | will be created on the next page, with the same position and
   3572     ;; | dimensions of the original frame.
   3573     ;; |
   3574     ;; | If the style:overflow-behavior property's value is
   3575     ;; | auto-create-new-frame and the text box has a minimum width or
   3576     ;; | height specified, then the text box will grow until the page
   3577     ;; | bounds are reached before a new frame is created.
   3578     ;; `----
   3579 
   3580     ;; Unfortunately, LibreOffice-3.4.6 doesn't honor
   3581     ;; auto-create-new-frame property and always resorts to clipping
   3582     ;; the text box.  This results in table being truncated.
   3583 
   3584     ;; So we solve the problem the hard (and fun) way using list
   3585     ;; continuations.
   3586 
   3587     ;; The problem only becomes more interesting if you take in to
   3588     ;; account the following facts:
   3589     ;;
   3590     ;; - Description lists are simulated as plain lists.
   3591     ;; - Low-level headlines can be listified.
   3592     ;; - In Org mode, a table can occur not only as a regular list
   3593     ;;   item, but also within description lists and low-level
   3594     ;;   headlines.
   3595 
   3596     ;; See `org-odt--translate-description-lists' for how this is
   3597     ;; tackled.
   3598 
   3599     (concat "\n"
   3600 	    ;; Discontinue the list.
   3601 	    (mapconcat 'car close-open-tags "\n")
   3602 	    ;; Put the table in an indented section.
   3603 	    (let* ((table (org-odt--table table contents info))
   3604 		   (level (/ (length (mapcar 'car close-open-tags)) 2))
   3605 		   (style (format "OrgIndentedSection-Level-%d" level)))
   3606 	      (when table (org-odt-format-section table style)))
   3607 	    ;; Continue the list.
   3608 	    (mapconcat 'cdr (nreverse close-open-tags) "\n"))))
   3609 
   3610 
   3611 ;;;; Target
   3612 
   3613 (defun org-odt-target (target _contents info)
   3614   "Transcode a TARGET object from Org to ODT.
   3615 CONTENTS is nil.  INFO is a plist holding contextual
   3616 information."
   3617   (org-odt--target "" (org-export-get-reference target info)))
   3618 
   3619 
   3620 ;;;; Timestamp
   3621 
   3622 (defun org-odt-timestamp (timestamp _contents info)
   3623   "Transcode a TIMESTAMP object from Org to ODT.
   3624 CONTENTS is nil.  INFO is a plist used as a communication
   3625 channel."
   3626   (let ((type (org-element-property :type timestamp)))
   3627     (if (not (plist-get info :odt-use-date-fields))
   3628 	(let ((value (org-odt-plain-text
   3629 		      (org-timestamp-translate timestamp) info)))
   3630 	  (cl-case (org-element-property :type timestamp)
   3631 	    ((active active-range)
   3632 	     (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3633 		     "OrgActiveTimestamp" value))
   3634 	    ((inactive inactive-range)
   3635 	     (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3636 		     "OrgInactiveTimestamp" value))
   3637 	    (otherwise value)))
   3638       (cl-case type
   3639 	(active
   3640 	 (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3641 		 "OrgActiveTimestamp"
   3642 		 (format "&lt;%s&gt;" (org-odt--format-timestamp timestamp))))
   3643 	(inactive
   3644 	 (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3645 		 "OrgInactiveTimestamp"
   3646 		 (format "[%s]" (org-odt--format-timestamp timestamp))))
   3647 	(active-range
   3648 	 (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3649 		 "OrgActiveTimestamp"
   3650 		 (format "&lt;%s&gt;&#x2013;&lt;%s&gt;"
   3651 			 (org-odt--format-timestamp timestamp)
   3652 			 (org-odt--format-timestamp timestamp 'end))))
   3653 	(inactive-range
   3654 	 (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3655 		 "OrgInactiveTimestamp"
   3656 		 (format "[%s]&#x2013;[%s]"
   3657 			 (org-odt--format-timestamp timestamp)
   3658 			 (org-odt--format-timestamp timestamp 'end))))
   3659 	(otherwise
   3660 	 (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3661 		 "OrgDiaryTimestamp"
   3662 		 (org-odt-plain-text (org-timestamp-translate timestamp)
   3663 				     info)))))))
   3664 
   3665 
   3666 ;;;; Underline
   3667 
   3668 (defun org-odt-underline (_underline contents _info)
   3669   "Transcode UNDERLINE from Org to ODT.
   3670 CONTENTS is the text with underline markup.  INFO is a plist
   3671 holding contextual information."
   3672   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3673 	  "Underline" contents))
   3674 
   3675 
   3676 ;;;; Verbatim
   3677 
   3678 (defun org-odt-verbatim (verbatim _contents _info)
   3679   "Transcode a VERBATIM object from Org to ODT.
   3680 CONTENTS is nil.  INFO is a plist used as a communication
   3681 channel."
   3682   (format "<text:span text:style-name=\"%s\">%s</text:span>"
   3683 	  "OrgCode" (org-odt--encode-plain-text
   3684 		     (org-element-property :value verbatim))))
   3685 
   3686 
   3687 ;;;; Verse Block
   3688 
   3689 (defun org-odt-verse-block (_verse-block contents _info)
   3690   "Transcode a VERSE-BLOCK element from Org to ODT.
   3691 CONTENTS is verse block contents.  INFO is a plist holding
   3692 contextual information."
   3693   (format "\n<text:p text:style-name=\"OrgVerse\">%s</text:p>"
   3694 	  (replace-regexp-in-string
   3695 	   ;; Replace leading tabs and spaces.
   3696 	   "^[ \t]+" #'org-odt--encode-tabs-and-spaces
   3697 	   ;; Add line breaks to each line of verse.
   3698 	   (replace-regexp-in-string
   3699 	    "\\(<text:line-break/>\\)?[ \t]*$" "<text:line-break/>" contents))))
   3700 
   3701 
   3702 
   3703 ;;; Filters
   3704 
   3705 ;;; Images
   3706 
   3707 (defun org-odt--translate-image-links (data _backend info)
   3708   (org-export-insert-image-links data info org-odt-inline-image-rules))
   3709 
   3710 ;;;; LaTeX fragments
   3711 
   3712 (defun org-odt--translate-latex-fragments (tree _backend info)
   3713   (let ((processing-type (plist-get info :with-latex))
   3714 	(count 0))
   3715     ;; Normalize processing-type to one of dvipng, mathml or verbatim.
   3716     ;; If the desired converter is not available, force verbatim
   3717     ;; processing.
   3718     (cl-case processing-type
   3719       ((t mathml)
   3720        (if (and (fboundp 'org-format-latex-mathml-available-p)
   3721 		(org-format-latex-mathml-available-p))
   3722 	   (setq processing-type 'mathml)
   3723 	 (message "LaTeX to MathML converter not available.")
   3724 	 (setq processing-type 'verbatim)))
   3725       ((dvipng imagemagick)
   3726        (unless (and (org-check-external-command "latex" "" t)
   3727 		    (org-check-external-command
   3728 		     (if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
   3729 	 (message "LaTeX to PNG converter not available.")
   3730 	 (setq processing-type 'verbatim)))
   3731       (otherwise
   3732        (message "Unknown LaTeX option.  Forcing verbatim.")
   3733        (setq processing-type 'verbatim)))
   3734 
   3735     ;; Store normalized value for later use.
   3736     (when (plist-get info :with-latex)
   3737       (plist-put info :with-latex processing-type))
   3738     (message "Formatting LaTeX using %s" processing-type)
   3739 
   3740     ;; Convert `latex-fragment's and `latex-environment's.
   3741     (when (memq processing-type '(mathml dvipng imagemagick))
   3742       (org-element-map tree '(latex-fragment latex-environment)
   3743 	(lambda (latex-*)
   3744 	  (cl-incf count)
   3745 	  (let* ((latex-frag (org-element-property :value latex-*))
   3746 		 (input-file (plist-get info :input-file))
   3747 		 (cache-dir (file-name-directory input-file))
   3748 		 (cache-subdir (concat
   3749 				(cl-case processing-type
   3750 				  ((dvipng imagemagick)
   3751 				   org-preview-latex-image-directory)
   3752 				  (mathml "ltxmathml/"))
   3753 				(file-name-sans-extension
   3754 				 (file-name-nondirectory input-file))))
   3755 		 (display-msg
   3756 		  (cl-case processing-type
   3757 		    ((dvipng imagemagick)
   3758 		     (format "Creating LaTeX Image %d..." count))
   3759 		    (mathml (format "Creating MathML snippet %d..." count))))
   3760 		 ;; Get an Org-style link to PNG image or the MathML
   3761 		 ;; file.
   3762 		 (link
   3763 		  (with-temp-buffer
   3764 		    (insert latex-frag)
   3765 		    ;; When converting to a PNG image, make sure to
   3766 		    ;; copy all LaTeX header specifications from the
   3767 		    ;; Org source.
   3768 		    (unless (eq processing-type 'mathml)
   3769 		      (let ((h (plist-get info :latex-header)))
   3770 			(when h
   3771 			  (insert "\n"
   3772 				  (replace-regexp-in-string
   3773 				   "^" "#+LATEX_HEADER: " h)))))
   3774 		    (org-format-latex cache-subdir nil nil cache-dir
   3775 				      nil display-msg nil
   3776 				      processing-type)
   3777 		    (goto-char (point-min))
   3778 		    (skip-chars-forward " \t\n")
   3779 		    (org-element-link-parser))))
   3780 	    (if (not (eq 'link (org-element-type link)))
   3781 		(message "LaTeX Conversion failed.")
   3782 	      ;; Conversion succeeded.  Parse above Org-style link to
   3783 	      ;; a `link' object.
   3784 	      (let ((replacement
   3785 		     (cl-case (org-element-type latex-*)
   3786 		       ;;LaTeX environment.  Mimic a "standalone image
   3787 		       ;; or formula" by enclosing the `link' in
   3788 		       ;; a `paragraph'.  Copy over original
   3789 		       ;; attributes, captions to the enclosing
   3790 		       ;; paragraph.
   3791 		       (latex-environment
   3792 			(org-element-adopt-elements
   3793 			    (list 'paragraph
   3794 			          (list :style "OrgFormula"
   3795 				        :name
   3796 				        (org-element-property :name latex-*)
   3797 				        :caption
   3798 				        (org-element-property :caption latex-*)))
   3799 			  link))
   3800 		       ;; LaTeX fragment.  No special action.
   3801 		       (latex-fragment link))))
   3802 		;; Note down the object that link replaces.
   3803 		(org-element-put-property replacement :replaces
   3804 					  (list (org-element-type latex-*)
   3805 						(list :value latex-frag)))
   3806 		;; Restore blank after initial element or object.
   3807 		(org-element-put-property
   3808 		 replacement :post-blank
   3809 		 (org-element-property :post-blank latex-*))
   3810 		;; Replace now.
   3811 		(org-element-set-element latex-* replacement)))))
   3812 	info nil nil t)))
   3813   tree)
   3814 
   3815 
   3816 ;;;; Description lists
   3817 
   3818 ;; This translator is necessary to handle indented tables in a uniform
   3819 ;; manner.  See comment in `org-odt--table'.
   3820 
   3821 (defun org-odt--translate-description-lists (tree _backend info)
   3822   ;; OpenDocument has no notion of a description list.  So simulate it
   3823   ;; using plain lists.  Description lists in the exported document
   3824   ;; are typeset in the same manner as they are in a typical HTML
   3825   ;; document.
   3826   ;;
   3827   ;; Specifically, a description list like this:
   3828   ;;
   3829   ;;     ,----
   3830   ;;     | - term-1 :: definition-1
   3831   ;;     | - term-2 :: definition-2
   3832   ;;     `----
   3833   ;;
   3834   ;; gets translated in to the following form:
   3835   ;;
   3836   ;;     ,----
   3837   ;;     | - term-1
   3838   ;;     |   - definition-1
   3839   ;;     | - term-2
   3840   ;;     |   - definition-2
   3841   ;;     `----
   3842   ;;
   3843   ;; Further effect is achieved by fixing the OD styles as below:
   3844   ;;
   3845   ;; 1. Set the :type property of the simulated lists to
   3846   ;;    `descriptive-1' and `descriptive-2'.  Map these to list-styles
   3847   ;;    that has *no* bullets whatsoever.
   3848   ;;
   3849   ;; 2. The paragraph containing the definition term is styled to be
   3850   ;;    in bold.
   3851   ;;
   3852   (org-element-map tree 'plain-list
   3853     (lambda (el)
   3854       (when (eq (org-element-property :type el) 'descriptive)
   3855 	(org-element-set-element
   3856 	 el
   3857 	 (apply 'org-element-adopt-elements
   3858 		(list 'plain-list (list :type 'descriptive-1))
   3859 		(mapcar
   3860 		 (lambda (item)
   3861 		   (org-element-adopt-elements
   3862 		       (list 'item (list :checkbox (org-element-property
   3863 						    :checkbox item)))
   3864 		     (list 'paragraph (list :style "Text_20_body_20_bold")
   3865 			   (or (org-element-property :tag item) "(no term)"))
   3866 		     (org-element-adopt-elements
   3867 		         (list 'plain-list (list :type 'descriptive-2))
   3868 		       (apply 'org-element-adopt-elements
   3869 			      (list 'item nil)
   3870 			      (org-element-contents item)))))
   3871 		 (org-element-contents el)))))
   3872       nil)
   3873     info)
   3874   tree)
   3875 
   3876 ;;;; List tables
   3877 
   3878 ;; Lists that are marked with attribute `:list-table' are called as
   3879 ;; list tables.  They will be rendered as a table within the exported
   3880 ;; document.
   3881 
   3882 ;; Consider an example.  The following list table
   3883 ;;
   3884 ;; #+attr_odt :list-table t
   3885 ;; - Row 1
   3886 ;;   - 1.1
   3887 ;;   - 1.2
   3888 ;;   - 1.3
   3889 ;; - Row 2
   3890 ;;   - 2.1
   3891 ;;   - 2.2
   3892 ;;   - 2.3
   3893 ;;
   3894 ;; will be exported as though it were an Org table like the one show
   3895 ;; below.
   3896 ;;
   3897 ;; | Row 1 | 1.1 | 1.2 | 1.3 |
   3898 ;; | Row 2 | 2.1 | 2.2 | 2.3 |
   3899 ;;
   3900 ;; Note that org-tables are NOT multi-line and each line is mapped to
   3901 ;; a unique row in the exported document.  So if an exported table
   3902 ;; needs to contain a single paragraph (with copious text) it needs to
   3903 ;; be typed up in a single line.  Editing such long lines using the
   3904 ;; table editor will be a cumbersome task.  Furthermore inclusion of
   3905 ;; multi-paragraph text in a table cell is well-nigh impossible.
   3906 ;;
   3907 ;; A LIST-TABLE circumvents above problems.
   3908 ;;
   3909 ;; Note that in the example above the list items could be paragraphs
   3910 ;; themselves and the list can be arbitrarily deep.
   3911 ;;
   3912 ;; Inspired by following thread:
   3913 ;; https://lists.gnu.org/r/emacs-orgmode/2011-03/msg01101.html
   3914 
   3915 ;; Translate lists to tables
   3916 
   3917 (defun org-odt--translate-list-tables (tree _backend info)
   3918   (org-element-map tree 'plain-list
   3919     (lambda (l1-list)
   3920       (when (org-export-read-attribute :attr_odt l1-list :list-table)
   3921 	;; Replace list with table.
   3922 	(org-element-set-element
   3923 	 l1-list
   3924 	 ;; Build replacement table.
   3925 	 (apply 'org-element-adopt-elements
   3926 		(list 'table '(:type org :attr_odt (":style \"GriddedTable\"")))
   3927 		(org-element-map l1-list 'item
   3928 		  (lambda (l1-item)
   3929 		    (let* ((l1-item-contents (org-element-contents l1-item))
   3930 			   l1-item-leading-text l2-list)
   3931 		      ;; Remove Level-2 list from the Level-item.  It
   3932 		      ;; will be subsequently attached as table-cells.
   3933 		      (let ((cur l1-item-contents) prev)
   3934 			(while (and cur (not (eq (org-element-type (car cur))
   3935 						 'plain-list)))
   3936 			  (setq prev cur)
   3937 			  (setq cur (cdr cur)))
   3938 			(when prev
   3939 			  (setcdr prev nil)
   3940 			  (setq l2-list (car cur)))
   3941 			(setq l1-item-leading-text l1-item-contents))
   3942 		      ;; Level-1 items start a table row.
   3943 		      (apply 'org-element-adopt-elements
   3944 			     (list 'table-row (list :type 'standard))
   3945 			     ;;  Leading text of level-1 item define
   3946 			     ;;  the first table-cell.
   3947 			     (apply 'org-element-adopt-elements
   3948 				    (list 'table-cell nil)
   3949 				    l1-item-leading-text)
   3950 			     ;; Level-2 items define subsequent
   3951 			     ;; table-cells of the row.
   3952 			     (org-element-map l2-list 'item
   3953 			       (lambda (l2-item)
   3954 				 (apply 'org-element-adopt-elements
   3955 					(list 'table-cell nil)
   3956 					(org-element-contents l2-item)))
   3957 			       info nil 'item))))
   3958 		  info nil 'item))))
   3959       nil)
   3960     info)
   3961   tree)
   3962 
   3963 
   3964 ;;; Interactive functions
   3965 
   3966 (defun org-odt-create-manifest-file-entry (&rest args)
   3967   (push args org-odt-manifest-file-entries))
   3968 
   3969 (defun org-odt-write-manifest-file ()
   3970   (make-directory (concat org-odt-zip-dir "META-INF"))
   3971   (let ((manifest-file (concat org-odt-zip-dir "META-INF/manifest.xml")))
   3972     (with-current-buffer
   3973 	(let ((nxml-auto-insert-xml-declaration-flag nil))
   3974 	  (find-file-noselect manifest-file t))
   3975       (insert
   3976        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
   3977      <manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">\n")
   3978       (dolist (file-entry org-odt-manifest-file-entries)
   3979 	(let* ((version (nth 2 file-entry))
   3980 	       (extra (if (not version) ""
   3981 			(format " manifest:version=\"%s\"" version))))
   3982 	  (insert
   3983 	   (format org-odt-manifest-file-entry-tag
   3984 		   (nth 0 file-entry) (nth 1 file-entry) extra))))
   3985       (insert "\n</manifest:manifest>"))))
   3986 
   3987 (defmacro org-odt--export-wrap (out-file &rest body)
   3988   `(let* ((--out-file ,out-file)
   3989 	  (out-file-type (file-name-extension --out-file))
   3990 	  (org-odt-xml-files '("META-INF/manifest.xml" "content.xml"
   3991 			       "meta.xml" "styles.xml"))
   3992 	  ;; Initialize temporary workarea.  All files that end up in
   3993 	  ;; the exported document get parked/created here.
   3994 	  (org-odt-zip-dir (file-name-as-directory
   3995 			    (make-temp-file (format "%s-" out-file-type) t)))
   3996 	  (org-odt-manifest-file-entries nil)
   3997 	  (--cleanup-xml-buffers
   3998 	   (lambda ()
   3999 	     ;; Kill all XML buffers.
   4000 	     (dolist (file org-odt-xml-files)
   4001 	       (let ((buf (find-buffer-visiting
   4002 			   (concat org-odt-zip-dir file))))
   4003 		 (when buf
   4004 		   (with-current-buffer buf
   4005 		     (set-buffer-modified-p nil)
   4006 		     (kill-buffer buf)))))
   4007 	     ;; Delete temporary directory and also other embedded
   4008 	     ;; files that get copied there.
   4009 	     (delete-directory org-odt-zip-dir t))))
   4010      (condition-case err
   4011 	 (progn
   4012 	   (unless (executable-find "zip")
   4013 	     ;; Not at all OSes ship with zip by default
   4014 	     (error "Executable \"zip\" needed for creating OpenDocument files"))
   4015 	   ;; Do export.  This creates a bunch of xml files ready to be
   4016 	   ;; saved and zipped.
   4017 	   (progn ,@body)
   4018 	   ;; Create a manifest entry for content.xml.
   4019 	   (org-odt-create-manifest-file-entry "text/xml" "content.xml")
   4020 	   ;; Write mimetype file
   4021 	   (let* ((mimetypes
   4022 		   '(("odt" . "application/vnd.oasis.opendocument.text")
   4023 		     ("odf" .  "application/vnd.oasis.opendocument.formula")))
   4024 		  (mimetype (cdr (assoc-string out-file-type mimetypes t))))
   4025 	     (unless mimetype
   4026 	       (error "Unknown OpenDocument backend %S" out-file-type))
   4027 	     (write-region mimetype nil (concat org-odt-zip-dir "mimetype"))
   4028 	     (org-odt-create-manifest-file-entry mimetype "/" "1.2"))
   4029 	   ;; Write out the manifest entries before zipping
   4030 	   (org-odt-write-manifest-file)
   4031 	   ;; Save all XML files.
   4032 	   (dolist (file org-odt-xml-files)
   4033 	     (let ((buf (find-buffer-visiting
   4034 			 (concat org-odt-zip-dir file))))
   4035 	       (when buf
   4036 		 (with-current-buffer buf
   4037 		   ;; Prettify output if needed.
   4038 		   (when org-odt-prettify-xml
   4039 		     (indent-region (point-min) (point-max)))
   4040 		   (save-buffer 0)))))
   4041 	   ;; Run zip.
   4042 	   (let* ((target --out-file)
   4043 		  (target-name (file-name-nondirectory target))
   4044 		  (cmds `(("zip" "-mX0" ,target-name "mimetype")
   4045 			  ("zip" "-rmTq" ,target-name "."))))
   4046 	     ;; If a file with same name as the desired output file
   4047 	     ;; exists, remove it.
   4048 	     (when (file-exists-p target)
   4049 	       (delete-file target))
   4050 	     ;; Zip up the xml files.
   4051 	     (let ((coding-system-for-write 'no-conversion) exitcode err-string)
   4052 	       (message "Creating ODT file...")
   4053 	       ;; Switch temporarily to content.xml.  This way Zip
   4054 	       ;; process will inherit `org-odt-zip-dir' as the current
   4055 	       ;; directory.
   4056 	       (with-current-buffer
   4057 		   (find-file-noselect (concat org-odt-zip-dir "content.xml") t)
   4058 		 (dolist (cmd cmds)
   4059 		   (message "Running %s" (mapconcat 'identity cmd " "))
   4060 		   (setq err-string
   4061 			 (with-output-to-string
   4062 			   (setq exitcode
   4063 				 (apply 'call-process (car cmd)
   4064 					nil standard-output nil (cdr cmd)))))
   4065 		   (or (zerop exitcode)
   4066 		       (error (concat "Unable to create OpenDocument file."
   4067 				      "  Zip failed with error (%s)")
   4068 			      err-string)))))
   4069 	     ;; Move the zip file from temporary work directory to
   4070 	     ;; user-mandated location.
   4071 	     (rename-file (concat org-odt-zip-dir target-name) target)
   4072 	     (message "Created %s" (expand-file-name target))
   4073 	     ;; Cleanup work directory and work files.
   4074 	     (funcall --cleanup-xml-buffers)
   4075 	     ;; Open the OpenDocument file in archive-mode for
   4076 	     ;; examination.
   4077 	     (find-file-noselect target t)
   4078 	     ;; Return exported file.
   4079 	     (cond
   4080 	      ;; Case 1: Conversion desired on exported file.  Run the
   4081 	      ;; converter on the OpenDocument file.  Return the
   4082 	      ;; converted file.
   4083 	      (org-odt-preferred-output-format
   4084 	       (or (org-odt-convert target org-odt-preferred-output-format)
   4085 		   target))
   4086 	      ;; Case 2: No further conversion.  Return exported
   4087 	      ;; OpenDocument file.
   4088 	      (t target))))
   4089        (error
   4090 	;; Cleanup work directory and work files.
   4091 	(funcall --cleanup-xml-buffers)
   4092 	(message "OpenDocument export failed: %s"
   4093 		 (error-message-string err))))))
   4094 
   4095 
   4096 ;;;; Export to OpenDocument formula
   4097 
   4098 ;;;###autoload
   4099 (defun org-odt-export-as-odf (latex-frag &optional odf-file)
   4100   "Export LATEX-FRAG as OpenDocument formula file ODF-FILE.
   4101 Use `org-create-math-formula' to convert LATEX-FRAG first to
   4102 MathML.  When invoked as an interactive command, use
   4103 `org-latex-regexps' to infer LATEX-FRAG from currently active
   4104 region.  If no LaTeX fragments are found, prompt for it.  Push
   4105 MathML source to kill ring depending on the value of
   4106 `org-export-copy-to-kill-ring'."
   4107   (interactive
   4108    `(,(let (frag)
   4109 	(setq frag (and (setq frag (and (region-active-p)
   4110 					(buffer-substring (region-beginning)
   4111 							  (region-end))))
   4112 			(cl-loop for e in org-latex-regexps
   4113 				 thereis (when (string-match (nth 1 e) frag)
   4114 					   (match-string (nth 2 e) frag)))))
   4115 	(read-string "LaTeX Fragment: " frag nil frag))
   4116      ,(let ((odf-filename (expand-file-name
   4117 			   (concat
   4118 			    (file-name-sans-extension
   4119 			     (or (file-name-nondirectory buffer-file-name)))
   4120 			    "." "odf")
   4121 			   (file-name-directory buffer-file-name))))
   4122 	(read-file-name "ODF filename: " nil odf-filename nil
   4123 			(file-name-nondirectory odf-filename)))))
   4124   (let ((filename (or odf-file
   4125 		      (expand-file-name
   4126 		       (concat
   4127 			(file-name-sans-extension
   4128 			 (or (file-name-nondirectory buffer-file-name)))
   4129 			"." "odf")
   4130 		       (file-name-directory buffer-file-name)))))
   4131     (org-odt--export-wrap
   4132      filename
   4133      (let* ((buffer (progn
   4134 		      (require 'nxml-mode)
   4135 		      (let ((nxml-auto-insert-xml-declaration-flag nil))
   4136 			(find-file-noselect (concat org-odt-zip-dir
   4137 						    "content.xml") t))))
   4138 	    (coding-system-for-write 'utf-8)
   4139 	    (save-buffer-coding-system 'utf-8))
   4140        (set-buffer buffer)
   4141        (set-buffer-file-coding-system coding-system-for-write)
   4142        (let ((mathml (org-create-math-formula latex-frag)))
   4143 	 (unless mathml (error "No Math formula created"))
   4144 	 (insert mathml)
   4145 	 ;; Add MathML to kill ring, if needed.
   4146 	 (when (org-export--copy-to-kill-ring-p)
   4147 	   (org-kill-new (buffer-string))))))))
   4148 
   4149 ;;;###autoload
   4150 (defun org-odt-export-as-odf-and-open ()
   4151   "Export LaTeX fragment as OpenDocument formula and immediately open it.
   4152 Use `org-odt-export-as-odf' to read LaTeX fragment and OpenDocument
   4153 formula file."
   4154   (interactive)
   4155   (org-open-file (call-interactively 'org-odt-export-as-odf) 'system))
   4156 
   4157 
   4158 ;;;; Export to OpenDocument Text
   4159 
   4160 ;;;###autoload
   4161 (defun org-odt-export-to-odt (&optional async subtreep visible-only ext-plist)
   4162   "Export current buffer to a ODT file.
   4163 
   4164 If narrowing is active in the current buffer, only export its
   4165 narrowed part.
   4166 
   4167 If a region is active, export that region.
   4168 
   4169 A non-nil optional argument ASYNC means the process should happen
   4170 asynchronously.  The resulting file should be accessible through
   4171 the `org-export-stack' interface.
   4172 
   4173 When optional argument SUBTREEP is non-nil, export the sub-tree
   4174 at point, extracting information from the headline properties
   4175 first.
   4176 
   4177 When optional argument VISIBLE-ONLY is non-nil, don't export
   4178 contents of hidden elements.
   4179 
   4180 EXT-PLIST, when provided, is a property list with external
   4181 parameters overriding Org default settings, but still inferior to
   4182 file-local settings.
   4183 
   4184 Return output file's name."
   4185   (interactive)
   4186   (let ((outfile (org-export-output-file-name ".odt" subtreep)))
   4187     (if async
   4188 	(org-export-async-start (lambda (f) (org-export-add-to-stack f 'odt))
   4189 	  `(expand-file-name
   4190 	    (org-odt--export-wrap
   4191 	     ,outfile
   4192 	     (let* ((org-odt-embedded-images-count 0)
   4193 		    (org-odt-embedded-formulas-count 0)
   4194 		    (org-odt-automatic-styles nil)
   4195 		    (org-odt-object-counters nil)
   4196 		    ;; Let `htmlfontify' know that we are interested in
   4197 		    ;; collecting styles.
   4198 		    (hfy-user-sheet-assoc nil))
   4199 	       ;; Initialize content.xml and kick-off the export
   4200 	       ;; process.
   4201 	       (let ((out-buf
   4202 		      (progn
   4203 			(require 'nxml-mode)
   4204 			(let ((nxml-auto-insert-xml-declaration-flag nil))
   4205 			  (find-file-noselect
   4206 			   (concat org-odt-zip-dir "content.xml") t))))
   4207 		     (output (org-export-as
   4208 			      'odt ,subtreep ,visible-only nil ,ext-plist)))
   4209 		 (with-current-buffer out-buf
   4210 		   (erase-buffer)
   4211 		   (insert output)))))))
   4212       (org-odt--export-wrap
   4213        outfile
   4214        (let* ((org-odt-embedded-images-count 0)
   4215 	      (org-odt-embedded-formulas-count 0)
   4216 	      (org-odt-automatic-styles nil)
   4217 	      (org-odt-object-counters nil)
   4218 	      ;; Let `htmlfontify' know that we are interested in collecting
   4219 	      ;; styles.
   4220 	      (hfy-user-sheet-assoc nil))
   4221 	 ;; Initialize content.xml and kick-off the export process.
   4222 	 (let ((output (org-export-as 'odt subtreep visible-only nil ext-plist))
   4223 	       (out-buf (progn
   4224 			  (require 'nxml-mode)
   4225 			  (let ((nxml-auto-insert-xml-declaration-flag nil))
   4226 			    (find-file-noselect
   4227 			     (concat org-odt-zip-dir "content.xml") t)))))
   4228 	   (with-current-buffer out-buf (erase-buffer) (insert output))))))))
   4229 
   4230 
   4231 ;;;; Convert between OpenDocument and other formats
   4232 
   4233 (defun org-odt-reachable-p (in-fmt out-fmt)
   4234   "Return non-nil if IN-FMT can be converted to OUT-FMT."
   4235   (catch 'done
   4236     (let ((reachable-formats (org-odt-do-reachable-formats in-fmt)))
   4237       (dolist (e reachable-formats)
   4238 	(let ((out-fmt-spec (assoc out-fmt (cdr e))))
   4239 	  (when out-fmt-spec
   4240 	    (throw 'done (cons (car e) out-fmt-spec))))))))
   4241 
   4242 (defun org-odt-do-convert (in-file out-fmt &optional open)
   4243   "Workhorse routine for `org-odt-convert'."
   4244   (require 'browse-url)
   4245   (let* ((in-file (let ((f (expand-file-name (or in-file buffer-file-name))))
   4246 		    (if (file-readable-p f) f
   4247 		      (error "Cannot read %s" in-file))))
   4248 	 (in-fmt (file-name-extension in-file))
   4249 	 (out-fmt (or out-fmt (error "Output format unspecified")))
   4250 	 (how (or (org-odt-reachable-p in-fmt out-fmt)
   4251 		  (error "Cannot convert from %s format to %s format?"
   4252 			 in-fmt out-fmt)))
   4253 	 (convert-process (car how))
   4254 	 (out-file (concat (file-name-sans-extension in-file) "."
   4255 			   (nth 1 (or (cdr how) out-fmt))))
   4256 	 (extra-options (or (nth 2 (cdr how)) ""))
   4257 	 (out-dir (file-name-directory in-file))
   4258 	 (cmd (format-spec convert-process
   4259 			   `((?i . ,(shell-quote-argument in-file))
   4260 			     (?I . ,(browse-url-file-url in-file))
   4261 			     (?f . ,out-fmt)
   4262 			     (?o . ,(shell-quote-argument out-file))
   4263 			     (?O . ,(browse-url-file-url out-file))
   4264 			     (?d . , (shell-quote-argument out-dir))
   4265 			     (?D . ,(browse-url-file-url out-dir))
   4266 			     (?x . ,extra-options)))))
   4267     (when (file-exists-p out-file)
   4268       (delete-file out-file))
   4269 
   4270     (message "Executing %s" cmd)
   4271     (let ((cmd-output (shell-command-to-string cmd)))
   4272       (message "%s" cmd-output))
   4273 
   4274     (cond
   4275      ((file-exists-p out-file)
   4276       (message "Exported to %s" out-file)
   4277       (when open
   4278 	(message "Opening %s..."  out-file)
   4279 	(org-open-file out-file 'system))
   4280       out-file)
   4281      (t
   4282       (message "Export to %s failed" out-file)
   4283       nil))))
   4284 
   4285 (defun org-odt-do-reachable-formats (in-fmt)
   4286   "Return verbose info about formats to which IN-FMT can be converted.
   4287 Return a list where each element is of the
   4288 form (CONVERTER-PROCESS . OUTPUT-FMT-ALIST).  See
   4289 `org-odt-convert-processes' for CONVERTER-PROCESS and see
   4290 `org-odt-convert-capabilities' for OUTPUT-FMT-ALIST."
   4291   (let* ((converter
   4292 	  (and org-odt-convert-process
   4293 	       (cadr (assoc-string org-odt-convert-process
   4294 				   org-odt-convert-processes t))))
   4295 	 (capabilities
   4296 	  (and org-odt-convert-process
   4297 	       (cadr (assoc-string org-odt-convert-process
   4298 				   org-odt-convert-processes t))
   4299 	       org-odt-convert-capabilities))
   4300 	 reachable-formats)
   4301     (when converter
   4302       (dolist (c capabilities)
   4303 	(when (member in-fmt (nth 1 c))
   4304 	  (push (cons converter (nth 2 c)) reachable-formats))))
   4305     reachable-formats))
   4306 
   4307 (defun org-odt-reachable-formats (in-fmt)
   4308   "Return list of formats to which IN-FMT can be converted.
   4309 The list of the form (OUTPUT-FMT-1 OUTPUT-FMT-2 ...)."
   4310   (copy-sequence
   4311    (apply #'append (mapcar
   4312 		    (lambda (e) (mapcar #'car (cdr e)))
   4313 		    (org-odt-do-reachable-formats in-fmt)))))
   4314 
   4315 (defun org-odt-convert-read-params ()
   4316   "Return IN-FILE and OUT-FMT params for `org-odt-do-convert'.
   4317 This is a helper routine for interactive use."
   4318   (let* ((input (if (featurep 'ido) 'ido-completing-read 'completing-read))
   4319 	 (in-file (read-file-name "File to be converted: "
   4320 				  nil buffer-file-name t))
   4321 	 (in-fmt (file-name-extension in-file))
   4322 	 (out-fmt-choices (org-odt-reachable-formats in-fmt))
   4323 	 (out-fmt
   4324 	  (or (and out-fmt-choices
   4325 		   (funcall input "Output format: "
   4326 			    out-fmt-choices nil nil nil))
   4327 	      (error
   4328 	       "No known converter or no known output formats for %s files"
   4329 	       in-fmt))))
   4330     (list in-file out-fmt)))
   4331 
   4332 ;;;###autoload
   4333 (defun org-odt-convert (&optional in-file out-fmt open)
   4334   "Convert IN-FILE to format OUT-FMT using a command line converter.
   4335 IN-FILE is the file to be converted.  If unspecified, it defaults
   4336 to variable `buffer-file-name'.  OUT-FMT is the desired output
   4337 format.  Use `org-odt-convert-process' as the converter.  If OPEN
   4338 is non-nil then the newly converted file is opened using
   4339 `org-open-file'."
   4340   (interactive
   4341    (append (org-odt-convert-read-params) current-prefix-arg))
   4342   (org-odt-do-convert in-file out-fmt open))
   4343 
   4344 ;;; Library Initializations
   4345 
   4346 (dolist (desc org-odt-file-extensions)
   4347   ;; Let Emacs open all OpenDocument files in archive mode.
   4348   (add-to-list 'auto-mode-alist
   4349 	       (cons (concat  "\\." (car desc) "\\'") 'archive-mode)))
   4350 
   4351 (provide 'ox-odt)
   4352 
   4353 ;; Local variables:
   4354 ;; generated-autoload-file: "org-loaddefs.el"
   4355 ;; End:
   4356 
   4357 ;;; ox-odt.el ends here