summaryrefslogtreecommitdiff
path: root/lisp/org/ox-latex.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/org/ox-latex.el')
-rw-r--r--lisp/org/ox-latex.el2587
1 files changed, 1698 insertions, 889 deletions
diff --git a/lisp/org/ox-latex.el b/lisp/org/ox-latex.el
index 3eee86a3ae7..61b6b8cca92 100644
--- a/lisp/org/ox-latex.el
+++ b/lisp/org/ox-latex.el
@@ -1,4 +1,4 @@
-;;; ox-latex.el --- LaTeX Back-End for Org Export Engine
+;;; ox-latex.el --- LaTeX Back-End for Org Export Engine -*- lexical-binding: t; -*-
;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
@@ -18,7 +18,7 @@
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
@@ -26,7 +26,7 @@
;;; Code:
-(eval-when-compile (require 'cl))
+(require 'cl-lib)
(require 'ox)
(require 'ox-publish)
@@ -43,8 +43,6 @@
(center-block . org-latex-center-block)
(clock . org-latex-clock)
(code . org-latex-code)
- (comment . (lambda (&rest args) ""))
- (comment-block . (lambda (&rest args) ""))
(drawer . org-latex-drawer)
(dynamic-block . org-latex-dynamic-block)
(entity . org-latex-entity)
@@ -65,13 +63,13 @@
(latex-fragment . org-latex-latex-fragment)
(line-break . org-latex-line-break)
(link . org-latex-link)
+ (node-property . org-latex-node-property)
(paragraph . org-latex-paragraph)
(plain-list . org-latex-plain-list)
(plain-text . org-latex-plain-text)
(planning . org-latex-planning)
- (property-drawer . (lambda (&rest args) ""))
+ (property-drawer . org-latex-property-drawer)
(quote-block . org-latex-quote-block)
- (quote-section . org-latex-quote-section)
(radio-target . org-latex-radio-target)
(section . org-latex-section)
(special-block . org-latex-special-block)
@@ -88,8 +86,10 @@
(timestamp . org-latex-timestamp)
(underline . org-latex-underline)
(verbatim . org-latex-verbatim)
- (verse-block . org-latex-verse-block))
- :export-block '("LATEX" "TEX")
+ (verse-block . org-latex-verse-block)
+ ;; Pseudo objects and elements.
+ (latex-math-block . org-latex-math-block)
+ (latex-matrices . org-latex-matrices))
:menu-entry
'(?l "Export to LaTeX"
((?L "As LaTeX buffer" org-latex-export-as-latex)
@@ -99,13 +99,58 @@
(lambda (a s v b)
(if a (org-latex-export-to-pdf t s v b)
(org-open-file (org-latex-export-to-pdf nil s v b)))))))
- :options-alist '((:latex-class "LATEX_CLASS" nil org-latex-default-class t)
- (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t)
- (:latex-header "LATEX_HEADER" nil nil newline)
- (:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline)
- (:latex-hyperref-p nil "texht" org-latex-with-hyperref t)
- ;; Redefine regular options.
- (:date "DATE" nil "\\today" t)))
+ :filters-alist '((:filter-options . org-latex-math-block-options-filter)
+ (:filter-paragraph . org-latex-clean-invalid-line-breaks)
+ (:filter-parse-tree org-latex-math-block-tree-filter
+ org-latex-matrices-tree-filter
+ org-latex-image-link-filter)
+ (:filter-verse-block . org-latex-clean-invalid-line-breaks))
+ :options-alist
+ '((:latex-class "LATEX_CLASS" nil org-latex-default-class t)
+ (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t)
+ (:latex-header "LATEX_HEADER" nil nil newline)
+ (:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline)
+ (:description "DESCRIPTION" nil nil parse)
+ (:keywords "KEYWORDS" nil nil parse)
+ (:subtitle "SUBTITLE" nil nil parse)
+ ;; Other variables.
+ (:latex-active-timestamp-format nil nil org-latex-active-timestamp-format)
+ (:latex-caption-above nil nil org-latex-caption-above)
+ (:latex-classes nil nil org-latex-classes)
+ (:latex-default-figure-position nil nil org-latex-default-figure-position)
+ (:latex-default-table-environment nil nil org-latex-default-table-environment)
+ (:latex-default-table-mode nil nil org-latex-default-table-mode)
+ (:latex-diary-timestamp-format nil nil org-latex-diary-timestamp-format)
+ (:latex-footnote-defined-format nil nil org-latex-footnote-defined-format)
+ (:latex-footnote-separator nil nil org-latex-footnote-separator)
+ (:latex-format-drawer-function nil nil org-latex-format-drawer-function)
+ (:latex-format-headline-function nil nil org-latex-format-headline-function)
+ (:latex-format-inlinetask-function nil nil org-latex-format-inlinetask-function)
+ (:latex-hyperref-template nil nil org-latex-hyperref-template t)
+ (:latex-image-default-height nil nil org-latex-image-default-height)
+ (:latex-image-default-option nil nil org-latex-image-default-option)
+ (:latex-image-default-width nil nil org-latex-image-default-width)
+ (:latex-images-centered nil nil org-latex-images-centered)
+ (:latex-inactive-timestamp-format nil nil org-latex-inactive-timestamp-format)
+ (:latex-inline-image-rules nil nil org-latex-inline-image-rules)
+ (:latex-link-with-unknown-path-format nil nil org-latex-link-with-unknown-path-format)
+ (:latex-listings nil nil org-latex-listings)
+ (:latex-listings-langs nil nil org-latex-listings-langs)
+ (:latex-listings-options nil nil org-latex-listings-options)
+ (:latex-minted-langs nil nil org-latex-minted-langs)
+ (:latex-minted-options nil nil org-latex-minted-options)
+ (:latex-prefer-user-labels nil nil org-latex-prefer-user-labels)
+ (:latex-subtitle-format nil nil org-latex-subtitle-format)
+ (:latex-subtitle-separate nil nil org-latex-subtitle-separate)
+ (:latex-table-scientific-notation nil nil org-latex-table-scientific-notation)
+ (:latex-tables-booktabs nil nil org-latex-tables-booktabs)
+ (:latex-tables-centered nil nil org-latex-tables-centered)
+ (:latex-text-markup-alist nil nil org-latex-text-markup-alist)
+ (:latex-title-command nil nil org-latex-title-command)
+ (:latex-toc-command nil nil org-latex-toc-command)
+ (:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
+ ;; Redefine regular options.
+ (:date "DATE" nil "\\today" parse)))
@@ -164,11 +209,112 @@
("uk" . "ukrainian"))
"Alist between language code and corresponding Babel option.")
+(defconst org-latex-polyglossia-language-alist
+ '(("am" "amharic")
+ ("ast" "asturian")
+ ("ar" "arabic")
+ ("bo" "tibetan")
+ ("bn" "bengali")
+ ("bg" "bulgarian")
+ ("br" "breton")
+ ("bt-br" "brazilian")
+ ("ca" "catalan")
+ ("cop" "coptic")
+ ("cs" "czech")
+ ("cy" "welsh")
+ ("da" "danish")
+ ("de" "german" "german")
+ ("de-at" "german" "austrian")
+ ("de-de" "german" "german")
+ ("dv" "divehi")
+ ("el" "greek")
+ ("en" "english" "usmax")
+ ("en-au" "english" "australian")
+ ("en-gb" "english" "uk")
+ ("en-nz" "english" "newzealand")
+ ("en-us" "english" "usmax")
+ ("eo" "esperanto")
+ ("es" "spanish")
+ ("et" "estonian")
+ ("eu" "basque")
+ ("fa" "farsi")
+ ("fi" "finnish")
+ ("fr" "french")
+ ("fu" "friulan")
+ ("ga" "irish")
+ ("gd" "scottish")
+ ("gl" "galician")
+ ("he" "hebrew")
+ ("hi" "hindi")
+ ("hr" "croatian")
+ ("hu" "magyar")
+ ("hy" "armenian")
+ ("id" "bahasai")
+ ("ia" "interlingua")
+ ("is" "icelandic")
+ ("it" "italian")
+ ("kn" "kannada")
+ ("la" "latin" "modern")
+ ("la-modern" "latin" "modern")
+ ("la-classic" "latin" "classic")
+ ("la-medieval" "latin" "medieval")
+ ("lo" "lao")
+ ("lt" "lithuanian")
+ ("lv" "latvian")
+ ("mr" "maranthi")
+ ("ml" "malayalam")
+ ("nl" "dutch")
+ ("nb" "norsk")
+ ("nn" "nynorsk")
+ ("nko" "nko")
+ ("no" "norsk")
+ ("oc" "occitan")
+ ("pl" "polish")
+ ("pms" "piedmontese")
+ ("pt" "portuges")
+ ("rm" "romansh")
+ ("ro" "romanian")
+ ("ru" "russian")
+ ("sa" "sanskrit")
+ ("hsb" "usorbian")
+ ("dsb" "lsorbian")
+ ("sk" "slovak")
+ ("sl" "slovenian")
+ ("se" "samin")
+ ("sq" "albanian")
+ ("sr" "serbian")
+ ("sv" "swedish")
+ ("syr" "syriac")
+ ("ta" "tamil")
+ ("te" "telugu")
+ ("th" "thai")
+ ("tk" "turkmen")
+ ("tr" "turkish")
+ ("uk" "ukrainian")
+ ("ur" "urdu")
+ ("vi" "vietnamese"))
+ "Alist between language code and corresponding Polyglossia option")
+
+
+
(defconst org-latex-table-matrix-macros '(("bordermatrix" . "\\cr")
- ("qbordermatrix" . "\\cr")
- ("kbordermatrix" . "\\\\"))
+ ("qbordermatrix" . "\\cr")
+ ("kbordermatrix" . "\\\\"))
"Alist between matrix macros and their row ending.")
+(defconst org-latex-math-environments-re
+ (format
+ "\\`[ \t]*\\\\begin{%s\\*?}"
+ (regexp-opt
+ '("equation" "eqnarray" "math" "displaymath"
+ "align" "gather" "multline" "flalign" "alignat"
+ "xalignat" "xxalignat"
+ "subequations"
+ ;; breqn
+ "dmath" "dseries" "dgroup" "darray"
+ ;; empheq
+ "empheq")))
+ "Regexp of LaTeX math environments.")
;;; User Configurable Variables
@@ -178,6 +324,79 @@
:tag "Org Export LaTeX"
:group 'org-export)
+;;;; Generic
+
+(defcustom org-latex-caption-above '(table)
+ "When non-nil, place caption string at the beginning of elements.
+Otherwise, place it near the end. When value is a list of
+symbols, put caption above selected elements only. Allowed
+symbols are: `image', `table', `src-block' and `special-block'."
+ :group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "8.3")
+ :type '(choice
+ (const :tag "For all elements" t)
+ (const :tag "For no element" nil)
+ (set :tag "For the following elements only" :greedy t
+ (const :tag "Images" image)
+ (const :tag "Tables" table)
+ (const :tag "Source code" src-block)
+ (const :tag "Special blocks" special-block))))
+
+(defcustom org-latex-prefer-user-labels nil
+ "Use user-provided labels instead of internal ones when non-nil.
+
+When this variable is non-nil, Org will use the value of
+CUSTOM_ID property, NAME keyword or Org target as the key for the
+\\label commands generated.
+
+By default, Org generates its own internal labels during LaTeX
+export. This process ensures that the \\label keys are unique
+and valid, but it means the keys are not available in advance of
+the export process.
+
+Setting this variable gives you control over how Org generates
+labels during LaTeX export, so that you may know their keys in
+advance. One reason to do this is that it allows you to refer to
+various elements using a single label both in Org's link syntax
+and in embedded LaTeX code.
+
+For example, when this variable is non-nil, a headline like this:
+
+ ** Some section
+ :PROPERTIES:
+ :CUSTOM_ID: sec:foo
+ :END:
+ This is section [[#sec:foo]].
+ #+BEGIN_EXPORT latex
+ And this is still section \\ref{sec:foo}.
+ #+END_EXPORT
+
+will be exported to LaTeX as:
+
+ \\subsection{Some section}
+ \\label{sec:foo}
+ This is section \\ref{sec:foo}.
+ And this is still section \\ref{sec:foo}.
+
+Note, however, that setting this variable introduces a limitation
+on the possible values for CUSTOM_ID and NAME. When this
+variable is non-nil, Org passes their value to \\label unchanged.
+You are responsible for ensuring that the value is a valid LaTeX
+\\label key, and that no other \\label commands with the same key
+appear elsewhere in your document. (Keys may contain letters,
+numbers, and the following punctuation: '_' '.' '-' ':'.) There
+are no such limitations on CUSTOM_ID and NAME when this variable
+is nil.
+
+For headlines that do not define the CUSTOM_ID property or
+elements without a NAME, Org will continue to use its default
+labeling scheme to generate labels and resolve links into proper
+references."
+ :group 'org-export-latex
+ :type 'boolean
+ :version "26.1"
+ :package-version '(Org . "8.3"))
;;;; Preamble
@@ -264,11 +483,15 @@ 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.
-Likewise, if your header contains \"\\usepackage[AUTO]{babel}\",
-AUTO will be replaced with the language related to the language
-code specified by `org-export-default-language', which see. Note
-that constructions such as \"\\usepackage[french,AUTO,english]{babel}\"
-are permitted.
+Likewise, if your header contains \"\\usepackage[AUTO]{babel}\"
+or \"\\usepackage[AUTO]{polyglossia}\", AUTO will be replaced
+with the language related to the language code specified by
+`org-export-default-language'. Note that constructions such as
+\"\\usepackage[french,AUTO,english]{babel}\" are permitted. For
+Polyglossia the language will be set via the macros
+\"\\setmainlanguage\" and \"\\setotherlanguage\". See also
+`org-latex-guess-babel-language' and
+`org-latex-guess-polyglossia-language'.
The sectioning structure
------------------------
@@ -328,11 +551,42 @@ are written as utf8 files."
(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."
+
+This format string may contain these elements:
+
+ %a for AUTHOR keyword
+ %t for TITLE keyword
+ %s for SUBTITLE keyword
+ %k for KEYWORDS line
+ %d for DESCRIPTION line
+ %c for CREATOR line
+ %l for Language keyword
+ %L for capitalized language keyword
+ %D for DATE keyword
+
+If you need to use a \"%\" character, you need to escape it
+like that: \"%%\".
+
+Setting :latex-title-command in publishing projects will take
+precedence over this variable."
:group 'org-export-latex
- :type 'string)
+ :type '(string :tag "Format string"))
+
+(defcustom org-latex-subtitle-format "\\\\\\medskip\n\\large %s"
+ "Format string used for transcoded subtitle.
+The format string should have at most one \"%s\"-expression,
+which is replaced with the subtitle."
+ :group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "8.3")
+ :type '(string :tag "Format string"))
+
+(defcustom org-latex-subtitle-separate nil
+ "Non-nil means the subtitle is not typeset as part of title."
+ :group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "8.3")
+ :type 'boolean)
(defcustom org-latex-toc-command "\\tableofcontents\n\n"
"LaTeX command to set the table of contents, list of figures, etc.
@@ -341,10 +595,36 @@ the toc:nil option, not to those generated with #+TOC keyword."
:group 'org-export-latex
:type 'string)
-(defcustom org-latex-with-hyperref t
- "Toggle insertion of \\hypersetup{...} in the preamble."
+(defcustom org-latex-hyperref-template
+ "\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k},
+ pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L}}\n"
+ "Template for hyperref package options.
+
+This format string may contain these elements:
+
+ %a for AUTHOR keyword
+ %t for TITLE keyword
+ %s for SUBTITLE keyword
+ %k for KEYWORDS line
+ %d for DESCRIPTION line
+ %c for CREATOR line
+ %l for Language keyword
+ %L for capitalized language keyword
+ %D for DATE keyword
+
+If you need to use a \"%\" character, you need to escape it
+like that: \"%%\".
+
+As a special case, a nil value prevents template from being
+inserted.
+
+Setting :latex-hyperref-template in publishing projects will take
+precedence over this variable."
:group 'org-export-latex
- :type 'boolean)
+ :version "26.1"
+ :package-version '(Org . "8.3")
+ :type '(choice (const :tag "No template" nil)
+ (string :tag "Format string")))
;;;; Headline
@@ -352,17 +632,15 @@ the toc:nil option, not to those generated with #+TOC keyword."
'org-latex-format-headline-default-function
"Function for formatting the headline's text.
-This function will be called with 5 arguments:
-TODO the todo keyword (string or nil).
+This function will be called with six 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 as a list of strings (list of strings or nil).
-
-The function result will be used in the section format string.
+TEXT the main headline text (string)
+TAGS the tags (list of strings or nil)
+INFO the export options (plist)
-Use `org-latex-format-headline-default-function' by default,
-which format headlines like for Org version prior to 8.0."
+The function result will be used in the section format string."
:group 'org-export-latex
:version "24.4"
:package-version '(Org . "8.0")
@@ -376,6 +654,16 @@ which format headlines like for Org version prior to 8.0."
:group 'org-export-latex
:type 'string)
+(defcustom org-latex-footnote-defined-format "\\textsuperscript{\\ref{%s}}"
+ "Format string used to format reference to footnote already defined.
+%s will be replaced by the label of the referred footnote."
+ :group 'org-export-latex
+ :type '(choice
+ (const :tag "Use plain superscript (default)" "\\textsuperscript{\\ref{%s}}")
+ (const :tag "Use Memoir/KOMA-Script footref" "\\footref{%s}")
+ (string :tag "Other format string"))
+ :version "26.1"
+ :package-version '(Org . "9.0"))
;;;; Timestamps
@@ -397,6 +685,14 @@ which format headlines like for Org version prior to 8.0."
;;;; Links
+(defcustom org-latex-images-centered t
+ "When non-nil, images are centered."
+ :group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "9.0")
+ :type 'boolean
+ :safe #'booleanp)
+
(defcustom org-latex-image-default-option ""
"Default option for images."
:group 'org-export-latex
@@ -422,13 +718,17 @@ environment."
:package-version '(Org . "8.0")
:type 'string)
-(defcustom org-latex-default-figure-position "htb"
- "Default position for latex figures."
+(defcustom org-latex-default-figure-position "htbp"
+ "Default position for LaTeX figures."
:group 'org-export-latex
- :type 'string)
+ :type 'string
+ :version "26.1"
+ :package-version '(Org . "9.0")
+ :safe #'stringp)
(defcustom org-latex-inline-image-rules
- '(("file" . "\\.\\(pdf\\|jpeg\\|jpg\\|png\\|ps\\|eps\\|tikz\\|pgf\\|svg\\)\\'"))
+ `(("file" . ,(regexp-opt
+ '("pdf" "jpeg" "jpg" "png" "ps" "eps" "tikz" "pgf" "svg"))))
"Rules characterizing image files that can be inlined into LaTeX.
A rule consists in an association whose key is the type of link
@@ -489,12 +789,14 @@ When modifying this variable, it may be useful to change
:type '(choice (const :tag "Table" table)
(const :tag "Matrix" math)
(const :tag "Inline matrix" inline-math)
- (const :tag "Verbatim" verbatim)))
+ (const :tag "Verbatim" verbatim))
+ :safe (lambda (s) (memq s '(table math inline-math verbatim))))
(defcustom org-latex-tables-centered t
"When non-nil, tables are exported in a center environment."
:group 'org-export-latex
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-latex-tables-booktabs nil
"When non-nil, display tables in a formal \"booktabs\" style.
@@ -505,13 +807,8 @@ attributes."
:group 'org-export-latex
:version "24.4"
:package-version '(Org . "8.0")
- :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)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-latex-table-scientific-notation "%s\\,(%s)"
"Format string to display numbers in scientific notation.
@@ -526,11 +823,10 @@ When nil, no transformation is made."
(string :tag "Format string")
(const :tag "No formatting" nil)))
-
;;;; Text markup
(defcustom org-latex-text-markup-alist '((bold . "\\textbf{%s}")
- (code . verb)
+ (code . protectedtexttt)
(italic . "\\emph{%s}")
(strike-through . "\\sout{%s}")
(underline . "\\uline{%s}")
@@ -550,14 +846,15 @@ to typeset and try to protect special characters.
If no association can be found for a given markup, text will be
returned as-is."
:group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "8.3")
:type 'alist
:options '(bold code italic strike-through underline verbatim))
;;;; Drawers
-(defcustom org-latex-format-drawer-function
- (lambda (name contents) contents)
+(defcustom org-latex-format-drawer-function (lambda (_ contents) contents)
"Function called to format a drawer in LaTeX code.
The function must accept two parameters:
@@ -568,51 +865,31 @@ The function should return the string to be exported.
The default function simply returns the value of CONTENTS."
:group 'org-export-latex
- :version "24.4"
+ :version "26.1"
:package-version '(Org . "8.3")
:type 'function)
;;;; Inlinetasks
-(defcustom org-latex-format-inlinetask-function 'ignore
+(defcustom org-latex-format-inlinetask-function
+ 'org-latex-format-inlinetask-default-function
"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 list of strings.
- CONTENTS the contents of the inlinetask, as a string.
-
-The function should return the string to be exported.
+The function must accept seven parameters:
+ TODO the todo keyword (string or nil)
+ TODO-TYPE the todo type (symbol: `todo', `done', nil)
+ PRIORITY the inlinetask priority (integer or nil)
+ NAME the inlinetask name (string)
+ TAGS the inlinetask tags (list of strings or nil)
+ CONTENTS the contents of the inlinetask (string or nil)
+ INFO the export options (plist)
-For example, the variable could be set to the following function
-in order to mimic default behavior:
-
-\(defun org-latex-format-inlinetask (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:}\"
- (mapconcat \\='identity 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))"
+The function should return the string to be exported."
:group 'org-export-latex
- :type 'function)
+ :type 'function
+ :version "26.1"
+ :package-version '(Org . "8.3"))
;; Src blocks
@@ -640,7 +917,7 @@ the minted package to `org-latex-packages-alist', for example
using customize, or with
(require \\='ox-latex)
- (add-to-list \\='org-latex-packages-alist \\='(\"\" \"minted\"))
+ (add-to-list \\='org-latex-packages-alist \\='(\"newfloat\" \"minted\"))
In addition, it is necessary to install pygments
\(http://pygments.org), and to configure the variable
@@ -656,7 +933,8 @@ into previewing problems, please consult
:type '(choice
(const :tag "Use listings" t)
(const :tag "Use minted" minted)
- (const :tag "Export verbatim" nil)))
+ (const :tag "Export verbatim" nil))
+ :safe (lambda (s) (memq s '(t nil minted))))
(defcustom org-latex-listings-langs
'((emacs-lisp "Lisp") (lisp "Lisp") (clojure "Lisp")
@@ -668,7 +946,9 @@ into previewing problems, please consult
(shell-script "bash")
(gnuplot "Gnuplot")
(ocaml "Caml") (caml "Caml")
- (sql "SQL") (sqlite "sql"))
+ (sql "SQL") (sqlite "sql")
+ (makefile "make")
+ (R "r"))
"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
@@ -676,6 +956,8 @@ 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
+ :version "26.1"
+ :package-version '(Org . "8.3")
:type '(repeat
(list
(symbol :tag "Major mode ")
@@ -697,7 +979,13 @@ 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."
+languages. If you need block-specific options, you may use the
+following syntax:
+
+ #+ATTR_LATEX: :options key1=value1,key2=value2
+ #+BEGIN_SRC <LANG>
+ ...
+ #+END_SRC"
:group 'org-export-latex
:type '(repeat
(list
@@ -744,41 +1032,132 @@ will result in src blocks being exported with
\\begin{minted}[bgcolor=bg,frame=lines]{<LANG>}
as the start of the minted environment. Note that the same
-options will be applied to blocks of all languages."
+options will be applied to blocks of all languages. If you need
+block-specific options, you may use the following syntax:
+
+ #+ATTR_LATEX: :options key1=value1,key2=value2
+ #+BEGIN_SRC <LANG>
+ ...
+ #+END_SRC"
:group 'org-export-latex
:type '(repeat
(list
(string :tag "Minted option name ")
(string :tag "Minted option value"))))
-(defvar org-latex-custom-lang-environments nil
+(defcustom org-latex-custom-lang-environments nil
"Alist mapping languages to language-specific LaTeX environments.
It is used during export of src blocks by the listings and minted
-latex packages. For example,
+latex packages. The environment may be a simple string, composed of
+only letters and numbers. In this case, the string is directly the
+name of the latex environment to use. The environment may also be
+a format string. In this case the format string will be directly
+exported. This format string may contain these elements:
+
+ %s for the formatted source
+ %c for the caption
+ %f for the float attribute
+ %l for an appropriate label
+ %o for the LaTeX attributes
+
+For example,
(setq org-latex-custom-lang-environments
- \\='((python \"pythoncode\")))
+ \\='((python \"pythoncode\")
+ (ocaml \"\\\\begin{listing}
+\\\\begin{minted}[%o]{ocaml}
+%s\\\\end{minted}
+\\\\caption{%c}
+\\\\label{%l}\")))
-would have the effect that if org encounters begin_src python
-during latex export it will output
+would have the effect that if Org encounters a Python source block
+during LaTeX export it will produce
\\begin{pythoncode}
<src block body>
- \\end{pythoncode}")
+ \\end{pythoncode}
+
+and if Org encounters an Ocaml source block during LaTeX export it
+will produce
+
+ \\begin{listing}
+ \\begin{minted}[<attr_latex options>]{ocaml}
+ <src block body>
+ \\end{minted}
+ \\caption{<caption>}
+ \\label{<label>}
+ \\end{listing}"
+ :group 'org-export-latex
+ :type '(repeat
+ (list
+ (symbol :tag "Language name ")
+ (string :tag "Environment name or format string")))
+ :version "26.1"
+ :package-version '(Org . "9.0"))
;;;; Compilation
+(defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n"
+ "LaTeX compiler format-string.
+See also `org-latex-compiler'."
+ :group 'org-export-latex
+ :type '(choice
+ (const :tag "Comment" "%% Intended LaTeX compiler: %s\n")
+ (const :tag "latex-mode file variable" "%% -*- latex-run-command: %s -*-\n")
+ (const :tag "AUCTeX file variable" "%% -*- LaTeX-command: %s -*-\n")
+ (string :tag "custom format" "%% %s"))
+ :version "26.1"
+ :package-version '(Org . "9.0"))
+
+(defcustom org-latex-compiler "pdflatex"
+ "LaTeX compiler to use.
+
+Must be an element in `org-latex-compilers' or the empty quote.
+Can also be set in buffers via #+LATEX_COMPILER. See also
+`org-latex-compiler-file-string'."
+ :group 'org-export-latex
+ :type '(choice
+ (const :tag "pdfLaTeX" "pdflatex")
+ (const :tag "XeLaTeX" "xelatex")
+ (const :tag "LuaLaTeX" "lualatex")
+ (const :tag "Unset" ""))
+ :version "26.1"
+ :package-version '(Org . "9.0"))
+
+(defconst org-latex-compilers '("pdflatex" "xelatex" "lualatex")
+ "Known LaTeX compilers.
+See also `org-latex-compiler'.")
+
+(defcustom org-latex-bib-compiler "bibtex"
+ "Command to process a LaTeX file's bibliography.
+
+The shorthand %bib in `org-latex-pdf-process' is replaced with
+this value.
+
+A better approach is to use a compiler suit such as `latexmk'."
+ :group 'org-export-latex
+ :type '(choice (const :tag "BibTeX" "bibtex")
+ (const :tag "Biber" "biber")
+ (string :tag "Other process"))
+ :version "26.1"
+ :package-version '(Org . "9.0"))
+
(defcustom org-latex-pdf-process
- '("pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f")
+ '("%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f")
"Commands to process a LaTeX file to a PDF file.
+
This is a list of strings, each of them will be given to the
shell as a command. %f in the command will be replaced by the
-full file name, %b by the file base name (i.e. without directory
-and extension parts) and %o by the base directory of the file.
+relative file name, %F by the absolute file name, %b by the file
+base name (i.e. without directory and extension parts), %o by the
+base directory of the file, %O by the absolute file name of the
+output file, %latex is the LaTeX compiler (see
+`org-latex-compiler'), and %bib is the BibTeX-like compiler (see
+`org-latex-bib-compiler').
The reason why this is a list is that it usually takes several
runs of `pdflatex', maybe mixed with a call to `bibtex'. Org
@@ -786,18 +1165,8 @@ does not have a clever mechanism to detect which of these
commands have to be run to get to a stable result, and it also
does not do any error checking.
-By default, Org uses 3 runs of `pdflatex' to do the processing.
-If you have texi2dvi on your system and if that does not cause
-the infamous egrep/locale bug:
-
- http://lists.gnu.org/archive/html/bug-texinfo/2010-03/msg00031.html
-
-then `texi2dvi' is the superior choice as it automates the LaTeX
-build process by calling the \"correct\" combinations of
-auxiliary programs. Org does offer `texi2dvi' as one of the
-customize options. Alternatively, `rubber' and `latexmk' also
-provide similar functionality. The latter supports `biber' out
-of the box.
+Consider a smart LaTeX compiler such as `texi2dvi' or `latexmk',
+which calls the \"correct\" combinations of auxiliary programs.
Alternatively, this may be a Lisp function that does the
processing, so you could use this to apply the machinery of
@@ -807,44 +1176,33 @@ file name as its single argument."
:type '(choice
(repeat :tag "Shell command sequence"
(string :tag "Shell command"))
- (const :tag "2 runs of pdflatex"
- ("pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f"))
- (const :tag "3 runs of pdflatex"
- ("pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f"))
- (const :tag "pdflatex,bibtex,pdflatex,pdflatex"
- ("pdflatex -interaction nonstopmode -output-directory %o %f"
- "bibtex %b"
- "pdflatex -interaction nonstopmode -output-directory %o %f"
- "pdflatex -interaction nonstopmode -output-directory %o %f"))
- (const :tag "2 runs of xelatex"
- ("xelatex -interaction nonstopmode -output-directory %o %f"
- "xelatex -interaction nonstopmode -output-directory %o %f"))
- (const :tag "3 runs of xelatex"
- ("xelatex -interaction nonstopmode -output-directory %o %f"
- "xelatex -interaction nonstopmode -output-directory %o %f"
- "xelatex -interaction nonstopmode -output-directory %o %f"))
- (const :tag "xelatex,bibtex,xelatex,xelatex"
- ("xelatex -interaction nonstopmode -output-directory %o %f"
- "bibtex %b"
- "xelatex -interaction nonstopmode -output-directory %o %f"
- "xelatex -interaction nonstopmode -output-directory %o %f"))
+ (const :tag "2 runs of latex"
+ ("%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f"))
+ (const :tag "3 runs of latex"
+ ("%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f"))
+ (const :tag "latex,bibtex,latex,latex"
+ ("%latex -interaction nonstopmode -output-directory %o %f"
+ "%bib %b"
+ "%latex -interaction nonstopmode -output-directory %o %f"
+ "%latex -interaction nonstopmode -output-directory %o %f"))
(const :tag "texi2dvi"
- ("texi2dvi -p -b -V %f"))
- (const :tag "rubber"
- ("rubber -d --into %o %f"))
+ ("cd %o; LATEX=\"%latex\" texi2dvi -p -b -V %b.tex"))
(const :tag "latexmk"
- ("latexmk -g -pdf %f"))
+ ("latexmk -g -pdf -pdflatex=\"%latex\" -outdir=%o %f"))
(function)))
(defcustom org-latex-logfiles-extensions
- '("aux" "idx" "log" "out" "toc" "nav" "snm" "vrb")
+ '("aux" "bcf" "blg" "fdb_latexmk" "fls" "figlist" "idx" "log" "nav" "out"
+ "ptc" "run.xml" "snm" "toc" "vrb" "xdv")
"The list of file extensions to consider as LaTeX logfiles.
-The logfiles will be remove if `org-latex-remove-logfiles' is
+The logfiles will be removed if `org-latex-remove-logfiles' is
non-nil."
:group 'org-export-latex
+ :version "26.1"
+ :package-version '(Org . "8.3")
:type '(repeat (string :tag "Extension")))
(defcustom org-latex-remove-logfiles t
@@ -855,19 +1213,20 @@ logfiles to remove, set `org-latex-logfiles-extensions'."
:group 'org-export-latex
:type 'boolean)
-(defcustom org-latex-known-errors
- '(("Reference.*?undefined" . "[undefined reference]")
- ("Citation.*?undefined" . "[undefined citation]")
- ("Undefined control sequence" . "[undefined control sequence]")
- ("^! LaTeX.*?Error" . "[LaTeX error]")
- ("^! Package.*?Error" . "[package error]")
- ("Runaway argument" . "Runaway argument"))
+(defcustom org-latex-known-warnings
+ '(("Reference.*?undefined" . "[undefined reference]")
+ ("Runaway argument" . "[runaway argument]")
+ ("Underfull \\hbox" . "[underfull hbox]")
+ ("Overfull \\hbox" . "[overfull hbox]")
+ ("Citation.*?undefined" . "[undefined citation]")
+ ("Undefined control sequence" . "[undefined control sequence]"))
"Alist of regular expressions and associated messages for the user.
-The regular expressions are used to find possible errors in the
-log of a latex-run."
+The regular expressions are used to find possible warnings in the
+log of a latex-run. These warnings will be reported after
+calling `org-latex-compile'."
:group 'org-export-latex
- :version "24.4"
- :package-version '(Org . "8.0")
+ :version "26.1"
+ :package-version '(Org . "8.3")
:type '(repeat
(cons
(string :tag "Regexp")
@@ -877,6 +1236,54 @@ log of a latex-run."
;;; Internal Functions
+(defun org-latex--caption-above-p (element info)
+ "Non nil when caption is expected to be located above ELEMENT.
+INFO is a plist holding contextual information."
+ (let ((above (plist-get info :latex-caption-above)))
+ (if (symbolp above) above
+ (let ((type (org-element-type element)))
+ (memq (if (eq type 'link) 'image type) above)))))
+
+(defun org-latex--label (datum info &optional force full)
+ "Return an appropriate label for DATUM.
+DATUM is an element or a `target' type object. INFO is the
+current export state, as a plist.
+
+Return nil if element DATUM has no NAME or VALUE affiliated
+keyword or no CUSTOM_ID property, unless FORCE is non-nil. In
+this case always return a unique label.
+
+Eventually, if FULL is non-nil, wrap label within \"\\label{}\"."
+ (let* ((type (org-element-type datum))
+ (user-label
+ (org-element-property
+ (cl-case type
+ ((headline inlinetask) :CUSTOM_ID)
+ (target :value)
+ (otherwise :name))
+ datum))
+ (label
+ (and (or user-label force)
+ (if (and user-label (plist-get info :latex-prefer-user-labels))
+ user-label
+ (concat (cl-case type
+ (headline "sec:")
+ (table "tab:")
+ (latex-environment
+ (and (string-match-p
+ org-latex-math-environments-re
+ (org-element-property :value datum))
+ "eq:"))
+ (paragraph
+ (and (org-element-property :caption datum)
+ "fig:")))
+ (org-export-get-reference datum info))))))
+ (cond ((not full) label)
+ (label (format "\\label{%s}%s"
+ label
+ (if (eq type 'target) "" "\n")))
+ (t ""))))
+
(defun org-latex--caption/label-string (element info)
"Return caption and label LaTeX string for ELEMENT.
@@ -884,25 +1291,43 @@ 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* ((label (org-element-property :name element))
- (label-str (if (not (org-string-nw-p label)) ""
- (format "\\label{%s}"
- (org-export-solidify-link-text label))))
+ (let* ((label (org-latex--label element info nil t))
(main (org-export-get-caption element))
+ (attr (org-export-read-attribute :attr_latex element))
+ (type (org-element-type element))
+ (nonfloat (or (and (plist-member attr :float)
+ (not (plist-get attr :float))
+ main)
+ (and (eq type 'src-block)
+ (not (plist-get attr :float))
+ (null (plist-get info :latex-listings)))))
(short (org-export-get-caption element t))
- (caption-from-attr-latex (org-export-read-attribute :attr_latex element :caption)))
+ (caption-from-attr-latex (plist-get attr :caption)))
(cond
((org-string-nw-p caption-from-attr-latex)
(concat caption-from-attr-latex "\n"))
- ((and (not main) (equal label-str "")) "")
- ((not main) (concat label-str "\n"))
+ ((and (not main) (equal label "")) "")
+ ((not main) label)
;; Option caption format with short name.
- (short (format "\\caption[%s]{%s%s}\n"
- (org-export-data short info)
- label-str
- (org-export-data main info)))
- ;; Standard caption format.
- (t (format "\\caption{%s%s}\n" label-str (org-export-data main info))))))
+ (t
+ (format (if nonfloat "\\captionof{%s}%s{%s%s}\n"
+ "\\caption%s%s{%s%s}\n")
+ (let ((type* (if (eq type 'latex-environment)
+ (org-latex--environment-type element)
+ type)))
+ (if nonfloat
+ (cl-case type*
+ (paragraph "figure")
+ (image "figure")
+ (special-block "figure")
+ (src-block (if (plist-get info :latex-listings)
+ "listing"
+ "figure"))
+ (t (symbol-name type*)))
+ ""))
+ (if short (format "[%s]" (org-export-data short info)) "")
+ label
+ (org-export-data main info))))))
(defun org-latex-guess-inputenc (header)
"Set the coding system in inputenc to what the buffer is.
@@ -945,8 +1370,8 @@ Return the new header."
header
(let ((options (save-match-data
(org-split-string (match-string 1 header) ",[ \t]*")))
- (language (cdr (assoc language-code
- org-latex-babel-language-alist))))
+ (language (cdr (assoc-string language-code
+ org-latex-babel-language-alist t))))
;; If LANGUAGE is already loaded, return header without AUTO.
;; Otherwise, replace AUTO with language or append language if
;; AUTO is not present.
@@ -958,13 +1383,90 @@ Return the new header."
", ")
t nil header 1)))))
+(defun org-latex-guess-polyglossia-language (header info)
+ "Set the Polyglossia language according to the LANGUAGE keyword.
+
+HEADER is the LaTeX header string. INFO is the plist used as
+a communication channel.
+
+Insertion of guessed language only happens when the Polyglossia
+package has been explicitly loaded.
+
+The argument to Polyglossia may be \"AUTO\" which is then
+replaced with the language of the document or
+`org-export-default-language'. Note, the language is really set
+using \setdefaultlanguage and not as an option to the package.
+
+Return the new header."
+ (let ((language (plist-get info :language)))
+ ;; If no language is set or Polyglossia is not loaded, return
+ ;; HEADER as-is.
+ (if (or (not (stringp language))
+ (not (string-match
+ "\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
+ header)))
+ header
+ (let* ((options (org-string-nw-p (match-string 1 header)))
+ (languages (and options
+ ;; Reverse as the last loaded language is
+ ;; the main language.
+ (nreverse
+ (delete-dups
+ (save-match-data
+ (org-split-string
+ (replace-regexp-in-string
+ "AUTO" language options t)
+ ",[ \t]*"))))))
+ (main-language-set
+ (string-match-p "\\\\setmainlanguage{.*?}" header)))
+ (replace-match
+ (concat "\\usepackage{polyglossia}\n"
+ (mapconcat
+ (lambda (l)
+ (let ((l (or (assoc l org-latex-polyglossia-language-alist)
+ l)))
+ (format (if main-language-set "\\setotherlanguage%s{%s}\n"
+ (setq main-language-set t)
+ "\\setmainlanguage%s{%s}\n")
+ (if (and (consp l) (= (length l) 3))
+ (format "[variant=%s]" (nth 2 l))
+ "")
+ (nth 1 l))))
+ languages
+ ""))
+ t t header 0)))))
+
+(defun org-latex--remove-packages (pkg-alist info)
+ "Remove packages based on the current LaTeX compiler.
+
+If the fourth argument of an element is set in pkg-alist, and it
+is not a member of the LaTeX compiler of the document, the packages
+is removed. See also `org-latex-compiler'.
+
+Return modified pkg-alist."
+ (let ((compiler (or (plist-get info :latex-compiler) "")))
+ (if (member-ignore-case compiler org-latex-compilers)
+ (delq nil
+ (mapcar
+ (lambda (pkg)
+ (unless (and
+ (listp pkg)
+ (let ((third (nth 3 pkg)))
+ (and third
+ (not (member-ignore-case
+ compiler
+ (if (listp third) third (list third)))))))
+ pkg))
+ pkg-alist))
+ pkg-alist)))
+
(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))))
+ (cl-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.
@@ -972,135 +1474,203 @@ 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)))))
+ (pcase-let ((`(,keyword ,value) pair))
+ (concat keyword
+ (and (> (length value) 0)
+ (concat "=" value)))))
options
","))
-(defun org-latex--wrap-label (element output)
+(defun org-latex--wrap-label (element output info)
"Wrap label associated to ELEMENT around OUTPUT, if appropriate.
-This function shouldn't be used for floats. See
+INFO is the current export state, as a plist. This function
+should not be used for floats. See
`org-latex--caption/label-string'."
- (let ((label (org-element-property :name element)))
- (if (not (and (org-string-nw-p output) (org-string-nw-p label))) output
- (concat (format "\\label{%s}\n" (org-export-solidify-link-text label))
- output))))
+ (if (not (and (org-string-nw-p output) (org-element-property :name element)))
+ output
+ (concat (format "\\phantomsection\n\\label{%s}\n"
+ (org-latex--label element info))
+ output)))
+
+(defun org-latex--protect-text (text)
+ "Protect special characters in string TEXT and return it."
+ (replace-regexp-in-string "[\\{}$%&_#~^]" "\\\\\\&" text))
-(defun org-latex--text-markup (text markup)
+(defun org-latex--text-markup (text markup info)
"Format TEXT depending on MARKUP text markup.
-See `org-latex-text-markup-alist' for details."
- (let ((fmt (cdr (assq markup org-latex-text-markup-alist))))
- (cond
- ;; No format string: Return raw text.
- ((not fmt) text)
- ;; Handle the `verb' special case: Find and appropriate separator
- ;; and use "\\verb" command.
- ((eq 'verb fmt)
- (let ((separator (org-latex--find-verb-separator text)))
- (concat "\\verb" separator
- (replace-regexp-in-string "\n" " " text)
- separator)))
- ;; Handle the `protectedtexttt' special case: Protect some
- ;; special chars and use "\texttt{%s}" format string.
- ((eq 'protectedtexttt fmt)
- (let ((start 0)
- (trans '(("\\" . "\\textbackslash{}")
- ("~" . "\\textasciitilde{}")
- ("^" . "\\textasciicircum{}")))
- (rtn "")
- char)
- (while (string-match "[\\{}$%&_#~^]" text)
- (setq char (match-string 0 text))
- (if (> (match-beginning 0) 0)
- (setq rtn (concat rtn (substring text 0 (match-beginning 0)))))
- (setq text (substring text (1+ (match-beginning 0))))
- (setq char (or (cdr (assoc char trans)) (concat "\\" char))
- rtn (concat rtn char)))
- (setq text (concat rtn text)
- fmt "\\texttt{%s}")
- (while (string-match "--" text)
- (setq text (replace-match "-{}-" t t text)))
- (format fmt text)))
- ;; Else use format string.
- (t (format fmt text)))))
+INFO is a plist used as a communication channel. See
+`org-latex-text-markup-alist' for details."
+ (let ((fmt (cdr (assq markup (plist-get info :latex-text-markup-alist)))))
+ (cl-case fmt
+ ;; No format string: Return raw text.
+ ((nil) text)
+ ;; Handle the `verb' special case: Find an appropriate separator
+ ;; and use "\\verb" command.
+ (verb
+ (let ((separator (org-latex--find-verb-separator text)))
+ (concat "\\verb"
+ separator
+ (replace-regexp-in-string "\n" " " text)
+ separator)))
+ ;; Handle the `protectedtexttt' special case: Protect some
+ ;; special chars and use "\texttt{%s}" format string.
+ (protectedtexttt
+ (format "\\texttt{%s}"
+ (replace-regexp-in-string
+ "--\\|[\\{}$%&_#~^]"
+ (lambda (m)
+ (cond ((equal m "--") "-{}-")
+ ((equal m "\\") "\\textbackslash{}")
+ ((equal m "~") "\\textasciitilde{}")
+ ((equal m "^") "\\textasciicircum{}")
+ (t (org-latex--protect-text m))))
+ text nil t)))
+ ;; Else use format string.
+ (t (format fmt text)))))
(defun org-latex--delayed-footnotes-definitions (element info)
"Return footnotes definitions in ELEMENT as a string.
INFO is a plist used as a communication channel.
-Footnotes definitions are returned within \"\\footnotetxt{}\"
+Footnotes definitions are returned within \"\\footnotetext{}\"
commands.
This function is used within constructs that don't support
-\"\\footnote{}\" command (i.e. an item's tag). In that case,
+\"\\footnote{}\" command (e.g., an item tag). In that case,
\"\\footnotemark\" is used within the construct and the function
just outside of it."
(mapconcat
(lambda (ref)
- (format
- "\\footnotetext[%s]{%s}"
- (org-export-get-footnote-number ref info)
- (org-trim
- (org-export-data
- (org-export-get-footnote-definition ref info) info))))
+ (let ((def (org-export-get-footnote-definition ref info)))
+ (format "\\footnotetext[%d]{%s%s}"
+ (org-export-get-footnote-number ref info)
+ (org-trim (org-latex--label def info t t))
+ (org-trim (org-export-data def info)))))
;; Find every footnote reference in ELEMENT.
- (let* (all-refs
- search-refs ; For byte-compiler.
- (search-refs
- (function
- (lambda (data)
- ;; Return a list of all footnote references never seen
- ;; before in DATA.
- (org-element-map data 'footnote-reference
- (lambda (ref)
- (when (org-export-footnote-first-reference-p ref info)
- (push ref all-refs)
- (when (eq (org-element-property :type ref) 'standard)
- (funcall search-refs
- (org-export-get-footnote-definition ref info)))))
- info)
- (reverse all-refs)))))
+ (letrec ((all-refs nil)
+ (search-refs
+ (lambda (data)
+ ;; Return a list of all footnote references never seen
+ ;; before in DATA.
+ (org-element-map data 'footnote-reference
+ (lambda (ref)
+ (when (org-export-footnote-first-reference-p ref info)
+ (push ref all-refs)
+ (when (eq (org-element-property :type ref) 'standard)
+ (funcall search-refs
+ (org-export-get-footnote-definition ref info)))))
+ info)
+ (reverse all-refs))))
(funcall search-refs element))
""))
+(defun org-latex--translate (s info)
+ "Translate string S according to specified language.
+INFO is a plist used as a communication channel."
+ (org-export-translate s :latex info))
+
+(defun org-latex--format-spec (info)
+ "Create a format-spec for document meta-data.
+INFO is a plist used as a communication channel."
+ (let ((language (let ((lang (plist-get info :language)))
+ (or (cdr (assoc-string lang org-latex-babel-language-alist t))
+ (nth 1 (assoc-string lang org-latex-polyglossia-language-alist t))
+ lang))))
+ `((?a . ,(org-export-data (plist-get info :author) info))
+ (?t . ,(org-export-data (plist-get info :title) info))
+ (?k . ,(org-export-data (org-latex--wrap-latex-math-block
+ (plist-get info :keywords) info)
+ info))
+ (?d . ,(org-export-data (org-latex--wrap-latex-math-block
+ (plist-get info :description) info)
+ info))
+ (?c . ,(plist-get info :creator))
+ (?l . ,language)
+ (?L . ,(capitalize language))
+ (?D . ,(org-export-get-date info)))))
+
+(defun org-latex--insert-compiler (info)
+ "Insert LaTeX_compiler info into the document.
+INFO is a plist used as a communication channel."
+ (let ((compiler (plist-get info :latex-compiler)))
+ (and (org-string-nw-p org-latex-compiler-file-string)
+ (member (or compiler "") org-latex-compilers)
+ (format org-latex-compiler-file-string compiler))))
+
+
+;;; Filters
+
+(defun org-latex-matrices-tree-filter (tree _backend info)
+ (org-latex--wrap-latex-matrices tree info))
+
+(defun org-latex-math-block-tree-filter (tree _backend info)
+ (org-latex--wrap-latex-math-block tree info))
+
+(defun org-latex-math-block-options-filter (info _backend)
+ (dolist (prop '(:author :date :title) info)
+ (plist-put info prop
+ (org-latex--wrap-latex-math-block (plist-get info prop) info))))
+
+(defun org-latex-clean-invalid-line-breaks (data _backend _info)
+ (replace-regexp-in-string
+ "\\(\\end{[A-Za-z0-9*]+}\\|^\\)[ \t]*\\\\\\\\[ \t]*$" "\\1"
+ data))
;;; Template
+;;;###autoload
+(defun org-latex-make-preamble (info &optional template snippet?)
+ "Return a formatted LaTeX preamble.
+INFO is a plist used as a communication channel. Optional
+argument TEMPLATE, when non-nil, is the header template string,
+as expected by `org-splice-latex-header'. When SNIPPET? is
+non-nil, only includes packages relevant to image generation, as
+specified in `org-latex-default-packages-alist' or
+`org-latex-packages-alist'."
+ (let* ((class (plist-get info :latex-class))
+ (class-template
+ (or template
+ (let* ((class-options (plist-get info :latex-class-options))
+ (header (nth 1 (assoc class (plist-get info :latex-classes)))))
+ (and (stringp header)
+ (if (not class-options) header
+ (replace-regexp-in-string
+ "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
+ class-options header t nil 1))))
+ (user-error "Unknown LaTeX class `%s'" class))))
+ (org-latex-guess-polyglossia-language
+ (org-latex-guess-babel-language
+ (org-latex-guess-inputenc
+ (org-element-normalize-string
+ (org-splice-latex-header
+ class-template
+ (org-latex--remove-packages org-latex-default-packages-alist info)
+ (org-latex--remove-packages org-latex-packages-alist info)
+ snippet?
+ (mapconcat #'org-element-normalize-string
+ (list (plist-get info :latex-header)
+ (and (not snippet?)
+ (plist-get info :latex-header-extra)))
+ ""))))
+ info)
+ info)))
+
(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-data (plist-get info :title) info)))
+ (let ((title (org-export-data (plist-get info :title) info))
+ (spec (org-latex--format-spec info)))
(concat
;; Time-stamp.
(and (plist-get info :time-stamp-file)
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
+ ;; LaTeX compiler.
+ (org-latex--insert-compiler info)
;; Document class and packages.
- (let* ((class (plist-get info :latex-class))
- (class-options (plist-get info :latex-class-options))
- (header (nth 1 (assoc class org-latex-classes)))
- (document-class-string
- (and (stringp header)
- (if (not class-options) header
- (replace-regexp-in-string
- "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
- class-options header t nil 1)))))
- (if (not document-class-string)
- (user-error "Unknown LaTeX class `%s'" class)
- (org-latex-guess-babel-language
- (org-latex-guess-inputenc
- (org-element-normalize-string
- (org-splice-latex-header
- document-class-string
- org-latex-default-packages-alist
- org-latex-packages-alist nil
- (concat (org-element-normalize-string
- (plist-get info :latex-header))
- (plist-get info :latex-header-extra)))))
- info)))
+ (org-latex-make-preamble info)
;; Possibly limit depth for headline numbering.
(let ((sec-num (plist-get info :section-numbers)))
(when (integerp sec-num)
@@ -1117,40 +1687,46 @@ holding export options."
;; Date.
(let ((date (and (plist-get info :with-date) (org-export-get-date info))))
(format "\\date{%s}\n" (org-export-data date info)))
- ;; Title
- (format "\\title{%s}\n" title)
+ ;; Title and subtitle.
+ (let* ((subtitle (plist-get info :subtitle))
+ (formatted-subtitle
+ (when subtitle
+ (format (plist-get info :latex-subtitle-format)
+ (org-export-data subtitle info))))
+ (separate (plist-get info :latex-subtitle-separate)))
+ (concat
+ (format "\\title{%s%s}\n" title
+ (if separate "" (or formatted-subtitle "")))
+ (when (and separate subtitle)
+ (concat formatted-subtitle "\n"))))
;; Hyperref options.
- (when (plist-get info :latex-hyperref-p)
- (format "\\hypersetup{\n pdfkeywords={%s},\n pdfsubject={%s},\n pdfcreator={%s}}\n"
- (or (plist-get info :keywords) "")
- (or (plist-get info :description) "")
- (if (not (plist-get info :with-creator)) ""
- (plist-get info :creator))))
+ (let ((template (plist-get info :latex-hyperref-template)))
+ (and (stringp template)
+ (format-spec template spec)))
;; Document start.
"\\begin{document}\n\n"
;; 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)))
+ (let* ((title-command (plist-get info :latex-title-command))
+ (command (and (stringp title-command)
+ (format-spec title-command spec))))
+ (org-element-normalize-string
+ (cond ((not (plist-get info :with-title)) nil)
+ ((string= "" title) nil)
+ ((not (stringp command)) nil)
+ ((string-match "\\(?:[^%]\\|^\\)%s" command)
+ (format command title))
+ (t command))))
;; Table of contents.
(let ((depth (plist-get info :with-toc)))
(when depth
(concat (when (wholenump depth)
(format "\\setcounter{tocdepth}{%d}\n" depth))
- org-latex-toc-command)))
+ (plist-get info :latex-toc-command))))
;; Document's body.
contents
;; 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"))))
+ (and (plist-get info :with-creator)
+ (concat (plist-get info :creator) "\n"))
;; Document end.
"\\end{document}")))
@@ -1160,11 +1736,11 @@ holding export options."
;;;; Bold
-(defun org-latex-bold (bold contents info)
+(defun org-latex-bold (_bold contents info)
"Transcode BOLD from Org to LaTeX.
CONTENTS is the text with bold markup. INFO is a plist holding
contextual information."
- (org-latex--text-markup contents 'bold))
+ (org-latex--text-markup contents 'bold info))
;;;; Center Block
@@ -1174,23 +1750,20 @@ contextual information."
CONTENTS holds the contents of the center block. INFO is a plist
holding contextual information."
(org-latex--wrap-label
- center-block
- (format "\\begin{center}\n%s\\end{center}" contents)))
+ center-block (format "\\begin{center}\n%s\\end{center}" contents) info))
;;;; Clock
-(defun org-latex-clock (clock contents info)
+(defun org-latex-clock (clock _contents info)
"Transcode a CLOCK element from Org to LaTeX.
CONTENTS is nil. INFO is a plist holding contextual
information."
(concat
"\\noindent"
(format "\\textbf{%s} " org-clock-string)
- (format org-latex-inactive-timestamp-format
- (concat (org-translate-time
- (org-element-property :raw-value
- (org-element-property :value clock)))
+ (format (plist-get info :latex-inactive-timestamp-format)
+ (concat (org-timestamp-translate (org-element-property :value clock))
(let ((time (org-element-property :duration clock)))
(and time (format " (%s)" time)))))
"\\\\"))
@@ -1198,11 +1771,11 @@ information."
;;;; Code
-(defun org-latex-code (code contents info)
+(defun org-latex-code (code _contents info)
"Transcode a CODE object from Org to LaTeX.
CONTENTS is nil. INFO is a plist used as a communication
channel."
- (org-latex--text-markup (org-element-property :value code) 'code))
+ (org-latex--text-markup (org-element-property :value code) 'code info))
;;;; Drawer
@@ -1212,9 +1785,9 @@ channel."
CONTENTS holds the contents of the block. INFO is a plist
holding contextual information."
(let* ((name (org-element-property :drawer-name drawer))
- (output (funcall org-latex-format-drawer-function
+ (output (funcall (plist-get info :latex-format-drawer-function)
name contents)))
- (org-latex--wrap-label drawer output)))
+ (org-latex--wrap-label drawer output info)))
;;;; Dynamic Block
@@ -1223,35 +1796,40 @@ holding contextual information."
"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))
+ (org-latex--wrap-label dynamic-block contents info))
;;;; Entity
-(defun org-latex-entity (entity contents info)
+(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-property :latex entity)))
- (if (org-element-property :latex-math-p entity) (format "$%s$" ent) ent)))
+ (org-element-property :latex entity))
;;;; Example Block
-(defun org-latex-example-block (example-block contents info)
+(defun org-latex-example-block (example-block _contents info)
"Transcode an EXAMPLE-BLOCK element from Org to LaTeX.
CONTENTS is nil. INFO is a plist holding contextual
information."
(when (org-string-nw-p (org-element-property :value example-block))
- (org-latex--wrap-label
- example-block
- (format "\\begin{verbatim}\n%s\\end{verbatim}"
- (org-export-format-code-default example-block info)))))
+ (let ((environment (or (org-export-read-attribute
+ :attr_latex example-block :environment)
+ "verbatim")))
+ (org-latex--wrap-label
+ example-block
+ (format "\\begin{%s}\n%s\\end{%s}"
+ environment
+ (org-export-format-code-default example-block info)
+ environment)
+ info))))
;;;; Export Block
-(defun org-latex-export-block (export-block contents info)
+(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 (member (org-element-property :type export-block) '("LATEX" "TEX"))
@@ -1260,7 +1838,7 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Export Snippet
-(defun org-latex-export-snippet (export-snippet contents info)
+(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."
(when (eq (org-export-snippet-backend export-snippet) 'latex)
@@ -1269,46 +1847,60 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Fixed Width
-(defun org-latex-fixed-width (fixed-width contents info)
+(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."
(org-latex--wrap-label
fixed-width
(format "\\begin{verbatim}\n%s\\end{verbatim}"
(org-remove-indentation
- (org-element-property :value fixed-width)))))
+ (org-element-property :value fixed-width)))
+ info))
;;;; Footnote Reference
-(defun org-latex-footnote-reference (footnote-reference contents info)
+(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.
- (let ((prev (org-export-get-previous-element footnote-reference info)))
- (when (eq (org-element-type prev) 'footnote-reference)
- org-latex-footnote-separator))
- (cond
- ;; Use \footnotemark if the footnote has already been defined.
- ((not (org-export-footnote-first-reference-p footnote-reference info))
- (format "\\footnotemark[%s]{}"
- (org-export-get-footnote-number footnote-reference info)))
- ;; Use \footnotemark if reference is within another footnote
- ;; reference, footnote definition or table cell.
- ((loop for parent in (org-export-get-genealogy footnote-reference)
- thereis (memq (org-element-type parent)
- '(footnote-reference footnote-definition table-cell)))
- "\\footnotemark")
- ;; Otherwise, define it with \footnote command.
- (t
- (let ((def (org-export-get-footnote-definition footnote-reference info)))
- (concat
- (format "\\footnote{%s}" (org-trim (org-export-data def info)))
- ;; Retrieve all footnote references within the footnote and
- ;; add their definition after it, since LaTeX doesn't support
- ;; them inside.
- (org-latex--delayed-footnotes-definitions def info)))))))
+ (let ((label (org-element-property :label footnote-reference)))
+ (concat
+ ;; Insert separator between two footnotes in a row.
+ (let ((prev (org-export-get-previous-element footnote-reference info)))
+ (when (eq (org-element-type prev) 'footnote-reference)
+ (plist-get info :latex-footnote-separator)))
+ (cond
+ ;; Use `:latex-footnote-defined-format' if the footnote has
+ ;; already been defined.
+ ((not (org-export-footnote-first-reference-p footnote-reference info))
+ (format (plist-get info :latex-footnote-defined-format)
+ (org-latex--label
+ (org-export-get-footnote-definition footnote-reference info)
+ info t)))
+ ;; Use \footnotemark if reference is within another footnote
+ ;; reference, footnote definition or table cell.
+ ((org-element-lineage footnote-reference
+ '(footnote-reference footnote-definition table-cell))
+ "\\footnotemark")
+ ;; Otherwise, define it with \footnote command.
+ (t
+ (let ((def (org-export-get-footnote-definition footnote-reference info)))
+ (concat
+ (format "\\footnote{%s%s}" (org-trim (org-export-data def info))
+ ;; Only insert a \label if there exist another
+ ;; reference to def.
+ (cond ((not label) "")
+ ((org-element-map (plist-get info :parse-tree) 'footnote-reference
+ (lambda (f)
+ (and (not (eq f footnote-reference))
+ (equal (org-element-property :label f) label)
+ (org-trim (org-latex--label def info t t))))
+ info t))
+ (t "")))
+ ;; Retrieve all footnote references within the footnote and
+ ;; add their definition after it, since LaTeX doesn't support
+ ;; them inside.
+ (org-latex--delayed-footnotes-definitions def info))))))))
;;;; Headline
@@ -1321,7 +1913,7 @@ holding contextual information."
(let* ((class (plist-get info :latex-class))
(level (org-export-get-relative-level headline info))
(numberedp (org-export-numbered-headline-p headline info))
- (class-sectioning (assoc class org-latex-classes))
+ (class-sectioning (assoc class (plist-get info :latex-classes)))
;; Section formatting will set two placeholders: one for
;; the title and the other for the contents.
(section-fmt
@@ -1365,16 +1957,12 @@ holding contextual information."
(org-element-property :priority headline)))
;; Create the headline text along with a no-tag version.
;; The latter is required to remove tags from toc.
- (full-text (funcall org-latex-format-headline-function
- todo todo-type priority text tags))
+ (full-text (funcall (plist-get info :latex-format-headline-function)
+ todo todo-type priority text tags info))
;; Associate \label to the headline for internal links.
- (headline-label
- (format "\\label{sec-%s}\n"
- (mapconcat 'number-to-string
- (org-export-get-headline-number headline info)
- "-")))
+ (headline-label (org-latex--label headline info t t))
(pre-blanks
- (make-string (org-element-property :pre-blank headline) 10)))
+ (make-string (org-element-property :pre-blank headline) ?\n)))
(if (or (not section-fmt) (org-export-low-level-p headline info))
;; This is a deep sub-tree: export it as a list item. Also
;; export as items headlines for which no section format has
@@ -1386,7 +1974,8 @@ holding contextual information."
(format "\\begin{%s}\n" (if numberedp 'enumerate 'itemize)))
;; Itemize headline
"\\item"
- (and full-text (org-string-match-p "\\`[ \t]*\\[" full-text)
+ (and full-text
+ (string-match-p "\\`[ \t]*\\[" full-text)
"\\relax")
" " full-text "\n"
headline-label
@@ -1404,15 +1993,32 @@ holding contextual information."
;; an alternative heading when possible, and when this is not
;; identical to the usual heading.
(let ((opt-title
- (funcall org-latex-format-headline-function
+ (funcall (plist-get info :latex-format-headline-function)
todo todo-type priority
(org-export-data-with-backend
(org-export-get-alt-title headline info)
section-back-end info)
- (and (eq (plist-get info :with-tags) t) tags))))
- (if (and numberedp opt-title
+ (and (eq (plist-get info :with-tags) t) tags)
+ info))
+ ;; Maybe end local TOC (see `org-latex-keyword').
+ (contents
+ (concat
+ contents
+ (let ((case-fold-search t)
+ (section
+ (let ((first (car (org-element-contents headline))))
+ (and (eq (org-element-type first) 'section) first))))
+ (org-element-map section 'keyword
+ (lambda (k)
+ (and (equal (org-element-property :key k) "TOC")
+ (let ((v (org-element-property :value k)))
+ (and (string-match-p "\\<headlines\\>" v)
+ (string-match-p "\\<local\\>" v)
+ (format "\\stopcontents[level-%d]" level)))))
+ info t)))))
+ (if (and opt-title
(not (equal opt-title full-text))
- (string-match "\\`\\\\\\(.*?[^*]\\){" section-fmt))
+ (string-match "\\`\\\\\\(.+?\\){" section-fmt))
(format (replace-match "\\1[%s]" nil nil section-fmt 1)
;; Replace square brackets with parenthesis
;; since square brackets are not supported in
@@ -1427,7 +2033,7 @@ holding contextual information."
(concat headline-label pre-blanks contents))))))))
(defun org-latex-format-headline-default-function
- (todo todo-type priority text tags)
+ (todo _todo-type priority text tags _info)
"Default format function for a headline.
See `org-latex-format-headline-function' for details."
(concat
@@ -1435,12 +2041,13 @@ See `org-latex-format-headline-function' for details."
(and priority (format "\\framebox{\\#%c} " priority))
text
(and tags
- (format "\\hfill{}\\textsc{%s}" (mapconcat 'identity tags ":")))))
+ (format "\\hfill{}\\textsc{%s}"
+ (mapconcat #'org-latex--protect-text tags ":")))))
;;;; Horizontal Rule
-(defun org-latex-horizontal-rule (horizontal-rule contents info)
+(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 (org-export-read-attribute :attr_latex horizontal-rule))
@@ -1454,47 +2061,47 @@ CONTENTS is nil. INFO is a plist holding contextual information."
"\n")
(org-latex--wrap-label
horizontal-rule
- (format "\\rule{%s}{%s}"
- (or (plist-get attr :width) "\\linewidth")
- (or (plist-get attr :thickness) "0.5pt"))))))
+ (format "\\noindent\\rule{%s}{%s}"
+ (or (plist-get attr :width) "\\textwidth")
+ (or (plist-get attr :thickness) "0.5pt"))
+ info))))
;;;; Inline Src Block
-(defun org-latex-inline-src-block (inline-src-block contents info)
+(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-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-property :language inline-src-block))
- (mint-lang (or (cadr (assq (intern org-lang)
- org-latex-minted-langs))
- (downcase 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-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))))))
+ (cl-case (plist-get info :latex-listings)
+ ;; Do not use a special package: transcode it verbatim.
+ ((nil) (format "\\texttt{%s}" (org-latex--text-markup code 'code info)))
+ ;; Use minted package.
+ (minted
+ (let* ((org-lang (org-element-property :language inline-src-block))
+ (mint-lang (or (cadr (assq (intern org-lang)
+ (plist-get info :latex-minted-langs)))
+ (downcase org-lang)))
+ (options (org-latex--make-option-string
+ (plist-get info :latex-minted-options))))
+ (format "\\mintinline%s{%s}{%s}"
+ (if (string= options "") "" (format "[%s]" options))
+ mint-lang
+ code)))
+ ;; Use listings package.
+ (otherwise
+ ;; Maybe translate language's name.
+ (let* ((org-lang (org-element-property :language inline-src-block))
+ (lst-lang (or (cadr (assq (intern org-lang)
+ (plist-get info :latex-listings-langs)))
+ org-lang))
+ (options (org-latex--make-option-string
+ (append (plist-get info :latex-listings-options)
+ `(("language" ,lst-lang))))))
+ (concat (format "\\lstinline[%s]" options)
+ separator code separator))))))
;;;; Inlinetask
@@ -1511,40 +2118,40 @@ holding contextual information."
(tags (and (plist-get info :with-tags)
(org-export-get-tags inlinetask info)))
(priority (and (plist-get info :with-priority)
- (org-element-property :priority inlinetask))))
- ;; If `org-latex-format-inlinetask-function' is provided, call it
- ;; with appropriate arguments.
- (if (not (eq org-latex-format-inlinetask-function 'ignore))
- (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:}"
- (mapconcat #'identity tags ":"))))))
- (concat "\\begin{center}\n"
- "\\fbox{\n"
- "\\begin{minipage}[c]{.6\\textwidth}\n"
- full-title "\n\n"
- (and (org-string-nw-p contents)
- (concat "\\rule[.8em]{\\textwidth}{2pt}\n\n" contents))
- "\\end{minipage}\n"
- "}\n"
- "\\end{center}"))))))
+ (org-element-property :priority inlinetask)))
+ (contents (concat (org-latex--label inlinetask info) contents)))
+ (funcall (plist-get info :latex-format-inlinetask-function)
+ todo todo-type priority title tags contents info)))
+
+(defun org-latex-format-inlinetask-default-function
+ (todo _todo-type priority title tags contents _info)
+ "Default format function for a inlinetasks.
+See `org-latex-format-inlinetask-function' for details."
+ (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:}"
+ (mapconcat #'org-latex--protect-text tags ":"))))))
+ (concat "\\begin{center}\n"
+ "\\fbox{\n"
+ "\\begin{minipage}[c]{.6\\textwidth}\n"
+ full-title "\n\n"
+ (and (org-string-nw-p contents)
+ (concat "\\rule[.8em]{\\textwidth}{2pt}\n\n" contents))
+ "\\end{minipage}\n"
+ "}\n"
+ "\\end{center}")))
;;;; Italic
-(defun org-latex-italic (italic contents info)
+(defun org-latex-italic (_italic contents info)
"Transcode ITALIC from Org to LaTeX.
CONTENTS is the text with italic markup. INFO is a plist holding
contextual information."
- (org-latex--text-markup contents 'italic))
+ (org-latex--text-markup contents 'italic info))
;;;; Item
@@ -1565,14 +2172,14 @@ contextual information."
(when (and (eq (org-element-type parent) 'plain-list)
(eq (org-element-property :type parent)
'ordered))
- (incf level)))
+ (cl-incf level)))
level)))
(and count
(< level 5)
(format "\\setcounter{enum%s}{%s}\n"
(nth (1- level) '("i" "ii" "iii" "iv"))
(1- count)))))
- (checkbox (case (org-element-property :checkbox item)
+ (checkbox (cl-case (org-element-property :checkbox item)
(on "$\\boxtimes$ ")
(off "$\\square$ ")
(trans "$\\boxminus$ ")))
@@ -1591,7 +2198,7 @@ contextual information."
;; unless the brackets comes from an initial export
;; snippet (i.e. it is inserted willingly by the user).
((and contents
- (org-string-match-p "\\`[ \t]*\\[" contents)
+ (string-match-p "\\`[ \t]*\\[" contents)
(not (let ((e (car (org-element-contents item))))
(and (eq (org-element-type e) 'paragraph)
(let ((o (car (org-element-contents e))))
@@ -1612,7 +2219,7 @@ contextual information."
;;;; Keyword
-(defun org-latex-keyword (keyword contents info)
+(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 (org-element-property :key keyword))
@@ -1621,60 +2228,107 @@ CONTENTS is nil. INFO is a plist holding contextual information."
((string= key "LATEX") value)
((string= key "INDEX") (format "\\index{%s}" value))
((string= key "TOC")
- (let ((value (downcase value)))
+ (let ((case-fold-search t))
(cond
- ((string-match "\\<headlines\\>" 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= "listings" value)
- (cond
- ((eq org-latex-listings 'minted) "\\listoflistings")
- (org-latex-listings "\\lstlistoflistings")
- ;; At the moment, src blocks with a caption are wrapped
- ;; into a figure environment.
- (t "\\listoffigures")))))))))
+ ((string-match-p "\\<headlines\\>" value)
+ (let* ((localp (string-match-p "\\<local\\>" value))
+ (parent (org-element-lineage keyword '(headline)))
+ (level (if (not (and localp parent)) 0
+ (org-export-get-relative-level parent info)))
+ (depth
+ (and (string-match "\\<[0-9]+\\>" value)
+ (format
+ "\\setcounter{tocdepth}{%d}"
+ (+ (string-to-number (match-string 0 value)) level)))))
+ (if (and localp parent)
+ ;; Start local TOC, assuming package "titletoc" is
+ ;; required.
+ (format "\\startcontents[level-%d]
+\\printcontents[level-%d]{}{0}{%s}"
+ level level (or depth ""))
+ (concat depth (and depth "\n") "\\tableofcontents"))))
+ ((string-match-p "\\<tables\\>" value) "\\listoftables")
+ ((string-match-p "\\<listings\\>" value)
+ (cl-case (plist-get info :latex-listings)
+ ((nil) "\\listoffigures")
+ (minted "\\listoflistings")
+ (otherwise "\\lstlistoflistings")))))))))
;;;; Latex Environment
-(defun org-latex-latex-environment (latex-environment contents info)
+(defun org-latex--environment-type (latex-environment)
+ "Return the TYPE of LATEX-ENVIRONMENT.
+
+The TYPE is determined from the actual latex environment, and
+could be a member of `org-latex-caption-above' or `math'."
+ (let* ((latex-begin-re "\\\\begin{\\([A-Za-z0-9*]+\\)}")
+ (value (org-remove-indentation
+ (org-element-property :value latex-environment)))
+ (env (or (and (string-match latex-begin-re value)
+ (match-string 1 value))
+ "")))
+ (cond
+ ((string-match-p org-latex-math-environments-re value) 'math)
+ ((string-match-p
+ (eval-when-compile
+ (regexp-opt '("table" "longtable" "tabular" "tabu" "longtabu")))
+ env)
+ 'table)
+ ((string-match-p "figure" env) 'image)
+ ((string-match-p
+ (eval-when-compile
+ (regexp-opt '("lstlisting" "listing" "verbatim" "minted")))
+ env)
+ 'src-block)
+ (t 'special-block))))
+
+(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."
(when (plist-get info :with-latex)
- (let ((label (org-element-property :name latex-environment))
- (value (org-remove-indentation
- (org-element-property :value latex-environment))))
- (if (not (org-string-nw-p label)) value
+ (let* ((value (org-remove-indentation
+ (org-element-property :value latex-environment)))
+ (type (org-latex--environment-type latex-environment))
+ (caption (if (eq type 'math)
+ (org-latex--label latex-environment info nil t)
+ (org-latex--caption/label-string latex-environment info)))
+ (caption-above-p
+ (memq type (append (plist-get info :latex-caption-above) '(math)))))
+ (if (not (or (org-element-property :name latex-environment)
+ (org-element-property :caption latex-environment)))
+ value
;; Environment is labeled: label must be within the environment
;; (otherwise, a reference pointing to that element will count
- ;; the section instead).
+ ;; the section instead). Also insert caption if `latex-environment'
+ ;; is not a math environment.
(with-temp-buffer
(insert value)
- (goto-char (point-min))
- (forward-line)
- (insert
- (format "\\label{%s}\n" (org-export-solidify-link-text label)))
+ (if caption-above-p
+ (progn
+ (goto-char (point-min))
+ (forward-line))
+ (goto-char (point-max))
+ (forward-line -1))
+ (insert caption)
(buffer-string))))))
-
;;;; Latex Fragment
-(defun org-latex-latex-fragment (latex-fragment contents info)
+(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."
- (when (plist-get info :with-latex)
- (org-element-property :value latex-fragment)))
+ (let ((value (org-element-property :value latex-fragment)))
+ ;; Trim math markers since the fragment is enclosed within
+ ;; a latex-math-block object anyway.
+ (cond ((string-match-p "\\`\\$[^$]" value) (substring value 1 -1))
+ ((string-prefix-p "\\(" value) (substring value 2 -2))
+ (t value))))
;;;; Line Break
-(defun org-latex-line-break (line-break contents info)
+(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."
"\\\\\n")
@@ -1682,6 +2336,9 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Link
+(defun org-latex-image-link-filter (data _backend info)
+ (org-export-insert-image-links data info org-latex-inline-image-rules))
+
(defun org-latex--inline-image (link info)
"Return LaTeX code for an inline image.
LINK is the link pointing to the inline image. INFO is a plist
@@ -1692,36 +2349,43 @@ used as a communication channel."
(expand-file-name raw-path))))
(filetype (file-name-extension path))
(caption (org-latex--caption/label-string parent info))
+ (caption-above-p (org-latex--caption-above-p link info))
;; Retrieve latex attributes from the element around.
(attr (org-export-read-attribute :attr_latex parent))
(float (let ((float (plist-get attr :float)))
- (cond ((and (not float) (plist-member attr :float)) nil)
- ((string= float "wrap") 'wrap)
+ (cond ((string= float "wrap") 'wrap)
+ ((string= float "sideways") 'sideways)
((string= float "multicolumn") 'multicolumn)
+ ((and (plist-member attr :float) (not float)) 'nonfloat)
((or float
(org-element-property :caption parent)
(org-string-nw-p (plist-get attr :caption)))
- 'figure))))
+ 'figure)
+ (t 'nonfloat))))
(placement
(let ((place (plist-get attr :placement)))
- (cond (place (format "%s" place))
- ((eq float 'wrap) "{l}{0.5\\textwidth}")
- ((eq float 'figure)
- (format "[%s]" org-latex-default-figure-position))
- (t ""))))
+ (cond
+ (place (format "%s" place))
+ ((eq float 'wrap) "{l}{0.5\\textwidth}")
+ ((eq float 'figure)
+ (format "[%s]" (plist-get info :latex-default-figure-position)))
+ (t ""))))
+ (center
+ (if (plist-member attr :center) (plist-get attr :center)
+ (plist-get info :latex-images-centered)))
(comment-include (if (plist-get attr :comment-include) "%" ""))
;; It is possible to specify width and height in the
;; ATTR_LATEX line, and also via default variables.
(width (cond ((plist-get attr :width))
((plist-get attr :height) "")
((eq float 'wrap) "0.48\\textwidth")
- (t org-latex-image-default-width)))
+ (t (plist-get info :latex-image-default-width))))
(height (cond ((plist-get attr :height))
((or (plist-get attr :width)
(memq float '(figure wrap))) "")
- (t org-latex-image-default-height)))
+ (t (plist-get info :latex-image-default-height))))
(options (let ((opt (or (plist-get attr :options)
- org-latex-image-default-option)))
+ (plist-get info :latex-image-default-option))))
(if (not (string-match "\\`\\[\\(.*\\)\\]\\'" opt)) opt
(match-string 1 opt))))
image-code)
@@ -1750,11 +2414,17 @@ used as a communication channel."
(setq options (concat options ",width=" width)))
(when (org-string-nw-p height)
(setq options (concat options ",height=" height)))
+ (let ((search-option (org-element-property :search-option link)))
+ (when (and search-option
+ (equal filetype "pdf")
+ (string-match-p "\\`[0-9]+\\'" search-option)
+ (not (string-match-p "page=" options)))
+ (setq options (concat options ",page=" search-option))))
(setq image-code
(format "\\includegraphics%s{%s}"
(cond ((not (org-string-nw-p options)) "")
- ((= (aref options 0) ?,)
- (format "[%s]"(substring options 1)))
+ ((string-prefix-p "," options)
+ (format "[%s]" (substring options 1)))
(t (format "[%s]" options)))
path))
(when (equal filetype "svg")
@@ -1767,20 +2437,53 @@ used as a communication channel."
image-code
nil t))))
;; Return proper string, depending on FLOAT.
- (case float
- (wrap (format "\\begin{wrapfigure}%s
-\\centering
+ (pcase float
+ (`wrap (format "\\begin{wrapfigure}%s
+%s%s
+%s%s
+%s\\end{wrapfigure}"
+ placement
+ (if caption-above-p caption "")
+ (if center "\\centering" "")
+ comment-include image-code
+ (if caption-above-p "" caption)))
+ (`sideways (format "\\begin{sidewaysfigure}
+%s%s
+%s%s
+%s\\end{sidewaysfigure}"
+ (if caption-above-p caption "")
+ (if center "\\centering" "")
+ comment-include image-code
+ (if caption-above-p "" caption)))
+ (`multicolumn (format "\\begin{figure*}%s
%s%s
-%s\\end{wrapfigure}" placement comment-include image-code caption))
- (multicolumn (format "\\begin{figure*}%s
-\\centering
%s%s
-%s\\end{figure*}" placement comment-include image-code caption))
- (figure (format "\\begin{figure}%s
-\\centering
+%s\\end{figure*}"
+ placement
+ (if caption-above-p caption "")
+ (if center "\\centering" "")
+ comment-include image-code
+ (if caption-above-p "" caption)))
+ (`figure (format "\\begin{figure}%s
%s%s
-%s\\end{figure}" placement comment-include image-code caption))
- (otherwise image-code))))
+%s%s
+%s\\end{figure}"
+ placement
+ (if caption-above-p caption "")
+ (if center "\\centering" "")
+ comment-include image-code
+ (if caption-above-p "" caption)))
+ ((guard center)
+ (format "\\begin{center}
+%s%s
+%s\\end{center}"
+ (if caption-above-p caption "")
+ image-code
+ (if caption-above-p "" caption)))
+ (_
+ (concat (if caption-above-p caption "")
+ image-code
+ (if caption-above-p caption ""))))))
(defun org-latex-link (link desc info)
"Transcode a LINK object from Org to LaTeX.
@@ -1789,20 +2492,19 @@ DESC is the description part of the link, or the empty string.
INFO is a plist holding contextual information. See
`org-export-data'."
(let* ((type (org-element-property :type link))
- (raw-path (replace-regexp-in-string
- "%" "\\%" (org-element-property :path link) nil t))
+ (raw-path (org-element-property :path link))
;; Ensure DESC really exists, or set it to nil.
(desc (and (not (string= desc "")) desc))
(imagep (org-export-inline-image-p
- link org-latex-inline-image-rules))
- (path (cond
- ((member type '("http" "https" "ftp" "mailto"))
- (concat type ":" raw-path))
- ((and (string= type "file") (file-name-absolute-p raw-path))
- (concat "file:" raw-path))
- (t raw-path)))
- protocol)
+ link (plist-get info :latex-inline-image-rules)))
+ (path (org-latex--protect-text
+ (cond ((member type '("http" "https" "ftp" "mailto" "doi"))
+ (concat type ":" raw-path))
+ ((string= type "file") (org-export-file-uri raw-path))
+ (t raw-path)))))
(cond
+ ;; Link type is handled by a special function.
+ ((org-export-custom-protocol-maybe link desc 'latex))
;; Image file.
(imagep (org-latex--inline-image link info))
;; Radio link: Transcode target's contents and use them as link's
@@ -1811,8 +2513,7 @@ INFO is a plist holding contextual information. See
(let ((destination (org-export-resolve-radio-link link info)))
(if (not destination) desc
(format "\\hyperref[%s]{%s}"
- (org-export-solidify-link-text
- (org-element-property :value destination))
+ (org-export-get-reference destination info)
desc))))
;; Links pointing to a headline: Find destination and build
;; appropriate referencing command.
@@ -1820,14 +2521,14 @@ INFO is a plist holding contextual information. See
(let ((destination (if (string= type "fuzzy")
(org-export-resolve-fuzzy-link link info)
(org-export-resolve-id-link link info))))
- (case (org-element-type destination)
+ (cl-case (org-element-type destination)
;; Id link points to an external file.
(plain-text
(if desc (format "\\href{%s}{%s}" destination desc)
(format "\\url{%s}" destination)))
;; Fuzzy link points nowhere.
- ('nil
- (format org-latex-link-with-unknown-path-format
+ ((nil)
+ (format (plist-get info :latex-link-with-unknown-path-format)
(or desc
(org-export-data
(org-element-property :raw-link link) info))))
@@ -1836,12 +2537,7 @@ INFO is a plist holding contextual information. See
;; number. Otherwise, display description or headline's
;; title.
(headline
- (let ((label
- (format "sec-%s"
- (mapconcat
- 'number-to-string
- (org-export-get-headline-number destination info)
- "-"))))
+ (let ((label (org-latex--label destination info t)))
(if (and (not desc)
(org-export-numbered-headline-p destination info))
(format "\\ref{%s}" label)
@@ -1851,28 +2547,37 @@ INFO is a plist holding contextual information. See
(org-element-property :title destination) info))))))
;; Fuzzy link points to a target. Do as above.
(otherwise
- (let ((path (org-export-solidify-link-text path)))
- (if (not desc) (format "\\ref{%s}" path)
- (format "\\hyperref[%s]{%s}" path desc)))))))
+ (let ((ref (org-latex--label destination info t)))
+ (if (not desc) (format "\\ref{%s}" ref)
+ (format "\\hyperref[%s]{%s}" ref desc)))))))
;; Coderef: replace link with the reference name or the
;; equivalent line number.
((string= type "coderef")
(format (org-export-get-coderef-format path desc)
(org-export-resolve-coderef path info)))
- ;; Link type is handled by a special function.
- ((functionp (setq protocol (nth 2 (assoc type org-link-protocols))))
- (funcall protocol (org-link-unescape path) desc 'latex))
;; External link with a description part.
((and path desc) (format "\\href{%s}{%s}" path desc))
;; External link without a description part.
(path (format "\\url{%s}" path))
;; No path, only description. Try to do something useful.
- (t (format org-latex-link-with-unknown-path-format desc)))))
+ (t (format (plist-get info :latex-link-with-unknown-path-format) desc)))))
+
+
+;;;; Node Property
+
+(defun org-latex-node-property (node-property _contents _info)
+ "Transcode a NODE-PROPERTY element from Org to LaTeX.
+CONTENTS is nil. INFO is a plist holding contextual
+information."
+ (format "%s:%s"
+ (org-element-property :key node-property)
+ (let ((value (org-element-property :value node-property)))
+ (if value (concat " " value) ""))))
;;;; Paragraph
-(defun org-latex-paragraph (paragraph contents info)
+(defun org-latex-paragraph (_paragraph contents _info)
"Transcode a PARAGRAPH element from Org to LaTeX.
CONTENTS is the contents of the paragraph, as a string. INFO is
the plist used as a communication channel."
@@ -1898,7 +2603,8 @@ contextual information."
latex-type
(or (plist-get attr :options) "")
contents
- latex-type))))
+ latex-type)
+ info)))
;;;; Plain Text
@@ -1907,54 +2613,42 @@ contextual information."
"Transcode a TEXT string from Org to LaTeX.
TEXT is the string to transcode. INFO is a plist holding
contextual information."
- (let ((specialp (plist-get info :with-special-strings))
- (output text))
- ;; Protect %, #, &, $, _, { and }.
- (while (string-match "\\([^\\]\\|^\\)\\([%$#&{}_]\\)" output)
- (setq output
- (replace-match
- (format "\\%s" (match-string 2 output)) nil t output 2)))
- ;; Protect ^.
- (setq output
- (replace-regexp-in-string
- "\\([^\\]\\|^\\)\\(\\^\\)" "\\\\^{}" output nil nil 2))
- ;; Protect \. If special strings are used, be careful not to
- ;; protect "\" in "\-" constructs.
- (let ((symbols (if specialp "-%$#&{}^_\\" "%$#&{}^_\\")))
- (setq output
+ (let* ((specialp (plist-get info :with-special-strings))
+ (output
+ ;; Turn LaTeX into \LaTeX{} and TeX into \TeX{}.
+ (let ((case-fold-search nil))
(replace-regexp-in-string
- (format "\\(?:[^\\]\\|^\\)\\(\\\\\\)\\(?:[^%s]\\|$\\)" symbols)
- "$\\backslash$" output nil t 1)))
- ;; Protect ~.
- (setq output
- (replace-regexp-in-string
- "\\([^\\]\\|^\\)\\(~\\)" "\\textasciitilde{}" output nil t 2))
+ "\\<\\(?:La\\)?TeX\\>" "\\\\\\&{}"
+ ;; Protect ^, ~, %, #, &, $, _, { and }. Also protect \.
+ ;; However, if special strings are used, be careful not
+ ;; to protect "\" in "\-" constructs.
+ (replace-regexp-in-string
+ (concat "[%$#&{}_~^]\\|\\\\" (and specialp "\\([^-]\\|$\\)"))
+ (lambda (m)
+ (cl-case (string-to-char m)
+ (?\\ "$\\\\backslash$\\1")
+ (?~ "\\\\textasciitilde{}")
+ (?^ "\\\\^{}")
+ (t "\\\\\\&")))
+ text)))))
;; Activate smart quotes. Be sure to provide original TEXT string
;; since OUTPUT may have been modified.
(when (plist-get info :with-smart-quotes)
(setq output (org-export-activate-smart-quotes output :latex info text)))
- ;; LaTeX into \LaTeX{} and TeX into \TeX{}.
- (let ((case-fold-search nil)
- (start 0))
- (while (string-match "\\<\\(\\(?:La\\)?TeX\\)\\>" output start)
- (setq output (replace-match
- (format "\\%s{}" (match-string 1 output)) nil t output)
- start (match-end 0))))
;; Convert special strings.
(when specialp
- (setq output
- (replace-regexp-in-string "\\.\\.\\." "\\ldots{}" output nil t)))
+ (setq output (replace-regexp-in-string "\\.\\.\\." "\\\\ldots{}" output)))
;; Handle break preservation if required.
(when (plist-get info :preserve-breaks)
(setq output (replace-regexp-in-string
- "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" output)))
+ "\\(?:[ \t]*\\\\\\\\\\)?[ \t]*\n" "\\\\\n" output nil t)))
;; Return value.
output))
;;;; Planning
-(defun org-latex-planning (planning contents info)
+(defun org-latex-planning (planning _contents info)
"Transcode a PLANNING element from Org to LaTeX.
CONTENTS is nil. INFO is a plist holding contextual
information."
@@ -1968,27 +2662,165 @@ information."
(when closed
(concat
(format "\\textbf{%s} " org-closed-string)
- (format org-latex-inactive-timestamp-format
- (org-translate-time
- (org-element-property :raw-value closed))))))
+ (format (plist-get info :latex-inactive-timestamp-format)
+ (org-timestamp-translate closed)))))
(let ((deadline (org-element-property :deadline planning)))
(when deadline
(concat
(format "\\textbf{%s} " org-deadline-string)
- (format org-latex-active-timestamp-format
- (org-translate-time
- (org-element-property :raw-value deadline))))))
+ (format (plist-get info :latex-active-timestamp-format)
+ (org-timestamp-translate deadline)))))
(let ((scheduled (org-element-property :scheduled planning)))
(when scheduled
(concat
(format "\\textbf{%s} " org-scheduled-string)
- (format org-latex-active-timestamp-format
- (org-translate-time
- (org-element-property :raw-value scheduled))))))))
+ (format (plist-get info :latex-active-timestamp-format)
+ (org-timestamp-translate scheduled)))))))
" ")
"\\\\"))
+;;;; Property Drawer
+
+(defun org-latex-property-drawer (_property-drawer contents _info)
+ "Transcode a PROPERTY-DRAWER element from Org to LaTeX.
+CONTENTS holds the contents of the drawer. INFO is a plist
+holding contextual information."
+ (and (org-string-nw-p contents)
+ (format "\\begin{verbatim}\n%s\\end{verbatim}" contents)))
+
+
+;;;; Pseudo Element: LaTeX Matrices
+
+;; `latex-matrices' elements have the following properties:
+;; `:caption', `:post-blank' and `:markup' (`inline', `equation' or
+;; `math').
+
+(defun org-latex--wrap-latex-matrices (data info)
+ "Merge contiguous tables with the same mode within a pseudo-element.
+DATA is a parse tree or a secondary string. INFO is a plist
+containing export options. Modify DATA by side-effect and return
+it."
+ (org-element-map data 'table
+ (lambda (table)
+ (when (eq (org-element-property :type table) 'org)
+ (let ((mode (or (org-export-read-attribute :attr_latex table :mode)
+ (plist-get info :latex-default-table-mode))))
+ (when (and (member mode '("inline-math" "math"))
+ ;; Do not wrap twice the same table.
+ (not (eq (org-element-type
+ (org-element-property :parent table))
+ 'latex-matrices)))
+ (let* ((caption (and (not (string= mode "inline-math"))
+ (org-element-property :caption table)))
+ (matrices
+ (list 'latex-matrices
+ (list :caption caption
+ :markup
+ (cond ((string= mode "inline-math") 'inline)
+ (caption 'equation)
+ (t 'math)))))
+ (previous table)
+ (next (org-export-get-next-element table info)))
+ (org-element-insert-before matrices table)
+ ;; Swallow all contiguous tables sharing the same mode.
+ (while (and
+ (zerop (or (org-element-property :post-blank previous) 0))
+ (setq next (org-export-get-next-element previous info))
+ (eq (org-element-type next) 'table)
+ (eq (org-element-property :type next) 'org)
+ (string= (or (org-export-read-attribute
+ :attr_latex next :mode)
+ (plist-get info :latex-default-table-mode))
+ mode))
+ (org-element-extract-element previous)
+ (org-element-adopt-elements matrices previous)
+ (setq previous next))
+ ;; Inherit `:post-blank' from the value of the last
+ ;; swallowed table. Set the latter's `:post-blank'
+ ;; value to 0 so as to not duplicate empty lines.
+ (org-element-put-property
+ matrices :post-blank (org-element-property :post-blank previous))
+ (org-element-put-property previous :post-blank 0)
+ (org-element-extract-element previous)
+ (org-element-adopt-elements matrices previous))))))
+ info)
+ data)
+
+(defun org-latex-matrices (matrices contents _info)
+ "Transcode a MATRICES element from Org to LaTeX.
+CONTENTS is a string. INFO is a plist used as a communication
+channel."
+ (format (cl-case (org-element-property :markup matrices)
+ (inline "\\(%s\\)")
+ (equation "\\begin{equation}\n%s\\end{equation}")
+ (t "\\[\n%s\\]"))
+ contents))
+
+
+;;;; Pseudo Object: LaTeX Math Block
+
+;; `latex-math-block' objects have the following property:
+;; `:post-blank'.
+
+(defun org-latex--wrap-latex-math-block (data info)
+ "Merge contiguous math objects in a pseudo-object container.
+DATA is a parse tree or a secondary string. INFO is a plist
+containing export options. Modify DATA by side-effect and return it."
+ (let ((valid-object-p
+ ;; Non-nil when OBJ can be added to the latex math block B.
+ (lambda (obj b)
+ (pcase (org-element-type obj)
+ (`entity (org-element-property :latex-math-p obj))
+ (`latex-fragment
+ (let ((value (org-element-property :value obj)))
+ (or (string-prefix-p "\\(" value)
+ (string-match-p "\\`\\$[^$]" value))))
+ ((and type (or `subscript `superscript))
+ (not (memq type (mapcar #'org-element-type
+ (org-element-contents b)))))))))
+ (org-element-map data '(entity latex-fragment subscript superscript)
+ (lambda (object)
+ ;; Skip objects already wrapped.
+ (when (and (not (eq (org-element-type
+ (org-element-property :parent object))
+ 'latex-math-block))
+ (funcall valid-object-p object nil))
+ (let ((math-block (list 'latex-math-block nil))
+ (next-elements (org-export-get-next-element object info t))
+ (last object))
+ ;; Wrap MATH-BLOCK around OBJECT in DATA.
+ (org-element-insert-before math-block object)
+ (org-element-extract-element object)
+ (org-element-adopt-elements math-block object)
+ (when (zerop (or (org-element-property :post-blank object) 0))
+ ;; MATH-BLOCK swallows consecutive math objects.
+ (catch 'exit
+ (dolist (next next-elements)
+ (unless (funcall valid-object-p next math-block)
+ (throw 'exit nil))
+ (org-element-extract-element next)
+ (org-element-adopt-elements math-block next)
+ ;; Eschew the case: \beta$x$ -> \(\betax\).
+ (unless (memq (org-element-type next)
+ '(subscript superscript))
+ (org-element-put-property last :post-blank 1))
+ (setq last next)
+ (when (> (or (org-element-property :post-blank next) 0) 0)
+ (throw 'exit nil)))))
+ (org-element-put-property
+ math-block :post-blank (org-element-property :post-blank last)))))
+ info nil '(subscript superscript latex-math-block) t)
+ ;; Return updated DATA.
+ data))
+
+(defun org-latex-math-block (_math-block contents _info)
+ "Transcode a MATH-BLOCK object from Org to LaTeX.
+CONTENTS is a string. INFO is a plist used as a communication
+channel."
+ (when (org-string-nw-p contents)
+ (format "\\(%s\\)" (org-trim contents))))
+
;;;; Quote Block
(defun org-latex-quote-block (quote-block contents info)
@@ -1996,18 +2828,7 @@ information."
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-property :value quote-section))))
- (when value (format "\\begin{verbatim}\n%s\\end{verbatim}" value))))
+ quote-block (format "\\begin{quote}\n%s\\end{quote}" contents) info))
;;;; Radio Target
@@ -2016,15 +2837,12 @@ CONTENTS is nil. INFO is a plist holding contextual information."
"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-property :value radio-target))
- text))
+ (format "\\label{%s}%s" (org-export-get-reference radio-target info) text))
;;;; Section
-(defun org-latex-section (section contents info)
+(defun org-latex-section (_section contents _info)
"Transcode a SECTION element from Org to LaTeX.
CONTENTS holds the contents of the section. INFO is a plist
holding contextual information."
@@ -2037,85 +2855,110 @@ holding contextual information."
"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-property :type special-block)))
- (opt (org-export-read-attribute :attr_latex special-block :options)))
+ (let ((type (org-element-property :type special-block))
+ (opt (org-export-read-attribute :attr_latex special-block :options))
+ (caption (org-latex--caption/label-string special-block info))
+ (caption-above-p (org-latex--caption-above-p special-block info)))
(concat (format "\\begin{%s}%s\n" type (or opt ""))
- ;; Insert any label or caption within the block
- ;; (otherwise, a reference pointing to that element will
- ;; count the section instead).
- (org-latex--caption/label-string special-block info)
+ (and caption-above-p caption)
contents
+ (and (not caption-above-p) caption)
(format "\\end{%s}" type))))
;;;; Src Block
-(defun org-latex-src-block (src-block contents info)
+(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."
(when (org-string-nw-p (org-element-property :value src-block))
(let* ((lang (org-element-property :language src-block))
(caption (org-element-property :caption src-block))
+ (caption-above-p (org-latex--caption-above-p src-block info))
(label (org-element-property :name src-block))
(custom-env (and lang
(cadr (assq (intern lang)
org-latex-custom-lang-environments))))
- (num-start (case (org-element-property :number-lines src-block)
- (continued (org-export-get-loc src-block info))
- (new 0)))
+ (num-start (org-export-get-loc src-block info))
(retain-labels (org-element-property :retain-labels src-block))
(attributes (org-export-read-attribute :attr_latex src-block))
- (float (plist-get attributes :float)))
+ (float (plist-get attributes :float))
+ (listings (plist-get info :latex-listings)))
(cond
;; Case 1. No source fontification.
- ((not org-latex-listings)
+ ((not listings)
(let* ((caption-str (org-latex--caption/label-string src-block info))
(float-env
- (cond ((and (not float) (plist-member attributes :float)) "%s")
- ((string= "multicolumn" float)
- (format "\\begin{figure*}[%s]\n%%s%s\n\\end{figure*}"
- org-latex-default-figure-position
- caption-str))
- ((or caption float)
- (format "\\begin{figure}[H]\n%%s%s\n\\end{figure}"
- caption-str))
+ (cond ((string= "multicolumn" float)
+ (format "\\begin{figure*}[%s]\n%s%%s\n%s\\end{figure*}"
+ (plist-get info :latex-default-figure-position)
+ (if caption-above-p caption-str "")
+ (if caption-above-p "" caption-str)))
+ (caption (concat
+ (if caption-above-p caption-str "")
+ "%s"
+ (if caption-above-p "" (concat "\n" caption-str))))
(t "%s"))))
(format
float-env
(concat (format "\\begin{verbatim}\n%s\\end{verbatim}"
(org-export-format-code-default src-block info))))))
;; Case 2. Custom environment.
- (custom-env (format "\\begin{%s}\n%s\\end{%s}\n"
- custom-env
- (org-export-format-code-default src-block info)
- custom-env))
+ (custom-env
+ (let ((caption-str (org-latex--caption/label-string src-block info))
+ (formatted-src (org-export-format-code-default src-block info)))
+ (if (string-match-p "\\`[a-zA-Z0-9]+\\'" custom-env)
+ (format "\\begin{%s}\n%s\\end{%s}\n"
+ custom-env
+ (concat (and caption-above-p caption-str)
+ formatted-src
+ (and (not caption-above-p) caption-str))
+ custom-env)
+ (format-spec custom-env
+ `((?s . ,formatted-src)
+ (?c . ,caption)
+ (?f . ,float)
+ (?l . ,(org-latex--label src-block info))
+ (?o . ,(or (plist-get attributes :options) "")))))))
;; Case 3. Use minted package.
- ((eq org-latex-listings 'minted)
+ ((eq listings 'minted)
(let* ((caption-str (org-latex--caption/label-string src-block info))
(float-env
- (cond ((and (not float) (plist-member attributes :float)) "%s")
- ((string= "multicolumn" float)
- (format "\\begin{listing*}\n%%s\n%s\\end{listing*}"
- caption-str))
- ((or caption float)
- (format "\\begin{listing}[H]\n%%s\n%s\\end{listing}"
- caption-str))
- (t "%s")))
+ (cond
+ ((string= "multicolumn" float)
+ (format "\\begin{listing*}[%s]\n%s%%s\n%s\\end{listing*}"
+ (plist-get info :latex-default-figure-position)
+ (if caption-above-p caption-str "")
+ (if caption-above-p "" caption-str)))
+ (caption
+ (format "\\begin{listing}[%s]\n%s%%s\n%s\\end{listing}"
+ (plist-get info :latex-default-figure-position)
+ (if caption-above-p caption-str "")
+ (if caption-above-p "" caption-str)))
+ ((string= "t" float)
+ (concat (format "\\begin{listing}[%s]\n"
+ (plist-get info :latex-default-figure-position))
+ "%s\n\\end{listing}"))
+ (t "%s")))
+ (options (plist-get info :latex-minted-options))
(body
(format
"\\begin{minted}[%s]{%s}\n%s\\end{minted}"
;; Options.
- (org-latex--make-option-string
- (if (or (not num-start)
- (assoc "linenos" org-latex-minted-options))
- org-latex-minted-options
- (append
- `(("linenos")
- ("firstnumber" ,(number-to-string (1+ num-start))))
- org-latex-minted-options)))
+ (concat
+ (org-latex--make-option-string
+ (if (or (not num-start) (assoc "linenos" options))
+ options
+ (append
+ `(("linenos")
+ ("firstnumber" ,(number-to-string (1+ num-start))))
+ options)))
+ (let ((local-options (plist-get attributes :options)))
+ (and local-options (concat "," local-options))))
;; Language.
- (or (cadr (assq (intern lang) org-latex-minted-langs))
+ (or (cadr (assq (intern lang)
+ (plist-get info :latex-minted-langs)))
(downcase lang))
;; Source code.
(let* ((code-info (org-export-unravel-code src-block))
@@ -2126,7 +2969,7 @@ contextual information."
"\n")))))
(org-export-format-code
(car code-info)
- (lambda (loc num ref)
+ (lambda (loc _num ref)
(concat
loc
(when ref
@@ -2142,7 +2985,9 @@ contextual information."
;; Case 4. Use listings package.
(t
(let ((lst-lang
- (or (cadr (assq (intern lang) org-latex-listings-langs)) lang))
+ (or (cadr (assq (intern lang)
+ (plist-get info :latex-listings-langs)))
+ lang))
(caption-str
(when caption
(let ((main (org-export-get-caption src-block))
@@ -2151,28 +2996,33 @@ contextual information."
(format "{%s}" (org-export-data main info))
(format "{[%s]%s}"
(org-export-data secondary info)
- (org-export-data main info)))))))
+ (org-export-data main info))))))
+ (lst-opt (plist-get info :latex-listings-options)))
(concat
;; Options.
(format
"\\lstset{%s}\n"
- (org-latex--make-option-string
- (append
- org-latex-listings-options
- (cond
- ((and (not float) (plist-member attributes :float)) nil)
- ((string= "multicolumn" float) '(("float" "*")))
- ((and float (not (assoc "float" org-latex-listings-options)))
- `(("float" ,org-latex-default-figure-position))))
- `(("language" ,lst-lang))
- (if label `(("label" ,label)) '(("label" " ")))
- (if caption-str `(("caption" ,caption-str)) '(("caption" " ")))
- (cond ((assoc "numbers" org-latex-listings-options) nil)
- ((not num-start) '(("numbers" "none")))
- ((zerop num-start) '(("numbers" "left")))
- (t `(("numbers" "left")
- ("firstnumber"
- ,(number-to-string (1+ num-start)))))))))
+ (concat
+ (org-latex--make-option-string
+ (append
+ lst-opt
+ (cond
+ ((and (not float) (plist-member attributes :float)) nil)
+ ((string= "multicolumn" float) '(("float" "*")))
+ ((and float (not (assoc "float" lst-opt)))
+ `(("float" ,(plist-get info :latex-default-figure-position)))))
+ `(("language" ,lst-lang))
+ (if label
+ `(("label" ,(org-latex--label src-block info)))
+ '(("label" " ")))
+ (if caption-str `(("caption" ,caption-str)) '(("caption" " ")))
+ `(("captionpos" ,(if caption-above-p "t" "b")))
+ (cond ((assoc "numbers" lst-opt) nil)
+ ((not num-start) '(("numbers" "none")))
+ (t `(("firstnumber" ,(number-to-string (1+ num-start)))
+ ("numbers" "left"))))))
+ (let ((local-options (plist-get attributes :options)))
+ (and local-options (concat "," local-options)))))
;; Source code.
(format
"\\begin{lstlisting}\n%s\\end{lstlisting}"
@@ -2183,21 +3033,21 @@ contextual information."
(org-split-string (car code-info) "\n")))))
(org-export-format-code
(car code-info)
- (lambda (loc num ref)
+ (lambda (loc _num ref)
(concat
loc
(when ref
;; Ensure references are flushed to the right,
;; separated with 6 spaces from the widest line of
;; code
- (concat (make-string (+ (- max-width (length loc)) 6) ? )
+ (concat (make-string (+ (- max-width (length loc)) 6) ?\s)
(format "(%s)" ref)))))
nil (and retain-labels (cdr code-info))))))))))))
;;;; Statistics Cookie
-(defun org-latex-statistics-cookie (statistics-cookie contents info)
+(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."
(replace-regexp-in-string
@@ -2206,11 +3056,11 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Strike-Through
-(defun org-latex-strike-through (strike-through contents info)
+(defun org-latex-strike-through (_strike-through contents info)
"Transcode STRIKE-THROUGH from Org to LaTeX.
CONTENTS is the text with strike-through markup. INFO is a plist
holding contextual information."
- (org-latex--text-markup contents 'strike-through))
+ (org-latex--text-markup contents 'strike-through info))
;;;; Subscript
@@ -2219,22 +3069,11 @@ holding contextual information."
"Transcode a subscript or superscript object.
OBJECT is an Org object. INFO is a plist used as a communication
channel."
- (let ((in-script-p
- ;; Non-nil if object is already in a sub/superscript.
- (let ((parent object))
- (catch 'exit
- (while (setq parent (org-export-get-parent parent))
- (let ((type (org-element-type parent)))
- (cond ((memq type '(subscript superscript))
- (throw 'exit t))
- ((memq type org-element-all-elements)
- (throw 'exit nil))))))))
- (type (org-element-type object))
- (output ""))
+ (let ((output ""))
(org-element-map (org-element-contents object)
(cons 'plain-text org-element-all-objects)
(lambda (obj)
- (case (org-element-type obj)
+ (cl-case (org-element-type obj)
((entity latex-fragment)
(let ((data (org-trim (org-export-data obj info))))
(string-match
@@ -2255,33 +3094,14 @@ channel."
(let ((blank (org-element-property :post-blank obj)))
(and blank (> blank 0) "\\ ")))))))
info nil org-element-recursive-objects)
- ;; Result. Do not wrap into math mode if already in a subscript
- ;; or superscript. Do not wrap into curly brackets if OUTPUT is
- ;; a single character. Also merge consecutive subscript and
- ;; superscript into the same math snippet.
- (concat (and (not in-script-p)
- (let ((prev (org-export-get-previous-element object info)))
- (or (not prev)
- (not (eq (org-element-type prev)
- (if (eq type 'subscript) 'superscript
- 'subscript)))
- (let ((blank (org-element-property :post-blank prev)))
- (and blank (> blank 0)))))
- "$")
- (if (eq (org-element-type object) 'subscript) "_" "^")
+ ;; Result. Do not wrap into curly brackets if OUTPUT is a single
+ ;; character.
+ (concat (if (eq (org-element-type object) 'subscript) "_" "^")
(and (> (length output) 1) "{")
output
- (and (> (length output) 1) "}")
- (and (not in-script-p)
- (or (let ((blank (org-element-property :post-blank object)))
- (and blank (> blank 0)))
- (not (eq (org-element-type
- (org-export-get-next-element object info))
- (if (eq type 'subscript) 'superscript
- 'subscript))))
- "$"))))
+ (and (> (length output) 1) "}"))))
-(defun org-latex-subscript (subscript contents info)
+(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."
@@ -2290,7 +3110,7 @@ contextual information."
;;;; Superscript
-(defun org-latex-superscript (superscript contents info)
+(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."
@@ -2316,7 +3136,7 @@ contextual information."
;; "table.el" table. Convert it using appropriate tools.
(org-latex--table.el-table table info)
(let ((type (or (org-export-read-attribute :attr_latex table :mode)
- org-latex-default-table-mode)))
+ (plist-get info :latex-default-table-mode))))
(cond
;; Case 1: Verbatim table.
((string= type "verbatim")
@@ -2333,10 +3153,12 @@ contextual information."
;; table, insert their definition just after it.
(org-latex--delayed-footnotes-definitions table info)))))))
-(defun org-latex--align-string (table info)
+(defun org-latex--align-string (table info &optional math?)
"Return an appropriate LaTeX alignment string.
TABLE is the considered table. INFO is a plist used as
-a communication channel."
+a communication channel. When optional argument MATH? is
+non-nil, TABLE is meant to be a matrix, where all cells are
+centered."
(or (org-export-read-attribute :attr_latex table :align)
(let (align)
;; Extract column groups and alignment from first (non-rule)
@@ -2352,10 +3174,11 @@ a communication channel."
;; Check left border for the first cell only.
(when (and (memq 'left borders) (not align))
(push "|" align))
- (push (case (org-export-table-cell-alignment cell info)
- (left "l")
- (right "r")
- (center "c"))
+ (push (if math? "c" ;center cells in matrices
+ (cl-case (org-export-table-cell-alignment cell info)
+ (left "l")
+ (right "r")
+ (center "c")))
align)
(when (memq 'right borders) (push "|" align))))
info)
@@ -2376,14 +3199,15 @@ This function assumes TABLE has `org' as its `:type' property and
(alignment (org-latex--align-string table info))
;; Determine environment for the table: longtable, tabular...
(table-env (or (plist-get attr :environment)
- org-latex-default-table-environment))
+ (plist-get info :latex-default-table-environment)))
;; If table is a float, determine environment: table, table*
;; or sidewaystable.
(float-env (unless (member table-env '("longtable" "longtabu"))
(let ((float (plist-get attr :float)))
(cond
((and (not float) (plist-member attr :float)) nil)
- ((string= float "sidewaystable") "sidewaystable")
+ ((or (string= float "sidewaystable")
+ (string= float "sideways")) "sidewaystable")
((string= float "multicolumn") "table*")
((or float
(org-element-property :caption table)
@@ -2392,23 +3216,26 @@ This function assumes TABLE has `org' as its `:type' property and
;; Extract others display options.
(fontsize (let ((font (plist-get attr :font)))
(and font (concat font "\n"))))
- (width (plist-get attr :width))
+ ;; "tabular" environment doesn't allow to define a width.
+ (width (and (not (equal table-env "tabular")) (plist-get attr :width)))
(spreadp (plist-get attr :spread))
- (placement (or (plist-get attr :placement)
- (format "[%s]" org-latex-default-figure-position)))
+ (placement
+ (or (plist-get attr :placement)
+ (format "[%s]" (plist-get info :latex-default-figure-position))))
(centerp (if (plist-member attr :center) (plist-get attr :center)
- org-latex-tables-centered)))
+ (plist-get info :latex-tables-centered)))
+ (caption-above-p (org-latex--caption-above-p table info)))
;; Prepare the final format string for the table.
(cond
;; Longtable.
((equal "longtable" table-env)
(concat (and fontsize (concat "{" fontsize))
(format "\\begin{longtable}{%s}\n" alignment)
- (and org-latex-table-caption-above
+ (and caption-above-p
(org-string-nw-p caption)
(concat caption "\\\\\n"))
contents
- (and (not org-latex-table-caption-above)
+ (and (not caption-above-p)
(org-string-nw-p caption)
(concat caption "\\\\\n"))
"\\end{longtable}\n"
@@ -2421,11 +3248,11 @@ This function assumes TABLE has `org' as its `:type' property and
(format " %s %s "
(if spreadp "spread" "to") width) "")
alignment)
- (and org-latex-table-caption-above
+ (and caption-above-p
(org-string-nw-p caption)
(concat caption "\\\\\n"))
contents
- (and (not org-latex-table-caption-above)
+ (and (not caption-above-p)
(org-string-nw-p caption)
(concat caption "\\\\\n"))
"\\end{longtabu}\n"
@@ -2434,9 +3261,15 @@ This function assumes TABLE has `org' as its `:type' property and
(t (concat (cond
(float-env
(concat (format "\\begin{%s}%s\n" float-env placement)
- (if org-latex-table-caption-above caption "")
+ (if caption-above-p caption "")
(when centerp "\\centering\n")
fontsize))
+ ((and (not float-env) caption)
+ (concat
+ (and centerp "\\begin{center}\n" )
+ (if caption-above-p caption "")
+ (cond ((and fontsize centerp) fontsize)
+ (fontsize (concat "{" fontsize)))))
(centerp (concat "\\begin{center}\n" fontsize))
(fontsize (concat "{" fontsize)))
(cond ((equal "tabu" table-env)
@@ -2454,8 +3287,13 @@ This function assumes TABLE has `org' as its `:type' property and
table-env)))
(cond
(float-env
- (concat (if org-latex-table-caption-above "" caption)
+ (concat (if caption-above-p "" (concat "\n" caption))
(format "\n\\end{%s}" float-env)))
+ ((and (not float-env) caption)
+ (concat
+ (if caption-above-p "" (concat "\n" caption))
+ (and centerp "\n\\end{center}")
+ (and fontsize (not centerp) "}")))
(centerp "\n\\end{center}")
(fontsize "}")))))))
@@ -2489,10 +3327,10 @@ property."
(let ((n 0) (pos 0))
(while (and (< (length output) pos)
(setq pos (string-match "^\\\\hline\n?" output pos)))
- (incf n)
+ (cl-incf n)
(unless (= n 2) (setq output (replace-match "" nil nil output))))))
(let ((centerp (if (plist-member attr :center) (plist-get attr :center)
- org-latex-tables-centered)))
+ (plist-get info :latex-tables-centered))))
(if (not centerp) output
(format "\\begin{center}\n%s\n\\end{center}" output))))))
@@ -2503,54 +3341,30 @@ TABLE is the table type element to transcode. INFO is a plist
used as a communication channel.
This function assumes TABLE has `org' as its `:type' property and
-`inline-math' or `math' as its `:mode' attribute.."
- (let* ((caption (org-latex--caption/label-string table info))
- (attr (org-export-read-attribute :attr_latex table))
- (inlinep (equal (plist-get attr :mode) "inline-math"))
+`inline-math' or `math' as its `:mode' attribute."
+ (let* ((attr (org-export-read-attribute :attr_latex table))
(env (or (plist-get attr :environment)
- org-latex-default-table-environment))
+ (plist-get info :latex-default-table-environment)))
(contents
(mapconcat
(lambda (row)
- ;; Ignore horizontal rules.
- (when (eq (org-element-property :type row) 'standard)
+ (if (eq (org-element-property :type row) 'rule) "\\hline"
;; Return each cell unmodified.
(concat
(mapconcat
(lambda (cell)
(substring (org-element-interpret-data cell) 0 -1))
- (org-element-map row 'table-cell 'identity info) "&")
+ (org-element-map row 'table-cell #'identity info) "&")
(or (cdr (assoc env org-latex-table-matrix-macros)) "\\\\")
"\n")))
- (org-element-map table 'table-row 'identity info) ""))
- ;; Variables related to math clusters (contiguous math tables
- ;; of the same type).
- (mode (org-export-read-attribute :attr_latex table :mode))
- (prev (org-export-get-previous-element table info))
- (next (org-export-get-next-element table info))
- (same-mode-p
- (lambda (table)
- ;; Non-nil when TABLE has the same mode as current table.
- (string= (or (org-export-read-attribute :attr_latex table :mode)
- org-latex-default-table-mode)
- mode))))
+ (org-element-map table 'table-row #'identity info) "")))
(concat
- ;; Opening string. If TABLE is in the middle of a table cluster,
- ;; do not insert any.
- (cond ((and prev
- (eq (org-element-type prev) 'table)
- (memq (org-element-property :post-blank prev) '(0 nil))
- (funcall same-mode-p prev))
- nil)
- (inlinep "\\(")
- ((org-string-nw-p caption) (concat "\\begin{equation}\n" caption))
- (t "\\["))
;; Prefix.
- (or (plist-get attr :math-prefix) "")
+ (plist-get attr :math-prefix)
;; Environment. Also treat special cases.
- (cond ((equal env "array")
- (let ((align (org-latex--align-string table info)))
- (format "\\begin{array}{%s}\n%s\\end{array}" align contents)))
+ (cond ((member env '("array" "tabular"))
+ (format "\\begin{%s}{%s}\n%s\\end{%s}"
+ env (org-latex--align-string table info t) contents env))
((assoc env org-latex-table-matrix-macros)
(format "\\%s%s{\n%s}"
env
@@ -2558,28 +3372,7 @@ This function assumes TABLE has `org' as its `:type' property and
contents))
(t (format "\\begin{%s}\n%s\\end{%s}" env contents env)))
;; Suffix.
- (or (plist-get attr :math-suffix) "")
- ;; Closing string. If TABLE is in the middle of a table cluster,
- ;; do not insert any. If it closes such a cluster, be sure to
- ;; close the cluster with a string matching the opening string.
- (cond ((and next
- (eq (org-element-type next) 'table)
- (memq (org-element-property :post-blank table) '(0 nil))
- (funcall same-mode-p next))
- nil)
- (inlinep "\\)")
- ;; Find cluster beginning to know which environment to use.
- ((let ((cluster-beg table) prev)
- (while (and (setq prev (org-export-get-previous-element
- cluster-beg info))
- (memq (org-element-property :post-blank prev)
- '(0 nil))
- (funcall same-mode-p prev))
- (setq cluster-beg prev))
- (and (or (org-element-property :caption cluster-beg)
- (org-element-property :name cluster-beg))
- "\n\\end{equation}")))
- (t "\\]")))))
+ (plist-get attr :math-suffix))))
;;;; Table Cell
@@ -2588,16 +3381,18 @@ This function assumes TABLE has `org' as its `:type' property and
"Transcode a TABLE-CELL element from Org to LaTeX.
CONTENTS is the cell contents. INFO is a plist used as
a communication channel."
- (concat (if (and contents
- org-latex-table-scientific-notation
- (string-match orgtbl-exp-regexp contents))
- ;; Use appropriate format string for scientific
- ;; notation.
- (format org-latex-table-scientific-notation
- (match-string 1 contents)
- (match-string 2 contents))
- contents)
- (when (org-export-get-next-element table-cell info) " & ")))
+ (concat
+ (let ((scientific-format (plist-get info :latex-table-scientific-notation)))
+ (if (and contents
+ scientific-format
+ (string-match orgtbl-exp-regexp contents))
+ ;; Use appropriate format string for scientific
+ ;; notation.
+ (format scientific-format
+ (match-string 1 contents)
+ (match-string 2 contents))
+ contents))
+ (when (org-export-get-next-element table-cell info) " & ")))
;;;; Table Row
@@ -2606,87 +3401,106 @@ a communication channel."
"Transcode a TABLE-ROW element from Org to LaTeX.
CONTENTS is the contents of the row. INFO is a plist used as
a communication channel."
- ;; Rules are ignored since table separators are deduced from
- ;; borders of the current row.
- (when (eq (org-element-property :type table-row) 'standard)
- (let* ((attr (org-export-read-attribute :attr_latex
- (org-export-get-parent table-row)))
- (longtablep (member (or (plist-get attr :environment)
- org-latex-default-table-environment)
- '("longtable" "longtabu")))
- (booktabsp (if (plist-member attr :booktabs)
- (plist-get attr :booktabs)
- org-latex-tables-booktabs))
- ;; TABLE-ROW's borders are extracted from its first cell.
- (borders (org-export-table-cell-borders
- (car (org-element-contents table-row)) info)))
+ (let* ((attr (org-export-read-attribute :attr_latex
+ (org-export-get-parent table-row)))
+ (booktabsp (if (plist-member attr :booktabs) (plist-get attr :booktabs)
+ (plist-get info :latex-tables-booktabs)))
+ (longtablep
+ (member (or (plist-get attr :environment)
+ (plist-get info :latex-default-table-environment))
+ '("longtable" "longtabu"))))
+ (if (eq (org-element-property :type table-row) 'rule)
+ (cond
+ ((not booktabsp) "\\hline")
+ ((not (org-export-get-previous-element table-row info)) "\\toprule")
+ ((not (org-export-get-next-element table-row info)) "\\bottomrule")
+ ((and longtablep
+ (org-export-table-row-ends-header-p
+ (org-export-get-previous-element table-row info) info))
+ "")
+ (t "\\midrule"))
(concat
;; When BOOKTABS are activated enforce top-rule even when no
;; hline was specifically marked.
- (cond ((and booktabsp (memq 'top borders)) "\\toprule\n")
- ((and (memq 'top borders) (memq 'above borders)) "\\hline\n"))
+ (and booktabsp (not (org-export-get-previous-element table-row info))
+ "\\toprule\n")
contents "\\\\\n"
(cond
- ;; Special case for long tables. Define header and footers.
+ ;; Special case for long tables. Define header and footers.
((and longtablep (org-export-table-row-ends-header-p table-row info))
- (format "%s
+ (let ((columns (cdr (org-export-table-dimensions
+ (org-export-get-parent-table table-row) info))))
+ (format "%s
+\\endfirsthead
+\\multicolumn{%d}{l}{%s} \\\\
+%s
+%s \\\\\n
+%s
\\endhead
-%s\\multicolumn{%d}{r}{Continued on next page} \\\\
+%s\\multicolumn{%d}{r}{%s} \\\\
\\endfoot
\\endlastfoot"
- (if booktabsp "\\midrule" "\\hline")
- (if booktabsp "\\midrule" "\\hline")
- ;; Number of columns.
- (cdr (org-export-table-dimensions
- (org-export-get-parent-table table-row) info))))
+ (if booktabsp "\\midrule" "\\hline")
+ columns
+ (org-latex--translate "Continued from previous page" info)
+ (cond
+ ((not (org-export-table-row-starts-header-p table-row info))
+ "")
+ (booktabsp "\\toprule\n")
+ (t "\\hline\n"))
+ contents
+ (if booktabsp "\\midrule" "\\hline")
+ (if booktabsp "\\midrule" "\\hline")
+ columns
+ (org-latex--translate "Continued on next page" info))))
;; When BOOKTABS are activated enforce bottom rule even when
;; no hline was specifically marked.
- ((and booktabsp (memq 'bottom borders)) "\\bottomrule")
- ((and (memq 'bottom borders) (memq 'below borders)) "\\hline")
- ((memq 'below borders) (if booktabsp "\\midrule" "\\hline")))))))
+ ((and booktabsp (not (org-export-get-next-element table-row info)))
+ "\\bottomrule"))))))
;;;; Target
-(defun org-latex-target (target contents info)
+(defun org-latex-target (target _contents info)
"Transcode a TARGET object from Org to LaTeX.
CONTENTS is nil. INFO is a plist holding contextual
information."
- (format "\\label{%s}"
- (org-export-solidify-link-text (org-element-property :value target))))
+ (format "\\label{%s}" (org-latex--label target info)))
;;;; Timestamp
-(defun org-latex-timestamp (timestamp contents info)
+(defun org-latex-timestamp (timestamp _contents info)
"Transcode a TIMESTAMP object from Org to LaTeX.
CONTENTS is nil. INFO is a plist holding contextual
information."
- (let ((value (org-latex-plain-text
- (org-timestamp-translate timestamp) info)))
- (case (org-element-property :type timestamp)
- ((active active-range) (format org-latex-active-timestamp-format value))
- ((inactive inactive-range)
- (format org-latex-inactive-timestamp-format value))
- (otherwise (format org-latex-diary-timestamp-format value)))))
+ (let ((value (org-latex-plain-text (org-timestamp-translate timestamp) info)))
+ (format
+ (plist-get info
+ (cl-case (org-element-property :type timestamp)
+ ((active active-range) :latex-active-timestamp-format)
+ ((inactive inactive-range) :latex-inactive-timestamp-format)
+ (otherwise :latex-diary-timestamp-format)))
+ value)))
;;;; Underline
-(defun org-latex-underline (underline contents info)
+(defun org-latex-underline (_underline contents info)
"Transcode UNDERLINE from Org to LaTeX.
CONTENTS is the text with underline markup. INFO is a plist
holding contextual information."
- (org-latex--text-markup contents 'underline))
+ (org-latex--text-markup contents 'underline info))
;;;; Verbatim
-(defun org-latex-verbatim (verbatim contents info)
+(defun org-latex-verbatim (verbatim _contents info)
"Transcode a VERBATIM object from Org to LaTeX.
CONTENTS is nil. INFO is a plist used as a communication
channel."
- (org-latex--text-markup (org-element-property :value verbatim) 'verbatim))
+ (org-latex--text-markup
+ (org-element-property :value verbatim) 'verbatim info))
;;;; Verse Block
@@ -2701,16 +3515,15 @@ contextual information."
;; 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" contents)))
- (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))))
+ (format "\\begin{verse}\n%s\\end{verse}"
+ (replace-regexp-in-string
+ "^[ \t]+" (lambda (m) (format "\\hspace*{%dem}" (length m)))
+ (replace-regexp-in-string
+ "^[ \t]*\\\\\\\\$" "\\vspace*{1em}"
+ (replace-regexp-in-string
+ "\\([ \t]*\\\\\\\\\\)?[ \t]*\n" "\\\\\n"
+ contents nil t) nil t) nil t))
+ info))
@@ -2753,9 +3566,9 @@ is non-nil."
;;;###autoload
(defun org-latex-convert-region-to-latex ()
- "Assume the current region has org-mode syntax, and convert it to LaTeX.
+ "Assume the current region has Org syntax, and convert it to LaTeX.
This can be used in any buffer. For example, you can write an
-itemized list in org-mode syntax in an LaTeX buffer and use this
+itemized list in Org syntax in an LaTeX buffer and use this
command to convert it."
(interactive)
(org-export-replace-region-by 'latex))
@@ -2831,86 +3644,78 @@ Return PDF file's name."
"Compile a TeX file.
TEXFILE is the name of the file being compiled. Processing is
-done through the command specified in `org-latex-pdf-process'.
+done through the command specified in `org-latex-pdf-process',
+which see. Output is redirected to \"*Org PDF LaTeX Output*\"
+buffer.
When optional argument SNIPPET is non-nil, TEXFILE is a temporary
file used to preview a LaTeX snippet. In this case, do not
-create a log buffer and do not bother removing log files.
-
-Return PDF file name or an error if it couldn't be produced."
- (let* ((base-name (file-name-sans-extension (file-name-nondirectory texfile)))
- (full-name (file-truename texfile))
- (out-dir (file-name-directory texfile))
- ;; Properly set working directory for compilation.
- (default-directory (if (file-name-absolute-p texfile)
- (file-name-directory full-name)
- default-directory))
- errors)
- (unless snippet (message "Processing LaTeX file %s..." texfile))
- (save-window-excursion
- (cond
- ;; A function is provided: Apply it.
- ((functionp org-latex-pdf-process)
- (funcall org-latex-pdf-process (shell-quote-argument texfile)))
- ;; A list is provided: Replace %b, %f and %o with appropriate
- ;; values in each command before applying it. Output is
- ;; redirected to "*Org PDF LaTeX Output*" buffer.
- ((consp org-latex-pdf-process)
- (let ((outbuf (and (not snippet)
- (get-buffer-create "*Org PDF LaTeX Output*"))))
- (mapc
- (lambda (command)
- (shell-command
- (replace-regexp-in-string
- "%b" (shell-quote-argument base-name)
- (replace-regexp-in-string
- "%f" (shell-quote-argument full-name)
- (replace-regexp-in-string
- "%o" (shell-quote-argument out-dir) command t t) t t) t t)
- outbuf))
- org-latex-pdf-process)
- ;; Collect standard errors from output buffer.
- (setq errors (and (not snippet) (org-latex--collect-errors outbuf)))))
- (t (error "No valid command to process to PDF")))
- (let ((pdffile (concat out-dir base-name ".pdf")))
- ;; Check for process failure. Provide collected errors if
- ;; possible.
- (if (not (file-exists-p pdffile))
- (error "PDF file %s wasn't produced%s" pdffile
- (if errors (concat ": " errors) ""))
- ;; Else remove log files, when specified, and signal end of
- ;; process to user, along with any error encountered.
- (when (and (not snippet) org-latex-remove-logfiles)
- (dolist (file (directory-files
- out-dir t
- (concat (regexp-quote base-name)
- "\\(?:\\.[0-9]+\\)?"
- "\\."
- (regexp-opt org-latex-logfiles-extensions))))
- (delete-file file)))
- (message (concat "Process completed"
- (if (not errors) "."
- (concat " with errors: " errors)))))
- ;; Return output file name.
- pdffile))))
-
-(defun org-latex--collect-errors (buffer)
- "Collect some kind of errors from \"pdflatex\" command output.
-
-BUFFER is the buffer containing output.
-
-Return collected error types as a string, or nil if there was
-none."
+create a log buffer and do not remove log files.
+
+Return PDF file name or raise an error if it couldn't be
+produced."
+ (unless snippet (message "Processing LaTeX file %s..." texfile))
+ (let* ((compiler
+ (or (with-temp-buffer
+ (save-excursion (insert-file-contents texfile))
+ (and (search-forward-regexp (regexp-opt org-latex-compilers)
+ (line-end-position 2)
+ t)
+ (progn (beginning-of-line) (looking-at-p "%"))
+ (match-string 0)))
+ "pdflatex"))
+ (process (if (functionp org-latex-pdf-process) org-latex-pdf-process
+ ;; Replace "%latex" and "%bibtex" with,
+ ;; respectively, "%L" and "%B" so as to adhere to
+ ;; `format-spec' specifications.
+ (mapcar (lambda (command)
+ (replace-regexp-in-string
+ "%\\(?:bib\\|la\\)tex\\>"
+ (lambda (m) (upcase (substring m 0 2)))
+ command))
+ org-latex-pdf-process)))
+ (spec `((?B . ,(shell-quote-argument org-latex-bib-compiler))
+ (?L . ,(shell-quote-argument compiler))))
+ (log-buf-name "*Org PDF LaTeX Output*")
+ (log-buf (and (not snippet) (get-buffer-create log-buf-name)))
+ (outfile (org-compile-file texfile process "pdf"
+ (format "See %S for details" log-buf-name)
+ log-buf spec)))
+ (unless snippet
+ (when org-latex-remove-logfiles
+ (mapc #'delete-file
+ (directory-files
+ (file-name-directory outfile)
+ t
+ (concat (regexp-quote (file-name-base outfile))
+ "\\(?:\\.[0-9]+\\)?\\."
+ (regexp-opt org-latex-logfiles-extensions))
+ t)))
+ (let ((warnings (org-latex--collect-warnings log-buf)))
+ (message (concat "PDF file produced"
+ (cond
+ ((eq warnings 'error) " with errors.")
+ (warnings (concat " with warnings: " warnings))
+ (t "."))))))
+ ;; Return output file name.
+ outfile))
+
+(defun org-latex--collect-warnings (buffer)
+ "Collect some warnings from \"pdflatex\" command output.
+BUFFER is the buffer containing output. Return collected
+warnings types as a string, `error' if a LaTeX error was
+encountered or nil if there was none."
(with-current-buffer buffer
(save-excursion
(goto-char (point-max))
(when (re-search-backward "^[ \t]*This is .*?TeX.*?Version" nil t)
- (let ((case-fold-search t)
- (errors ""))
- (dolist (latex-error org-latex-known-errors)
- (when (save-excursion (re-search-forward (car latex-error) nil t))
- (setq errors (concat errors " " (cdr latex-error)))))
- (and (org-string-nw-p errors) (org-trim errors)))))))
+ (if (re-search-forward "^!" nil t) 'error
+ (let ((case-fold-search t)
+ (warnings ""))
+ (dolist (warning org-latex-known-warnings)
+ (when (save-excursion (re-search-forward (car warning) nil t))
+ (setq warnings (concat warnings " " (cdr warning)))))
+ (org-string-nw-p (org-trim warnings))))))))
;;;###autoload
(defun org-latex-publish-to-latex (plist filename pub-dir)
@@ -2936,9 +3741,13 @@ Return output file name."
;; in working directory and then moved to publishing directory.
(org-publish-attachment
plist
- (org-latex-compile
- (org-publish-org-to
- 'latex filename ".tex" plist (file-name-directory filename)))
+ ;; Default directory could be anywhere when this function is
+ ;; called. We ensure it is set to source file directory during
+ ;; compilation so as to not break links to external documents.
+ (let ((default-directory (file-name-directory filename)))
+ (org-latex-compile
+ (org-publish-org-to
+ 'latex filename ".tex" plist (file-name-directory filename))))
pub-dir))