diff options
Diffstat (limited to 'lisp/ox-latex.el')
-rw-r--r-- | lisp/ox-latex.el | 1924 |
1 files changed, 1256 insertions, 668 deletions
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index 2c71f7d..c3eb1ea 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -1,6 +1,6 @@ ;;; ox-latex.el --- LaTeX Back-End for Org Export Engine -;; Copyright (C) 2011-2014 Free Software Foundation, Inc. +;; Copyright (C) 2011-2015 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com> ;; Keywords: outlines, hypermedia, calendar, wp @@ -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,7 +86,10 @@ (timestamp . org-latex-timestamp) (underline . org-latex-underline) (verbatim . org-latex-verbatim) - (verse-block . org-latex-verse-block)) + (verse-block . org-latex-verse-block) + ;; Pseudo objects and elements. + (latex-math-block . org-latex-math-block) + (latex-matrices . org-latex-matrices)) :export-block '("LATEX" "TEX") :menu-entry '(?l "Export to LaTeX" @@ -99,13 +100,52 @@ (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-parse-tree org-latex-math-block-tree-filter + org-latex-matrices-tree-filter)) + :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-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-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) + ;; Redefine regular options. + (:date "DATE" nil "\\today" parse))) @@ -164,11 +204,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 +319,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 "25.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_LATEX + And this is still section \\ref{sec:foo}. + #+END_LATEX + +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 "25.1" + :package-version '(Org . "8.3")) ;;;; Preamble @@ -264,11 +478,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 +546,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 "25.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 "25.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 +590,32 @@ 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: \"%%\". + +Setting :latex-hyperref-template in publishing projects will take +precedence over this variable." :group 'org-export-latex - :type 'boolean) + :version "25.1" + :package-version '(Org . "8.3") + :type '(string :tag "Format string")) ;;;; Headline @@ -352,17 +623,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). +TEXT the main headline text (string) +TAGS the tags (list of strings or nil) +INFO the export options (plist) -The function result will be used in the section format string. - -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") @@ -489,12 +758,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 +776,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 +792,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,6 +815,8 @@ 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 "25.1" + :package-version '(Org . "8.3") :type 'alist :options '(bold code italic strike-through underline verbatim)) @@ -575,44 +842,24 @@ The default function simply returns the value of CONTENTS." ;;;; 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 behaviour: - -\(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 "25.1" + :package-version '(Org . "8.3")) ;; Src blocks @@ -640,7 +887,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 +903,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 +916,8 @@ into previewing problems, please consult (shell-script "bash") (gnuplot "Gnuplot") (ocaml "Caml") (caml "Caml") - (sql "SQL") (sqlite "sql")) + (sql "SQL") (sqlite "sql") + (makefile "make")) "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 +925,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 "24.4" + :package-version '(Org . "8.3") :type '(repeat (list (symbol :tag "Major mode ") @@ -697,7 +948,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,7 +1001,13 @@ 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 @@ -840,11 +1103,14 @@ file name as its single argument." (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 "25.1" + :package-version '(Org . "8.3") :type '(repeat (string :tag "Extension"))) (defcustom org-latex-remove-logfiles t @@ -855,19 +1121,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 "25.1" + :package-version '(Org . "8.3") :type '(repeat (cons (string :tag "Regexp") @@ -877,6 +1144,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 + (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 (case type + (headline "sec:") + (table "tab:") + (latex-environment + (and (org-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 +1199,39 @@ 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)) + (memq (plist-get info :latex-listings) + '(nil minted))))) (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) (concat label "\n")) ;; 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") + (if nonfloat + (case type + (paragraph "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. @@ -958,6 +1287,59 @@ 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--find-verb-separator (s) "Return a character not used in string S. This is used to choose a separator for constructs like \\verb." @@ -978,52 +1360,50 @@ nil." 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)))) - -(defun org-latex--text-markup (text markup) + (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 + "--\\|[\\{}$%&_#~^]" + (lambda (m) + (cond ((equal m "--") "-{}-") + ((equal m "\\") "\\textbackslash{}") + ((equal m "~") "\\textasciitilde{}") + ((equal m "^") "\\textasciicircum{}") + (t (concat "\\" m)))) + text nil t)) + +(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))))) + (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}" (org-latex--protect-text text))) + ;; Else use format string. + (t (format fmt text))))) (defun org-latex--delayed-footnotes-definitions (element info) "Return footnotes definitions in ELEMENT as a string. @@ -1065,6 +1445,57 @@ just outside of it." (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 lang org-latex-babel-language-alist)) + 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--make-header (info) + "Return a formatted LaTeX header. +INFO is a plist used as a communication channel." + (let* ((class (plist-get info :latex-class)) + (class-options (plist-get info :latex-class-options)) + (header (nth 1 (assoc class (plist-get info :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-polyglossia-language + (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) + info)))) ;;; Template @@ -1073,34 +1504,14 @@ just outside of it." "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")) ;; 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-header info) ;; Possibly limit depth for headline numbering. (let ((sec-num (plist-get info :section-numbers))) (when (integerp sec-num) @@ -1117,40 +1528,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}"))) @@ -1164,7 +1581,7 @@ holding export options." "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,8 +1591,7 @@ 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 @@ -1187,10 +1603,8 @@ 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))))) "\\\\")) @@ -1202,7 +1616,7 @@ information." "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 +1626,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,7 +1637,7 @@ 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 @@ -1232,8 +1646,7 @@ holding contextual information. See `org-export-data'." "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 @@ -1243,10 +1656,16 @@ contextual information." 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 @@ -1276,7 +1695,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." 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 @@ -1288,7 +1708,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;; 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)) + (plist-get info :latex-footnote-separator))) (cond ;; Use \footnotemark if the footnote has already been defined. ((not (org-export-footnote-first-reference-p footnote-reference info)) @@ -1296,9 +1716,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." (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))) + ((org-element-lineage footnote-reference + '(footnote-reference footnote-definition table-cell)) "\\footnotemark") ;; Otherwise, define it with \footnote command. (t @@ -1321,7 +1740,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 +1784,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 @@ -1404,15 +1819,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 (org-string-match-p "\\<headlines\\>" v) + (org-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 +1859,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,7 +1867,9 @@ 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 (lambda (tag) (org-latex-plain-text tag info)) + tags ":"))))) ;;;; Horizontal Rule @@ -1456,7 +1890,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." horizontal-rule (format "\\rule{%s}{%s}" (or (plist-get attr :width) "\\linewidth") - (or (plist-get attr :thickness) "0.5pt")))))) + (or (plist-get attr :thickness) "0.5pt")) + info)))) ;;;; Inline Src Block @@ -1467,34 +1902,33 @@ 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)))))) + (case (plist-get info :latex-listings) + ;; Do not use a special package: transcode it verbatim. + ((nil) (format "\\texttt{%s}" (org-latex--protect-text code))) + ;; 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)))) + (concat (format "\\mint%s{%s}" + (if (string= options "") "" (format "[%s]" options)) + mint-lang) + separator code separator))) + ;; 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,31 +1945,33 @@ 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 + (lambda (tag) (org-latex-plain-text tag info)) + 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 @@ -1544,7 +1980,7 @@ holding contextual information." "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 @@ -1621,24 +2057,31 @@ 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"))))))))) + ((org-string-match-p "\\<headlines\\>" value) + (let* ((localp (org-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")))) + ((org-string-match-p "\\<tables\\>" value) "\\listoftables") + ((org-string-match-p "\\<listings\\>" value) + (case (plist-get info :latex-listings) + ((nil) "\\listoffigures") + (minted "\\listoflistings") + (otherwise "\\lstlistoflistings"))))))))) ;;;; Latex Environment @@ -1647,10 +2090,9 @@ CONTENTS is nil. INFO is a plist holding contextual information." "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 + (let ((value (org-remove-indentation (org-element-property :value latex-environment)))) - (if (not (org-string-nw-p label)) value + (if (not (org-element-property :name latex-environment)) value ;; Environment is labeled: label must be within the environment ;; (otherwise, a reference pointing to that element will count ;; the section instead). @@ -1658,8 +2100,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." (insert value) (goto-char (point-min)) (forward-line) - (insert - (format "\\label{%s}\n" (org-export-solidify-link-text label))) + (insert (org-latex--label latex-environment info nil t)) (buffer-string)))))) @@ -1668,8 +2109,14 @@ CONTENTS is nil. INFO is a plist holding contextual information." (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 "\\`\\(\\$\\{1,2\\}\\)\\([^\000]*\\)\\1\\'" value) + (match-string 2 value)) + ((string-match "\\`\\\\(\\([^\000]*\\)\\\\)\\'" value) + (match-string 1 value)) + (t value)))) ;;;; Line Break @@ -1692,36 +2139,41 @@ 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) ((or float (org-element-property :caption parent) (org-string-nw-p (plist-get attr :caption))) - 'figure)))) + (if (and (plist-member attr :float) (not float)) + 'nonfloat + 'figure)) + ((and (not float) (plist-member attr :float)) nil)))) (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 "")))) (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,6 +2202,12 @@ 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") + (org-string-match-p "\\`[0-9]+\\'" search-option) + (not (org-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)) "") @@ -1769,17 +2227,43 @@ used as a communication channel." ;; Return proper string, depending on FLOAT. (case float (wrap (format "\\begin{wrapfigure}%s -\\centering +%s\\centering +%s%s +%s\\end{wrapfigure}" + placement + (if caption-above-p caption "") + comment-include image-code + (if caption-above-p "" caption))) + (sideways (format "\\begin{sidewaysfigure} +%s\\centering %s%s -%s\\end{wrapfigure}" placement comment-include image-code caption)) +%s\\end{sidewaysfigure}" + (if caption-above-p caption "") + comment-include image-code + (if caption-above-p "" caption))) (multicolumn (format "\\begin{figure*}%s -\\centering +%s\\centering %s%s -%s\\end{figure*}" placement comment-include image-code caption)) +%s\\end{figure*}" + placement + (if caption-above-p caption "") + comment-include image-code + (if caption-above-p "" caption))) (figure (format "\\begin{figure}%s -\\centering +%s\\centering +%s%s +%s\\end{figure}" + placement + (if caption-above-p caption "") + comment-include image-code + (if caption-above-p "" caption))) + (nonfloat + (format "\\begin{center} %s%s -%s\\end{figure}" placement comment-include image-code caption)) +%s\\end{center}" + (if caption-above-p caption "") + image-code + (if caption-above-p "" caption))) (otherwise image-code)))) (defun org-latex-link (link desc info) @@ -1794,15 +2278,15 @@ INFO is a plist holding contextual information. See ;; 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)) + link (plist-get info :latex-inline-image-rules))) (path (cond - ((member type '("http" "https" "ftp" "mailto")) + ((member type '("http" "https" "ftp" "mailto" "doi")) (concat type ":" raw-path)) - ((and (string= type "file") (file-name-absolute-p raw-path)) - (concat "file:" raw-path)) - (t raw-path))) - protocol) + ((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 +2295,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. @@ -1826,8 +2309,8 @@ INFO is a plist holding contextual information. See (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 +2319,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,23 +2329,32 @@ 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 @@ -1898,7 +2385,8 @@ contextual information." latex-type (or (plist-get attr :options) "") contents - latex-type)))) + latex-type) + info))) ;;;; Plain Text @@ -1907,47 +2395,35 @@ 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) + (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)) @@ -1968,27 +2444,169 @@ 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)) + (org-element-put-property + matrices :post-blank (org-element-property :post-blank previous)) + (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 (case (org-element-property :markup matrices) + (inline "\\(%s\\)") + (equation "\\begin{equation}\n%s\\end{equation}") + (t "\\[\n%s\\]")) + contents)) + +(defun org-latex-matrices-tree-filter (tree backend info) + (org-latex--wrap-latex-matrices tree info)) + +;;;; 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 + (function + ;; Non-nil when OBJ can be added to the latex math block. + (lambda (obj) + (case (org-element-type obj) + (entity (org-element-property :latex-math-p obj)) + (latex-fragment + (let ((value (org-element-property :value obj))) + (or (org-string-match-p "\\`\\\\([^\000]*\\\\)\\'" value) + (org-string-match-p "\\`\\$[^\000]*\\$\\'" value)))) + ((subscript superscript) t)))))) + (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)) + (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) + (if (not (funcall valid-object-p next)) (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-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-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 +2614,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,10 +2623,7 @@ 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 @@ -2037,14 +2641,14 @@ 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)))) @@ -2057,6 +2661,7 @@ 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) @@ -2066,56 +2671,68 @@ contextual information." (new 0))) (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))) + (format "\\begin{%s}\n%s\\end{%s}\n" + custom-env + (concat (and caption-above-p caption-str) + (org-export-format-code-default src-block info) + (and (not caption-above-p) caption-str)) + custom-env))) ;; 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*}\n%s%%s\n%s\\end{listing*}" + (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"))) + (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)) @@ -2142,7 +2759,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 +2770,32 @@ 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" ,label)) '(("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"))) + ((zerop num-start) '(("numbers" "left"))) + (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}" @@ -2210,7 +2833,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." "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,17 +2842,7 @@ 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)) + (let ((type (org-element-type object)) (output "")) (org-element-map (org-element-contents object) (cons 'plain-text org-element-all-objects) @@ -2255,31 +2868,12 @@ 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) "Transcode a SUBSCRIPT object from Org to LaTeX. @@ -2316,7 +2910,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") @@ -2376,14 +2970,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 +2987,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 +3019,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 +3032,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 +3058,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 "}"))))))) @@ -2492,7 +3101,7 @@ property." (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,12 +3112,10 @@ 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) @@ -2519,38 +3126,18 @@ This function assumes TABLE has `org' as its `:type' property and (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")) + (let ((align (make-string + (cdr (org-export-table-dimensions table info)) ?c))) + (format "\\begin{%s}{%s}\n%s\\end{%s}" env align contents env))) ((assoc env org-latex-table-matrix-macros) (format "\\%s%s{\n%s}" env @@ -2558,28 +3145,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 +3154,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,44 +3174,62 @@ 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 @@ -2652,8 +3238,7 @@ a communication channel." "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 @@ -2662,13 +3247,14 @@ information." "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 + (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 @@ -2677,7 +3263,7 @@ information." "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 @@ -2686,7 +3272,8 @@ holding contextual information." "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 +3288,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)) @@ -2845,7 +3431,8 @@ Return PDF file name or an error if it couldn't be produced." (default-directory (if (file-name-absolute-p texfile) (file-name-directory full-name) default-directory)) - errors) + (time (current-time)) + warnings) (unless snippet (message (format "Processing LaTeX file %s..." texfile))) (save-window-excursion (cond @@ -2858,59 +3445,60 @@ Return PDF file name or an error if it couldn't be produced." ((consp org-latex-pdf-process) (let ((outbuf (and (not snippet) (get-buffer-create "*Org PDF LaTeX Output*")))) - (mapc - (lambda (command) - (shell-command + (dolist (command org-latex-pdf-process) + (shell-command + (replace-regexp-in-string + "%b" (shell-quote-argument base-name) (replace-regexp-in-string - "%b" (shell-quote-argument base-name) + "%f" (shell-quote-argument full-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) + "%o" (shell-quote-argument out-dir) command t t) t t) t t) + outbuf)) ;; Collect standard errors from output buffer. - (setq errors (and (not snippet) (org-latex--collect-errors outbuf))))) + (setq warnings (and (not snippet) + (org-latex--collect-warnings 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 (concat (format "PDF file %s wasn't produced" pdffile) - (when errors (concat ": " errors)))) + (if (or (not (file-exists-p pdffile)) + (time-less-p (nth 5 (file-attributes pdffile)) time)) + (error (format "PDF file %s wasn't produced" pdffile)) ;; 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))))) + (unless snippet + (when 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 "PDF file produced" + (cond + ((eq warnings 'error) " with errors.") + (warnings (concat " with warnings: " warnings)) + (t ".")))))) ;; 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." +(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) |