From 14dceebeff73ff49e197f18b8235a40497bdbf55 Mon Sep 17 00:00:00 2001 Message-Id: <14dceebeff73ff49e197f18b8235a40497bdbf55.1665206183.git.yantar92@gmail.com> From: Ihor Radchenko Date: Fri, 7 Oct 2022 16:24:32 +0800 Subject: [PATCH v2] ox-latex: Protect [...] after \\ to be interpreted as LaTeX argument * lisp/ox-latex.el (org-latex-linebreak-safe): New constant holding safe version of LaTeX line break. (org-latex-linebreak-safe-newline): New constant holding `org-latex-linebreak-safe' with newline appended. (org-latex-table-matrix-macros): (org-latex-clock): (org-latex-line-break): (org-latex-plain-text): (org-latex-planning): (org-latex--org-table): (org-latex--math-table): (org-latex-table-row): (org-latex-verse-block): Use the new constants. * testing/lisp/test-org-table.el (test-org-table/to-latex): Update tests. Reported-by: Stewart Thomas Link: https://orgmode.org/list/ce760fc3-5aae-144d-2d02-7dea215f73fc@gmail.com --- lisp/ox-latex.el | 50 ++++++++++++++++++++++++---------- testing/lisp/test-org-table.el | 6 ++-- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index 34ff52b81..d340b9c95 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -278,10 +278,26 @@ (defconst org-latex-language-alist - `:lang-name' the actual name of the language.") +(defconst org-latex-linebreak-safe "\\\\\\relax" + "Linebreak protecting the following [...]. -(defconst org-latex-table-matrix-macros '(("bordermatrix" . "\\cr") +Without \"\\relax\" it would be interpreted as an optional argument to +the \\\\. + +This constant, for example, makes the below code not err: + +\\begin{tabular}{c|c} + [t] & s\\\\\\relax + [I] & A\\\\\\relax + [m] & kg +\\end{tabular}") + +(defconst org-latex-linebreak-safe-newline (concat org-latex-linebreak-safe "\n") + "`org-latex-linebreak-safe' with newline.") + +(defconst org-latex-table-matrix-macros `(("bordermatrix" . "\\cr") ("qbordermatrix" . "\\cr") - ("kbordermatrix" . "\\\\")) + ("kbordermatrix" . ,org-latex-linebreak-safe)) "Alist between matrix macros and their row ending.") (defconst org-latex-math-environments-re @@ -2062,7 +2078,7 @@ (defun org-latex-clock (clock _contents info) (concat (org-timestamp-translate (org-element-property :value clock)) (let ((time (org-element-property :duration clock))) (and time (format " (%s)" time))))) - "\\\\")) + org-latex-linebreak-safe)) ;;;; Code @@ -2659,7 +2675,7 @@ ;;;; Line Break (defun org-latex-line-break (_line-break _contents _info) "Transcode a LINE-BREAK object from Org to LaTeX. CONTENTS is nil. INFO is a plist holding contextual information." - "\\\\\n") + org-latex-linebreak-safe-newline) ;;;; Link @@ -3005,7 +3021,9 @@ (defun org-latex-plain-text (text info) ;; Handle break preservation if required. (when (plist-get info :preserve-breaks) (setq output (replace-regexp-in-string - "\\(?:[ \t]*\\\\\\\\\\)?[ \t]*\n" "\\\\\n" output nil t))) + "\\(?:[ \t]*\\\\\\\\\\)?[ \t]*\n" + org-latex-linebreak-safe-newline + output nil t))) ;; Return value. output)) @@ -3041,7 +3059,7 @@ (defun org-latex-planning (planning _contents info) (format (plist-get info :latex-active-timestamp-format) (org-timestamp-translate scheduled))))))) " ") - "\\\\")) + org-latex-linebreak-safe)) ;;;; Property Drawer @@ -3821,11 +3839,11 @@ (defun org-latex--org-table (table contents info) (format "\\begin{%s}%s{%s}\n" table-env width alignment) (and above? (org-string-nw-p caption) - (concat caption "\\\\\n")) + org-latex-linebreak-safe-newline) contents (and (not above?) (org-string-nw-p caption) - (concat caption "\\\\\n")) + org-latex-linebreak-safe-newline) (format "\\end{%s}" table-env) (and fontsize "}")))) (t @@ -3910,7 +3928,7 @@ (defun org-latex--math-table (table info) (lambda (cell) (substring (org-element-interpret-data cell) 0 -1)) (org-element-map row 'table-cell #'identity info) "&") - (or (cdr (assoc env org-latex-table-matrix-macros)) "\\\\") + (or (cdr (assoc env org-latex-table-matrix-macros)) org-latex-linebreak-safe) "\n"))) (org-element-map table 'table-row #'identity info) ""))) (concat @@ -3982,7 +4000,7 @@ (defun org-latex-table-row (table-row contents info) ;; hline was specifically marked. (and booktabsp (not (org-export-get-previous-element table-row info)) "\\toprule\n") - contents "\\\\\n" + contents org-latex-linebreak-safe-newline (cond ;; Special case for long tables. Define header and footers. ((and longtablep (org-export-table-row-ends-header-p table-row info)) @@ -3990,9 +4008,9 @@ (defun org-latex-table-row (table-row contents info) (org-export-get-parent-table table-row) info)))) (format "%s \\endfirsthead -\\multicolumn{%d}{l}{%s} \\\\ +\\multicolumn{%d}{l}{%s} \\\\\\relax %s -%s \\\\\n +%s \\\\\\relax\n %s \\endhead %s\\multicolumn{%d}{r}{%s} \\\\ @@ -4092,8 +4110,12 @@ (defun org-latex-verse-block (verse-block contents info) (replace-regexp-in-string "^[ \t]*\\\\\\\\$" "\\vspace*{1em}" (replace-regexp-in-string - "\\([ \t]*\\\\\\\\\\)?[ \t]*\n" "\\\\\n" - contents nil t) nil t) nil t) linreset) + "\\([ \t]*\\\\\\\\\\)?[ \t]*\n" + org-latex-linebreak-safe-newline + contents nil t) + nil t) + nil t) + linreset) info) ;; Insert footnote definitions, if any, after the environment, so ;; the special formatting above is not applied to them. diff --git a/testing/lisp/test-org-table.el b/testing/lisp/test-org-table.el index 76fe41630..3e7f18a10 100644 --- a/testing/lisp/test-org-table.el +++ b/testing/lisp/test-org-table.el @@ -1635,11 +1635,11 @@ (ert-deftest test-org-table/to-generic () (ert-deftest test-org-table/to-latex () "Test `orgtbl-to-latex' specifications." (should - (equal "\\begin{tabular}{l}\na\\\\\n\\end{tabular}" + (equal "\\begin{tabular}{l}\na\\\\\\relax\n\\end{tabular}" (orgtbl-to-latex (org-table-to-lisp "| a |") nil))) ;; Test :environment parameter. (should - (equal "\\begin{tabularx}{l}\na\\\\\n\\end{tabularx}" + (equal "\\begin{tabularx}{l}\na\\\\\\relax\n\\end{tabularx}" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:environment "tabularx")))) ;; Test :booktabs parameter. @@ -1648,7 +1648,7 @@ (ert-deftest test-org-table/to-latex () "\\toprule" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:booktabs t)))) ;; Handle LaTeX snippets. (should - (equal "\\begin{tabular}{l}\n\\(x\\)\\\\\n\\end{tabular}" + (equal "\\begin{tabular}{l}\n\\(x\\)\\\\\\relax\n\\end{tabular}" (orgtbl-to-latex (org-table-to-lisp "| $x$ |") nil))) ;; Test pseudo objects and :raw parameter. (should -- 2.35.1