From 2bdbb02901b9831f3bb6b3d29ff8050eadd69e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Simonyi?= Date: Tue, 27 Dec 2022 23:15:34 +0100 Subject: [PATCH] oc-csl: Improve LaTeX bibliography formatting * lisp/oc-csl.el (org-cite-csl--output-format): Use the dedicated 'org-latex' citeproc formatter to export references in LaTeX. (org-cite-csl-latex-preamble, org-cite-csl--generate-latex-preamble, org-cite-csl-finalizer): Insert a preamble fragment compatible with the 'org-latex' citeproc formatter. (org-cite-csl-latex-label-separator, org-cite-csl-latex-label-width-per-char): Introduce additional variables to control bibliography formatting. * etc/ORG-NEWS: Describe the introduced new options. --- etc/ORG-NEWS | 27 +++++++++ lisp/oc-csl.el | 145 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 155 insertions(+), 17 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index d4e9b4368..1185a51b2 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -13,6 +13,33 @@ Please send Org bug reports to mailto:emacs-orgmode@gnu.org. * Version 9.7 (not released yet) ** New options +*** New custom settings for the "csl" citation export processor's LaTeX output + +The settings ~org-cite-csl-latex-label-separator~ and +~org-cite-csl-latex-label-width-per-char~ allow the user to control +the indentation of entries for labeled bibliography styles when the +"csl" citation processor is used for LaTeX export. The indentation +length is computed as the sum of ~org-cite-csl-latex-label-separator~ +and the maximal label width, for example: + +#+begin_example + indentation length +<-------------------------> +max. label width separator +<---------------><--------> +[Doe22] John Doe. A title... +[DoeSmithJones19] John Doe, Jane Smith and... +[SmithDoe02] Jane Smith and John Doe... +#+end_example + +The maximal label width, in turn, is calculated as the product of +~org-cite-csl-latex-label-width-per-char~ and the maximal label length +measured in characters. + +The setting ~org-cite-csl-latex-preamble~ makes it possible to +customize the entire LaTeX fragment that the "csl" citation processor injects +into the preamble. + *** New ~org-latex-listings-src-omit-language~ customization for LaTeX export The ~org-latex-listings-src-omit-language~ customization variable diff --git a/lisp/oc-csl.el b/lisp/oc-csl.el index 1ccb74e92..11194b9b4 100644 --- a/lisp/oc-csl.el +++ b/lisp/oc-csl.el @@ -214,6 +214,111 @@ Used only when `second-field-align' is activated by the used CSL style." :type 'string :safe #'stringp) +(defcustom org-cite-csl-latex-label-separator "0.6em" + "Distance between citation label and bibliography item for LaTeX +output in valid LaTeX units. Used only when `second-field-align' +is activated by the used CSL style. + +The indentation length in these cases is computed as the sum of +`org-cite-csl-latex-label-separator' and the maximal label width, +for example, + + indentation length +<-------------------------> +max. label width separator +<---------------><--------> +[Doe22] John Doe. A title... +[DoeSmithJones19] John Doe, Jane Smith and... +[SmithDoe02] Jane Smith and John Doe... + +The maximal label width, in turn, is calculated as the product of +`org-cite-csl-latex-label-width-per-char' and the maximal label +length measured in characters." + :group 'org-cite + :package-version '(Org . "9.7") + :type 'string + :safe #'stringp) + +(defcustom org-cite-csl-latex-label-width-per-char "0.45em" + "Character width in LaTeX units for calculating entry label widths. +Used only when `second-field-align' is activated by the used CSL +style. + +See the documentation of `org-cite-csl-latex-label-separator' for +details." + :group 'org-cite + :package-version '(Org . "9.7") + :type 'string + :safe #'stringp) + +;; The following was inspired by and in many details follows how +;; Pandoc's () default LaTeX template +;; handles CSL output. Many thanks to the author, John MacFarlane! +(defcustom org-cite-csl-latex-preamble + "\\usepackage{calc} +\\newlength{\\cslhangindent} +\\setlength{\\cslhangindent}{[CSL-HANGINDENT]} +\\newlength{\\csllabelsep} +\\setlength{\\csllabelsep}{[CSL-LABELSEP]} +\\newlength{\\csllabelwidth} +\\setlength{\\csllabelwidth}{[CSL-LABELWIDTH-PER-CHAR] * [CSL-MAXLABEL-CHARS]} +\\newenvironment{cslbibliography}[2] % 1st arg. is hanging-indent, 2nd entry spacing. + {% By default, paragraphs are not indented. + \\setlength{\\parindent}{0pt} + % Hanging indent is turned on when first argument is 1. + \\ifodd #1 + \\let\\oldpar\\par + \\def\\par{\\hangindent=\\cslhangindent\\oldpar} + \\fi + % Set entry spacing based on the second argument. + \\setlength{\\parskip}{\\parskip + #2\\baselineskip} + }% + {} +\\newcommand{\\cslblock}[1]{#1\\hfill\\break} +\\newcommand{\\cslleftmargin}[1]{\\parbox[t]{\\csllabelsep + \\csllabelwidth}{#1}} +\\newcommand{\\cslrightinline}[1] + {\\parbox[t]{\\linewidth - \\csllabelsep - \\csllabelwidth}{#1}\\break} +\\newcommand{\\cslindent}[1]{\\hspace{\\cslhangindent}#1} +\\newcommand{\\cslbibitem}[2] + {\\leavevmode\\vadjust pre{\\hypertarget{citeproc_bib_item_#1}{}}#2} +\\makeatletter +\\newcommand{\\cslcitation}[2] + {\\protect\\hyper@linkstart{cite}{citeproc_bib_item_#1}#2\\hyper@linkend} +\\makeatother" + "LaTeX preamble content inserted by the `csl' citation processor. + +This preamble can be anything as long as it provides definitions +for the environment and commands that Citeproc's `org-latex' +formatter uses for formatting citations and bibliographies. In +particular, it has to define +- the commands \\cslblock{}, \\cslleftmargin{}, + \\cslrightinline{} and \\cslindent{} for formatting + text that have, respectively, the CSL display attributes + `block', `left-margin', `right-inline' and `indent'; +- the commands \\cslcitation{}{} and + \\cslbibitem{}{}, which are used to + format individual citations and bibliography items, including + hyperlinking citations to the corresponding bibliography entry + using their numerical id, which is passed as the first, + argument; +- and the environment \\cslbibliography{}{}, + in which bibliographies are wrapped; the value of the + argument is 1 if hanging indent should be + applied and 0 if not, while the argument is an + integer specifying the number of extra line-heights + required between bibliography entries in addition to normal + line spacing. + +When present, the placeholders [CSL-HANGINDENT], [CSL-LABELSEP], +[CSL-LABELWIDTH-PER-CHAR] and [CSL-MAXLABEL-CHARS] are replaced, +respectively, by the contents of the customizable variables +`org-cite-csl-latex-hanging-indent', `org-cite-csl-latex-label-separator', +`org-cite-csl-latex-label-width-per-char', and the maximal label length +in the bibliography measured in characters." + :group 'org-cite + :type 'string + :package-version '(Org . "9.7")) + ;;; Internal variables (defconst org-cite-csl--etc-dir @@ -413,7 +518,7 @@ corresponding to one of the output formats supported by Citeproc: `html', (let ((backend (plist-get info :back-end))) (cond ((org-export-derived-backend-p backend 'html) 'html) - ((org-export-derived-backend-p backend 'latex) 'latex) + ((org-export-derived-backend-p backend 'latex) 'org-latex) (t 'org)))) (defun org-cite-csl--style-file (info) @@ -670,6 +775,21 @@ value is the bibliography as rendered by Citeproc." (plist-put info :cite-citeproc-rendered-bibliographies result) result))))) +(defun org-cite-csl--generate-latex-preamble (info) + "Generate the CSL-related part of the LaTeX preamble. +INFO is the export state, as a property list." + (let* ((parameters (cadr (org-cite-csl--rendered-bibliographies info))) + (max-offset (cdr (assq 'max-offset parameters))) + (result org-cite-csl-latex-preamble)) + (map-do (lambda (placeholder replacement) + (when (string-match placeholder result) + (setq result (replace-match replacement t t result)))) + `("\\[CSL-HANGINDENT\\]" ,org-cite-csl-latex-hanging-indent + "\\[CSL-LABELSEP\\]" ,org-cite-csl-latex-label-separator + "\\[CSL-LABELWIDTH-PER-CHAR\\]" ,org-cite-csl-latex-label-width-per-char + "\\[CSL-MAXLABEL-CHARS\\]" ,(number-to-string max-offset))) + result)) + ;;; Export capability (defun org-cite-csl-render-citation (citation _style _backend info) @@ -688,8 +808,8 @@ INFO is the export state, as a property list." INFO is the export state, as a property list." (org-cite-csl--barf-without-citeproc) (pcase-let* ((format (org-cite-csl--output-format info)) - (`(,outputs ,parameters) (org-cite-csl--rendered-bibliographies info)) - (output (cdr (assoc props outputs)))) + (`(,outputs ,parameters) (org-cite-csl--rendered-bibliographies info)) + (output (cdr (assoc props outputs)))) (pcase format ('html (concat @@ -714,12 +834,7 @@ INFO is the export state, as a property list." org-cite-csl-html-hanging-indent org-cite-csl-html-hanging-indent)) output)) - ('latex - (if (cdr (assq 'hanging-indent parameters)) - (format "\\begin{hangparas}{%s}{1}\n%s\n\\end{hangparas}" - org-cite-csl-latex-hanging-indent - output) - output)) + ('org-latex output) (_ ;; Parse Org output to re-export it during the regular export ;; process. @@ -730,18 +845,14 @@ INFO is the export state, as a property list." OUTPUT is the export document, as a string. INFO is the export state, as a property list." (org-cite-csl--barf-without-citeproc) - (if (not (eq 'latex (org-cite-csl--output-format info))) + (if (not (eq 'org-latex (org-cite-csl--output-format info))) output (with-temp-buffer (save-excursion (insert output)) (when (search-forward "\\begin{document}" nil t) - (goto-char (match-beginning 0)) - ;; Ensure that \citeprocitem is defined for citeproc-el. - (insert "\\makeatletter\n\\newcommand{\\citeprocitem}[2]{\\hyper@linkstart{cite}{citeproc_bib_item_#1}#2\\hyper@linkend}\n\\makeatother\n\n") - ;; Ensure there is a \usepackage{hanging} somewhere or add one. - (let ((re (rx "\\usepackage" (opt "[" (*? nonl) "]") "{hanging}"))) - (unless (re-search-backward re nil t) - (insert "\\usepackage[notquote]{hanging}\n")))) + (goto-char (match-beginning 0)) + ;; Insert the CSL-specific parts of the LaTeX preamble. + (insert (org-cite-csl--generate-latex-preamble info))) (buffer-string)))) -- 2.25.1