diff --git a/EXPERIMENTAL/org-latex.el b/EXPERIMENTAL/org-latex.el new file mode 100644 index 000000000..47f7c29dc --- /dev/null +++ b/EXPERIMENTAL/org-latex.el @@ -0,0 +1,1839 @@ +;;; org-latex.el --- LaTeX Back-End For Org Export Engine + +;; Copyright (C) 2011 Free Software Foundation, Inc. + +;; Author: Nicolas Goaziou +;; Keywords: outlines, hypermedia, calendar, wp + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This library implements a LaTeX back-end for Org generic exporter. + +;; It introduces three new buffer keywords: "LATEX_CLASS", +;; "LATEX_CLASS_OPTIONS" and "LATEX_HEADER". + +;;; Code: + +(eval-when-compile (require 'cl)) +(require 'org-element) +(require 'org-export) + + + +;;; Internal Variables + +(defconst org-latex-option-alist + '((:date "DATE" nil org-latex-date-format t) + (:latex-class "LATEX_CLASS" nil org-latex-default-class t) + (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t) + (:latex-header-extra "LATEX_HEADER" nil nil newline)) + "Alist between LaTeX export properties and ways to set them. +See `org-export-option-alist' for more information on the +structure of the value.") + + + +;;; User Configurable Variables + +(defgroup org-export-latex nil + "Options for exporting Org mode files to LaTeX." + :tag "Org Export LaTeX" + :group 'org-export) + + +;;;; Preamble + +(defcustom org-latex-default-class "article" + "The default LaTeX class." + :group 'org-export-latex + :type '(string :tag "LaTeX class")) + +(defcustom org-latex-classes + '(("article" + "\\documentclass[11pt]{article}" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) + ("report" + "\\documentclass[11pt]{report}" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("book" + "\\documentclass[11pt]{book}" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))) + "Alist of LaTeX classes and associated header and structure. +If #+LaTeX_CLASS is set in the buffer, use its value and the +associated information. Here is the structure of each cell: + + \(class-name + header-string + \(numbered-section . unnumbered-section\) + ...\) + +The header string +----------------- + +The HEADER-STRING is the header that will be inserted into the LaTeX file. +It should contain the \\documentclass macro, and anything else that is needed +for this setup. To this header, the following commands will be added: + +- Calls to \\usepackage for all packages mentioned in the variables + `org-latex-default-packages-alist' and + `org-latex-packages-alist'. Thus, your header definitions should + avoid to also request these packages. + +- Lines specified via \"#+LaTeX_HEADER:\" + +If you need more control about the sequence in which the header is built +up, or if you want to exclude one of these building blocks for a particular +class, you can use the following macro-like placeholders. + + [DEFAULT-PACKAGES] \\usepackage statements for default packages + [NO-DEFAULT-PACKAGES] do not include any of the default packages + [PACKAGES] \\usepackage statements for packages + [NO-PACKAGES] do not include the packages + [EXTRA] the stuff from #+LaTeX_HEADER + [NO-EXTRA] do not include #+LaTeX_HEADER stuff + [BEAMER-HEADER-EXTRA] the beamer extra headers + +So a header like + + \\documentclass{article} + [NO-DEFAULT-PACKAGES] + [EXTRA] + \\providecommand{\\alert}[1]{\\textbf{#1}} + [PACKAGES] + +will omit the default packages, and will include the #+LaTeX_HEADER lines, +then have a call to \\providecommand, and then place \\usepackage commands +based on the content of `org-latex-packages-alist'. + +If your header or `org-latex-default-packages-alist' inserts +\"\\usepackage[AUTO]{inputenc}\", AUTO will automatically be replaced with +a coding system derived from `buffer-file-coding-system'. See also the +variable `org-latex-inputenc-alist' for a way to influence this +mechanism. + +The sectioning structure +------------------------ + +The sectioning structure of the class is given by the elements following +the header string. For each sectioning level, a number of strings is +specified. A %s formatter is mandatory in each section string and will +be replaced by the title of the section. + +Instead of a cons cell \(numbered . unnumbered\), you can also +provide a list of 2 or 4 elements, + + \(numbered-open numbered-close\) + +or + + \(numbered-open numbered-close unnumbered-open unnumbered-close\) + +providing opening and closing strings for a LaTeX environment that should +represent the document section. The opening clause should have a %s +to represent the section title. + +Instead of a list of sectioning commands, you can also specify a +function name. That function will be called with two parameters, +the (reduced) level of the headline, and a predicate non-nil when +the headline should be numbered. It must return a format string in +which the section title will be added." + :group 'org-export-latex + :type '(repeat + (list (string :tag "LaTeX class") + (string :tag "LaTeX header") + (repeat :tag "Levels" :inline t + (choice + (cons :tag "Heading" + (string :tag " numbered") + (string :tag "unnumbered")) + (list :tag "Environment" + (string :tag "Opening (numbered)") + (string :tag "Closing (numbered)") + (string :tag "Opening (unnumbered)") + (string :tag "Closing (unnumbered)")) + (function :tag "Hook computing sectioning")))))) + +(defcustom org-latex-inputenc-alist nil + "Alist of inputenc coding system names, and what should really be used. +For example, adding an entry + + (\"utf8\" . \"utf8x\") + +will cause \\usepackage[utf8x]{inputenc} to be used for buffers that +are written as utf8 files." + :group 'org-export-latex + :type '(repeat + (cons + (string :tag "Derived from buffer") + (string :tag "Use this instead")))) + +(defcustom org-latex-date-format + "\\today" + "Format string for \\date{...}." + :group 'org-export-latex + :type 'boolean) + +(defcustom org-latex-title-command "\\maketitle" + "The command used to insert the title just after \\begin{document}. +If this string contains the formatting specification \"%s\" then +it will be used as a formatting string, passing the title as an +argument." + :group 'org-export-latex + :type 'string) + + +;;;; Headline + +(defcustom org-latex-format-headline-function nil + "Function to format headline text. + +This function will be called with 5 arguments: +TODO the todo keyword \(string or nil\). +TODO-TYPE the type of todo \(symbol: `todo', `done', nil\) +PRIORITY the priority of the headline \(integer or nil\) +TEXT the main headline text \(string\). +TAGS the tags string, separated with colons \(string or nil\). + +The function result will be used in the section format string. + +As an example, one could set the variable to the following, in +order to reproduce the default set-up: + +\(defun org-latex-format-headline-default \(todo todo-type priority text tags\) + \"Default format function for an headline.\" + \(concat \(when todo \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo\)\) + \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\) + text + \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)" + :group 'org-export-latex + :type 'function) + + +;;;; Emphasis + +(defcustom org-latex-emphasis-alist + '(("*" . "\\textbf{%s}") + ("/" . "\\emph{%s}") + ("_" . "\\underline{%s}") + ("+" . "\\st{%s}") + ("=" . protectedtexttt) + ("~" . verb)) + "Alist of LaTeX expressions to convert emphasis fontifiers. + +The key is the character used as a marker for fontification. The +value is a formatting string to wrap fontified text with. + +Value can also be set to the following symbols: `verb' and +`protectedtexttt'. For the former, Org will use \"\\verb\" to +create a format string and select a delimiter character that +isn't in the string. For the latter, Org will use \"\\texttt\" +to typeset and try to protect special characters." + :group 'org-export-latex + :type 'alist) + + +;;;; Footnotes + +(defcustom org-latex-footnote-separator "\\textsuperscript{,}\\," + "Text used to separate footnotes." + :group 'org-export-latex + :type 'string) + + +;;;; Time-stamps + +(defcustom org-latex-active-timestamp-format "\\textit{%s}" + "A printf format string to be applied to active time-stamps." + :group 'org-export-latex + :type 'string) + +(defcustom org-latex-inactive-timestamp-format "\\textit{%s}" + "A printf format string to be applied to inactive time-stamps." + :group 'org-export-latex + :type 'string) + +(defcustom org-latex-diary-timestamp-format "\\textit{%s}" + "A printf format string to be applied to diary time-stamps." + :group 'org-export-latex + :type 'string) + + +;;;; Links + +(defcustom org-latex-image-default-option "width=.9\\linewidth" + "Default option for images." + :group 'org-export-latex + :type 'string) + +(defcustom org-latex-default-figure-position "htb" + "Default position for latex figures." + :group 'org-export-latex + :type 'string) + +(defcustom org-latex-inline-image-extensions + '("pdf" "jpeg" "jpg" "png" "ps" "eps") + "Extensions of image files that can be inlined into LaTeX. + +Note that the image extension *actually* allowed depend on the +way the LaTeX file is processed. When used with pdflatex, pdf, +jpg and png images are OK. When processing through dvi to +Postscript, only ps and eps are allowed. The default we use here +encompasses both." + :group 'org-export-latex + :type '(repeat (string :tag "Extension"))) + + +;;;; Tables + +(defcustom org-latex-default-table-environment "tabular" + "Default environment used to build tables." + :group 'org-export-latex + :type 'string) + +(defcustom org-latex-tables-centered t + "When non-nil, tables are exported in a center environment." + :group 'org-export-latex + :type 'boolean) + +(defcustom org-latex-tables-verbatim nil + "When non-nil, tables are exported verbatim." + :group 'org-export-latex + :type 'boolean) + +(defcustom org-latex-table-caption-above t + "When non-nil, place caption string at the beginning of the table. +Otherwise, place it near the end." + :group 'org-export-latex + :type 'boolean) + + +;;;; Drawers + +(defcustom org-latex-format-drawer-function nil + "Function called to format a drawer in LaTeX code. + +The function must accept two parameters: + NAME the drawer name, like \"LOGBOOK\" + CONTENTS the contents of the drawer. + +The function should return the string to be exported. + +For example, the variable could be set to the following function +in order to mimic default behaviour: + +\(defun org-latex-format-drawer-default \(name contents\) + \"Format a drawer element for LaTeX export.\" + contents\)" + :group 'org-export-latex + :type 'function) + + +;;;; Inlinetasks + +(defcustom org-latex-format-inlinetask-function nil + "Function called to format an inlinetask in LaTeX code. + +The function must accept six parameters: + TODO the todo keyword, as a string + TODO-TYPE the todo type, a symbol among `todo', `done' and nil. + PRIORITY the inlinetask priority, as a string + NAME the inlinetask name, as a string. + TAGS the inlinetask tags, as a string. + CONTENTS the contents of the inlinetask, as a string. + +The function should return the string to be exported. + +For example, the variable could be set to the following function +in order to mimic default behaviour: + +\(defun org-latex-format-inlinetask-default \(todo type priority name tags contents\) +\"Format an inline task element for LaTeX export.\" + \(let \(\(full-title + \(concat + \(when todo \(format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo\)\) + \(when priority \(format \"\\\\framebox{\\\\#%c} \" priority\)\) + title + \(when tags \(format \"\\\\hfill{}\\\\textsc{%s}\" tags\)\)\)\)\) + \(format \(concat \"\\\\begin{center}\\n\" + \"\\\\fbox{\\n\" + \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\" + \"%s\\n\\n\" + \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\" + \"%s\" + \"\\\\end{minipage}}\" + \"\\\\end{center}\"\) + full-title contents\)\)" + :group 'org-export-latex + :type 'function) + + +;; Src blocks + +(defcustom org-latex-listings nil + "Non-nil means export source code using the listings package. +This package will fontify source code, possibly even with color. +If you want to use this, you also need to make LaTeX use the +listings package, and if you want to have color, the color +package. Just add these to `org-latex-packages-alist', +for example using customize, or with something like + + (require 'org-latex) + (add-to-list 'org-export-latex-packages-alist '(\"\" \"listings\")) + (add-to-list 'org-export-latex-packages-alist '(\"\" \"color\")) + +Alternatively, + + (setq org-latex-listings 'minted) + +causes source code to be exported using the minted package as +opposed to listings. If you want to use minted, you need to add +the minted package to `org-latex-packages-alist', for +example using customize, or with + + (require 'org-latex) + (add-to-list 'org-latex-packages-alist '(\"\" \"minted\")) + +In addition, it is necessary to install +pygments (http://pygments.org), and to configure the variable +`org-latex-to-pdf-process' so that the -shell-escape option is +passed to pdflatex." + :group 'org-export-latex + :type '(choice + (const :tag "Use listings" t) + (const :tag "Use minted" 'minted) + (const :tag "Export verbatim" nil))) + +(defcustom org-latex-listings-langs + '((emacs-lisp "Lisp") (lisp "Lisp") (clojure "Lisp") + (c "C") (cc "C++") + (fortran "fortran") + (perl "Perl") (cperl "Perl") (python "Python") (ruby "Ruby") + (html "HTML") (xml "XML") + (tex "TeX") (latex "TeX") + (shell-script "bash") + (gnuplot "Gnuplot") + (ocaml "Caml") (caml "Caml") + (sql "SQL") (sqlite "sql")) + "Alist mapping languages to their listing language counterpart. +The key is a symbol, the major mode symbol without the \"-mode\". +The value is the string that should be inserted as the language parameter +for the listings package. If the mode name and the listings name are +the same, the language does not need an entry in this list - but it does not +hurt if it is present." + :group 'org-export-latex + :type '(repeat + (list + (symbol :tag "Major mode ") + (string :tag "Listings language")))) + +(defcustom org-latex-listings-options nil + "Association list of options for the latex listings package. + +These options are supplied as a comma-separated list to the +\\lstset command. Each element of the association list should be +a list containing two strings: the name of the option, and the +value. For example, + + (setq org-export-latex-listings-options + '((\"basicstyle\" \"\\small\") + (\"keywordstyle\" \"\\color{black}\\bfseries\\underbar\"))) + +will typeset the code in a small size font with underlined, bold +black keywords. + +Note that the same options will be applied to blocks of all +languages." + :group 'org-export-latex + :type '(repeat + (list + (string :tag "Listings option name ") + (string :tag "Listings option value")))) + +(defcustom org-latex-minted-langs + '((emacs-lisp "common-lisp") + (cc "c++") + (cperl "perl") + (shell-script "bash") + (caml "ocaml")) + "Alist mapping languages to their minted language counterpart. +The key is a symbol, the major mode symbol without the \"-mode\". +The value is the string that should be inserted as the language parameter +for the minted package. If the mode name and the listings name are +the same, the language does not need an entry in this list - but it does not +hurt if it is present. + +Note that minted uses all lower case for language identifiers, +and that the full list of language identifiers can be obtained +with: +pygmentize -L lexers" + :group 'org-export-latex + :type '(repeat + (list + (symbol :tag "Major mode ") + (string :tag "Minted language")))) + +(defcustom org-latex-minted-options nil + "Association list of options for the latex minted package. + +These options are supplied within square brackets in +\\begin{minted} environments. Each element of the alist should be +a list containing two strings: the name of the option, and the +value. For example, + + (setq org-export-latex-minted-options + '((\"bgcolor\" \"bg\") (\"frame\" \"lines\"))) + +will result in src blocks being exported with + +\\begin{minted}[bgcolor=bg,frame=lines]{} + +as the start of the minted environment. Note that the same +options will be applied to blocks of all languages." + :group 'org-export-latex + :type '(repeat + (list + (string :tag "Minted option name ") + (string :tag "Minted option value")))) + +(defvar org-latex-custom-lang-environments nil + "Association list mapping languages to language-specific latex +environments used during export of src blocks by the listings and +minted latex packages. For example, + + (setq org-export-latex-custom-lang-environments + '((python \"pythoncode\"))) + +would have the effect that if org encounters begin_src python +during latex export it will output + + \\begin{pythoncode} + + \\end{pythoncode}") + + +;;;; Plain text + +(defcustom org-latex-quotes + '(("fr" ("\\(\\s-\\|[[(]\\)\"" . "«~") ("\\(\\S-\\)\"" . "~»") ("\\(\\s-\\|(\\)'" . "'")) + ("en" ("\\(\\s-\\|[[(]\\)\"" . "``") ("\\(\\S-\\)\"" . "''") ("\\(\\s-\\|(\\)'" . "`"))) + "Alist for quotes to use when converting english double-quotes. + +The CAR of each item in this alist is the language code. +The CDR of each item in this alist is a list of three CONS: +- the first CONS defines the opening quote; +- the second CONS defines the closing quote; +- the last CONS defines single quotes. + +For each item in a CONS, the first string is a regexp +for allowed characters before/after the quote, the second +string defines the replacement string for this quote." + :group 'org-export-latex + :type '(list + (cons :tag "Opening quote" + (string :tag "Regexp for char before") + (string :tag "Replacement quote ")) + (cons :tag "Closing quote" + (string :tag "Regexp for char after ") + (string :tag "Replacement quote ")) + (cons :tag "Single quote" + (string :tag "Regexp for char before") + (string :tag "Replacement quote ")))) + + + +;;; Internal Functions + +(defun org-latex--caption/label-string (caption label info) + "Return caption and label LaTeX string for floats. + +CAPTION is a secondary string \(a list of strings and Org +objects\) and LABEL a string representing the label. INFO is +a plist holding contextual information. + +If there's no caption nor label, return the empty string. + +For non-floats, see `org-latex--wrap-label'." + (let ((caption-str (and caption + (org-export-secondary-string + caption 'latex info))) + (label-str (if label (format "\\label{%s}" label) ""))) + (cond + ((and (not caption-str) (not label)) "") + ((not caption-str) (format "\\label{%s}\n" label)) + ;; Option caption format with short name. + ((string-match "\\[\\([^][]*\\)\\]{\\([^{}]*\\)}" caption-str) + (format "\\caption[%s]{%s%s}\n" + (org-match-string-no-properties 1 caption-str) + label-str + (org-match-string-no-properties 2 caption-str))) + ;; Standard caption format. + (t (format "\\caption{%s%s}\n" label-str caption-str))))) + +(defun org-latex--guess-inputenc (header) + "Set the coding system in inputenc to what the buffer is. + +HEADER is the LaTeX header string. + +Return the new header." + (let* ((cs (or (ignore-errors + (latexenc-coding-system-to-inputenc + buffer-file-coding-system)) + "utf8"))) + (if (not cs) + header + ;; First translate if that is requested. + (setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs)) + ;; Then find the \usepackage statement and replace the option. + (replace-regexp-in-string "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}" + cs header t nil 1)))) + +(defun org-latex--find-verb-separator (s) + "Return a character not used in string S. +This is used to choose a separator for constructs like \\verb." + (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}")) + (loop for c across ll + when (not (string-match (regexp-quote (char-to-string c)) s)) + return (char-to-string c)))) + +(defun org-latex--make-option-string (options) + "Return a comma separated string of keywords and values. +OPTIONS is an alist where the key is the options keyword as +a string, and the value a list containing the keyword value, or +nil." + (mapconcat (lambda (pair) + (concat (first pair) + (when (> (length (second pair)) 0) + (concat "=" (second pair))))) + options + ",")) + +(defun org-latex--quotation-marks (text info) + "Export quotation marks depending on language conventions." + (mapc (lambda(l) + (let ((start 0)) + (while (setq start (string-match (car l) text start)) + (let ((new-quote (concat (match-string 1 text) (cdr l)))) + (setq text (replace-match new-quote t t text)))))) + (cdr (or (assoc (plist-get info :language) org-latex-quotes) + ;; Falls back on English. + (assoc "en" org-latex-quotes)))) + text) + +(defun org-latex--wrap-label (element output) + "Wrap label associated to ELEMENT around OUTPUT, if appropriate. +This function shouldn't be used for floats. See +`org-latex--caption/label-string'." + (let ((label (org-element-get-property :name element))) + (if (or (not output) (not label) (string= output "") (string= label "")) + output + (concat (format "\\label{%s}\n" label) output)))) + + + +;;; Template + +(defun org-latex-template (contents info) + "Return complete document string after LaTeX conversion. +CONTENTS is the transcoded contents string. INFO is a plist +holding export options." + (let ((title (org-export-secondary-string + (plist-get info :title) 'latex info))) + (concat + ;; 1. Time-stamp. + (and (plist-get info :time-stamp-file) + (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) + ;; 2. Document class and packages. + (let ((class (plist-get info :latex-class)) + (class-options (plist-get info :latex-class-options))) + (org-element-normalize-string + (let* ((header (nth 1 (assoc class org-latex-classes))) + (document-class-string + (and (stringp header) + (if class-options + (replace-regexp-in-string + "^[ \t]*\\\\documentclass\\(\\[.*?\\]\\)" + class-options header t nil 1) + header)))) + (org-latex--guess-inputenc + (org-splice-latex-header + document-class-string + org-export-latex-default-packages-alist ; defined in org.el + org-export-latex-packages-alist nil ; defined in org.el + (plist-get info :latex-header-extra)))))) + ;; 3. Define alert if not yet defined. + "\\providecommand{\\alert}[1]{\\textbf{#1}}\n" + ;; 4. Author. + (let ((author (and (plist-get info :with-author) + (let ((auth (plist-get info :author))) + (and auth (org-export-secondary-string + auth 'latex info))))) + (email (and (plist-get info :with-email) + (org-export-secondary-string + (plist-get info :email) 'latex info)))) + (cond ((and author email (not (string= "" email))) + (format "\\author{%s\\thanks{%s}}\n" author email)) + (author (format "\\author{%s}\n" author)) + (t "\\author{}\n"))) + ;; 5. Date. + (let ((date (plist-get info :date))) + (and date (format "\\date{%s}\n" date))) + ;; 6. Title + (format "\\title{%s}\n" title) + ;; 7. Hyperref options. + (format "\\hypersetup{\n pdfkeywords={%s},\n pdfsubject={%s},\n pdfcreator={%s}}\n" + (or (plist-get info :keywords) "") + (or (plist-get info :description) "") + (let ((creator-info (plist-get info :with-creator))) + (cond + ((not creator-info) "") + ((eq creator-info 'comment) "") + (t (plist-get info :creator))))) + ;; 7. Document start. + "\\begin{document}\n\n" + ;; 8. Title command. + (org-element-normalize-string + (cond ((string= "" title) nil) + ((not (stringp org-latex-title-command)) nil) + ((string-match "\\(?:[^%]\\|^\\)%s" + org-latex-title-command) + (format org-latex-title-command title)) + (t org-latex-title-command))) + ;; 9. Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth + (concat (when (wholenump depth) + (format "\\setcounter{tocdepth}{%d}\n" depth)) + "\\tableofcontents\n\\vspace*{1cm}\n\n"))) + ;; 10. Document's body. + contents + ;; 11. Creator. + (let ((creator-info (plist-get info :with-creator))) + (cond + ((not creator-info)) + ((eq creator-info 'comment) + (format "%% %s\n" (plist-get info :creator))) + (t (concat (plist-get info :creator) "\n")))) + ;; 12. Document end. + "\\end{document}"))) + + + +;;; Transcode Functions + +;;;; Block + +(defun org-latex-center-block (center-block contents info) + "Transcode a CENTER-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (org-latex--wrap-label + center-block + (format "\\begin{center}\n%s\\end{center}" contents))) + + +;;;; Comment + +;; Comments are ignored. + + +;;;; Comment Block + +;; Comment Blocks are ignored. + + +;;;; Drawer + +(defun org-latex-drawer (drawer contents info) + "Transcode a DRAWER element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let* ((name (org-element-get-property :drawer-name drawer)) + (output (if (functionp org-latex-format-drawer-function) + (funcall org-latex-format-drawer-function + name contents) + ;; If there's no user defined function: simply + ;; display contents of the drawer. + contents))) + (org-latex--wrap-label drawer output))) + + +;;;; Dynamic Block + +(defun org-latex-dynamic-block (dynamic-block contents info) + "Transcode a DYNAMIC-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information. See +`org-export-data'." + (org-latex--wrap-label dynamic-block contents)) + + +;;;; Emphasis + +(defun org-latex-emphasis (emphasis contents info) + "Transcode EMPHASIS from Org to LaTeX. +CONTENTS is the contents of the emphasized text. INFO is a plist +holding contextual information.." + (format (cdr (assoc (org-element-get-property :marker emphasis) + org-latex-emphasis-alist)) + contents)) + + +;;;; Entity + +(defun org-latex-entity (entity contents info) + "Transcode an ENTITY object from Org to LaTeX. +CONTENTS are the definition itself. INFO is a plist holding +contextual information." + (let ((ent (org-element-get-property :latex entity))) + (if (org-element-get-property :latex-math-p entity) + (format "$%s$" ent) + ent))) + + +;;;; Example Block + +(defun org-latex-example-block (example-block contents info) + "Transcode a EXAMPLE-BLOCK element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let* ((options (or (org-element-get-property :options example-block) "")) + (value (org-export-handle-code + (org-element-get-property :value example-block) options info))) + (org-latex--wrap-label example-block value))) + + +;;;; Export Snippet + +(defun org-latex-export-snippet (export-snippet contents info) + "Transcode a EXPORT-SNIPPET object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-element-get-property :value export-snippet)) + + +;;;; Export Block + +(defun org-latex-export-block (export-block contents info) + "Transcode a EXPORT-BLOCK element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (when (string= (org-element-get-property :type export-block) "latex") + (org-remove-indentation (org-element-get-property :value export-block)))) + + +;;;; Fixed Width + +(defun org-latex-fixed-width (fixed-width contents info) + "Transcode a FIXED-WIDTH element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let* ((value (org-element-normalize-string + (replace-regexp-in-string + "^[ \t]*: ?" "" + (org-element-get-property :value fixed-width))))) + (org-latex--wrap-label + fixed-width + (format "\\begin{verbatim}\n%s\\end{verbatim}" value)))) + + +;;;; Footnote Definition + +;; Footnote Definitions are ignored. + + +;;;; Footnote Reference + +(defun org-latex-footnote-reference (footnote-reference contents info) + "Transcode a FOOTNOTE-REFERENCE element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (concat + ;; Insert separator between two footnotes in a row. + (when (eq (plist-get info :previous-object) 'footnote-reference) + org-latex-footnote-separator) + ;; Use \footnotemark if the footnote has already been defined. + ;; Otherwise, define it with \footnote command. + (let* ((all-seen (plist-get org-export-persistent-properties + :seen-footnote-labels)) + (label (org-element-get-property :label footnote-reference)) + ;; Anonymous footnotes are always new footnotes. + (seenp (and label (member label all-seen))) + (inline-def-p (org-element-get-property + :inline-definition footnote-reference))) + (cond + (seenp (format "\\footnotemark[%s]" (length seenp))) + ;; Inline definitions are secondary strings. + (inline-def-p + (format "\\footnote{%s}" + (org-trim + (org-export-secondary-string inline-def-p 'latex info)))) + ;; Non-inline footnotes necessarily contain a label. Retrieve + ;; match definition in `:footnotes-labels-alist'. + (t + (format "\\footnote{%s}" + (org-trim + (org-export-data + (cdr (assoc label (plist-get info :footnotes-labels-alist))) + 'latex info)))))))) + + +;;;; Headline + +(defun org-latex-headline (headline contents info) + "Transcode an HEADLINE element from Org to LaTeX. +CONTENTS holds the contents of the headline. INFO is a plist +holding contextual information." + (let* ((class (plist-get info :latex-class)) + (numberedp (plist-get info :section-numbers)) + ;; Get level relative to current parsed data. + (level (+ (org-element-get-property :level headline) + (plist-get info :headline-offset))) + (class-sectionning (assoc class org-latex-classes)) + ;; Section formatting will set two placeholders: one for the + ;; title and the other for the contents. + (section-fmt + (let ((sec (if (and (symbolp (nth 2 class-sectionning)) + (fboundp (nth 2 class-sectionning))) + (funcall (nth 2 class-sectionning) level numberedp) + (nth (1+ level) class-sectionning)))) + (cond + ;; No section available for that LEVEL. + ((not sec) nil) + ;; Section format directly returned by a function. + ((stringp sec) sec) + ;; (numbered-section . unnumbered-section) + ((not (consp (cdr sec))) + (concat (funcall (if numberedp #'car #'cdr) sec) "\n%s")) + ;; (numbered-open numbered-close) + ((= (length sec) 2) + (when numberedp (concat (car sec) "\n%s" (nth 1 sec)))) + ;; (num-in num-out no-num-in no-num-out) + ((= (length sec) 4) + (if numberedp + (concat (car sec) "\n%s" (nth 1 sec)) + (concat (nth 2 sec) "\n%s" (nth 3 sec))))))) + (text (org-export-secondary-string + (org-element-get-property :title headline) 'latex info)) + (todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-get-property + :todo-keyword headline))) + (and todo + (org-export-secondary-string todo 'latex info))))) + (todo-type (and todo (org-element-get-property :todo-type headline))) + (tags (and (plist-get info :with-tags) + (org-element-get-property :tags headline))) + (priority (and (plist-get info :with-priority) + (org-element-get-property :priority headline))) + ;; Create the headline text. + (full-text (if (functionp org-latex-format-headline-function) + ;; User-defined formatting function. + (funcall org-latex-format-headline-function + todo todo-type priority text tags) + ;; Default formatting. + (concat + (when todo + (format "\\textbf{\\textsf{\\textsc{%s}}} " todo)) + (when priority (format "\\framebox{\\#%c} " priority)) + text + (when tags (format "\\hfill{}\\textsc{%s}" tags))))) + ;; Associate some \label to the headline for internal links. + (headline-labels (mapconcat + (lambda (p) + (let ((val (org-element-get-property p headline))) + (when val (format "\\label{%s}\n" + (if (eq p :begin) + (format "headline-%s" val) + val))))) + '(:custom-id :id :begin) "")) + (pre-blanks (make-string (org-element-get-property :pre-blank headline) + 10))) + (cond + ;; Case 1: This is a footnote section: ignore it. + ((org-element-get-property :footnote-section-p headline) nil) + ;; Case 2. This is a deep sub-tree: export it as a list item. + ;; Also export as items headlines for which no section + ;; format has been found. + ((or (not section-fmt) + (and (wholenump (plist-get info :headline-levels)) + (> level (plist-get info :headline-levels)))) + ;; Build the real contents of the sub-tree. + (let ((low-level-body + (concat + ;; If the headline is the first sibling, start a list. + (when (org-export-first-sibling-p headline info) + (format "\\begin{%s}\n" (if numberedp 'enumerate 'itemize))) + ;; Itemize headline + "\\item " full-text "\n" headline-labels pre-blanks contents))) + ;; If headline in the last sibling, close the list, before any + ;; blank line. Otherwise, simply return LOW-LEVEL-BODY. + (if (org-export-last-sibling-p headline info) + (replace-regexp-in-string + "[ \t\n]*\\'" + (format "\n\\\\end{%s}" (if numberedp 'enumerate 'itemize)) + low-level-body) + low-level-body))) + ;; Case 3. Standard headline. Export it as a section. + (t (format section-fmt full-text + (concat headline-labels pre-blanks contents)))))) + + +;;;; Horizontal Rule + +(defun org-latex-horizontal-rule (horizontal-rule contents info) + "Transcode an HORIZONTAL-RULE object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((attr (mapconcat #'identity + (org-element-get-property :attr_latex horizontal-rule) + " "))) + (org-latex--wrap-label horizontal-rule (concat "\\hrule " attr)))) + + +;;;; Inline Babel Call + +;; Inline Babel Calls are ignored. + + +;;;; Inline Src Block + +(defun org-latex-inline-src-block (inline-src-block contents info) + "Transcode an INLINE-SRC-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (let* ((code (org-element-get-property :value inline-src-block)) + (separator (org-latex--find-verb-separator code))) + (cond + ;; Do not use a special package: transcode it verbatim. + ((not org-latex-listings) + (concat "\\verb" separator code separator)) + ;; Use minted package. + ((eq org-latex-listings 'minted) + (let* ((org-lang (org-element-get-property :language inline-src-block)) + (mint-lang (or (cadr (assq (intern org-lang) + org-latex-minted-langs)) + org-lang)) + (options (org-latex--make-option-string + org-latex-minted-options))) + (concat (format "\\mint%s{%s}" + (if (string= options "") "" (format "[%s]" options)) + mint-lang) + separator code separator))) + ;; Use listings package. + (t + ;; Maybe translate language's name. + (let* ((org-lang (org-element-get-property :language inline-src-block)) + (lst-lang (or (cadr (assq (intern org-lang) + org-latex-listings-langs)) + org-lang)) + (options (org-latex--make-option-string + (append org-latex-listings-options + `(("language" ,lst-lang)))))) + (concat (format "\\lstinline[%s]" options) + separator code separator)))))) + + +;;;; Inlinetask + +(defun org-latex-inlinetask (inlinetask contents info) + "Transcode an INLINETASK element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let ((title (org-export-secondary-string + (org-element-get-property :title inlinetask) 'latex info)) + (todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-get-property + :todo-keyword inlinetask))) + (and todo + (org-export-secondary-string todo 'latex info))))) + (todo-type (org-element-get-property :todo-type inlinetask)) + (tags (and (plist-get info :with-tags) + (org-element-get-property :tags inlinetask))) + (priority (and (plist-get info :with-priority) + (org-element-get-property :priority inlinetask)))) + ;; If `org-latex-format-inlinetask-function' is provided, call it + ;; with appropriate arguments. + (if (functionp org-latex-format-inlinetask-function) + (funcall org-latex-format-inlinetask-function + todo todo-type priority title tags contents) + ;; Otherwise, use a default template. + (org-latex--wrap-label + inlinetask + (let ((full-title + (concat + (when todo (format "\\textbf{\\textsf{\\textsc{%s}}} " todo)) + (when priority (format "\\framebox{\\#%c} " priority)) + title + (when tags (format "\\hfill{}\\textsc{%s}" tags))))) + (format (concat "\\begin{center}\n" + "\\fbox{\n" + "\\begin{minipage}[c]{.6\\textwidth}\n" + "%s\n\n" + "\\rule[.8em]{\\textwidth}{2pt}\n\n" + "%s" + "\\end{minipage}\n" + "}\n" + "\\end{center}") + full-title contents)))))) + + +;;;; Item + +(defun org-latex-item (item contents info) + "Transcode an ITEM element from Org to LaTeX. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (let* ((level (plist-get (plist-get info :parent-properties) :level)) + (counter (let ((count (org-element-get-property :counter item))) + (and count + (< level 4) + (format "\\setcounter{enum%s}{%s}\n" + (nth level '("i" "ii" "iii" "iv")) + (1- count))))) + (checkbox (let ((checkbox (org-element-get-property :checkbox item))) + (cond ((eq checkbox 'on) "$\\boxtimes$ ") + ((eq checkbox 'off) "$\\Box$ ") + ((eq checkbox 'trans) "$\\boxminus$ ")))) + (tag (let ((tag (org-element-get-property :tag item))) + (and tag + (format "[%s]" (org-export-secondary-string + tag 'latex info)))))) + (concat counter "\\item" tag " " checkbox contents))) + + +;;;; Keyword + +(defun org-latex-keyword (keyword contents info) + "Transcode a KEYWORD element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((key (downcase (org-element-get-property :key keyword))) + (value (org-element-get-property :value keyword))) + (cond + ((string= key "latex") value) + ((string= key "index") (format "\\index{%s}" value)) + ((string= key "target") + (format "\\label{%s}" (org-export-solidify-link-text value))) + ((string= key "toc") + (let ((value (downcase value))) + (cond + ((string-match "\\" value) + (let ((depth (or (and (string-match "[0-9]+" value) + (string-to-number (match-string 0 value))) + (plist-get info :with-toc)))) + (concat + (when (wholenump depth) + (format "\\setcounter{tocdepth}{%s}\n" depth)) + "\\tableofcontents"))) + ((string= "tables" value) "\\listoftables") + ((string= "figures" value) "\\listoffigures") + ((string= "listings" value) "\\listoflistings")))) + ((string= key "include") + (org-export-included-file keyword 'latex info))))) + + +;;;; Latex Environment + +(defun org-latex-latex-environment (latex-environment contents info) + "Transcode a LATEX-ENVIRONMENT element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-latex--wrap-label + latex-environment + (org-remove-indentation (org-element-get-property :value latex-environment)))) + + +;;;; Latex Fragment + +(defun org-latex-latex-fragment (latex-fragment contents info) + "Transcode a LATEX-FRAGMENT object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-element-get-property :value latex-fragment)) + + +;;;; Line Break + +(defun org-latex-line-break (line-break contents info) + "Transcode a LINE-BREAK object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + "\\\\") + + +;;;; Link + +(defun org-latex-link--inline-image (path info) + "Return LaTeX code for an image at PATH. +INFO is a plist containing export options." + (let* ((parent-props (plist-get info :parent-properties)) + (caption (org-latex--caption/label-string + (plist-get parent-props :caption) + (plist-get parent-props :name) + info)) + ;; Retrieve latex attributes from the element around. + (attr (let ((raw-attr + (mapconcat #'identity + (plist-get parent-props :attr_latex) " "))) + (unless (string= raw-attr "") raw-attr))) + (disposition + (cond + ((and attr (string-match "\\" attr)) 'wrap) + ((and attr (string-match "\\" attr)) 'multicolumn) + ((or (and attr (string-match "\\" attr)) + (not (string= caption ""))) + 'float))) + (placement + (cond + ((and attr (string-match "\\" paralist-regexp) attr)) + (match-string 1 attr)) + ((eq type 'ordered) "enumerate") + ((eq type 'unordered) "itemize") + ((eq type 'descriptive) "description")))) + (org-latex--wrap-label + plain-list + (format "\\begin{%s}%s\n%s\\end{%s}" + latex-type + ;; Once special environment, if any, has been removed, the + ;; rest of the attributes will be optional arguments. + ;; They will be put inside square brackets if necessary. + (let ((opt (replace-regexp-in-string + (format " *%s *" paralist-regexp) "" attr))) + (cond ((string= opt "") "") + ((string-match "\\`\\[[^][]+\\]\\'" opt) opt) + (t (format "[%s]" opt)))) + contents + latex-type)))) + + +;;;; Plain Text + +(defun org-latex-plain-text (text info) + "Transcode a TEXT string from Org to LaTeX. +TEXT is the string to transcode. INFO is a plist holding +contextual information." + ;; Protect %, #, &, $, ~, ^, _, { and }. + (while (string-match "\\([^\\]\\|^\\)\\([%$#&{}~^_]\\)" text) + (setq text + (replace-match (format "\\%s" (match-string 2 text)) nil t text 2))) + ;; Protect \ + (setq text (replace-regexp-in-string + "\\(?:[^\\]\\|^\\)\\(\\\\\\)\\(?:[^%$#&{}~^_\\]\\|$\\)" + "$\\backslash$" text nil t 1)) + ;; LaTeX into \LaTeX{} and TeX into \TeX{}. + (let ((case-fold-search nil) + (start 0)) + (while (string-match "\\<\\(\\(?:La\\)?TeX\\)\\>" text start) + (setq text (replace-match + (format "\\%s{}" (match-string 1 text)) nil t text) + start (match-end 0)))) + ;; Handle quotation marks + (setq text (org-latex--quotation-marks text info)) + ;; Convert special strings. + (when (plist-get info :with-special-strings) + (while (string-match (regexp-quote "...") text) + (setq text (replace-match "\\ldots{}" nil t text)))) + ;; Handle break preservation if required. + (when (plist-get info :preserve-breaks) + (setq text (replace-regexp-in-string "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" + text))) + ;; Return value. + text) + + +;;;; Property Drawer + +(defun org-latex-property-drawer (property-drawer contents info) + "Transcode a PROPERTY-DRAWER element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual +information." + ;; The property drawer isn't exported but we want separating blank + ;; lines nonetheless. + "") + + +;;;; Quote Block + +(defun org-latex-quote-block (quote-block contents info) + "Transcode a QUOTE-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (org-latex--wrap-label + quote-block + (format "\\begin{quote}\n%s\\end{quote}" contents))) + + +;;;; Quote Section + +(defun org-latex-quote-section (quote-section contents info) + "Transcode a QUOTE-SECTION element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((value (org-remove-indentation + (org-element-get-property :value quote-section)))) + (when value (format "\\begin{verbatim}\n%s\\end{verbatim}" value)))) + + +;;;; Radio Target + +(defun org-latex-radio-target (radio-target text info) + "Transcode a RADIO-TARGET object from Org to LaTeX. +TEXT is the text of the target. INFO is a plist holding +contextual information." + (format "\\label{%s}%s" + (org-export-solidify-link-text + (org-element-get-property :raw-value radio-target)) + text)) + + +;;;; Special Block + +(defun org-latex-special-block (special-block contents info) + "Transcode a SPECIAL-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let ((type (downcase (org-element-get-property :type special-block)))) + (org-latex--wrap-label + special-block + (format "\\begin{%s}\n%s\\end{%s}" type contents type)))) + + +;;;; Src Block + +(defun org-latex-src-block (src-block contents info) + "Transcode a SRC-BLOCK element from Org to LaTeX. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (let* ((lang (org-element-get-property :language src-block)) + (code (org-export-handle-code + (org-element-get-property :value src-block) + (org-element-get-property :switches src-block) + info lang)) + (caption (org-element-get-property :caption src-block)) + (label (org-element-get-property :name src-block)) + (custom-env (and lang + (cadr (assq (intern lang) + org-latex-custom-lang-environments))))) + (cond + ;; No source fontification. + ((not org-latex-listings) + (let ((caption-str (org-latex--caption/label-string + caption label info)) + (float-env (when caption "\\begin{figure}[H]\n%s\n\\end{figure}"))) + (format (or float-env "%s") + (concat + caption-str + (format "\\begin{verbatim}\n%s\\end{verbatim}" code))))) + ;; Custom environment. + (custom-env + (format "\\begin{%s}\n%s\\end{%s}\n" custom-env code custom-env)) + ;; Use minted package. + ((eq org-latex-listings 'minted) + (let* ((mint-lang (or (cadr (assq (intern lang) org-latex-minted-langs)) + lang)) + (float-env (when (or label caption) + (format "\\begin{listing}[H]\n%%s\n%s\\end{listing}" + (org-latex--caption/label-string + caption label info)))) + (body (format "\\begin{minted}[%s]{%s}\n%s\\end{minted}" + (org-latex--make-option-string + org-latex-minted-options) + mint-lang code))) + (if float-env (format float-env body) body))) + ;; Use listings package. + (t + (let ((lst-lang (or (cadr (assq (intern lang) org-latex-listings-langs)) + lang)) + (caption-str (and caption + (org-export-secondary-string + (org-element-get-property :caption src-block) + 'latex info)))) + (concat (format "\\lstset{%s}\n" + (org-latex--make-option-string + (append org-latex-listings-options + `(("language" ,lst-lang)) + (when label `(("label" ,label))) + (when caption-str + `(("caption" ,caption-str)))))) + (format "\\begin{lstlisting}\n%s\\end{lstlisting}" code))))))) + + +;;;; Statistics Cookie + +(defun org-latex-statistics-cookie (statistics-cookie contents info) + "Transcode a STATISTICS-COOKIE object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-element-get-property :value statistics-cookie)) + + +;;;; Subscript + +(defun org-latex-subscript (subscript contents info) + "Transcode a SUBSCRIPT object from Org to LaTeX. +CONTENTS is the contents of the object. INFO is a plist holding +contextual information." + (format (if (= (length contents) 1) "$_%s$" "$_{\\mathrm{%s}}$") contents)) + + +;;;; Superscript + +(defun org-latex-superscript (superscript contents info) + "Transcode a SUPERSCRIPT object from Org to LaTeX. +CONTENTS is the contents of the object. INFO is a plist holding +contextual information." + (format (if (= (length contents) 1) "$^%s$" "$^{\\mathrm{%s}}$") contents)) + + +;;;; Table + +(defun org-latex-table--format-string (table info) + "Return an appropriate format string for TABLE. + +INFO is the plist containing format info about the table, as +returned by `org-export-table-format-info'. + +The format string one placeholder for the body of the table." + (let* ((label (org-element-get-property :name table)) + (caption (org-latex--caption/label-string + (org-element-get-property :caption table) label info)) + (attr (mapconcat #'identity + (org-element-get-property :attr_latex table) + " ")) + ;; Determine alignment string. + (alignment (org-latex-table--align-string attr info)) + ;; Determine environment for the table: longtable, tabular... + (table-env (cond + ((not attr) org-latex-default-table-environment) + ((string-match "\\" attr) "longtable") + ((string-match "\\(tabular.\\)" attr) + (org-match-string-no-properties 1 attr)) + (t org-latex-default-table-environment))) + ;; If table is a float, determine environment: table or table*. + (float-env (cond + ((string= "longtable" table-env) nil) + ((and attr + (or (string-match (regexp-quote "table*") attr) + (string-match "\\" attr))) + "table*") + ((or (not (string= caption "")) label) "table"))) + ;; Extract others display options. + (width (and attr + (string-match "\\" attr))) + (format "\\begin{verbatim}\n%s\n\\end{verbatim}" + (org-export-clean-table + raw-table + (plist-get (org-export-table-format-info raw-table) + :special-column-p)))) + ;; Case 2: table.el table. Convert it using appropriate tools. + ((eq (org-element-get-property :type table) 'table.el) + (require 'table) + ;; Ensure "*org-export-table*" buffer is empty. + (and (get-buffer "*org-export-table*") + (kill-buffer (get-buffer "*org-export-table*"))) + (let ((output (with-temp-buffer + (insert raw-table) + (goto-char 1) + (re-search-forward "^[ \t]*|[^|]" nil t) + (table-generate-source 'latex "*org-export-table*") + (with-current-buffer "*org-export-table*" + (org-trim (buffer-string)))))) + (kill-buffer (get-buffer "*org-export-table*")) + ;; Remove left out comments. + (while (string-match "^%.*\n" output) + (setq output (replace-match "" t t output))) + ;; When the "rmlines" attribute is provided, remove all hlines + ;; but the the one separating heading from the table body. + (when (and attr (string-match "\\" attr)) + (let ((n 0) (pos 0)) + (while (and (< (length output) pos) + (setq pos (string-match "^\\\\hline\n?" output pos))) + (incf n) + (unless (= n 2) (setq output (replace-match "" nil nil output)))))) + (if org-latex-tables-centered + (format "\\begin{center}\n%s\n\\end{center}" output) + output))) + ;; Case 3: Standard table. + (t (let* ( + (info (org-export-table-format-info raw-table)) + (clean-table (org-export-clean-table + raw-table (plist-get info :special-column-p))) + (columns-number (length (plist-get info :alignment)))) + ;; Convert ROWS to send them to `orgtbl-to-latex'. In + ;; particular, send each cell to + ;; `org-element-parse-secondary-string' to expand any Org + ;; object within. Eventually, flesh the format string out with + ;; the table. + (format (org-latex-table--format-string table info) + (orgtbl-to-latex + (mapcar + (lambda (row) + (if (string-match org-table-hline-regexp row) + 'hline + (mapcar + (lambda (cell) + (org-export-secondary-string + (org-element-parse-secondary-string + cell + (cdr (assq 'table org-element-string-restrictions))) + 'latex info)) + (org-split-string row "[ \t]*|[ \t]*")))) + (org-split-string clean-table "\n")) + `(:tstart nil :tend nil + ;; Longtable environment requires specific + ;; header line end. + :hlend ,(and attr + (string-match "\\" attr) + (format "\\\\ +\\hline +\\endhead +\\hline\\multicolumn{%d}{r}{Continued on next page}\\\\ +\\endfoot +\\endlastfoot" + columns-number)))))))))) + + +;;;; Target + +(defun org-latex-target (target text info) + "Transcode a TARGET object from Org to LaTeX. +TEXT is the text of the target. INFO is a plist holding +contextual information." + (format "\\label{%s}%s" + (org-export-solidify-link-text + (org-element-get-property :raw-value target)) + text)) + + +;;;; Time-stamp + +(defun org-latex-time-stamp (time-stamp contents info) + "Transcode a TIME-STAMP object from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((value (org-element-get-property :value time-stamp)) + (type (org-element-get-property :type time-stamp)) + (appt-type (org-element-get-property :appt-type time-stamp))) + (concat (cond ((eq appt-type 'scheduled) + (format "\\textbf{\\textsc{%s}} " org-scheduled-string)) + ((eq appt-type 'deadline) + (format "\\textbf{\\textsc{%s}} " org-deadline-string)) + ((eq appt-type 'closed) + (format "\\textbf{\\textsc{%s}} " org-closed-string))) + (cond ((memq type '(active active-range)) + (format org-latex-active-timestamp-format value)) + ((memq type '(inactive inactive-range)) + (format org-latex-inactive-timestamp-format value)) + (t + (format org-latex-diary-timestamp-format value)))))) + + +;;;; Verbatim + +(defun org-latex-verbatim (element contents info) + "Return verbatim text in LaTeX." + (let ((fmt (cdr (assoc (org-element-get-property :marker element) + org-latex-emphasis-alist))) + (value (org-element-get-property :value element))) + (cond + ;; Handle the `verb' special case. + ((eq 'verb fmt) + (let ((separator (org-latex--find-verb-separator value))) + (concat "\\verb" separator value separator))) + ;; Handle the `protectedtexttt' special case. + ((eq 'protectedtexttt fmt) + (let ((start 0) + (trans '(("\\" . "\\textbackslash{}") + ("~" . "\\textasciitilde{}") + ("^" . "\\textasciicircum{}"))) + (rtn "") + char) + (while (string-match "[\\{}$%&_#~^]" value) + (setq char (match-string 0 value)) + (if (> (match-beginning 0) 0) + (setq rtn (concat rtn (substring value 0 (match-beginning 0))))) + (setq value (substring value (1+ (match-beginning 0)))) + (setq char (or (cdr (assoc char trans)) (concat "\\" char)) + rtn (concat rtn char))) + (setq value (concat rtn value) + fmt "\\texttt{%s}") + (while (string-match "--" value) + (setq value (replace-match "-{}-" t t value))) + (format fmt value))) + ;; Else use format string. + (t (format fmt value))))) + + +;;;; Verse Block + +(defun org-latex-verse-block (verse-block contents info) + "Transcode a VERSE-BLOCK element from Org to LaTeX. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-latex--wrap-label + verse-block + ;; In a verse environment, add a line break to each newline + ;; character and change each white space at beginning of a line + ;; into a space of 1 em. Also change each blank line with + ;; a vertical space of 1 em. + (progn + (setq contents (replace-regexp-in-string + "^ *\\\\\\\\$" "\\\\vspace*{1em}" + (replace-regexp-in-string + "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" + (org-remove-indentation + (org-export-secondary-string + (org-element-get-property :value verse-block) + 'latex info))))) + (while (string-match "^[ \t]+" contents) + (let ((new-str (format "\\hspace*{%dem}" + (length (match-string 0 contents))))) + (setq contents (replace-match new-str nil t contents)))) + (format "\\begin{verse}\n%s\\end{verse}" contents)))) + + +(provide 'org-latex) +;;; org-latex.el ends here