oc-csl.el 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. ;;; oc-csl.el --- csl citation processor for Org -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2021 Free Software Foundation, Inc.
  3. ;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr>
  4. ;; This program is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation, either version 3 of the License, or
  7. ;; (at your option) any later version.
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;; You should have received a copy of the GNU General Public License
  13. ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. ;;; Commentary:
  15. ;; This library registers the `csl' citation processor, which provides
  16. ;; the "export" capability for citations.
  17. ;; The processor relies on the external Citeproc Emacs library, which must be
  18. ;; available prior to loading this library.
  19. ;; By default, citations are rendered in Chicago author-date CSL style. You can
  20. ;; use another style file by specifying it in `org-cite-export-processors' or
  21. ;; from within the document by adding the file name to "cite_export" keyword
  22. ;;
  23. ;; #+cite_export: csl /path/to/style-file.csl
  24. ;; #+cite_export: csl "/path/to/style-file.csl"
  25. ;;
  26. ;; With the variable `org-cite-csl-styles-dir' set appropriately, the
  27. ;; above can even be shortened to
  28. ;;
  29. ;; #+cite_export: csl style-file.csl
  30. ;;
  31. ;; Styles can be downloaded, for instance, from the Zotero Style Repository
  32. ;; (<https://www.zotero.org/styles>). Dependent styles (which are not "unique"
  33. ;; in the Zotero Style Repository terminology) are not supported.
  34. ;; The processor uses the "en-US" CSL locale file shipped with Org for rendering
  35. ;; localized dates and terms in the references, independently of the language
  36. ;; settings of the Org document. Additional CSL locales can be made available
  37. ;; by setting `org-cite-csl-locales-dir' to a directory containing the locale
  38. ;; files in question (see <https://github.com/citation-style-language/locales>
  39. ;; for such files).
  40. ;; Bibliography is defined with the "bibliography" keyword. It supports files
  41. ;; with ".bib", ".bibtex", and ".json" extensions. References are exported using
  42. ;; the "print_bibliography" keyword.
  43. ;; The library supports the following citation styles:
  44. ;;
  45. ;; - noauthor (na), including bare (b), caps (c) and bare-caps (bc) variants,
  46. ;; - default style, including bare (b), caps (c) and bare-caps (bc) variants.
  47. ;; CSL styles recognize "locator" in citation references' suffix. For example,
  48. ;; in the citation
  49. ;;
  50. ;; [cite:see @Tarski-1965 chapter 1, for an example]
  51. ;;
  52. ;; "chapter 1" is the locator. The whole citation is rendered as
  53. ;;
  54. ;; (see Tarski 1965, chap. 1 for an example)
  55. ;;
  56. ;; in the default CSL style.
  57. ;;
  58. ;; The locator starts with a locator term, among "bk.", "bks.", "book", "chap.",
  59. ;; "chaps.", "chapter", "col.", "cols.", "column", "figure", "fig.", "figs.",
  60. ;; "folio", "fol.", "fols.", "number", "no.", "nos.", "line", "l.", "ll.",
  61. ;; "note", "n.", "nn.", "opus", "op.", "opp.", "page", "p.", "pp.", "paragraph",
  62. ;; "para.", "paras.", "¶", "¶¶", "§", "§§", "part", "pt.", "pts.", "section",
  63. ;; "sec.", "secs.", "sub verbo", "s.v.", "s.vv.", "verse", "v.", "vv.",
  64. ;; "volume", "vol.", and "vols.". It ends with the last comma or digit in the
  65. ;; suffix, whichever comes last, or runs till the end of the suffix.
  66. ;;
  67. ;; The part of the suffix before the locator is appended to reference's prefix.
  68. ;; If no locator term is used, but a number is present, then "page" is assumed.
  69. ;; This library was heavily inspired by and borrows from András Simonyi's
  70. ;; Citeproc Org (<https://github.com/andras-simonyi/citeproc-org>) library.
  71. ;; Many thanks to him!
  72. ;;; Code:
  73. (require 'bibtex)
  74. (require 'json)
  75. (require 'org-cite)
  76. (require 'citeproc nil t)
  77. (declare-function citeproc-style-cite-note "ext:citeproc")
  78. (declare-function citeproc-proc-style "ext:citeproc")
  79. (declare-function citeproc-bt-entry-to-csl "ext:citeproc")
  80. (declare-function citeproc-locale-getter-from-dir "ext:citeproc")
  81. (declare-function citeproc-create "ext:citeproc")
  82. (declare-function citeproc-citation-create "ext:citeproc")
  83. (declare-function citeproc-append-citations "ext:citeproc")
  84. (declare-function citeproc-render-citations "ext:citeproc")
  85. (declare-function citeproc-render-bib "ext:citeproc")
  86. (declare-function org-element-interpret-data "org-element" (data))
  87. (declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
  88. (declare-function org-element-property "org-element" (property element))
  89. (declare-function org-element-put-property "org-element" (element property value))
  90. (declare-function org-export-data "org-export" (data info))
  91. (declare-function org-export-derived-backend-p "org-export" (backend &rest backends))
  92. (declare-function org-export-get-footnote-number "org-export" (footnote info &optional data body-first))
  93. ;;; Customization
  94. ;;;; Location of CSL directories
  95. (defcustom org-cite-csl-locales-dir nil
  96. "Directory of CSL locale files.
  97. If nil then only the fallback en-US locale will be available."
  98. :group 'org-cite
  99. :package-version '(Org . "9.5")
  100. :type '(choice
  101. (dir :tag "Locales directory")
  102. (const :tag "Use en-US locale only" nil))
  103. :safe t)
  104. (defcustom org-cite-csl-styles-dir nil
  105. "Directory of CSL style files.
  106. When non-nil, relative style file names are expanded relatively to this
  107. directory. This variable is ignored when style file is absolute."
  108. :group 'org-cite
  109. :package-version '(Org . "9.5")
  110. :type '(choice
  111. (dir :tag "Styles directory")
  112. (const :tag "Use absolute file names" nil))
  113. :safe t)
  114. ;;;; Citelinks
  115. (defcustom org-cite-csl-link-cites t
  116. "When non-nil, link cites to references."
  117. :group 'org-cite
  118. :package-version '(Org . "9.5")
  119. :type 'boolean
  120. :safe t)
  121. (defcustom org-cite-csl-no-citelinks-backends '(ascii)
  122. "List of export back-ends for which cite linking is disabled.
  123. Cite linking for export back-ends derived from any of the back-ends listed here,
  124. is also disabled."
  125. :group 'org-cite
  126. :package-version '(Org . "9.5")
  127. :type '(repeat symbol)
  128. :safe t)
  129. ;;;; Output-specific variables
  130. (defcustom org-cite-csl-html-hanging-indent "1.5em"
  131. "Size of hanging-indent for HTML output in valid CSS units."
  132. :group 'org-cite
  133. :package-version '(Org . "9.5")
  134. :type 'string
  135. :safe t)
  136. (defcustom org-cite-csl-html-label-width-per-char "0.6em"
  137. "Character width in CSS units for calculating entry label widths.
  138. Used only when `second-field-align' is activated by the used CSL style."
  139. :group 'org-cite
  140. :package-version '(Org . "9.5")
  141. :type 'string
  142. :safe t)
  143. (defcustom org-cite-csl-latex-hanging-indent "1.5em"
  144. "Size of hanging-indent for LaTeX output in valid LaTeX units."
  145. :group 'org-cite
  146. :package-version '(Org . "9.5")
  147. :type 'string
  148. :safe t)
  149. ;;; Internal variables
  150. (defconst org-cite-csl--etc-dir
  151. (let* ((oc-root (file-name-directory (locate-library "oc")))
  152. (oc-etc-dir-1 (expand-file-name "../etc/csl/" oc-root)))
  153. ;; package.el and straight will put all of org-mode/lisp/ in org-mode/.
  154. ;; This will cause .. to resolve to the directory above Org.
  155. ;; To make life easier for people using package.el or straight, we can
  156. ;; check to see if ../etc/csl exists, and if it doesn't try ./etc/csl.
  157. (if (file-exists-p oc-etc-dir-1) oc-etc-dir-1
  158. (expand-file-name "etc/csl/" oc-root)))
  159. "Directory \"etc/\" from repository.")
  160. (defconst org-cite-csl--fallback-locales-dir org-cite-csl--etc-dir
  161. "Fallback CSL locale files directory.")
  162. (defconst org-cite-csl--fallback-style-file
  163. (expand-file-name "chicago-author-date.csl"
  164. org-cite-csl--etc-dir)
  165. "Default CSL style file, or nil.
  166. If nil then the Chicago author-date style is used as a fallback.")
  167. (defconst org-cite-csl--label-alist
  168. '(("bk." . "book")
  169. ("bks." . "book")
  170. ("book" . "book")
  171. ("chap." . "chapter")
  172. ("chaps." . "chapter")
  173. ("chapter" . "chapter")
  174. ("col." . "column")
  175. ("cols." . "column")
  176. ("column" . "column")
  177. ("figure" . "figure")
  178. ("fig." . "figure")
  179. ("figs." . "figure")
  180. ("folio" . "folio")
  181. ("fol." . "folio")
  182. ("fols." . "folio")
  183. ("number" . "number")
  184. ("no." . "number")
  185. ("nos." . "number")
  186. ("line" . "line")
  187. ("l." . "line")
  188. ("ll." . "line")
  189. ("note" . "note")
  190. ("n." . "note")
  191. ("nn." . "note")
  192. ("opus" . "opus")
  193. ("op." . "opus")
  194. ("opp." . "opus")
  195. ("page" . "page")
  196. ("p" . "page")
  197. ("p." . "page")
  198. ("pp." . "page")
  199. ("paragraph" . "paragraph")
  200. ("para." . "paragraph")
  201. ("paras." . "paragraph")
  202. ("¶" . "paragraph")
  203. ("¶¶" . "paragraph")
  204. ("§" . "paragraph")
  205. ("§§" . "paragraph")
  206. ("part" . "part")
  207. ("pt." . "part")
  208. ("pts." . "part")
  209. ("section" . "section")
  210. ("sec." . "section")
  211. ("secs." . "section")
  212. ("sub verbo" . "sub verbo")
  213. ("s.v." . "sub verbo")
  214. ("s.vv." . "sub verbo")
  215. ("verse" . "verse")
  216. ("v." . "verse")
  217. ("vv." . "verse")
  218. ("volume" . "volume")
  219. ("vol." . "volume")
  220. ("vols." . "volume"))
  221. "Alist mapping locator names to locators.")
  222. (defconst org-cite-csl--label-regexp
  223. (rx word-start
  224. (regexp (regexp-opt (mapcar #'car org-cite-csl--label-alist) t))
  225. (0+ digit)
  226. (or word-start line-end (any ?\s ?\t)))
  227. "Regexp matching a label in a citation reference suffix.
  228. Label is in match group 1.")
  229. ;;; Internal functions
  230. (defun org-cite-csl--barf-without-citeproc ()
  231. "Raise an error if Citeproc library is not loaded."
  232. (unless (featurep 'citeproc) "Citeproc library is not loaded"))
  233. (defun org-cite-csl--note-style-p (info)
  234. "Non-nil when bibliography style implies wrapping citations in footnotes.
  235. INFO is the export state, as a property list."
  236. (citeproc-style-cite-note
  237. (citeproc-proc-style
  238. (org-cite-csl--processor info))))
  239. (defun org-cite-csl--no-affixes-p (citation info)
  240. "Non-nil when CITATION should be exported without affix.
  241. INFO is the export data, as a property list."
  242. (pcase (org-cite-citation-style citation info)
  243. (`(,(or "noauthor" "na" `nil) . ,(or "bare" "b" "bare-caps" "bc")) t)
  244. (_ nil)))
  245. (defun org-cite-csl--capitalize-p (citation info)
  246. "Non-nil when CITATION should be capitalized.
  247. INFO is the export-data, as a property list."
  248. (pcase (org-cite-citation-style citation info)
  249. (`(,(or "noauthor" "na" `nil) . ,(or "caps" "c" "bare-caps" "bc")) t)
  250. (_ nil)))
  251. (defun org-cite-csl--no-author-p (reference info)
  252. "Non-nil when citation REFERENCE should be exported without author.
  253. INFO is the export data, as a property list."
  254. (pcase (org-cite-citation-style (org-element-property :parent reference) info)
  255. (`(,(or "noauthor" "na") . ,_) t)
  256. (_ nil)))
  257. (defun org-cite-csl--no-citelinks-p (info)
  258. "Non-nil when export BACKEND should not create cite-reference links."
  259. (or (not org-cite-csl-link-cites)
  260. (and org-cite-csl-no-citelinks-backends
  261. (apply #'org-export-derived-backend-p
  262. (plist-get info :back-end)
  263. org-cite-csl-no-citelinks-backends))
  264. ;; No references are being exported anyway.
  265. (not (org-element-map (plist-get info :parse-tree) 'keyword
  266. (lambda (k)
  267. (equal "PRINT_BIBLIOGRAPHY" (org-element-property :key k)))
  268. info t))))
  269. (defun org-cite-csl--output-format (info)
  270. "Return expected Citeproc's output format.
  271. INFO is the export state, as a property list. The return value is a symbol
  272. corresponding to one of the output formats supported by Citeproc: `html',
  273. `latex', or `org'."
  274. (let ((backend (plist-get info :back-end)))
  275. (cond
  276. ((org-export-derived-backend-p backend 'html) 'html)
  277. ((org-export-derived-backend-p backend 'latex) 'latex)
  278. (t 'org))))
  279. (defun org-cite-csl--style-file (info)
  280. "Return style file associated to current export process.
  281. INFO is the export state, as a property list.
  282. When file name is relative, expand it according to `org-cite-csl-styles-dir',
  283. or raise an error if the variable is unset."
  284. (pcase (org-cite-bibliography-style info)
  285. ('nil org-cite-csl--fallback-style-file)
  286. ((and (pred file-name-absolute-p) file) file)
  287. ((and (guard org-cite-csl-styles-dir) file)
  288. (expand-file-name file org-cite-csl-styles-dir))
  289. (other
  290. (user-error "Cannot handle relative style file name" other))))
  291. (defun org-cite-csl--itemgetter (bibliography)
  292. "Return Citeproc's \"itemgetter\" function for BIBLIOGRAPHY files.
  293. The function handles \".bib\", \".bibtex\" and \".json\" files."
  294. (let ((cache (make-hash-table :test #'equal)))
  295. (dolist (file bibliography)
  296. (pcase (file-name-extension file)
  297. ("json"
  298. (let ((json-array-type 'list)
  299. (json-key-type 'symbol))
  300. (dolist (item (json-read-file file))
  301. (puthash (cdr (assq 'id item)) item cache))))
  302. ((and (or "bib" "bibtex") ext)
  303. (with-temp-buffer
  304. (insert-file-contents file)
  305. (goto-char (point-min))
  306. (bibtex-set-dialect (if (string= ext "bib") 'biblatex 'BibTeX) t)
  307. (bibtex-map-entries
  308. (lambda (key &rest _)
  309. (puthash key
  310. (citeproc-bt-entry-to-csl (bibtex-parse-entry))
  311. cache)))))
  312. (ext
  313. (user-error "Unknown bibliography extension: %S" ext))))
  314. (lambda (itemids)
  315. (mapcar (lambda (id)
  316. (cons id (gethash id cache)))
  317. itemids))))
  318. (defun org-cite-csl--locale-getter ()
  319. "Return a locale getter.
  320. The getter looks for locales in `org-cite-csl-locales-dir' directory. If it
  321. cannot find them, it retrieves the default \"en_US\" from
  322. `org-cite-csl--fallback-locales-dir'."
  323. (lambda (loc)
  324. (or (and org-cite-csl-locales-dir
  325. (ignore-errors
  326. (funcall (citeproc-locale-getter-from-dir org-cite-csl-locales-dir)
  327. loc)))
  328. (funcall (citeproc-locale-getter-from-dir
  329. org-cite-csl--fallback-locales-dir)
  330. loc))))
  331. (defun org-cite-csl--processor (info)
  332. "Return Citeproc processor reading items from current bibliography.
  333. INFO is the export state, as a property list.
  334. Newly created processor is stored as the value of the `:cite-citeproc-processor'
  335. property in INFO."
  336. (or (plist-get info :cite-citeproc-processor)
  337. (let* ((bibliography (plist-get info :bibliography))
  338. (locale (or (plist-get info :language) "en_US"))
  339. (processor
  340. (citeproc-create
  341. (org-cite-csl--style-file info)
  342. (org-cite-csl--itemgetter bibliography)
  343. (org-cite-csl--locale-getter)
  344. locale)))
  345. (plist-put info :cite-citeproc-processor processor)
  346. processor)))
  347. (defun org-cite-csl--parse-reference (reference info)
  348. "Return Citeproc's structure associated to citation REFERENCE.
  349. INFO is the export state, as a property list.
  350. The result is a association list. Keys are: `id', `suppress-author', `prefix',
  351. `suffix', `location', `locator' and `label'."
  352. (let (label location-start locator-start location locator prefix suffix)
  353. ;; Parse suffix. Insert it in a temporary buffer to find
  354. ;; different parts: pre-label, label, locator, location (label +
  355. ;; locator), and suffix.
  356. (with-temp-buffer
  357. (save-excursion
  358. (insert (org-element-interpret-data
  359. (org-element-property :suffix reference))))
  360. (cond
  361. ((re-search-forward org-cite-csl--label-regexp nil t)
  362. (setq location-start (match-beginning 0))
  363. (setq label (cdr (assoc (match-string 1) org-cite-csl--label-alist)))
  364. (setq locator-start (match-end 1)))
  365. ((re-search-forward (rx digit) nil t)
  366. (setq location-start (match-beginning 0))
  367. (setq label "page")
  368. (setq locator-start location-start))
  369. (t
  370. (setq suffix (org-element-property :suffix reference))))
  371. ;; Find locator's end, and suffix, if any. To that effect, look
  372. ;; for the last comma or digit after label, whichever comes
  373. ;; last.
  374. (unless suffix
  375. (goto-char (point-max))
  376. (let ((re (rx (or "," (group digit)))))
  377. (when (re-search-backward re location-start t)
  378. (goto-char (or (match-end 1) (match-beginning 0)))
  379. (setq location (buffer-substring location-start (point)))
  380. (setq locator (org-trim (buffer-substring locator-start (point))))
  381. ;; Skip comma in suffix.
  382. (setq suffix
  383. (org-cite-parse-objects
  384. (buffer-substring (match-end 0) (point-max))
  385. t)))))
  386. (setq prefix
  387. (org-cite-concat
  388. (org-element-property :prefix reference)
  389. (and location-start
  390. (org-cite-parse-objects
  391. (buffer-substring 1 location-start)
  392. t)))))
  393. ;; Return value.
  394. (let ((export
  395. (lambda (data)
  396. (org-string-nw-p
  397. (org-trim
  398. ;; When Citeproc exports to Org syntax, avoid mix and
  399. ;; matching output formats by also generating Org
  400. ;; syntax for prefix and suffix.
  401. (if (eq 'org (org-cite-csl--output-format info))
  402. (org-element-interpret-data data)
  403. (org-export-data data info)))))))
  404. `((id . ,(org-element-property :key reference))
  405. (prefix . ,(funcall export prefix))
  406. (suffix . ,(funcall export suffix))
  407. (locator . ,locator)
  408. (label . ,label)
  409. (location . ,location)
  410. (suppress-author . ,(org-cite-csl--no-author-p reference info))))))
  411. (defun org-cite-csl--create-structure (citation info)
  412. "Create Citeproc structure for CITATION object.
  413. INFO is the export state, as a property list."
  414. (let* ((cites (mapcar (lambda (r)
  415. (org-cite-csl--parse-reference r info))
  416. (org-cite-get-references citation)))
  417. (footnote (org-cite-inside-footnote-p citation)))
  418. ;; Global prefix is inserted in front of the prefix of the first
  419. ;; reference.
  420. (let ((global-prefix (org-element-property :prefix citation)))
  421. (when global-prefix
  422. (let* ((first (car cites))
  423. (prefix (org-element-property :prefix first)))
  424. (org-element-put-property
  425. first :prefix (org-cite-concat global-prefix prefix)))))
  426. ;; Global suffix is appended to the suffix of the last reference.
  427. (let ((global-suffix (org-element-property :suffix citation)))
  428. (when global-suffix
  429. (let* ((last (org-last cites))
  430. (suffix (org-element-property :suffix last)))
  431. (org-element-put-property
  432. last :suffix (org-cite-concat suffix global-suffix)))))
  433. ;; Check if CITATION needs wrapping, i.e., it should be wrapped in
  434. ;; a footnote, but isn't yet.
  435. (when (and (not footnote) (org-cite-csl--note-style-p info))
  436. (org-cite-adjust-note citation info)
  437. (org-cite-wrap-citation citation info))
  438. ;; Return structure.
  439. (citeproc-citation-create
  440. :note-index (and footnote (org-export-get-footnote-number footnote info))
  441. :cites cites
  442. :capitalize-first (or footnote (org-cite-csl--capitalize-p citation info))
  443. :suppress-affixes (org-cite-csl--no-affixes-p citation info))))
  444. (defun org-cite-csl--rendered-citations (info)
  445. "Return the rendered citations as an association list.
  446. INFO is the export state, as a property list.
  447. Return an alist (CITATION . OUTPUT) where CITATION object has been rendered as
  448. OUTPUT using Citeproc."
  449. (or (plist-get info :cite-citeproc-rendered-citations)
  450. (let* ((citations (org-cite-list-citations info))
  451. (processor (org-cite-csl--processor info))
  452. (structures
  453. (mapcar (lambda (c) (org-cite-csl--create-structure c info))
  454. citations)))
  455. (citeproc-append-citations structures processor)
  456. (let* ((rendered
  457. (citeproc-render-citations
  458. processor
  459. (org-cite-csl--output-format info)
  460. (org-cite-csl--no-citelinks-p info)))
  461. (result (seq-mapn #'cons citations rendered)))
  462. (plist-put info :cite-citeproc-rendered-citations result)
  463. result))))
  464. ;;; Export capability
  465. (defun org-cite-csl-render-citation (citation _style _backend info)
  466. "Export CITATION object.
  467. INFO is the export state, as a property list."
  468. (org-cite-csl--barf-without-citeproc)
  469. (let ((output (cdr (assq citation (org-cite-csl--rendered-citations info)))))
  470. (if (not (eq 'org (org-cite-csl--output-format info)))
  471. output
  472. ;; Parse Org output to re-export it during the regular export
  473. ;; process.
  474. (org-cite-parse-objects output))))
  475. (defun org-cite-csl-render-bibliography (_keys _files _style _props _backend info)
  476. "Export bibliography.
  477. INFO is the export state, as a property list."
  478. (org-cite-csl--barf-without-citeproc)
  479. (pcase-let* ((format (org-cite-csl--output-format info))
  480. (`(,output . ,parameters)
  481. (citeproc-render-bib
  482. (org-cite-csl--processor info)
  483. format
  484. (org-cite-csl--no-citelinks-p info))))
  485. (pcase format
  486. ('html
  487. (concat
  488. (and (cdr (assq 'second-field-align parameters))
  489. (let* ((max-offset (cdr (assq 'max-offset parameters)))
  490. (char-width
  491. (string-to-number org-cite-csl-html-label-width-per-char))
  492. (char-width-unit
  493. (progn
  494. (string-match (number-to-string char-width)
  495. org-cite-csl-html-label-width-per-char)
  496. (substring org-cite-csl-html-label-width-per-char
  497. (match-end 0)))))
  498. (format
  499. "<style>.csl-left-margin{float: left; padding-right: 0em;}
  500. .csl-right-inline{margin: 0 0 0 %d%s;}</style>"
  501. (* max-offset char-width)
  502. char-width-unit)))
  503. (and (cdr (assq 'hanging-indent parameters))
  504. (format
  505. "<style>.csl-entry{text-indent: -%s; margin-left: %s;}</style>"
  506. org-cite-csl-html-hanging-indent
  507. org-cite-csl-html-hanging-indent))
  508. output))
  509. ('latex
  510. (if (cdr (assq 'hanging-indent parameters))
  511. (format "\\begin{hangparas}{%s}{1}\n%s\n\\end{hangparas}"
  512. org-cite-csl-latex-hanging-indent
  513. output)
  514. output))
  515. (_
  516. ;; Parse Org output to re-export it during the regular export
  517. ;; process.
  518. (org-cite-parse-elements output)))))
  519. (defun org-cite-csl-finalizer (output _keys _files _style _backend info)
  520. "Add \"hanging\" package if missing from LaTeX output.
  521. OUTPUT is the export document, as a string. INFO is the export state, as a
  522. property list."
  523. (org-cite-csl--barf-without-citeproc)
  524. (if (not (eq 'latex (org-cite-csl--output-format info)))
  525. output
  526. (with-temp-buffer
  527. (save-excursion (insert output))
  528. (when (search-forward "\\begin{document}" nil t)
  529. ;; Ensure there is a \usepackage{hanging} somewhere or add one.
  530. (goto-char (match-beginning 0))
  531. (let ((re (rx "\\usepackage" (opt "[" (*? nonl) "]") "{hanging}")))
  532. (unless (re-search-backward re nil t)
  533. (insert "\\usepackage{hanging}\n"))))
  534. (buffer-string))))
  535. ;;; Register `csl' processor
  536. (org-cite-register-processor 'csl
  537. :export-citation #'org-cite-csl-render-citation
  538. :export-bibliography #'org-cite-csl-render-bibliography
  539. :export-finalizer #'org-cite-csl-finalizer
  540. :cite-styles
  541. '((("noauthor" "na") ("bare" "b") ("bare-caps" "bc") ("caps" "c"))
  542. (("nil") ("bare" "b") ("bare-caps" "bc") ("caps" "c"))))
  543. (provide 'org-cite-csl)
  544. (provide 'oc-csl)
  545. ;;; oc-citeproc.el ends here