oc-csl.el (36657B)
1 ;;; oc-csl.el --- csl citation processor for Org -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. 4 5 ;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr> 6 ;; Maintainer: András Simonyi <andras.simonyi@gmail.com> 7 8 ;; This file is part of GNU Emacs. 9 10 ;; GNU Emacs is free software: you can redistribute it and/or modify 11 ;; it under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation, either version 3 of the License, or 13 ;; (at your option) any later version. 14 15 ;; GNU Emacs is distributed in the hope that it will be useful, 16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 ;; GNU General Public License for more details. 19 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 22 23 ;;; Commentary: 24 25 ;; This library registers the `csl' citation processor, which provides 26 ;; the "export" capability for citations. 27 28 ;; The processor relies on the external Citeproc Emacs library, which must be 29 ;; available prior to loading this library. 30 31 ;; By default, citations are rendered in Chicago author-date CSL style. You can 32 ;; use another style file by specifying it in `org-cite-export-processors' or 33 ;; from within the document by adding the file name to "cite_export" keyword 34 ;; 35 ;; #+cite_export: csl /path/to/style-file.csl 36 ;; #+cite_export: csl "/path/to/style-file.csl" 37 ;; 38 ;; With the variable `org-cite-csl-styles-dir' set appropriately, the 39 ;; above can even be shortened to 40 ;; 41 ;; #+cite_export: csl style-file.csl 42 ;; 43 ;; Styles can be downloaded, for instance, from the Zotero Style Repository 44 ;; (<https://www.zotero.org/styles>). Dependent styles (which are not "unique" 45 ;; in the Zotero Style Repository terminology) are not supported. 46 47 ;; The processor uses the "en-US" CSL locale file shipped with Org for rendering 48 ;; localized dates and terms in the references, independently of the language 49 ;; settings of the Org document. Additional CSL locales can be made available 50 ;; by setting `org-cite-csl-locales-dir' to a directory containing the locale 51 ;; files in question (see <https://github.com/citation-style-language/locales> 52 ;; for such files). 53 54 ;; Bibliography is defined with the "bibliography" keyword. It supports files 55 ;; with ".bib", ".bibtex", and ".json" extensions. References are exported using 56 ;; the "print_bibliography" keyword. 57 58 ;; The library supports the following citation styles: 59 ;; 60 ;; - author (a), including bare (b), caps (c), bare-caps (bc), full (f), 61 ;; caps-full (cf), and bare-caps-full (bcf) variants, 62 ;; - noauthor (na), including bare (b), caps (c) and bare-caps (bc) variants, 63 ;; - nocite (n), 64 ;; - year (y), including a bare (b) variant, 65 ;; - text (t), including caps (c), full (f), and caps-full (cf) variants, 66 ;; - title (ti), including a bare (b) variant, 67 ;; - locators (l), including a bare (b) variant, 68 ;; - bibentry (b), including a bare (b) variant, 69 ;; - default style, including bare (b), caps (c) and bare-caps (bc) variants. 70 ;; 71 ;; Using "*" as a key in a nocite citation includes all available 72 ;; items in the printed bibliography. The "bibentry" citation style, 73 ;; similarly to biblatex's \fullcite, creates a citation which is 74 ;; similar to the bibliography entry. 75 76 ;; CSL styles recognize "locator" in citation references' suffix. For example, 77 ;; in the citation 78 ;; 79 ;; [cite:see @Tarski-1965 chapter 1, for an example] 80 ;; 81 ;; "chapter 1" is the locator. The whole citation is rendered as 82 ;; 83 ;; (see Tarski 1965, chap. 1 for an example) 84 ;; 85 ;; in the default CSL style. 86 ;; 87 ;; The locator starts with a locator term, among "bk.", "bks.", "book", "chap.", 88 ;; "chaps.", "chapter", "col.", "cols.", "column", "figure", "fig.", "figs.", 89 ;; "folio", "fol.", "fols.", "number", "no.", "nos.", "line", "l.", "ll.", 90 ;; "note", "n.", "nn.", "opus", "op.", "opp.", "page", "p.", "pp.", "paragraph", 91 ;; "para.", "paras.", "¶", "¶¶", "§", "§§", "part", "pt.", "pts.", "section", 92 ;; "sec.", "secs.", "sub verbo", "s.v.", "s.vv.", "verse", "v.", "vv.", 93 ;; "volume", "vol.", and "vols.". It ends with the last comma or digit in the 94 ;; suffix, whichever comes last, or runs till the end of the suffix. 95 ;; 96 ;; The part of the suffix before the locator is appended to reference's prefix. 97 ;; If no locator term is used, but a number is present, then "page" is assumed. 98 99 ;; Filtered sub-bibliographies can be printed by passing filtering 100 ;; options to the "print_bibliography" keywords. E.g., 101 ;; 102 ;; #+print_bibliography: :type book keyword: emacs 103 ;; 104 ;; If you need to use a key multiple times, you can separate its 105 ;; values with commas, but without any space in-between: 106 ;; 107 ;; #+print_bibliography: :keyword abc,xyz :type article 108 109 ;; This library was heavily inspired by and borrows from András Simonyi's 110 ;; Citeproc Org (<https://github.com/andras-simonyi/citeproc-org>) library. 111 ;; Many thanks to him! 112 113 ;;; Code: 114 115 (require 'org-macs) 116 (org-assert-version) 117 118 (require 'cl-lib) 119 (require 'map) 120 (require 'bibtex) 121 (require 'json) 122 (require 'oc) 123 124 (require 'citeproc nil t) 125 (declare-function citeproc-style-cite-note "ext:citeproc") 126 (declare-function citeproc-proc-style "ext:citeproc") 127 (declare-function citeproc-bt-entry-to-csl "ext:citeproc") 128 (declare-function citeproc-locale-getter-from-dir "ext:citeproc") 129 (declare-function citeproc-create "ext:citeproc") 130 (declare-function citeproc-citation-create "ext:citeproc") 131 (declare-function citeproc-append-citations "ext:citeproc") 132 (declare-function citeproc-add-uncited "ext:citeproc") 133 (declare-function citeproc-render-citations "ext:citeproc") 134 (declare-function citeproc-render-bib "ext:citeproc") 135 (declare-function citeproc-hash-itemgetter-from-any "ext:citeproc") 136 (declare-function citeproc-add-subbib-filters "ext:citeproc") 137 (declare-function citeproc-style-cite-superscript-p "ext:citeproc") 138 139 (declare-function org-element-interpret-data "org-element" (data)) 140 (declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated)) 141 (declare-function org-element-property "org-element-ast" (property node)) 142 (declare-function org-element-put-property "org-element-ast" (node property value)) 143 144 (declare-function org-export-data "org-export" (data info)) 145 (declare-function org-export-derived-backend-p "org-export" (backend &rest backends)) 146 (declare-function org-export-get-footnote-number "org-export" (footnote info &optional data body-first)) 147 148 149 ;;; Customization 150 151 ;;;; Location of CSL directories 152 (defcustom org-cite-csl-locales-dir nil 153 "Directory of CSL locale files. 154 If nil then only the fallback en-US locale will be available." 155 :group 'org-cite 156 :package-version '(Org . "9.5") 157 :type '(choice 158 (directory :tag "Locales directory") 159 (const :tag "Use en-US locale only" nil)) 160 ;; It's not obvious to me that arbitrary locations are safe. 161 ;;; :safe #'string-or-null-p 162 ) 163 164 (defcustom org-cite-csl-styles-dir nil 165 "Directory of CSL style files. 166 167 Relative style file names are expanded according to document's 168 default directory. If it fails and the variable is non-nil, Org 169 looks for style files in this directory, too." 170 :group 'org-cite 171 :package-version '(Org . "9.5") 172 :type '(choice 173 (directory :tag "Styles directory") 174 (const :tag "No central directory for style files" nil)) 175 ;; It's not obvious to me that arbitrary locations are safe. 176 ;;; :safe #'string-or-null-p 177 ) 178 179 ;;;; Citelinks 180 (defcustom org-cite-csl-link-cites t 181 "When non-nil, link cites to references." 182 :group 'org-cite 183 :package-version '(Org . "9.5") 184 :type 'boolean 185 :safe #'booleanp) 186 187 (defcustom org-cite-csl-no-citelinks-backends '(ascii) 188 "List of export backends for which cite linking is disabled. 189 Cite linking for export backends derived from any of the backends listed here, 190 is also disabled." 191 :group 'org-cite 192 :package-version '(Org . "9.5") 193 :type '(repeat symbol)) 194 195 ;;;; Output-specific variables 196 (defcustom org-cite-csl-html-hanging-indent "1.5em" 197 "Size of hanging-indent for HTML output in valid CSS units." 198 :group 'org-cite 199 :package-version '(Org . "9.5") 200 :type 'string 201 :safe #'stringp) 202 203 (defcustom org-cite-csl-html-label-width-per-char "0.6em" 204 "Character width in CSS units for calculating entry label widths. 205 Used only when `second-field-align' is activated by the used CSL style." 206 :group 'org-cite 207 :package-version '(Org . "9.5") 208 :type 'string 209 :safe #'stringp) 210 211 (defcustom org-cite-csl-latex-hanging-indent "1.5em" 212 "Size of hanging-indent for LaTeX output in valid LaTeX units." 213 :group 'org-cite 214 :package-version '(Org . "9.5") 215 :type 'string 216 :safe #'stringp) 217 218 (defcustom org-cite-csl-latex-label-separator "0.6em" 219 "Distance between citation label and bibliography item for LaTeX output. 220 The value is a string representing the distance in valid LaTeX units. 221 Used only when `second-field-align' is activated by the used CSL 222 style. 223 224 The indentation length in these cases is computed as the sum of 225 `org-cite-csl-latex-label-separator' and the maximal label width, for 226 example, 227 228 indentation length 229 <-------------------------> 230 max. label width separator 231 <---------------><--------> 232 [Doe22] John Doe. A title... 233 [DoeSmithJones19] John Doe, Jane Smith and... 234 [SmithDoe02] Jane Smith and John Doe... 235 236 The maximal label width, in turn, is calculated as the product of 237 `org-cite-csl-latex-label-width-per-char' and the maximal label 238 length measured in characters." 239 :group 'org-cite 240 :package-version '(Org . "9.7") 241 :type 'string 242 :safe #'stringp) 243 244 (defcustom org-cite-csl-latex-label-width-per-char "0.45em" 245 "Character width in LaTeX units for calculating entry label widths. 246 Used only when `second-field-align' is activated by the used CSL 247 style. 248 249 See the documentation of `org-cite-csl-latex-label-separator' for 250 details." 251 :group 'org-cite 252 :package-version '(Org . "9.7") 253 :type 'string 254 :safe #'stringp) 255 256 ;; The following was inspired by and in many details follows how 257 ;; Pandoc's (<https://github.com/jgm/pandoc>) default LaTeX template 258 ;; handles CSL output. Many thanks to the author, John MacFarlane! 259 (defcustom org-cite-csl-latex-preamble 260 "\\usepackage{calc} 261 \\newlength{\\cslhangindent} 262 \\setlength{\\cslhangindent}{[CSL-HANGINDENT]} 263 \\newlength{\\csllabelsep} 264 \\setlength{\\csllabelsep}{[CSL-LABELSEP]} 265 \\newlength{\\csllabelwidth} 266 \\setlength{\\csllabelwidth}{[CSL-LABELWIDTH-PER-CHAR] * [CSL-MAXLABEL-CHARS]} 267 \\newenvironment{cslbibliography}[2] % 1st arg. is hanging-indent, 2nd entry spacing. 268 {% By default, paragraphs are not indented. 269 \\setlength{\\parindent}{0pt} 270 % Hanging indent is turned on when first argument is 1. 271 \\ifodd #1 272 \\let\\oldpar\\par 273 \\def\\par{\\hangindent=\\cslhangindent\\oldpar} 274 \\fi 275 % Set entry spacing based on the second argument. 276 \\setlength{\\parskip}{\\parskip + #2\\baselineskip} 277 }% 278 {} 279 \\newcommand{\\cslblock}[1]{#1\\hfill\\break} 280 \\newcommand{\\cslleftmargin}[1]{\\parbox[t]{\\csllabelsep + \\csllabelwidth}{#1}} 281 \\newcommand{\\cslrightinline}[1] 282 {\\parbox[t]{\\linewidth - \\csllabelsep - \\csllabelwidth}{#1}\\break} 283 \\newcommand{\\cslindent}[1]{\\hspace{\\cslhangindent}#1} 284 \\newcommand{\\cslbibitem}[2] 285 {\\leavevmode\\vadjust pre{\\hypertarget{citeproc_bib_item_#1}{}}#2} 286 \\makeatletter 287 \\newcommand{\\cslcitation}[2] 288 {\\protect\\hyper@linkstart{cite}{citeproc_bib_item_#1}#2\\hyper@linkend} 289 \\makeatother" 290 "LaTeX preamble content inserted by the `csl' citation processor. 291 292 This preamble can be anything as long as it provides definitions 293 for the environment and commands that Citeproc's `org-latex' 294 formatter uses for formatting citations and bibliographies. In 295 particular, it has to define 296 - the commands \\cslblock{<text>}, \\cslleftmargin{<text>}, 297 \\cslrightinline{<text>} and \\cslindent{<text>} for formatting 298 text that have, respectively, the CSL display attributes 299 `block', `left-margin', `right-inline' and `indent'; 300 - the commands \\cslcitation{<item_no>}{<item_text>} and 301 \\cslbibitem{<item_no>}{<item_text>}, which are used to 302 format individual citations and bibliography items, including 303 hyperlinking citations to the corresponding bibliography entry 304 using their numerical id, which is passed as the first, 305 <item_no> argument; 306 - and the environment \\cslbibliography{<hanging-indent>}{<entry-spacing>}, 307 in which bibliographies are wrapped; the value of the 308 <hanging-indent> argument is 1 if hanging indent should be 309 applied and 0 if not, while the <entry-spacing> argument is an 310 integer specifying the number of extra line-heights 311 required between bibliography entries in addition to normal 312 line spacing. 313 314 When present, the placeholders [CSL-HANGINDENT], [CSL-LABELSEP], 315 [CSL-LABELWIDTH-PER-CHAR] and [CSL-MAXLABEL-CHARS] are replaced, 316 respectively, by the contents of the customizable variables 317 `org-cite-csl-latex-hanging-indent', `org-cite-csl-latex-label-separator', 318 `org-cite-csl-latex-label-width-per-char', and the maximal label length 319 in the bibliography measured in characters." 320 :group 'org-cite 321 :type 'string 322 :package-version '(Org . "9.7")) 323 324 325 ;;; Internal variables 326 (defconst org-cite-csl--etc-dir 327 (let ((oc-root (file-name-directory (locate-library "oc")))) 328 (cond 329 ;; First check whether it looks like we're running from the main 330 ;; Org repository. 331 ((let ((csl-org (expand-file-name "../etc/csl/" oc-root))) 332 (and (file-directory-p csl-org) csl-org))) 333 ;; Next look for the directory alongside oc.el because package.el 334 ;; and straight will put all of org-mode/lisp/ in org-mode/. 335 ((let ((csl-pkg (expand-file-name "etc/csl/" oc-root))) 336 (and (file-directory-p csl-pkg) csl-pkg))) 337 ;; Finally fall back the location used by shared system installs 338 ;; and when running directly from Emacs repository. 339 (t 340 (expand-file-name "org/csl/" data-directory)))) 341 "Directory containing CSL-related data files.") 342 343 (defconst org-cite-csl--fallback-locales-dir org-cite-csl--etc-dir 344 "Fallback CSL locale files directory.") 345 346 (defconst org-cite-csl--fallback-style-file 347 (expand-file-name "chicago-author-date.csl" 348 org-cite-csl--etc-dir) 349 "Default CSL style file, or nil. 350 If nil then the Chicago author-date style is used as a fallback.") 351 352 (defconst org-cite-csl--label-alist 353 '(("bk." . "book") 354 ("bks." . "book") 355 ("book" . "book") 356 ("chap." . "chapter") 357 ("chaps." . "chapter") 358 ("chapter" . "chapter") 359 ("col." . "column") 360 ("cols." . "column") 361 ("column" . "column") 362 ("figure" . "figure") 363 ("fig." . "figure") 364 ("figs." . "figure") 365 ("folio" . "folio") 366 ("fol." . "folio") 367 ("fols." . "folio") 368 ("number" . "number") 369 ("no." . "number") 370 ("nos." . "number") 371 ("line" . "line") 372 ("l." . "line") 373 ("ll." . "line") 374 ("note" . "note") 375 ("n." . "note") 376 ("nn." . "note") 377 ("opus" . "opus") 378 ("op." . "opus") 379 ("opp." . "opus") 380 ("page" . "page") 381 ("p" . "page") 382 ("p." . "page") 383 ("pp." . "page") 384 ("paragraph" . "paragraph") 385 ("para." . "paragraph") 386 ("paras." . "paragraph") 387 ("\\P" . "paragraph") 388 ("¶" . "paragraph") 389 ("\\P\\P" . "paragraph") 390 ("¶¶" . "paragraph") 391 ("part" . "part") 392 ("pt." . "part") 393 ("pts." . "part") 394 ("§" . "section") 395 ("\\S" . "section") 396 ("§§" . "section") 397 ("\\S\\S" . "section") 398 ("section" . "section") 399 ("sec." . "section") 400 ("secs." . "section") 401 ("sub verbo" . "sub verbo") 402 ("s.v." . "sub verbo") 403 ("s.vv." . "sub verbo") 404 ("verse" . "verse") 405 ("v." . "verse") 406 ("vv." . "verse") 407 ("volume" . "volume") 408 ("vol." . "volume") 409 ("vols." . "volume")) 410 "Alist mapping locator names to locators.") 411 412 (defconst org-cite-csl--label-regexp 413 ;; Prior to Emacs-27.1 argument of `regexp' form must be a string literal. 414 ;; It is the reason why `rx' is avoided here. 415 (rx-to-string 416 `(seq (or line-start space) 417 (regexp ,(regexp-opt (mapcar #'car org-cite-csl--label-alist) t)) 418 (0+ digit) 419 (or word-end line-end space " ")) 420 t) 421 "Regexp matching a label in a citation reference suffix. 422 Label is in match group 1.") 423 424 425 ;;; Internal functions 426 427 (defun org-cite-csl--note-style-p (info) 428 "Non-nil when bibliography style implies wrapping citations in footnotes. 429 INFO is the export state, as a property list." 430 (citeproc-style-cite-note 431 (citeproc-proc-style 432 (org-cite-csl--processor info)))) 433 434 (defun org-cite-csl--style-cite-superscript-p (info) 435 "Non-nil when bibliography style produces citations in superscript. 436 INFO is the export state, as a property list." 437 (citeproc-style-cite-superscript-p 438 (citeproc-proc-style 439 (org-cite-csl--processor info)))) 440 441 (defun org-cite-csl--nocite-p (citation info) 442 "Non-nil when CITATION object's style is nocite. 443 INFO is the export state, as a property list." 444 (member (car (org-cite-citation-style citation info)) 445 '("nocite" "n"))) 446 447 (defun org-cite-csl--create-structure-params (citation info) 448 "Return citeproc structure creation params for CITATION object. 449 STYLE is the citation style, as a string or nil. INFO is the export 450 state, as a property list." 451 (let ((style (org-cite-citation-style citation info))) 452 (pcase style 453 ;; "author" style. 454 (`(,(or "author" "a") . ,variant) 455 (pcase variant 456 ((or "bare" "b") '(:mode author-only :suppress-affixes t)) 457 ((or "caps" "c") '(:mode author-only :capitalize-first t)) 458 ((or "full" "f") '(:mode author-only :ignore-et-al t)) 459 ((or "bare-caps" "bc") '(:mode author-only :suppress-affixes t :capitalize-first t)) 460 ((or "bare-full" "bf") '(:mode author-only :suppress-affixes t :ignore-et-al t)) 461 ((or "caps-full" "cf") '(:mode author-only :capitalize-first t :ignore-et-al t)) 462 ((or "bare-caps-full" "bcf") '(:mode author-only :suppress-affixes t :capitalize-first t :ignore-et-al t)) 463 (_ '(:mode author-only)))) 464 ;; "noauthor" style. 465 (`(,(or "noauthor" "na") . ,variant) 466 (pcase variant 467 ((or "bare" "b") '(:mode suppress-author :suppress-affixes t)) 468 ((or "caps" "c") '(:mode suppress-author :capitalize-first t)) 469 ((or "bare-caps" "bc") 470 '(:mode suppress-author :suppress-affixes t :capitalize-first t)) 471 (_ '(:mode suppress-author)))) 472 ;; "year" style. 473 (`(,(or "year" "y") . ,variant) 474 (pcase variant 475 ((or "bare" "b") '(:mode year-only :suppress-affixes t)) 476 (_ '(:mode year-only)))) 477 ;; "bibentry" style. 478 (`(,(or "bibentry" "b") . ,variant) 479 (pcase variant 480 ((or "bare" "b") '(:mode bib-entry :suppress-affixes t)) 481 (_ '(:mode bib-entry)))) 482 ;; "locators" style. 483 (`(,(or "locators" "l") . ,variant) 484 (pcase variant 485 ((or "bare" "b") '(:mode locator-only :suppress-affixes t)) 486 (_ '(:mode locator-only)))) 487 ;; "title" style. 488 (`(,(or "title" "ti") . ,variant) 489 (pcase variant 490 ((or "bare" "b") '(:mode title-only :suppress-affixes t)) 491 (_ '(:mode title-only)))) 492 ;; "text" style. 493 (`(,(or "text" "t") . ,variant) 494 (pcase variant 495 ((or "caps" "c") '(:mode textual :capitalize-first t)) 496 ((or "full" "f") '(:mode textual :ignore-et-al t)) 497 ((or "caps-full" "cf") '(:mode textual :ignore-et-al t :capitalize-first t)) 498 (_ '(:mode textual)))) 499 ;; Default "nil" style. 500 (`(,_ . ,variant) 501 (pcase variant 502 ((or "caps" "c") '(:capitalize-first t)) 503 ((or "bare" "b") '(:suppress-affixes t)) 504 ((or "bare-caps" "bc") '(:suppress-affixes t :capitalize-first t)) 505 (_ nil))) 506 ;; This should not happen. 507 (_ (error "Invalid style: %S" style))))) 508 509 (defun org-cite-csl--no-citelinks-p (info) 510 "Non-nil when export backend should not create cite-reference links. 511 INFO is the info channel plist." 512 (or (not org-cite-csl-link-cites) 513 (and org-cite-csl-no-citelinks-backends 514 (apply #'org-export-derived-backend-p 515 (plist-get info :back-end) 516 org-cite-csl-no-citelinks-backends)) 517 ;; No references are being exported anyway. 518 (not (org-element-map (plist-get info :parse-tree) 'keyword 519 (lambda (k) 520 (equal "PRINT_BIBLIOGRAPHY" (org-element-property :key k))) 521 info t)))) 522 523 (defun org-cite-csl--output-format (info) 524 "Return expected Citeproc's output format. 525 INFO is the export state, as a property list. The return value is a symbol 526 corresponding to one of the output formats supported by Citeproc: `html', 527 `latex', or `org'." 528 (let ((backend (plist-get info :back-end))) 529 (cond 530 ((org-export-derived-backend-p backend 'html) 'html) 531 ((org-export-derived-backend-p backend 'latex) 'org-latex) 532 (t 'org)))) 533 534 (defun org-cite-csl--style-file (info) 535 "Return style file associated to current export process. 536 537 INFO is the export state, as a property list. 538 539 When file name is relative, look for it in buffer's default 540 directory, failing that in `org-cite-csl-styles-dir' if non-nil. 541 Raise an error if no style file can be found." 542 (pcase (org-cite-bibliography-style info) 543 ('nil org-cite-csl--fallback-style-file) 544 ((and (pred file-name-absolute-p) file) file) 545 ((and (pred file-exists-p) file) (expand-file-name file)) 546 ((and (guard org-cite-csl-styles-dir) 547 (pred (lambda (f) 548 (file-exists-p 549 (expand-file-name f org-cite-csl-styles-dir)))) 550 file) 551 (expand-file-name file org-cite-csl-styles-dir)) 552 (other 553 (user-error "CSL style file not found: %S" other)))) 554 555 (defun org-cite-csl--locale-getter () 556 "Return a locale getter. 557 The getter looks for locales in `org-cite-csl-locales-dir' directory. If it 558 cannot find them, it retrieves the default \"en_US\" from 559 `org-cite-csl--fallback-locales-dir'." 560 (lambda (loc) 561 (or (and org-cite-csl-locales-dir 562 (ignore-errors 563 (funcall (citeproc-locale-getter-from-dir org-cite-csl-locales-dir) 564 loc))) 565 (funcall (citeproc-locale-getter-from-dir 566 org-cite-csl--fallback-locales-dir) 567 loc)))) 568 569 (defun org-cite-csl--processor (info) 570 "Return Citeproc processor reading items from current bibliography. 571 572 INFO is the export state, as a property list. 573 574 Newly created processor is stored as the value of the `:cite-citeproc-processor' 575 property in INFO." 576 (or (plist-get info :cite-citeproc-processor) 577 (let* ((bibliography (plist-get info :bibliography)) 578 (locale (or (plist-get info :language) "en_US")) 579 (processor 580 (citeproc-create 581 (org-cite-csl--style-file info) 582 (citeproc-hash-itemgetter-from-any bibliography) 583 (org-cite-csl--locale-getter) 584 locale))) 585 (plist-put info :cite-citeproc-processor processor) 586 processor))) 587 588 (defun org-cite-csl--parse-reference (reference info) 589 "Return Citeproc's structure associated to citation REFERENCE. 590 591 INFO is the export state, as a property list. 592 593 The result is a association list. Keys are: `id', `prefix',`suffix', 594 `location', `locator' and `label'." 595 (let (label location-start locator-start location locator prefix suffix) 596 ;; Parse suffix. Insert it in a temporary buffer to find 597 ;; different parts: pre-label, label, locator, location (label + 598 ;; locator), and suffix. 599 (with-temp-buffer 600 (save-excursion 601 (insert (org-element-interpret-data 602 (org-element-property :suffix reference)))) 603 (cond 604 ((re-search-forward org-cite-csl--label-regexp nil t) 605 (setq location-start (match-beginning 0)) 606 (setq label (cdr (assoc (match-string 1) org-cite-csl--label-alist))) 607 (goto-char (match-end 1)) 608 (skip-chars-forward "[:space:] ") 609 (setq locator-start (point))) 610 ((re-search-forward (rx digit) nil t) 611 (setq location-start (match-beginning 0)) 612 (setq label "page") 613 (setq locator-start location-start)) 614 (t 615 (setq suffix (org-element-property :suffix reference)))) 616 ;; Find locator's end, and suffix, if any. To that effect, look 617 ;; for the last comma or digit after label, whichever comes 618 ;; last. 619 (unless suffix 620 (goto-char (point-max)) 621 (let ((re (rx (or "," (group digit))))) 622 (when (re-search-backward re location-start t) 623 (goto-char (or (match-end 1) (match-beginning 0))) 624 (setq location (buffer-substring location-start (point))) 625 (setq locator (org-trim (buffer-substring locator-start (point)))) 626 ;; Skip comma in suffix. 627 (setq suffix 628 (org-cite-parse-objects 629 (buffer-substring (match-end 0) (point-max)) 630 t))))) 631 (setq prefix 632 (org-cite-concat 633 (org-element-property :prefix reference) 634 (and location-start 635 (org-cite-parse-objects 636 (buffer-substring 1 location-start) 637 t))))) 638 ;; Return value. 639 (let ((export 640 (lambda (data) 641 (org-string-nw-p 642 (org-trim 643 ;; When Citeproc exports to Org syntax, avoid mix and 644 ;; matching output formats by also generating Org 645 ;; syntax for prefix and suffix. 646 (if (eq 'org (org-cite-csl--output-format info)) 647 (org-element-interpret-data data) 648 (org-export-data data info))))))) 649 `((id . ,(org-element-property :key reference)) 650 (prefix . ,(funcall export prefix)) 651 (suffix . ,(funcall export suffix)) 652 (locator . ,locator) 653 (label . ,label) 654 (location . ,location))))) 655 656 (defun org-cite-csl--create-structure (citation info) 657 "Create Citeproc structure for CITATION object. 658 INFO is the export state, as a property list." 659 (let* ((cites (mapcar (lambda (r) 660 (org-cite-csl--parse-reference r info)) 661 (org-cite-get-references citation))) 662 (footnote (org-cite-inside-footnote-p citation))) 663 ;; Global prefix is inserted in front of the prefix of the first 664 ;; reference. 665 (let ((global-prefix (org-element-property :prefix citation))) 666 (when global-prefix 667 (let* ((first (car cites)) 668 (prefix-item (assq 'prefix first))) 669 (setcdr prefix-item 670 (concat (org-element-interpret-data global-prefix) 671 " " 672 (cdr prefix-item)))))) 673 ;; Global suffix is appended to the suffix of the last reference. 674 (let ((global-suffix (org-element-property :suffix citation))) 675 (when global-suffix 676 (let* ((last (org-last cites)) 677 (suffix-item (assq 'suffix last))) 678 (setcdr suffix-item 679 (concat (cdr suffix-item) 680 " " 681 (org-element-interpret-data global-suffix)))))) 682 ;; Check if CITATION needs wrapping, i.e., it should be wrapped in 683 ;; a footnote, but isn't yet. 684 (when (and (not footnote) (org-cite-csl--note-style-p info)) 685 (org-cite-adjust-note citation info) 686 (setq footnote (org-cite-wrap-citation citation info))) 687 ;; Remove white space before CITATION when it is in superscript. 688 (when (org-cite-csl--style-cite-superscript-p info) 689 (org-cite--set-previous-post-blank citation 0 info)) 690 ;; Return structure. 691 (apply #'citeproc-citation-create 692 `(:note-index 693 ,(and footnote (org-export-get-footnote-number footnote info)) 694 :cites ,cites 695 ,@(org-cite-csl--create-structure-params citation info))))) 696 697 (defun org-cite-csl--rendered-citations (info) 698 "Return the rendered citations as an association list. 699 700 INFO is the export state, as a property list. 701 702 Return an alist (CITATION . OUTPUT) where CITATION object has been rendered as 703 OUTPUT using Citeproc." 704 (or (plist-get info :cite-citeproc-rendered-citations) 705 (let ((citations (org-cite-list-citations info)) 706 (processor (org-cite-csl--processor info)) 707 normal-citations nocite-ids) 708 (dolist (citation citations) 709 (if (org-cite-csl--nocite-p citation info) 710 (setq nocite-ids (append (org-cite-get-references citation t) nocite-ids)) 711 (push citation normal-citations))) 712 (let ((structures 713 (mapcar (lambda (c) (org-cite-csl--create-structure c info)) 714 (nreverse normal-citations)))) 715 (citeproc-append-citations structures processor)) 716 (when nocite-ids 717 (citeproc-add-uncited nocite-ids processor)) 718 ;; All bibliographies have to be rendered in order to have 719 ;; correct citation numbers even if there are several 720 ;; sub-bibliograhies. 721 (org-cite-csl--rendered-bibliographies info) 722 (let (result 723 (rendered (citeproc-render-citations 724 processor 725 (org-cite-csl--output-format info) 726 (org-cite-csl--no-citelinks-p info)))) 727 (dolist (citation citations) 728 (push (cons citation 729 (if (org-cite-csl--nocite-p citation info) "" (pop rendered))) 730 result)) 731 (setq result (nreverse result)) 732 (plist-put info :cite-citeproc-rendered-citations result) 733 result)))) 734 735 (defun org-cite-csl--bibliography-filter (bib-props) 736 "Return the sub-bibliography filter corresponding to bibliography properties. 737 738 BIB-PROPS should be a plist representing the properties 739 associated with a \"print_bibliography\" keyword, as returned by 740 `org-cite-bibliography-properties'." 741 (let (result 742 (remove-keyword-colon (lambda (x) (intern (substring (symbol-name x) 1))))) 743 (map-do 744 (lambda (key value) 745 (pcase key 746 ((or :keyword :notkeyword :nottype :notcsltype :filter) 747 (dolist (v (split-string value ",")) 748 (push (cons (funcall remove-keyword-colon key) v) result))) 749 ((or :type :csltype) 750 (if (string-match-p "," value) 751 (user-error "The \"%s\" print_bibliography option does not support comma-separated values" key) 752 (push (cons (funcall remove-keyword-colon key) value) result))))) 753 bib-props) 754 result)) 755 756 (defun org-cite-csl--rendered-bibliographies (info) 757 "Return the rendered bibliographies. 758 759 INFO is the export state, as a property list. 760 761 Return an (OUTPUTS PARAMETERS) list where OUTPUTS is an alist 762 of (BIB-PROPS . OUTPUT) pairs where each key is a property list 763 of a \"print_bibliography\" keyword and the corresponding OUTPUT 764 value is the bibliography as rendered by Citeproc." 765 (or (plist-get info :cite-citeproc-rendered-bibliographies) 766 (let (bib-plists bib-filters) 767 ;; Collect bibliography property lists and the corresponding 768 ;; Citeproc sub-bib filters. 769 (org-element-map (plist-get info :parse-tree) 'keyword 770 (lambda (keyword) 771 (when (equal "PRINT_BIBLIOGRAPHY" (org-element-property :key keyword)) 772 (let ((bib-plist (org-cite-bibliography-properties keyword))) 773 (push bib-plist bib-plists) 774 (push (org-cite-csl--bibliography-filter bib-plist) bib-filters))))) 775 (setq bib-filters (nreverse bib-filters) 776 bib-plists (nreverse bib-plists)) 777 ;; Render and return all bibliographies. 778 (let ((processor (org-cite-csl--processor info))) 779 (citeproc-add-subbib-filters bib-filters processor) 780 (pcase-let* ((format (org-cite-csl--output-format info)) 781 (`(,rendered-bibs . ,parameters) 782 (citeproc-render-bib 783 (org-cite-csl--processor info) 784 format 785 (org-cite-csl--no-citelinks-p info))) 786 (outputs (cl-mapcar #'cons bib-plists rendered-bibs)) 787 (result (list outputs parameters))) 788 (plist-put info :cite-citeproc-rendered-bibliographies result) 789 result))))) 790 791 (defun org-cite-csl--generate-latex-preamble (info) 792 "Generate the CSL-related part of the LaTeX preamble. 793 INFO is the export state, as a property list." 794 (let* ((parameters (cadr (org-cite-csl--rendered-bibliographies info))) 795 (max-offset (cdr (assq 'max-offset parameters))) 796 (result org-cite-csl-latex-preamble)) 797 (map-do (lambda (placeholder replacement) 798 (when (string-match placeholder result) 799 (setq result (replace-match replacement t t result)))) 800 `("\\[CSL-HANGINDENT\\]" ,org-cite-csl-latex-hanging-indent 801 "\\[CSL-LABELSEP\\]" ,org-cite-csl-latex-label-separator 802 "\\[CSL-LABELWIDTH-PER-CHAR\\]" ,org-cite-csl-latex-label-width-per-char 803 "\\[CSL-MAXLABEL-CHARS\\]" ,(number-to-string max-offset))) 804 result)) 805 806 807 ;;; Export capability 808 (defun org-cite-csl-render-citation (citation _style _backend info) 809 "Export CITATION object. 810 INFO is the export state, as a property list." 811 (org-require-package 'citeproc) 812 (let ((output (cdr (assq citation (org-cite-csl--rendered-citations info))))) 813 (if (not (eq 'org (org-cite-csl--output-format info))) 814 output 815 ;; Parse Org output to re-export it during the regular export 816 ;; process. 817 (org-cite-parse-objects output)))) 818 819 (defun org-cite-csl-render-bibliography (_keys _files _style props _backend info) 820 "Export bibliography. 821 INFO is the export state, as a property list." 822 (org-require-package 'citeproc) 823 (pcase-let* ((format (org-cite-csl--output-format info)) 824 (`(,outputs ,parameters) (org-cite-csl--rendered-bibliographies info)) 825 (output (cdr (assoc props outputs)))) 826 (pcase format 827 ('html 828 (concat 829 (and (cdr (assq 'second-field-align parameters)) 830 (let* ((max-offset (cdr (assq 'max-offset parameters))) 831 (char-width 832 (string-to-number org-cite-csl-html-label-width-per-char)) 833 (char-width-unit 834 (progn 835 (string-match (number-to-string char-width) 836 org-cite-csl-html-label-width-per-char) 837 (substring org-cite-csl-html-label-width-per-char 838 (match-end 0))))) 839 (format 840 "<style>.csl-left-margin{float: left; padding-right: 0em;} 841 .csl-right-inline{margin: 0 0 0 %d%s;}</style>" 842 (* max-offset char-width) 843 char-width-unit))) 844 (and (cdr (assq 'hanging-indent parameters)) 845 (format 846 "<style>.csl-entry{text-indent: -%s; margin-left: %s;}</style>" 847 org-cite-csl-html-hanging-indent 848 org-cite-csl-html-hanging-indent)) 849 output)) 850 ('org-latex output) 851 (_ 852 ;; Parse Org output to re-export it during the regular export 853 ;; process. 854 (org-cite-parse-elements output))))) 855 856 (defun org-cite-csl-finalizer (output _keys _files _style _backend info) 857 "Add \"hanging\" package if missing from LaTeX output. 858 OUTPUT is the export document, as a string. INFO is the export state, as a 859 property list." 860 (org-require-package 'citeproc) 861 (if (not (eq 'org-latex (org-cite-csl--output-format info))) 862 output 863 (with-temp-buffer 864 (save-excursion (insert output)) 865 (when (search-forward "\\begin{document}" nil t) 866 (goto-char (match-beginning 0)) 867 ;; Insert the CSL-specific parts of the LaTeX preamble. 868 (insert (org-cite-csl--generate-latex-preamble info))) 869 (buffer-string)))) 870 871 872 ;;; Register `csl' processor 873 (org-cite-register-processor 'csl 874 :export-citation #'org-cite-csl-render-citation 875 :export-bibliography #'org-cite-csl-render-bibliography 876 :export-finalizer #'org-cite-csl-finalizer 877 :cite-styles 878 '((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf")) 879 (("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc")) 880 (("year" "y") ("bare" "b")) 881 (("text" "t") ("caps" "c") ("full" "f") ("caps-full" "cf")) 882 (("nil") ("bare" "b") ("caps" "c") ("bare-caps" "bc")) 883 (("nocite" "n")) 884 (("title" "ti") ("bare" "b")) 885 (("bibentry" "b") ("bare" "b")) 886 (("locators" "l") ("bare" "b")))) 887 888 (provide 'oc-csl) 889 ;;; oc-csl.el ends here