ox-latex: Handle entities cluster

* lisp/ox-latex.el (org-latex-pseudo-objects): New variable.
(org-latex--wrap-latex-math-block): New function.
(org-latex-table, org-latex--math-table): declare pseudo objects.

This patch provides support for constructs like:

  \alpha_b

which should be translated as

  $\alpha_{\text{b}}$

instead of

  $\alpha$$_{\text{b}}$
This commit is contained in:
Nicolas Goaziou 2013-11-09 18:26:47 +01:00
parent da05ab32f1
commit 24b61d9105
1 changed files with 82 additions and 42 deletions

View File

@ -89,7 +89,9 @@
(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.
(latex-math-block . org-latex-math-block))
:export-block '("LATEX" "TEX")
:menu-entry
'(?l "Export to LaTeX"
@ -106,7 +108,8 @@
(: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)))
(:date "DATE" nil "\\today" t))
:filters-alist '((:filter-parse-tree . org-latex--wrap-latex-math-block)))
@ -170,6 +173,9 @@
("kbordermatrix" . "\\\\"))
"Alist between matrix macros and their row ending.")
(defconst org-latex-pseudo-objects '(latex-math-block)
"List of pseudo-object types introduced in the back-end.")
;;; User Configurable Variables
@ -1241,8 +1247,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
@ -1644,8 +1649,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
@ -1990,6 +2001,61 @@ holding contextual information."
(format "\\begin{verbatim}\n%s\\end{verbatim}" contents)))
;;;; Pseudo Object: LaTeX Math Block
(defun org-latex--wrap-latex-math-block (tree backend info)
"Merge contiguous math objects in a pseudo-object container.
TREE is the parse tree. BACKEND is the export back-end used.
INFO is a plist used as a communication channel. Return parse
tree."
(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 tree '(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-objects (org-export-get-next-element object info t)))
;; Insert empty MATH-BLOCK in parse tree.
(org-element-insert-before math-block object)
;; MATH-BLOCK swallows consecutive math objects.
(while (and (let ((blank (org-element-property :post-blank object)))
(or (null blank) (zerop blank)))
next-objects
(funcall valid-object-p (setq next (pop next-objects))))
(org-element-adopt-elements math-block
(org-element-extract-element object))
;; Eschew the following case: \alpha$x$ -> \(\alphax\).
(unless (memq (org-element-type next) '(subscript subscript))
(org-element-put-property object :post-blank 1))
(setq object next))
(org-element-put-property
math-block :post-blank (org-element-property :post-blank object))
(org-element-adopt-elements math-block
(org-element-extract-element object)))))
info nil '(subscript superscript latex-math-block) t)
;; Return updated parse tree.
tree))
(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)
@ -2219,17 +2285,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 +2311,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.
@ -2323,7 +2360,8 @@ contextual information."
(format "\\begin{verbatim}\n%s\n\\end{verbatim}"
;; Re-create table, without affiliated keywords.
(org-trim (org-element-interpret-data
`(table nil ,@(org-element-contents table))))))
`(table nil ,@(org-element-contents table))
org-latex-pseudo-objects))))
;; Case 2: Matrix.
((or (string= type "math") (string= type "inline-math"))
(org-latex--math-table table info))
@ -2518,7 +2556,9 @@ This function assumes TABLE has `org' as its `:type' property and
(concat
(mapconcat
(lambda (cell)
(substring (org-element-interpret-data cell) 0 -1))
(substring
(org-element-interpret-data cell org-latex-pseudo-objects)
0 -1))
(org-element-map row 'table-cell 'identity info) "&")
(or (cdr (assoc env org-latex-table-matrix-macros)) "\\\\")
"\n")))