Function: org-odt-template
org-odt-template is a byte-compiled function defined in ox-odt.el.gz.
Signature
(org-odt-template CONTENTS INFO)
Documentation
Return complete document string after ODT conversion.
CONTENTS is the transcoded contents string. RAW-DATA is the original parsed data. INFO is a plist holding export options.
Source Code
;; Defined in /usr/src/emacs/lisp/org/ox-odt.el.gz
(defun org-odt-template (contents info)
"Return complete document string after ODT conversion.
CONTENTS is the transcoded contents string. RAW-DATA is the
original parsed data. INFO is a plist holding export options."
;; Write meta file.
(let ((title (org-export-data (plist-get info :title) info))
(subtitle (org-export-data (plist-get info :subtitle) info))
(author (let ((author (plist-get info :author)))
(if (not author) "" (org-export-data author info))))
(keywords (or (plist-get info :keywords) ""))
(description (or (plist-get info :description) "")))
(write-region
(concat
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<office:document-meta
xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
xmlns:xlink=\"http://www.w3.org/1999/xlink\"
xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
xmlns:ooo=\"http://openoffice.org/2004/office\"
office:version=\"1.2\">
<office:meta>\n"
(format "<dc:creator>%s</dc:creator>\n" author)
(format "<meta:initial-creator>%s</meta:initial-creator>\n" author)
;; Date, if required.
(when (plist-get info :with-date)
;; Check if DATE is specified as an Org-timestamp. If yes,
;; include it as meta information. Otherwise, just use
;; today's date.
(let* ((date (let ((date (plist-get info :date)))
(and (not (cdr date))
(eq (org-element-type (car date)) 'timestamp)
(car date)))))
(let ((iso-date (org-odt--format-timestamp date nil 'iso-date)))
(concat
(format "<dc:date>%s</dc:date>\n" iso-date)
(format "<meta:creation-date>%s</meta:creation-date>\n"
iso-date)))))
(format "<meta:generator>%s</meta:generator>\n"
(plist-get info :creator))
(format "<meta:keyword>%s</meta:keyword>\n" keywords)
(format "<dc:subject>%s</dc:subject>\n" description)
(format "<dc:title>%s</dc:title>\n" title)
(when (org-string-nw-p subtitle)
(format
"<meta:user-defined meta:name=\"subtitle\">%s</meta:user-defined>\n"
subtitle))
"\n"
" </office:meta>\n" "</office:document-meta>")
nil (concat org-odt-zip-dir "meta.xml"))
;; Add meta.xml in to manifest.
(org-odt-create-manifest-file-entry "text/xml" "meta.xml"))
;; Update styles file.
;; Copy styles.xml. Also dump htmlfontify styles, if there is any.
;; Write styles file.
(let* ((styles-file
(pcase (plist-get info :odt-styles-file)
(`nil (expand-file-name "OrgOdtStyles.xml" org-odt-styles-dir))
((and s (pred (string-match-p "\\`(.*)\\'")))
(condition-case nil
(read s)
(error (user-error "Invalid styles file specification: %S" s))))
(filename (org-strip-quotes filename)))))
(cond
;; Non-availability of styles.xml is not a critical error. For
;; now, throw an error.
((null styles-file) (error "Missing styles file"))
((listp styles-file)
(let ((archive (nth 0 styles-file))
(members (nth 1 styles-file)))
(org-odt--zip-extract archive members org-odt-zip-dir)
(dolist (member members)
(when (org-file-image-p member)
(let* ((image-type (file-name-extension member))
(media-type (format "image/%s" image-type)))
(org-odt-create-manifest-file-entry media-type member))))))
((file-exists-p styles-file)
(let ((styles-file-type (file-name-extension styles-file)))
(cond
((string= styles-file-type "xml")
(copy-file styles-file (concat org-odt-zip-dir "styles.xml") t))
((member styles-file-type '("odt" "ott"))
(org-odt--zip-extract styles-file "styles.xml" org-odt-zip-dir)))))
(t
(error "Invalid specification of styles.xml file: %S"
(plist-get info :odt-styles-file))))
;; create a manifest entry for styles.xml
(org-odt-create-manifest-file-entry "text/xml" "styles.xml")
;; Ensure we have write permissions to this file.
(set-file-modes (concat org-odt-zip-dir "styles.xml") #o600)
;; FIXME: Who is opening an empty styles.xml before this point?
(with-current-buffer
(find-file-noselect (concat org-odt-zip-dir "styles.xml") t)
(revert-buffer t t)
;; Write custom styles for source blocks
;; Save STYLES used for colorizing of source blocks.
;; Update styles.xml with styles that were collected as part of
;; `org-odt-hfy-face-to-css' callbacks.
(let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
hfy-user-sheet-assoc "")))
(when styles
(goto-char (point-min))
(when (re-search-forward "</office:styles>" nil t)
(goto-char (match-beginning 0))
(insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
;; Update styles.xml - take care of outline numbering
;; Don't make automatic backup of styles.xml file. This setting
;; prevents the backed-up styles.xml file from being zipped in to
;; odt file. This is more of a hackish fix. Better alternative
;; would be to fix the zip command so that the output odt file
;; includes only the needed files and excludes any auto-generated
;; extra files like backups and auto-saves etc etc. Note that
;; currently the zip command zips up the entire temp directory so
;; that any auto-generated files created under the hood ends up in
;; the resulting odt file.
(setq-local backup-inhibited t)
;; Outline numbering is retained only up to LEVEL.
;; To disable outline numbering pass a LEVEL of 0.
(goto-char (point-min))
(let ((regex
"<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
(replacement
"<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
(while (re-search-forward regex nil t)
(unless (let ((sec-num (plist-get info :section-numbers))
(level (string-to-number (match-string 2))))
(if (wholenump sec-num) (<= level sec-num) sec-num))
(replace-match replacement t nil))))
(save-buffer 0)))
;; Update content.xml.
(let* ( ;; `org-display-custom-times' should be accessed right
;; within the context of the Org buffer. So obtain its
;; value before moving on to temp-buffer context down below.
(custom-time-fmts
(if org-display-custom-times
(cons (org-time-stamp-format
nil 'no-brackets 'custom)
(org-time-stamp-format
'with-time 'no-brackets 'custom))
'("%Y-%M-%d %a" . "%Y-%M-%d %a %H:%M"))))
(with-temp-buffer
(insert-file-contents
(or (plist-get info :odt-content-template-file)
(expand-file-name "OrgOdtContentTemplate.xml"
org-odt-styles-dir)))
;; Write automatic styles.
;; - Position the cursor.
(goto-char (point-min))
(re-search-forward " </office:automatic-styles>" nil t)
(goto-char (match-beginning 0))
;; - Dump automatic table styles.
(cl-loop for (style-name props) in
(plist-get org-odt-automatic-styles 'Table) do
(when (setq props (or (plist-get props :rel-width) "96"))
(insert (format org-odt-table-style-format style-name props))))
;; - Dump date-styles.
(when (plist-get info :odt-use-date-fields)
(insert (org-odt--build-date-styles (car custom-time-fmts)
"OrgDate1")
(org-odt--build-date-styles (cdr custom-time-fmts)
"OrgDate2")))
;; Update display level.
;; - Remove existing sequence decls. Also position the cursor.
(goto-char (point-min))
(when (re-search-forward "<text:sequence-decls" nil t)
(delete-region (match-beginning 0)
(re-search-forward "</text:sequence-decls>" nil nil)))
;; Update sequence decls according to user preference.
(insert
(format
"\n<text:sequence-decls>\n%s\n</text:sequence-decls>"
(mapconcat
(lambda (x)
(format
"<text:sequence-decl text:display-outline-level=\"%d\" text:name=\"%s\"/>"
(plist-get info :odt-display-outline-level)
(nth 1 x)))
org-odt-category-map-alist "\n")))
;; Position the cursor to document body.
(goto-char (point-min))
(re-search-forward "</office:text>" nil nil)
(goto-char (match-beginning 0))
;; Preamble - Title, Author, Date etc.
(insert
(let* ((title (and (plist-get info :with-title)
(org-export-data (plist-get info :title) info)))
(subtitle (when title
(org-export-data (plist-get info :subtitle) info)))
(author (and (plist-get info :with-author)
(let ((auth (plist-get info :author)))
(and auth (org-export-data auth info)))))
(email (plist-get info :email))
;; Switch on or off above vars based on user settings
(author (and (plist-get info :with-author) (or author email)))
(email (and (plist-get info :with-email) email)))
(concat
;; Title.
(when (org-string-nw-p title)
(concat
(format "\n<text:p text:style-name=\"%s\">%s</text:p>\n"
"OrgTitle" (format "\n<text:title>%s</text:title>" title))
;; Separator.
"\n<text:p text:style-name=\"OrgTitle\"/>\n"
;; Subtitle.
(when (org-string-nw-p subtitle)
(concat
(format "<text:p text:style-name=\"OrgSubtitle\">\n%s\n</text:p>\n"
(concat
"<text:user-defined style:data-style-name=\"N0\" text:name=\"subtitle\">\n"
subtitle
"</text:user-defined>\n"))
;; Separator.
"<text:p text:style-name=\"OrgSubtitle\"/>\n"))))
(cond
((and author (not email))
;; Author only.
(concat
(format "\n<text:p text:style-name=\"%s\">%s</text:p>"
"OrgSubtitle"
(format "<text:initial-creator>%s</text:initial-creator>" author))
;; Separator.
"\n<text:p text:style-name=\"OrgSubtitle\"/>"))
((and author email)
;; Author and E-mail.
(concat
(format
"\n<text:p text:style-name=\"%s\">%s</text:p>"
"OrgSubtitle"
(format
"<text:a xlink:type=\"simple\" xlink:href=\"%s\">%s</text:a>"
(concat "mailto:" email)
(format "<text:initial-creator>%s</text:initial-creator>" author)))
;; Separator.
"\n<text:p text:style-name=\"OrgSubtitle\"/>")))
;; Date, if required.
(when (plist-get info :with-date)
(let* ((date (plist-get info :date))
;; Check if DATE is specified as a timestamp.
(timestamp (and (not (cdr date))
(eq (org-element-type (car date)) 'timestamp)
(car date))))
(when date
(concat
(format "\n<text:p text:style-name=\"%s\">%s</text:p>"
"OrgSubtitle"
(if (and (plist-get info :odt-use-date-fields) timestamp)
(org-odt--format-timestamp (car date))
(org-export-data date info)))
;; Separator
"<text:p text:style-name=\"OrgSubtitle\"/>")))))))
;; Table of Contents
(let* ((with-toc (plist-get info :with-toc))
(depth (and with-toc (if (wholenump with-toc)
with-toc
(plist-get info :headline-levels)))))
(when depth (insert (or (org-odt-toc depth info) ""))))
;; Contents.
(insert contents)
;; Return contents.
(buffer-substring-no-properties (point-min) (point-max)))))