config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

ox-odt.el (164359B)


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