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))
			    (org-element-type-p (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)

    (let ((styles-xml (concat org-odt-zip-dir "styles.xml")))
      (with-temp-buffer
        (when (file-exists-p styles-xml)
          (insert-file-contents styles-xml))

        ;; 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
        ;; Outline numbering is retained only up to LEVEL.
        ;; To disable outline numbering pass a LEVEL of 0.

        (let ((regex
	       "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
	      (replacement
	       "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
          (goto-char (point-min))
          (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))))

        ;; Write back the new contents.
        (write-region nil nil styles-xml))))
  ;; 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))
				   (org-element-type-p (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)))))