From 1772be123f1cfa713b25548f6fec135e7ab339a3 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Fri, 19 Aug 2022 03:00:56 +0000 Subject: CTAN sync 202208190300 --- FILES.byname | 483 ++-- FILES.byname.gz | Bin 2447565 -> 2447738 bytes FILES.last07days | 508 ++--- biblio/citation-style-language/CHANGELOG.md | 22 +- biblio/citation-style-language/DEPENDS.txt | 10 + biblio/citation-style-language/README.md | 2 +- .../american-chemical-society.csl | 21 +- .../american-medical-association.csl | 13 +- .../american-sociological-association.csl | 28 +- biblio/citation-style-language/apa.csl | 34 +- .../chicago-author-date.csl | 24 + .../chicago-fullnote-bibliography.csl | 2 +- .../chicago-note-bibliography.csl | 2 +- .../citation-style-language-doc.pdf | Bin 219427 -> 220489 bytes .../citation-style-language-doc.tex | 48 +- .../citation-style-language.sty | 146 +- biblio/citation-style-language/citeproc | 194 -- .../citation-style-language/citeproc-bib-data.json | 2349 -------------------- .../citation-style-language/citeproc-bib-data.lua | 1759 +++++++++++++++ biblio/citation-style-language/citeproc-bib.lua | 27 +- biblio/citation-style-language/citeproc-cli.lua | 196 ++ .../citation-style-language/citeproc-context.lua | 335 +++ .../citation-style-language/citeproc-element.lua | 744 ++++--- biblio/citation-style-language/citeproc-engine.lua | 2043 +++++++++++++++-- .../citation-style-language/citeproc-formats.lua | 211 -- .../citation-style-language/citeproc-ir-node.lua | 153 ++ .../citeproc-latex-core.lua | 302 +++ biblio/citation-style-language/citeproc-latex.lua | 150 ++ biblio/citation-style-language/citeproc-lua.1 | 26 + biblio/citation-style-language/citeproc-lua.lua | 12 + .../citeproc-node-choose.lua | 330 ++- .../citation-style-language/citeproc-node-date.lua | 671 +++--- .../citeproc-node-group.lua | 37 +- .../citeproc-node-label.lua | 142 +- .../citeproc-node-layout.lua | 243 +- .../citeproc-node-locale.lua | 280 ++- .../citeproc-node-names.lua | 1374 ++++++++---- .../citeproc-node-number.lua | 118 +- .../citation-style-language/citeproc-node-sort.lua | 213 +- .../citeproc-node-style.lua | 573 +++-- .../citation-style-language/citeproc-node-text.lua | 330 ++- biblio/citation-style-language/citeproc-output.lua | 1534 +++++++++++++ .../citation-style-language/citeproc-richtext.lua | 779 ------- biblio/citation-style-language/citeproc-util.lua | 309 ++- biblio/citation-style-language/citeproc.1 | 26 - biblio/citation-style-language/citeproc.lua | 2 +- biblio/citation-style-language/csl-core.lua | 306 --- .../citation-style-language/csl-locales-en-GB.xml | 20 +- .../citation-style-language/csl-locales-en-US.xml | 20 +- .../citation-style-language/csl-locales-fr-FR.xml | 395 ++-- biblio/citation-style-language/csl.lua | 143 -- .../harvard-cite-them-right.csl | 13 +- biblio/citation-style-language/ieee.csl | 2 +- .../modern-language-association.csl | 4 +- biblio/citation-style-language/nature.csl | 26 +- biblio/citation-style-language/vancouver.csl | 2 +- graphics/pgf/contrib/fancyqr/README.md | 36 + graphics/pgf/contrib/fancyqr/fancyqr-doc.pdf | Bin 0 -> 248260 bytes graphics/pgf/contrib/fancyqr/fancyqr-doc.tex | 87 + graphics/pgf/contrib/fancyqr/fancyqr.sty | 235 ++ graphics/pgf/contrib/fancyqr/qr-example.tex | 25 + macros/texinfo/latest/texinfo.tex | 404 ++-- .../texlive/tlnet/tlpkg/installer/ctan-mirrors.pl | 2 + systems/texlive/tlnet/tlpkg/texlive.tlpdb | 241 +- systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 | 2 +- systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 | 2 +- .../texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc | 14 +- systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz | Bin 2399360 -> 2397372 bytes systems/win32/miktex/tm/packages/next/pr.ini | 8 +- systems/win32/miktex/tm/packages/pr.ini | 8 +- timestamp | 2 +- 71 files changed, 11366 insertions(+), 7436 deletions(-) create mode 100644 biblio/citation-style-language/DEPENDS.txt delete mode 100755 biblio/citation-style-language/citeproc delete mode 100644 biblio/citation-style-language/citeproc-bib-data.json create mode 100644 biblio/citation-style-language/citeproc-bib-data.lua create mode 100644 biblio/citation-style-language/citeproc-cli.lua create mode 100644 biblio/citation-style-language/citeproc-context.lua delete mode 100644 biblio/citation-style-language/citeproc-formats.lua create mode 100644 biblio/citation-style-language/citeproc-ir-node.lua create mode 100644 biblio/citation-style-language/citeproc-latex-core.lua create mode 100644 biblio/citation-style-language/citeproc-latex.lua create mode 100644 biblio/citation-style-language/citeproc-lua.1 create mode 100755 biblio/citation-style-language/citeproc-lua.lua create mode 100644 biblio/citation-style-language/citeproc-output.lua delete mode 100644 biblio/citation-style-language/citeproc-richtext.lua delete mode 100644 biblio/citation-style-language/citeproc.1 delete mode 100644 biblio/citation-style-language/csl-core.lua delete mode 100644 biblio/citation-style-language/csl.lua create mode 100644 graphics/pgf/contrib/fancyqr/README.md create mode 100644 graphics/pgf/contrib/fancyqr/fancyqr-doc.pdf create mode 100644 graphics/pgf/contrib/fancyqr/fancyqr-doc.tex create mode 100644 graphics/pgf/contrib/fancyqr/fancyqr.sty create mode 100644 graphics/pgf/contrib/fancyqr/qr-example.tex diff --git a/FILES.byname b/FILES.byname index 766586f702..5e96684943 100644 --- a/FILES.byname +++ b/FILES.byname @@ -1788,105 +1788,108 @@ 1995/06/20 | 49681 | biblio/bibtex/vms/bibtex.vms-changes 1995/04/14 | 620 | biblio/bibtex/vms/link_bibtex.com 1995/04/14 | 1316 | biblio/bibtex/vms/make_bibtex.com -2022/03/21 | 9898 | biblio/citation-style-language/american-chemical-society.csl -2022/03/21 | 10684 | biblio/citation-style-language/american-medical-association.csl -2022/03/21 | 8052 | biblio/citation-style-language/american-political-science-association.csl -2022/03/21 | 10503 | biblio/citation-style-language/american-sociological-association.csl -2022/03/21 | 69389 | biblio/citation-style-language/apa.csl -2022/03/21 | 1142 | biblio/citation-style-language/CHANGELOG.md -2022/03/21 | 22195 | biblio/citation-style-language/chicago-author-date.csl -2022/03/21 | 47520 | biblio/citation-style-language/chicago-fullnote-bibliography.csl -2022/03/21 | 45970 | biblio/citation-style-language/chicago-note-bibliography.csl -2022/03/21 | 219427 | biblio/citation-style-language/citation-style-language-doc.pdf -2022/03/21 | 13296 | biblio/citation-style-language/citation-style-language-doc.tex -2022/03/21 | 29414 | biblio/citation-style-language/citation-style-language.sty -2022/03/21 | 4815 | biblio/citation-style-language/citeproc -2022/03/21 | 605 | biblio/citation-style-language/citeproc.1 -2022/03/21 | 61229 | biblio/citation-style-language/citeproc-bib-data.json -2022/03/21 | 14177 | biblio/citation-style-language/citeproc-bib.lua -2022/03/21 | 10421 | biblio/citation-style-language/citeproc-element.lua -2022/03/21 | 11279 | biblio/citation-style-language/citeproc-engine.lua -2022/03/21 | 7287 | biblio/citation-style-language/citeproc-formats.lua -2022/03/21 | 411 | biblio/citation-style-language/citeproc.lua -2022/03/21 | 3504 | biblio/citation-style-language/citeproc-node-choose.lua -2022/03/21 | 10463 | biblio/citation-style-language/citeproc-node-date.lua -2022/03/21 | 826 | biblio/citation-style-language/citeproc-node-group.lua -2022/03/21 | 3110 | biblio/citation-style-language/citeproc-node-label.lua -2022/03/21 | 6840 | biblio/citation-style-language/citeproc-node-layout.lua -2022/03/21 | 3183 | biblio/citation-style-language/citeproc-node-locale.lua -2022/03/21 | 20062 | biblio/citation-style-language/citeproc-node-names.lua -2022/03/21 | 2491 | biblio/citation-style-language/citeproc-node-number.lua -2022/03/21 | 1476 | biblio/citation-style-language/citeproc-nodes.lua -2022/03/21 | 5080 | biblio/citation-style-language/citeproc-node-sort.lua -2022/03/21 | 5163 | biblio/citation-style-language/citeproc-node-style.lua -2022/03/21 | 6204 | biblio/citation-style-language/citeproc-node-text.lua -2022/03/21 | 20541 | biblio/citation-style-language/citeproc-richtext.lua -2022/03/21 | 17094 | biblio/citation-style-language/citeproc-util.lua -2022/03/21 | 7765 | biblio/citation-style-language/csl-core.lua -2022/03/21 | 21433 | biblio/citation-style-language/csl-locales-af-ZA.xml -2022/03/21 | 19817 | biblio/citation-style-language/csl-locales-ar.xml -2022/03/21 | 24466 | biblio/citation-style-language/csl-locales-bg-BG.xml -2022/03/21 | 21302 | biblio/citation-style-language/csl-locales-ca-AD.xml -2022/03/21 | 21579 | biblio/citation-style-language/csl-locales-cs-CZ.xml -2022/03/21 | 21576 | biblio/citation-style-language/csl-locales-cy-GB.xml -2022/03/21 | 21470 | biblio/citation-style-language/csl-locales-da-DK.xml -2022/03/21 | 22218 | biblio/citation-style-language/csl-locales-de-AT.xml -2022/03/21 | 21681 | biblio/citation-style-language/csl-locales-de-CH.xml -2022/03/21 | 22143 | biblio/citation-style-language/csl-locales-de-DE.xml -2022/03/21 | 23024 | biblio/citation-style-language/csl-locales-el-GR.xml -2022/03/21 | 22355 | biblio/citation-style-language/csl-locales-en-GB.xml -2022/03/21 | 22510 | biblio/citation-style-language/csl-locales-en-US.xml -2022/03/21 | 21406 | biblio/citation-style-language/csl-locales-es-CL.xml -2022/03/21 | 21327 | biblio/citation-style-language/csl-locales-es-ES.xml -2022/03/21 | 22434 | biblio/citation-style-language/csl-locales-es-MX.xml -2022/03/21 | 21261 | biblio/citation-style-language/csl-locales-et-EE.xml -2022/03/21 | 21512 | biblio/citation-style-language/csl-locales-eu.xml -2022/03/21 | 22337 | biblio/citation-style-language/csl-locales-fa-IR.xml -2022/03/21 | 21623 | biblio/citation-style-language/csl-locales-fi-FI.xml -2022/03/21 | 22098 | biblio/citation-style-language/csl-locales-fr-CA.xml -2022/03/21 | 22101 | biblio/citation-style-language/csl-locales-fr-FR.xml -2022/03/21 | 21822 | biblio/citation-style-language/csl-locales-he-IL.xml -2022/03/21 | 24345 | biblio/citation-style-language/csl-locales-hi-IN.xml -2022/03/21 | 21363 | biblio/citation-style-language/csl-locales-hr-HR.xml -2022/03/21 | 21466 | biblio/citation-style-language/csl-locales-hu-HU.xml -2022/03/21 | 21654 | biblio/citation-style-language/csl-locales-id-ID.xml -2022/03/21 | 21469 | biblio/citation-style-language/csl-locales-is-IS.xml -2022/03/21 | 23360 | biblio/citation-style-language/csl-locales-it-IT.xml -2022/03/21 | 21482 | biblio/citation-style-language/csl-locales-ja-JP.xml -2022/03/21 | 21966 | biblio/citation-style-language/csl-locales-km-KH.xml -2022/03/21 | 21414 | biblio/citation-style-language/csl-locales-ko-KR.xml -2022/03/21 | 21302 | biblio/citation-style-language/csl-locales-la.xml -2022/03/21 | 22752 | biblio/citation-style-language/csl-locales-lt-LT.xml -2022/03/21 | 23553 | biblio/citation-style-language/csl-locales-lv-LV.xml -2022/03/21 | 21941 | biblio/citation-style-language/csl-locales-mn-MN.xml -2022/03/21 | 21323 | biblio/citation-style-language/csl-locales-nb-NO.xml -2022/03/21 | 22358 | biblio/citation-style-language/csl-locales-nl-NL.xml -2022/03/21 | 21301 | biblio/citation-style-language/csl-locales-nn-NO.xml -2022/03/21 | 21446 | biblio/citation-style-language/csl-locales-pl-PL.xml -2022/03/21 | 22542 | biblio/citation-style-language/csl-locales-pt-BR.xml -2022/03/21 | 22454 | biblio/citation-style-language/csl-locales-pt-PT.xml -2022/03/21 | 21462 | biblio/citation-style-language/csl-locales-ro-RO.xml -2022/03/21 | 25715 | biblio/citation-style-language/csl-locales-ru-RU.xml -2022/03/21 | 21574 | biblio/citation-style-language/csl-locales-sk-SK.xml -2022/03/21 | 21330 | biblio/citation-style-language/csl-locales-sl-SI.xml -2022/03/21 | 22122 | biblio/citation-style-language/csl-locales-sr-RS.xml -2022/03/21 | 21715 | biblio/citation-style-language/csl-locales-sv-SE.xml -2022/03/21 | 23467 | biblio/citation-style-language/csl-locales-th-TH.xml -2022/03/21 | 21635 | biblio/citation-style-language/csl-locales-tr-TR.xml -2022/03/21 | 20747 | biblio/citation-style-language/csl-locales-uk-UA.xml -2022/03/21 | 21812 | biblio/citation-style-language/csl-locales-vi-VN.xml -2022/03/21 | 19056 | biblio/citation-style-language/csl-locales-zh-CN.xml -2022/03/21 | 19423 | biblio/citation-style-language/csl-locales-zh-TW.xml -2022/03/21 | 3443 | biblio/citation-style-language/csl.lua -2022/03/21 | 8128 | biblio/citation-style-language/elsevier-harvard.csl -2022/03/21 | 10619 | biblio/citation-style-language/harvard-cite-them-right.csl -2022/03/21 | 15413 | biblio/citation-style-language/ieee.csl -2022/03/21 | 16054 | biblio/citation-style-language/modern-humanities-research-association.csl -2022/03/21 | 11226 | biblio/citation-style-language/modern-language-association.csl -2022/03/21 | 4534 | biblio/citation-style-language/nature.csl -2022/03/21 | 1669 | biblio/citation-style-language/README.md -2022/03/21 | 11927 | biblio/citation-style-language/vancouver.csl -2022/03/21 | 555431 | biblio/citation-style-language.zip +2022/08/19 | 10019 | biblio/citation-style-language/american-chemical-society.csl +2022/08/19 | 11034 | biblio/citation-style-language/american-medical-association.csl +2022/08/19 | 8052 | biblio/citation-style-language/american-political-science-association.csl +2022/08/19 | 11060 | biblio/citation-style-language/american-sociological-association.csl +2022/08/19 | 69907 | biblio/citation-style-language/apa.csl +2022/08/19 | 2145 | biblio/citation-style-language/CHANGELOG.md +2022/08/19 | 23079 | biblio/citation-style-language/chicago-author-date.csl +2022/08/19 | 47520 | biblio/citation-style-language/chicago-fullnote-bibliography.csl +2022/08/19 | 45970 | biblio/citation-style-language/chicago-note-bibliography.csl +2022/08/19 | 220489 | biblio/citation-style-language/citation-style-language-doc.pdf +2022/08/19 | 13278 | biblio/citation-style-language/citation-style-language-doc.tex +2022/08/19 | 31273 | biblio/citation-style-language/citation-style-language.sty +2022/08/19 | 40700 | biblio/citation-style-language/citeproc-bib-data.lua +2022/08/19 | 14024 | biblio/citation-style-language/citeproc-bib.lua +2022/08/19 | 4797 | biblio/citation-style-language/citeproc-cli.lua +2022/08/19 | 8425 | biblio/citation-style-language/citeproc-context.lua +2022/08/19 | 14833 | biblio/citation-style-language/citeproc-element.lua +2022/08/19 | 61972 | biblio/citation-style-language/citeproc-engine.lua +2022/08/19 | 2993 | biblio/citation-style-language/citeproc-ir-node.lua +2022/08/19 | 7685 | biblio/citation-style-language/citeproc-latex-core.lua +2022/08/19 | 3696 | biblio/citation-style-language/citeproc-latex.lua +2022/08/19 | 411 | biblio/citation-style-language/citeproc.lua +2022/08/19 | 617 | biblio/citation-style-language/citeproc-lua.1 +2022/08/19 | 240 | biblio/citation-style-language/citeproc-lua.lua +2022/08/19 | 7308 | biblio/citation-style-language/citeproc-node-choose.lua +2022/08/19 | 13585 | biblio/citation-style-language/citeproc-node-date.lua +2022/08/19 | 904 | biblio/citation-style-language/citeproc-node-group.lua +2022/08/19 | 2655 | biblio/citation-style-language/citeproc-node-label.lua +2022/08/19 | 1702 | biblio/citation-style-language/citeproc-node-layout.lua +2022/08/19 | 6100 | biblio/citation-style-language/citeproc-node-locale.lua +2022/08/19 | 33702 | biblio/citation-style-language/citeproc-node-names.lua +2022/08/19 | 1675 | biblio/citation-style-language/citeproc-node-number.lua +2022/08/19 | 1476 | biblio/citation-style-language/citeproc-nodes.lua +2022/08/19 | 6283 | biblio/citation-style-language/citeproc-node-sort.lua +2022/08/19 | 15617 | biblio/citation-style-language/citeproc-node-style.lua +2022/08/19 | 5498 | biblio/citation-style-language/citeproc-node-text.lua +2022/08/19 | 44466 | biblio/citation-style-language/citeproc-output.lua +2022/08/19 | 21517 | biblio/citation-style-language/citeproc-util.lua +2022/08/19 | 21433 | biblio/citation-style-language/csl-locales-af-ZA.xml +2022/08/19 | 19817 | biblio/citation-style-language/csl-locales-ar.xml +2022/08/19 | 24466 | biblio/citation-style-language/csl-locales-bg-BG.xml +2022/08/19 | 21302 | biblio/citation-style-language/csl-locales-ca-AD.xml +2022/08/19 | 21579 | biblio/citation-style-language/csl-locales-cs-CZ.xml +2022/08/19 | 21576 | biblio/citation-style-language/csl-locales-cy-GB.xml +2022/08/19 | 21470 | biblio/citation-style-language/csl-locales-da-DK.xml +2022/08/19 | 22218 | biblio/citation-style-language/csl-locales-de-AT.xml +2022/08/19 | 21681 | biblio/citation-style-language/csl-locales-de-CH.xml +2022/08/19 | 22143 | biblio/citation-style-language/csl-locales-de-DE.xml +2022/08/19 | 23024 | biblio/citation-style-language/csl-locales-el-GR.xml +2022/08/19 | 22472 | biblio/citation-style-language/csl-locales-en-GB.xml +2022/08/19 | 22627 | biblio/citation-style-language/csl-locales-en-US.xml +2022/08/19 | 21406 | biblio/citation-style-language/csl-locales-es-CL.xml +2022/08/19 | 21327 | biblio/citation-style-language/csl-locales-es-ES.xml +2022/08/19 | 22434 | biblio/citation-style-language/csl-locales-es-MX.xml +2022/08/19 | 21261 | biblio/citation-style-language/csl-locales-et-EE.xml +2022/08/19 | 21512 | biblio/citation-style-language/csl-locales-eu.xml +2022/08/19 | 22337 | biblio/citation-style-language/csl-locales-fa-IR.xml +2022/08/19 | 21623 | biblio/citation-style-language/csl-locales-fi-FI.xml +2022/08/19 | 22098 | biblio/citation-style-language/csl-locales-fr-CA.xml +2022/08/19 | 22356 | biblio/citation-style-language/csl-locales-fr-FR.xml +2022/08/19 | 21822 | biblio/citation-style-language/csl-locales-he-IL.xml +2022/08/19 | 24345 | biblio/citation-style-language/csl-locales-hi-IN.xml +2022/08/19 | 21363 | biblio/citation-style-language/csl-locales-hr-HR.xml +2022/08/19 | 21466 | biblio/citation-style-language/csl-locales-hu-HU.xml +2022/08/19 | 21654 | biblio/citation-style-language/csl-locales-id-ID.xml +2022/08/19 | 21469 | biblio/citation-style-language/csl-locales-is-IS.xml +2022/08/19 | 23360 | biblio/citation-style-language/csl-locales-it-IT.xml +2022/08/19 | 21482 | biblio/citation-style-language/csl-locales-ja-JP.xml +2022/08/19 | 21966 | biblio/citation-style-language/csl-locales-km-KH.xml +2022/08/19 | 21414 | biblio/citation-style-language/csl-locales-ko-KR.xml +2022/08/19 | 21302 | biblio/citation-style-language/csl-locales-la.xml +2022/08/19 | 22752 | biblio/citation-style-language/csl-locales-lt-LT.xml +2022/08/19 | 23553 | biblio/citation-style-language/csl-locales-lv-LV.xml +2022/08/19 | 21941 | biblio/citation-style-language/csl-locales-mn-MN.xml +2022/08/19 | 21323 | biblio/citation-style-language/csl-locales-nb-NO.xml +2022/08/19 | 22358 | biblio/citation-style-language/csl-locales-nl-NL.xml +2022/08/19 | 21301 | biblio/citation-style-language/csl-locales-nn-NO.xml +2022/08/19 | 21446 | biblio/citation-style-language/csl-locales-pl-PL.xml +2022/08/19 | 22542 | biblio/citation-style-language/csl-locales-pt-BR.xml +2022/08/19 | 22454 | biblio/citation-style-language/csl-locales-pt-PT.xml +2022/08/19 | 21462 | biblio/citation-style-language/csl-locales-ro-RO.xml +2022/08/19 | 25715 | biblio/citation-style-language/csl-locales-ru-RU.xml +2022/08/19 | 21574 | biblio/citation-style-language/csl-locales-sk-SK.xml +2022/08/19 | 21330 | biblio/citation-style-language/csl-locales-sl-SI.xml +2022/08/19 | 22122 | biblio/citation-style-language/csl-locales-sr-RS.xml +2022/08/19 | 21715 | biblio/citation-style-language/csl-locales-sv-SE.xml +2022/08/19 | 23467 | biblio/citation-style-language/csl-locales-th-TH.xml +2022/08/19 | 21635 | biblio/citation-style-language/csl-locales-tr-TR.xml +2022/08/19 | 20747 | biblio/citation-style-language/csl-locales-uk-UA.xml +2022/08/19 | 21812 | biblio/citation-style-language/csl-locales-vi-VN.xml +2022/08/19 | 19056 | biblio/citation-style-language/csl-locales-zh-CN.xml +2022/08/19 | 19423 | biblio/citation-style-language/csl-locales-zh-TW.xml +2022/08/19 | 196 | biblio/citation-style-language/DEPENDS.txt +2022/08/19 | 8128 | biblio/citation-style-language/elsevier-harvard.csl +2022/08/19 | 10723 | biblio/citation-style-language/harvard-cite-them-right.csl +2022/08/19 | 15413 | biblio/citation-style-language/ieee.csl +2022/08/19 | 16054 | biblio/citation-style-language/modern-humanities-research-association.csl +2022/08/19 | 11209 | biblio/citation-style-language/modern-language-association.csl +2022/08/19 | 5288 | biblio/citation-style-language/nature.csl +2022/08/19 | 1673 | biblio/citation-style-language/README.md +2022/08/19 | 11958 | biblio/citation-style-language/vancouver.csl +2022/08/18 | 577055 | biblio/citation-style-language.zip 2022/02/06 | 3110 | biblio/pbibtex/pbibtex-base/cpp.awk 2022/02/06 | 1009 | biblio/pbibtex/pbibtex-base/generate.sh 2022/02/06 | 22422 | biblio/pbibtex/pbibtex-base/jabbrv.bst @@ -5264,7 +5267,7 @@ 1990/10/02 | 5440 | dviware/vutex/vaxvms.hlp 1990/10/02 | 812 | dviware/vutex/vaxvms.ins 1990/10/02 | 127576 | dviware/vutex/vutex.web -2022/08/17 | 89208 | FILES.last07days +2022/08/18 | 95093 | FILES.last07days 2021/07/24 | 1139410 | fonts/aboensis/Aboensis.glyphs 2021/07/24 | 18907382 | fonts/aboensis/aboensis.pdf 2021/07/24 | 137860 | fonts/aboensis/Aboensis-Regular.otf @@ -158429,6 +158432,12 @@ 2021/07/17 | 313369 | graphics/pgf/contrib/easing/easing.pdf 2021/07/17 | 399 | graphics/pgf/contrib/easing/README 2021/07/17 | 319237 | graphics/pgf/contrib/easing.zip +2022/08/18 | 248260 | graphics/pgf/contrib/fancyqr/fancyqr-doc.pdf +2022/08/18 | 4485 | graphics/pgf/contrib/fancyqr/fancyqr-doc.tex +2022/08/18 | 10265 | graphics/pgf/contrib/fancyqr/fancyqr.sty +2022/08/18 | 813 | graphics/pgf/contrib/fancyqr/qr-example.tex +2022/08/18 | 3582 | graphics/pgf/contrib/fancyqr/README.md +2022/08/18 | 251020 | graphics/pgf/contrib/fancyqr.zip 2013/02/27 | 17141 | graphics/pgf/contrib/fast-diagram/fast-diagram.sty 2013/02/27 | 1050633 | graphics/pgf/contrib/fast-diagram/help.pdf 2013/02/27 | 1437 | graphics/pgf/contrib/fast-diagram/help.tex @@ -188723,7 +188732,7 @@ 2022/06/20 | 140748 | info/yet-another-guide-latex2e/Yet-Another-Guide-LaTeX2e_v1.tex 2022/06/20 | 899385 | info/yet-another-guide-latex2e.zip 2021/02/04 | 4944311 | install/biblio/bibtex/contrib/gost.tds.zip -2022/03/21 | 559357 | install/biblio/citation-style-language.tds.zip +2022/08/19 | 580030 | install/biblio/citation-style-language.tds.zip 2013/11/06 | 795801 | install/fonts/accanthis.tds.zip 2009/05/23 | 246025 | install/fonts/ae.tds.zip 2019/10/12 | 38525845 | install/fonts/alegreya.tds.zip @@ -276442,7 +276451,7 @@ 2021/05/03 | 629 | support/TeX4ht/doc/README 2020/08/24 | 358454 | support/TeX4ht/doc/TeX4ht-doc.tex 2021/05/11 | 262 | support/TeX4ht/README -2022/08/11 | 199643 | support/TeX4ht/source/ChangeLog +2022/08/17 | 200199 | support/TeX4ht/source/ChangeLog 2021/05/03 | 1375 | support/TeX4ht/source/checklog.lua 2021/05/03 | 1189 | support/TeX4ht/source/common-code.tex 2021/05/03 | 2727 | support/TeX4ht/source/common-info.tex @@ -276450,11 +276459,11 @@ 2021/05/06 | 15 | support/TeX4ht/source/extra-bin/vcss 2021/05/06 | 15 | support/TeX4ht/source/extra-bin/xmllint 2022/08/07 | 27321 | support/TeX4ht/source/Makefile -2022/06/20 | 8082 | support/TeX4ht/source/mktex4ht-cnf.tex +2022/08/17 | 8111 | support/TeX4ht/source/mktex4ht-cnf.tex 2021/05/03 | 1121 | support/TeX4ht/source/README 2021/05/03 | 1744 | support/TeX4ht/source/t1fontmake.lua 2021/05/03 | 297 | support/TeX4ht/source/t1fontsample.tex -2022/08/07 | 1127331 | support/TeX4ht/source/tex4ht-4ht.tex +2022/08/17 | 1128043 | support/TeX4ht/source/tex4ht-4ht.tex 2021/05/03 | 7127 | support/TeX4ht/source/tex4ht-auto-script.tex 2021/05/16 | 1539 | support/TeX4ht/source/tex4ht-bibtex2.tex 2022/06/05 | 21307 | support/TeX4ht/source/tex4ht-cond4ht.tex @@ -276480,7 +276489,7 @@ 2021/05/03 | 14218 | support/TeX4ht/source/tex4ht-htcmd.tex 2021/05/03 | 70683 | support/TeX4ht/source/tex4ht-html0.tex 2021/05/03 | 164748 | support/TeX4ht/source/tex4ht-html32.tex -2022/05/22 | 617510 | support/TeX4ht/source/tex4ht-html4.tex +2022/08/17 | 617532 | support/TeX4ht/source/tex4ht-html4.tex 2022/08/11 | 11077 | support/TeX4ht/source/tex4ht-html5.tex 2021/06/17 | 648233 | support/TeX4ht/source/tex4ht-html-speech.tex 2021/05/03 | 59483 | support/TeX4ht/source/tex4ht-html-speech-xtpipes.tex @@ -277780,10 +277789,10 @@ 2007/07/22 | 600 | support/wp2latex/readme.1st 2021/08/01 | 7401 | support/wp2latex/readme.txt 2020/02/11 | 305310 | support/wp2latex/wp2latex-3.87.deb -2020/10/12 | 3641715 | support/wp2latex/wp2latex-3.92.zip +2021/01/23 | 3690318 | support/wp2latex/wp2latex-3.93.zip 2018/01/26 | 45297 | support/wp2latex/wp2latex.doc.htm 2018/01/26 | 7259 | support/wp2latex/wp2latex.faq -2020/10/11 | 1642418 | support/wp2latex/WP2LaTeX_Installer-3.92.exe +2021/01/23 | 1635025 | support/wp2latex/WP2LaTeX_Installer-3.93b.exe 2021/08/01 | 1241 | support/wp2latex/wp2latex.lsm 2018/01/26 | 35145 | support/wp2latex/wp2latex_pas-2.3.zip 2000/02/22 | 481 | support/wsuipa2tipa/README @@ -284422,14 +284431,14 @@ 2019/02/28 | 377000 | systems/texlive/tlnet/archive/examdesign.doc.r15878.tar.xz 2019/02/28 | 10780 | systems/texlive/tlnet/archive/examdesign.r15878.tar.xz 2019/02/28 | 29924 | systems/texlive/tlnet/archive/examdesign.source.r15878.tar.xz -2021/03/02 | 515428 | systems/texlive/tlnet/archive/exam.doc.r58023.tar.xz +2022/08/18 | 524800 | systems/texlive/tlnet/archive/exam.doc.r64134.tar.xz 2019/02/28 | 820032 | systems/texlive/tlnet/archive/exam-n.doc.r42755.tar.xz 2019/02/28 | 11804 | systems/texlive/tlnet/archive/exam-n.r42755.tar.xz 2019/02/28 | 34424 | systems/texlive/tlnet/archive/exam-n.source.r42755.tar.xz 2020/05/25 | 379128 | systems/texlive/tlnet/archive/examplep.doc.r55265.tar.xz 2020/05/25 | 33788 | systems/texlive/tlnet/archive/examplep.r55265.tar.xz 2019/02/28 | 5516 | systems/texlive/tlnet/archive/example.r33398.tar.xz -2021/03/02 | 49904 | systems/texlive/tlnet/archive/exam.r58023.tar.xz +2022/08/18 | 49900 | systems/texlive/tlnet/archive/exam.r64134.tar.xz 2022/01/24 | 313712 | systems/texlive/tlnet/archive/exam-randomizechoices.doc.r61719.tar.xz 2022/01/24 | 5624 | systems/texlive/tlnet/archive/exam-randomizechoices.r61719.tar.xz 2022/08/16 | 1739528 | systems/texlive/tlnet/archive/exam-zh.doc.r64123.tar.xz @@ -284726,9 +284735,9 @@ 2019/02/28 | 496576 | systems/texlive/tlnet/archive/fitbox.doc.r50088.tar.xz 2019/02/28 | 2228 | systems/texlive/tlnet/archive/fitbox.r50088.tar.xz 2019/02/28 | 5812 | systems/texlive/tlnet/archive/fitbox.source.r50088.tar.xz -2021/10/19 | 729804 | systems/texlive/tlnet/archive/fithesis.doc.r60795.tar.xz -2021/10/19 | 791532 | systems/texlive/tlnet/archive/fithesis.r60795.tar.xz -2021/10/19 | 44752 | systems/texlive/tlnet/archive/fithesis.source.r60795.tar.xz +2022/08/18 | 730120 | systems/texlive/tlnet/archive/fithesis.doc.r64135.tar.xz +2022/08/18 | 792156 | systems/texlive/tlnet/archive/fithesis.r64135.tar.xz +2022/08/18 | 45332 | systems/texlive/tlnet/archive/fithesis.source.r64135.tar.xz 2019/02/28 | 212644 | systems/texlive/tlnet/archive/fix2col.doc.r38770.tar.xz 2019/02/28 | 2684 | systems/texlive/tlnet/archive/fix2col.r38770.tar.xz 2019/02/28 | 6376 | systems/texlive/tlnet/archive/fix2col.source.r38770.tar.xz @@ -285644,8 +285653,8 @@ 2021/11/28 | 6640 | systems/texlive/tlnet/archive/hep-title.source.r61173.tar.xz 2020/04/16 | 100060 | systems/texlive/tlnet/archive/hepunits.doc.r54758.tar.xz 2020/04/16 | 1804 | systems/texlive/tlnet/archive/hepunits.r54758.tar.xz -2022/08/15 | 63056 | systems/texlive/tlnet/archive/hereapplies.doc.r64117.tar.xz -2022/08/15 | 3216 | systems/texlive/tlnet/archive/hereapplies.r64117.tar.xz +2022/08/18 | 64028 | systems/texlive/tlnet/archive/hereapplies.doc.r64136.tar.xz +2022/08/18 | 3748 | systems/texlive/tlnet/archive/hereapplies.r64136.tar.xz 2019/02/28 | 596 | systems/texlive/tlnet/archive/here.doc.r16135.tar.xz 2019/02/28 | 896 | systems/texlive/tlnet/archive/here.r16135.tar.xz 2019/02/28 | 213100 | systems/texlive/tlnet/archive/he-she.doc.r41359.tar.xz @@ -289779,8 +289788,8 @@ 2019/02/28 | 378272 | systems/texlive/tlnet/archive/productbox.doc.r20886.tar.xz 2019/02/28 | 4944 | systems/texlive/tlnet/archive/productbox.r20886.tar.xz 2019/02/28 | 15512 | systems/texlive/tlnet/archive/productbox.source.r20886.tar.xz -2022/08/03 | 10015344 | systems/texlive/tlnet/archive/profcollege.doc.r64032.tar.xz -2022/08/03 | 1210836 | systems/texlive/tlnet/archive/profcollege.r64032.tar.xz +2022/08/18 | 10136040 | systems/texlive/tlnet/archive/profcollege.doc.r64137.tar.xz +2022/08/18 | 1211724 | systems/texlive/tlnet/archive/profcollege.r64137.tar.xz 2022/04/27 | 134096 | systems/texlive/tlnet/archive/proflabo.doc.r63147.tar.xz 2022/04/27 | 2748 | systems/texlive/tlnet/archive/proflabo.r63147.tar.xz 2022/08/17 | 855220 | systems/texlive/tlnet/archive/proflycee.doc.r64130.tar.xz @@ -291650,14 +291659,14 @@ 2022/02/27 | 108804 | systems/texlive/tlnet/archive/tex4ht.amd64-freebsd.r62206.tar.xz 2022/02/27 | 87364 | systems/texlive/tlnet/archive/tex4ht.amd64-netbsd.r62220.tar.xz 2022/04/21 | 69948 | systems/texlive/tlnet/archive/tex4ht.armhf-linux.r63092.tar.xz -2022/08/12 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64099.tar.xz +2022/08/18 | 136388 | systems/texlive/tlnet/archive/tex4ht.doc.r64138.tar.xz 2022/03/01 | 58328 | systems/texlive/tlnet/archive/tex4ht.i386-cygwin.r62282.tar.xz 2022/02/27 | 93760 | systems/texlive/tlnet/archive/tex4ht.i386-freebsd.r62206.tar.xz 2022/02/27 | 91280 | systems/texlive/tlnet/archive/tex4ht.i386-linux.r62210.tar.xz 2022/02/27 | 76200 | systems/texlive/tlnet/archive/tex4ht.i386-netbsd.r62220.tar.xz 2022/02/27 | 83964 | systems/texlive/tlnet/archive/tex4ht.i386-solaris.r62206.tar.xz -2022/08/12 | 952476 | systems/texlive/tlnet/archive/tex4ht.r64099.tar.xz -2022/08/12 | 1272264 | systems/texlive/tlnet/archive/tex4ht.source.r64099.tar.xz +2022/08/18 | 952684 | systems/texlive/tlnet/archive/tex4ht.r64138.tar.xz +2022/08/18 | 1274892 | systems/texlive/tlnet/archive/tex4ht.source.r64138.tar.xz 2022/02/28 | 184748 | systems/texlive/tlnet/archive/tex4ht.universal-darwin.r62258.tar.xz 2022/02/27 | 57768 | systems/texlive/tlnet/archive/tex4ht.win32.r62199.tar.xz 2022/03/01 | 58016 | systems/texlive/tlnet/archive/tex4ht.x86_64-cygwin.r62279.tar.xz @@ -291892,7 +291901,7 @@ 2020/05/17 | 592 | systems/texlive/tlnet/archive/texlive-scripts.amd64-freebsd.r55172.tar.xz 2020/05/17 | 588 | systems/texlive/tlnet/archive/texlive-scripts.amd64-netbsd.r55172.tar.xz 2020/05/17 | 592 | systems/texlive/tlnet/archive/texlive-scripts.armhf-linux.r55172.tar.xz -2022/08/17 | 419496 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64126.tar.xz +2022/08/18 | 419376 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64133.tar.xz 2020/01/27 | 916 | systems/texlive/tlnet/archive/texlive-scripts-extra.aarch64-linux.r53577.tar.xz 2020/01/27 | 924 | systems/texlive/tlnet/archive/texlive-scripts-extra.amd64-freebsd.r53577.tar.xz 2020/01/27 | 916 | systems/texlive/tlnet/archive/texlive-scripts-extra.amd64-netbsd.r53577.tar.xz @@ -291916,7 +291925,7 @@ 2020/05/17 | 588 | systems/texlive/tlnet/archive/texlive-scripts.i386-linux.r55172.tar.xz 2020/05/17 | 592 | systems/texlive/tlnet/archive/texlive-scripts.i386-netbsd.r55172.tar.xz 2020/05/17 | 588 | systems/texlive/tlnet/archive/texlive-scripts.i386-solaris.r55172.tar.xz -2022/08/17 | 113524 | systems/texlive/tlnet/archive/texlive-scripts.r64126.tar.xz +2022/08/18 | 113500 | systems/texlive/tlnet/archive/texlive-scripts.r64133.tar.xz 2021/02/25 | 572 | systems/texlive/tlnet/archive/texlive-scripts.universal-darwin.r57908.tar.xz 2022/04/19 | 36296 | systems/texlive/tlnet/archive/texlive-scripts.win32.r63068.tar.xz 2020/05/17 | 588 | systems/texlive/tlnet/archive/texlive-scripts.x86_64-cygwin.r55172.tar.xz @@ -293741,16 +293750,16 @@ 2020/07/18 | 47816 | systems/texlive/tlnet/archive/zztex.doc.r55862.tar.xz 2020/07/18 | 101724 | systems/texlive/tlnet/archive/zztex.r55862.tar.xz 2022/07/17 | 124404 | systems/texlive/tlnet/install-tl -2022/08/17 | 5831896 | systems/texlive/tlnet/install-tl-unx.tar.gz -2022/08/17 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512 -2022/08/17 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc +2022/08/18 | 5831895 | systems/texlive/tlnet/install-tl-unx.tar.gz +2022/08/18 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512 +2022/08/18 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc 2021/04/11 | 4945 | systems/texlive/tlnet/install-tl-windows.bat -2022/08/17 | 20436545 | systems/texlive/tlnet/install-tl-windows.exe -2022/08/17 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512 -2022/08/17 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc -2022/08/17 | 25516446 | systems/texlive/tlnet/install-tl.zip -2022/08/17 | 145 | systems/texlive/tlnet/install-tl.zip.sha512 -2022/08/17 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc +2022/08/18 | 20433402 | systems/texlive/tlnet/install-tl-windows.exe +2022/08/18 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512 +2022/08/18 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc +2022/08/18 | 25516427 | systems/texlive/tlnet/install-tl.zip +2022/08/18 | 145 | systems/texlive/tlnet/install-tl.zip.sha512 +2022/08/18 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc 2022/04/03 | 1229 | systems/texlive/tlnet/README.md 2022/04/03 | 0 | systems/texlive/tlnet/TEXLIVE_2022 2022/02/28 | 7791 | systems/texlive/tlnet/tlpkg/gpg/pubring.gpg @@ -293760,7 +293769,7 @@ 2022/02/28 | 1280 | systems/texlive/tlnet/tlpkg/gpg/trustdb.gpg 2022/05/27 | 49794 | systems/texlive/tlnet/tlpkg/installer/config.guess 2010/11/11 | 11651 | systems/texlive/tlnet/tlpkg/installer/COPYING.MinGW-runtime.txt -2022/08/16 | 7321 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl +2022/08/17 | 7221 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl 2022/07/21 | 222477 | systems/texlive/tlnet/tlpkg/installer/curl/curl-ca-bundle.crt 2020/04/15 | 2606592 | systems/texlive/tlnet/tlpkg/installer/curl/curl.exe 2017/04/16 | 2629 | systems/texlive/tlnet/tlpkg/installer/installer-options.txt @@ -293803,12 +293812,12 @@ 2021/05/16 | 20371 | systems/texlive/tlnet/tlpkg/TeXLive/TLCrypto.pm 2021/12/21 | 5429 | systems/texlive/tlnet/tlpkg/TeXLive/TLDownload.pm 2021/12/21 | 27582 | systems/texlive/tlnet/tlpkg/TeXLive/TLPaper.pm -2022/08/17 | 17582689 | systems/texlive/tlnet/tlpkg/texlive.tlpdb -2022/08/17 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 +2022/08/18 | 17582691 | systems/texlive/tlnet/tlpkg/texlive.tlpdb +2022/08/18 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 2022/04/18 | 86658 | systems/texlive/tlnet/tlpkg/TeXLive/TLPDB.pm -2022/08/17 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 -2022/08/17 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc -2022/08/17 | 2397600 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz +2022/08/18 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 +2022/08/18 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc +2022/08/18 | 2399360 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz 2021/12/21 | 58848 | systems/texlive/tlnet/tlpkg/TeXLive/TLPOBJ.pm 2022/08/01 | 42206 | systems/texlive/tlnet/tlpkg/TeXLive/TLPSRC.pm 2021/12/21 | 16130 | systems/texlive/tlnet/tlpkg/TeXLive/TLTREE.pm @@ -299081,7 +299090,7 @@ 2020/10/03 | 318630 | systems/win32/miktex/tm/packages/filehook.tar.lzma 2012/12/04 | 924573 | systems/win32/miktex/tm/packages/fileinfo.tar.lzma 2011/09/21 | 281409 | systems/win32/miktex/tm/packages/filemod.tar.lzma -2022/08/17 | 659342 | systems/win32/miktex/tm/packages/files.csv.lzma +2022/08/18 | 659342 | systems/win32/miktex/tm/packages/files.csv.lzma 2001/05/14 | 5617 | systems/win32/miktex/tm/packages/finbib.tar.lzma 2015/10/20 | 44379 | systems/win32/miktex/tm/packages/findhyph.tar.lzma 2011/10/21 | 89323 | systems/win32/miktex/tm/packages/fink.tar.lzma @@ -300763,9 +300772,9 @@ 2018/01/12 | 157226 | systems/win32/miktex/tm/packages/miktex-zip-bin-x64.tar.lzma 2022/07/26 | 39695 | systems/win32/miktex/tm/packages/miktex-zlib-bin-2.9.tar.lzma 2022/08/13 | 43580 | systems/win32/miktex/tm/packages/miktex-zlib-bin-x64-2.9.tar.lzma -2022/08/17 | 289560 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma -2022/08/17 | 1482162 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma -2022/08/17 | 1364924 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma +2022/08/18 | 289514 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma +2022/08/18 | 1483018 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma +2022/08/18 | 1364956 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma 2022/07/26 | 13519 | systems/win32/miktex/tm/packages/miktex-zzip-bin-2.9.tar.lzma 2022/08/13 | 14417 | systems/win32/miktex/tm/packages/miktex-zzip-bin-x64-2.9.tar.lzma 2016/07/15 | 35914 | systems/win32/miktex/tm/packages/mil3.tar.lzma @@ -301006,102 +301015,102 @@ 2018/04/09 | 400132 | systems/win32/miktex/tm/packages/newunicodechar.tar.lzma 2011/09/19 | 101312 | systems/win32/miktex/tm/packages/newvbtm.tar.lzma 2021/03/17 | 232249 | systems/win32/miktex/tm/packages/newverbs.tar.lzma -2022/08/17 | 659852 | systems/win32/miktex/tm/packages/next/files.csv.lzma -2022/08/17 | 57317 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma -2022/08/17 | 1161751 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma -2022/08/17 | 31585 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma -2022/08/17 | 35264 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma -2022/08/17 | 65130 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma -2022/08/17 | 95467 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma -2022/08/17 | 77209 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma -2022/08/17 | 31561 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma -2022/08/17 | 394142 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma -2022/08/17 | 37050 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma -2022/08/17 | 57505 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma -2022/08/17 | 270464 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma -2022/08/17 | 196212 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma -2022/08/17 | 103245 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma -2022/08/17 | 24210 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma -2022/08/17 | 481892 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma -2022/08/17 | 52037 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma -2022/08/17 | 325978 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma -2022/08/17 | 45793 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma -2022/08/17 | 135465 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma -2022/08/17 | 1458151 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma -2022/08/17 | 112979 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma -2022/08/17 | 48851 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma -2022/08/17 | 28509 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma -2022/08/17 | 63239 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma -2022/08/17 | 155431 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma -2022/08/17 | 79062 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma -2022/08/17 | 81448 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma -2022/08/17 | 295943 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma -2022/08/17 | 25118 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma -2022/08/17 | 112877 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma -2022/08/17 | 73262 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma -2022/08/17 | 115580 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma -2022/08/17 | 43888 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma -2022/08/17 | 565081 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma -2022/08/17 | 247205 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma -2022/08/17 | 169677 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma -2022/08/17 | 109650 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma -2022/08/17 | 18382 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma -2022/08/17 | 30570 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma -2022/08/17 | 682213 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma -2022/08/17 | 599105 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma -2022/08/17 | 527570 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma -2022/08/17 | 103596 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma -2022/08/17 | 2023133 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma -2022/08/17 | 74099 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma -2022/08/17 | 32072 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma -2022/08/17 | 8824 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma -2022/08/17 | 209137 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma -2022/08/17 | 286684 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma -2022/08/17 | 104921 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma -2022/08/17 | 140012 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma -2022/08/17 | 325483 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma -2022/08/17 | 181723 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma -2022/08/17 | 55319 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma -2022/08/17 | 35228 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma -2022/08/17 | 43616 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma -2022/08/17 | 48037 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma -2022/08/17 | 52369 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma -2022/08/17 | 213846 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma -2022/08/17 | 41179 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma -2022/08/17 | 824994 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma -2022/08/17 | 203418 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma -2022/08/17 | 245151 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma -2022/08/17 | 88483 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma -2022/08/17 | 1323744 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma -2022/08/17 | 23949 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma -2022/08/17 | 37507 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma -2022/08/17 | 72978 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma -2022/08/17 | 58633 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma -2022/08/17 | 769650 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma -2022/08/17 | 1503052 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma -2022/08/17 | 52627 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma -2022/08/17 | 63801 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma -2022/08/17 | 27464 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma -2022/08/17 | 18510 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma -2022/08/17 | 70939 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma -2022/08/17 | 173177 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma -2022/08/17 | 68146 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma -2022/08/17 | 107436 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma -2022/08/17 | 2821670 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma -2022/08/17 | 62359 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma -2022/08/17 | 41189 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma -2022/08/17 | 33861 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma -2022/08/17 | 73684 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma -2022/08/17 | 352464 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma -2022/08/17 | 57501 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma -2022/08/17 | 630433 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma -2022/08/17 | 43527 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma -2022/08/17 | 289497 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma -2022/08/17 | 1481405 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma -2022/08/17 | 1364461 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma -2022/08/17 | 14433 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma +2022/08/18 | 659852 | systems/win32/miktex/tm/packages/next/files.csv.lzma +2022/08/18 | 57316 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma +2022/08/18 | 1161753 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma +2022/08/18 | 31587 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma +2022/08/18 | 35264 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma +2022/08/18 | 65131 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma +2022/08/18 | 95468 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma +2022/08/18 | 77211 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma +2022/08/18 | 31563 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma +2022/08/18 | 394143 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma +2022/08/18 | 37051 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma +2022/08/18 | 57503 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma +2022/08/18 | 270467 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma +2022/08/18 | 196213 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma +2022/08/18 | 103246 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma +2022/08/18 | 24207 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma +2022/08/18 | 481893 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma +2022/08/18 | 52037 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma +2022/08/18 | 325980 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma +2022/08/18 | 45794 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma +2022/08/18 | 135465 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma +2022/08/18 | 1458151 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma +2022/08/18 | 112980 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma +2022/08/18 | 48852 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma +2022/08/18 | 28510 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma +2022/08/18 | 63240 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma +2022/08/18 | 155431 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma +2022/08/18 | 79063 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma +2022/08/18 | 81448 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma +2022/08/18 | 295944 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma +2022/08/18 | 25118 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma +2022/08/18 | 112878 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma +2022/08/18 | 73263 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma +2022/08/18 | 115580 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma +2022/08/18 | 43887 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma +2022/08/18 | 565082 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma +2022/08/18 | 247208 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma +2022/08/18 | 169678 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma +2022/08/18 | 109651 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma +2022/08/18 | 18382 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma +2022/08/18 | 30570 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma +2022/08/18 | 682214 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma +2022/08/18 | 599103 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma +2022/08/18 | 527569 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma +2022/08/18 | 103598 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma +2022/08/18 | 2023132 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma +2022/08/18 | 74099 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma +2022/08/18 | 32073 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma +2022/08/18 | 8825 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma +2022/08/18 | 209139 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma +2022/08/18 | 286685 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma +2022/08/18 | 104920 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma +2022/08/18 | 140012 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma +2022/08/18 | 325483 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma +2022/08/18 | 181724 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma +2022/08/18 | 55320 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma +2022/08/18 | 35231 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma +2022/08/18 | 43618 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma +2022/08/18 | 48038 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma +2022/08/18 | 52369 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma +2022/08/18 | 213847 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma +2022/08/18 | 41179 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma +2022/08/18 | 824995 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma +2022/08/18 | 203420 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma +2022/08/18 | 245151 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma +2022/08/18 | 88483 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma +2022/08/18 | 1323744 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma +2022/08/18 | 23948 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma +2022/08/18 | 37506 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma +2022/08/18 | 72978 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma +2022/08/18 | 58634 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma +2022/08/18 | 769652 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma +2022/08/18 | 1503053 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma +2022/08/18 | 52628 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma +2022/08/18 | 63803 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma +2022/08/18 | 27465 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma +2022/08/18 | 18511 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma +2022/08/18 | 70939 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma +2022/08/18 | 173177 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma +2022/08/18 | 68147 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma +2022/08/18 | 107437 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma +2022/08/18 | 2821671 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma +2022/08/18 | 62359 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma +2022/08/18 | 41190 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma +2022/08/18 | 33862 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma +2022/08/18 | 73685 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma +2022/08/18 | 352464 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma +2022/08/18 | 57501 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma +2022/08/18 | 630436 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma +2022/08/18 | 43527 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma +2022/08/18 | 289517 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma +2022/08/18 | 1480825 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma +2022/08/18 | 1364365 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma +2022/08/18 | 14433 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma 2019/03/12 | 1412 | systems/win32/miktex/tm/packages/nextpage.tar.lzma -2022/08/17 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini +2022/08/18 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini 2018/07/06 | 145915 | systems/win32/miktex/tm/packages/nexus-otf.tar.lzma 2017/03/30 | 191149 | systems/win32/miktex/tm/packages/nfssext-cfr.tar.lzma 2012/12/20 | 612251 | systems/win32/miktex/tm/packages/nicefilelist.tar.lzma @@ -301590,7 +301599,7 @@ 2022/02/22 | 6917 | systems/win32/miktex/tm/packages/preview.tar.lzma 2019/06/20 | 375963 | systems/win32/miktex/tm/packages/prftree.tar.lzma 2021/04/23 | 274028 | systems/win32/miktex/tm/packages/principia.tar.lzma -2022/08/17 | 933 | systems/win32/miktex/tm/packages/pr.ini +2022/08/18 | 933 | systems/win32/miktex/tm/packages/pr.ini 2010/09/27 | 248469 | systems/win32/miktex/tm/packages/printlen.tar.lzma 2008/06/17 | 77313 | systems/win32/miktex/tm/packages/proba.tar.lzma 2017/07/12 | 988486 | systems/win32/miktex/tm/packages/probsoln.tar.lzma @@ -303529,7 +303538,7 @@ 2004/06/04 | 404 | tds/tds.sed 2004/06/23 | 65418 | tds/tds.tex 2019/05/29 | 304264 | tds.zip -2022/08/18 | 186 | timestamp +2022/08/19 | 186 | timestamp 2002/08/26 | 61898 | usergrps/dante/antrag.pdf 2002/08/26 | 118217 | usergrps/dante/antrag.ps 2022/02/28 | 917 | usergrps/dante/dtk/doc/beispiel.bib diff --git a/FILES.byname.gz b/FILES.byname.gz index 712b8678b3..39453ea6d4 100644 Binary files a/FILES.byname.gz and b/FILES.byname.gz differ diff --git a/FILES.last07days b/FILES.last07days index 26cc067371..9d4a18f0ba 100644 --- a/FILES.last07days +++ b/FILES.last07days @@ -1,3 +1,240 @@ +2022/08/19 | 904 | biblio/citation-style-language/citeproc-node-group.lua +2022/08/19 | 8425 | biblio/citation-style-language/citeproc-context.lua +2022/08/19 | 8128 | biblio/citation-style-language/elsevier-harvard.csl +2022/08/19 | 8052 | biblio/citation-style-language/american-political-science-association.csl +2022/08/19 | 7685 | biblio/citation-style-language/citeproc-latex-core.lua +2022/08/19 | 7308 | biblio/citation-style-language/citeproc-node-choose.lua +2022/08/19 | 69907 | biblio/citation-style-language/apa.csl +2022/08/19 | 6283 | biblio/citation-style-language/citeproc-node-sort.lua +2022/08/19 | 61972 | biblio/citation-style-language/citeproc-engine.lua +2022/08/19 | 617 | biblio/citation-style-language/citeproc-lua.1 +2022/08/19 | 6100 | biblio/citation-style-language/citeproc-node-locale.lua +2022/08/19 | 580030 | install/biblio/citation-style-language.tds.zip +2022/08/19 | 5498 | biblio/citation-style-language/citeproc-node-text.lua +2022/08/19 | 5288 | biblio/citation-style-language/nature.csl +2022/08/19 | 4797 | biblio/citation-style-language/citeproc-cli.lua +2022/08/19 | 47520 | biblio/citation-style-language/chicago-fullnote-bibliography.csl +2022/08/19 | 45970 | biblio/citation-style-language/chicago-note-bibliography.csl +2022/08/19 | 44466 | biblio/citation-style-language/citeproc-output.lua +2022/08/19 | 411 | biblio/citation-style-language/citeproc.lua +2022/08/19 | 40700 | biblio/citation-style-language/citeproc-bib-data.lua +2022/08/19 | 3696 | biblio/citation-style-language/citeproc-latex.lua +2022/08/19 | 33702 | biblio/citation-style-language/citeproc-node-names.lua +2022/08/19 | 31273 | biblio/citation-style-language/citation-style-language.sty +2022/08/19 | 2993 | biblio/citation-style-language/citeproc-ir-node.lua +2022/08/19 | 2655 | biblio/citation-style-language/citeproc-node-label.lua +2022/08/19 | 25715 | biblio/citation-style-language/csl-locales-ru-RU.xml +2022/08/19 | 24466 | biblio/citation-style-language/csl-locales-bg-BG.xml +2022/08/19 | 24345 | biblio/citation-style-language/csl-locales-hi-IN.xml +2022/08/19 | 240 | biblio/citation-style-language/citeproc-lua.lua +2022/08/19 | 23553 | biblio/citation-style-language/csl-locales-lv-LV.xml +2022/08/19 | 23467 | biblio/citation-style-language/csl-locales-th-TH.xml +2022/08/19 | 23360 | biblio/citation-style-language/csl-locales-it-IT.xml +2022/08/19 | 23079 | biblio/citation-style-language/chicago-author-date.csl +2022/08/19 | 23024 | biblio/citation-style-language/csl-locales-el-GR.xml +2022/08/19 | 22752 | biblio/citation-style-language/csl-locales-lt-LT.xml +2022/08/19 | 22627 | biblio/citation-style-language/csl-locales-en-US.xml +2022/08/19 | 22542 | biblio/citation-style-language/csl-locales-pt-BR.xml +2022/08/19 | 22472 | biblio/citation-style-language/csl-locales-en-GB.xml +2022/08/19 | 22454 | biblio/citation-style-language/csl-locales-pt-PT.xml +2022/08/19 | 22434 | biblio/citation-style-language/csl-locales-es-MX.xml +2022/08/19 | 22358 | biblio/citation-style-language/csl-locales-nl-NL.xml +2022/08/19 | 22356 | biblio/citation-style-language/csl-locales-fr-FR.xml +2022/08/19 | 22337 | biblio/citation-style-language/csl-locales-fa-IR.xml +2022/08/19 | 22218 | biblio/citation-style-language/csl-locales-de-AT.xml +2022/08/19 | 22143 | biblio/citation-style-language/csl-locales-de-DE.xml +2022/08/19 | 22122 | biblio/citation-style-language/csl-locales-sr-RS.xml +2022/08/19 | 22098 | biblio/citation-style-language/csl-locales-fr-CA.xml +2022/08/19 | 220489 | biblio/citation-style-language/citation-style-language-doc.pdf +2022/08/19 | 21966 | biblio/citation-style-language/csl-locales-km-KH.xml +2022/08/19 | 21941 | biblio/citation-style-language/csl-locales-mn-MN.xml +2022/08/19 | 21822 | biblio/citation-style-language/csl-locales-he-IL.xml +2022/08/19 | 21812 | biblio/citation-style-language/csl-locales-vi-VN.xml +2022/08/19 | 21715 | biblio/citation-style-language/csl-locales-sv-SE.xml +2022/08/19 | 21681 | biblio/citation-style-language/csl-locales-de-CH.xml +2022/08/19 | 21654 | biblio/citation-style-language/csl-locales-id-ID.xml +2022/08/19 | 21635 | biblio/citation-style-language/csl-locales-tr-TR.xml +2022/08/19 | 21623 | biblio/citation-style-language/csl-locales-fi-FI.xml +2022/08/19 | 21579 | biblio/citation-style-language/csl-locales-cs-CZ.xml +2022/08/19 | 21576 | biblio/citation-style-language/csl-locales-cy-GB.xml +2022/08/19 | 21574 | biblio/citation-style-language/csl-locales-sk-SK.xml +2022/08/19 | 21517 | biblio/citation-style-language/citeproc-util.lua +2022/08/19 | 21512 | biblio/citation-style-language/csl-locales-eu.xml +2022/08/19 | 21482 | biblio/citation-style-language/csl-locales-ja-JP.xml +2022/08/19 | 21470 | biblio/citation-style-language/csl-locales-da-DK.xml +2022/08/19 | 21469 | biblio/citation-style-language/csl-locales-is-IS.xml +2022/08/19 | 21466 | biblio/citation-style-language/csl-locales-hu-HU.xml +2022/08/19 | 21462 | biblio/citation-style-language/csl-locales-ro-RO.xml +2022/08/19 | 2145 | biblio/citation-style-language/CHANGELOG.md +2022/08/19 | 21446 | biblio/citation-style-language/csl-locales-pl-PL.xml +2022/08/19 | 21433 | biblio/citation-style-language/csl-locales-af-ZA.xml +2022/08/19 | 21414 | biblio/citation-style-language/csl-locales-ko-KR.xml +2022/08/19 | 21406 | biblio/citation-style-language/csl-locales-es-CL.xml +2022/08/19 | 21363 | biblio/citation-style-language/csl-locales-hr-HR.xml +2022/08/19 | 21330 | biblio/citation-style-language/csl-locales-sl-SI.xml +2022/08/19 | 21327 | biblio/citation-style-language/csl-locales-es-ES.xml +2022/08/19 | 21323 | biblio/citation-style-language/csl-locales-nb-NO.xml +2022/08/19 | 21302 | biblio/citation-style-language/csl-locales-la.xml +2022/08/19 | 21302 | biblio/citation-style-language/csl-locales-ca-AD.xml +2022/08/19 | 21301 | biblio/citation-style-language/csl-locales-nn-NO.xml +2022/08/19 | 21261 | biblio/citation-style-language/csl-locales-et-EE.xml +2022/08/19 | 20747 | biblio/citation-style-language/csl-locales-uk-UA.xml +2022/08/19 | 19817 | biblio/citation-style-language/csl-locales-ar.xml +2022/08/19 | 196 | biblio/citation-style-language/DEPENDS.txt +2022/08/19 | 19423 | biblio/citation-style-language/csl-locales-zh-TW.xml +2022/08/19 | 19056 | biblio/citation-style-language/csl-locales-zh-CN.xml +2022/08/19 | 1702 | biblio/citation-style-language/citeproc-node-layout.lua +2022/08/19 | 1675 | biblio/citation-style-language/citeproc-node-number.lua +2022/08/19 | 1673 | biblio/citation-style-language/README.md +2022/08/19 | 16054 | biblio/citation-style-language/modern-humanities-research-association.csl +2022/08/19 | 15617 | biblio/citation-style-language/citeproc-node-style.lua +2022/08/19 | 15413 | biblio/citation-style-language/ieee.csl +2022/08/19 | 14833 | biblio/citation-style-language/citeproc-element.lua +2022/08/19 | 1476 | biblio/citation-style-language/citeproc-nodes.lua +2022/08/19 | 14024 | biblio/citation-style-language/citeproc-bib.lua +2022/08/19 | 13585 | biblio/citation-style-language/citeproc-node-date.lua +2022/08/19 | 13278 | biblio/citation-style-language/citation-style-language-doc.tex +2022/08/19 | 11958 | biblio/citation-style-language/vancouver.csl +2022/08/19 | 11209 | biblio/citation-style-language/modern-language-association.csl +2022/08/19 | 11060 | biblio/citation-style-language/american-sociological-association.csl +2022/08/19 | 11034 | biblio/citation-style-language/american-medical-association.csl +2022/08/19 | 10723 | biblio/citation-style-language/harvard-cite-them-right.csl +2022/08/19 | 10019 | biblio/citation-style-language/american-chemical-society.csl +2022/08/18 | 95468 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma +2022/08/18 | 952684 | systems/texlive/tlnet/archive/tex4ht.r64138.tar.xz +2022/08/18 | 933 | systems/win32/miktex/tm/packages/pr.ini +2022/08/18 | 88483 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma +2022/08/18 | 8825 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma +2022/08/18 | 824995 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma +2022/08/18 | 81448 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma +2022/08/18 | 813 | graphics/pgf/contrib/fancyqr/qr-example.tex +2022/08/18 | 792156 | systems/texlive/tlnet/archive/fithesis.r64135.tar.xz +2022/08/18 | 79063 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma +2022/08/18 | 77211 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma +2022/08/18 | 769652 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma +2022/08/18 | 74099 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma +2022/08/18 | 73685 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma +2022/08/18 | 73263 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma +2022/08/18 | 730120 | systems/texlive/tlnet/archive/fithesis.doc.r64135.tar.xz +2022/08/18 | 72978 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma +2022/08/18 | 70939 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma +2022/08/18 | 682214 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma +2022/08/18 | 68147 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma +2022/08/18 | 659852 | systems/win32/miktex/tm/packages/next/files.csv.lzma +2022/08/18 | 659342 | systems/win32/miktex/tm/packages/files.csv.lzma +2022/08/18 | 65131 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma +2022/08/18 | 64028 | systems/texlive/tlnet/archive/hereapplies.doc.r64136.tar.xz +2022/08/18 | 63803 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma +2022/08/18 | 63240 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma +2022/08/18 | 630436 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma +2022/08/18 | 62359 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma +2022/08/18 | 599103 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma +2022/08/18 | 58634 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma +2022/08/18 | 5831895 | systems/texlive/tlnet/install-tl-unx.tar.gz +2022/08/18 | 577055 | biblio/citation-style-language.zip +2022/08/18 | 57503 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma +2022/08/18 | 57501 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma +2022/08/18 | 57316 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma +2022/08/18 | 565082 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma +2022/08/18 | 55320 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma +2022/08/18 | 527569 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma +2022/08/18 | 52628 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma +2022/08/18 | 524800 | systems/texlive/tlnet/archive/exam.doc.r64134.tar.xz +2022/08/18 | 52369 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma +2022/08/18 | 52037 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma +2022/08/18 | 49900 | systems/texlive/tlnet/archive/exam.r64134.tar.xz +2022/08/18 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 +2022/08/18 | 48852 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma +2022/08/18 | 481893 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma +2022/08/18 | 48038 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma +2022/08/18 | 45794 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma +2022/08/18 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc +2022/08/18 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc +2022/08/18 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc +2022/08/18 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc +2022/08/18 | 45332 | systems/texlive/tlnet/archive/fithesis.source.r64135.tar.xz +2022/08/18 | 4485 | graphics/pgf/contrib/fancyqr/fancyqr-doc.tex +2022/08/18 | 43887 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma +2022/08/18 | 43618 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma +2022/08/18 | 43527 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma +2022/08/18 | 419376 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64133.tar.xz +2022/08/18 | 41190 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma +2022/08/18 | 41179 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma +2022/08/18 | 394143 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma +2022/08/18 | 37506 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma +2022/08/18 | 3748 | systems/texlive/tlnet/archive/hereapplies.r64136.tar.xz +2022/08/18 | 37051 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma +2022/08/18 | 3582 | graphics/pgf/contrib/fancyqr/README.md +2022/08/18 | 35264 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma +2022/08/18 | 352464 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma +2022/08/18 | 35231 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma +2022/08/18 | 33862 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma +2022/08/18 | 325980 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma +2022/08/18 | 325483 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma +2022/08/18 | 32073 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma +2022/08/18 | 31587 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma +2022/08/18 | 31563 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma +2022/08/18 | 30570 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma +2022/08/18 | 295944 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma +2022/08/18 | 289517 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma +2022/08/18 | 289514 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma +2022/08/18 | 286685 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma +2022/08/18 | 28510 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma +2022/08/18 | 2821671 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma +2022/08/18 | 27465 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma +2022/08/18 | 270467 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma +2022/08/18 | 25516427 | systems/texlive/tlnet/install-tl.zip +2022/08/18 | 25118 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma +2022/08/18 | 251020 | graphics/pgf/contrib/fancyqr.zip +2022/08/18 | 248260 | graphics/pgf/contrib/fancyqr/fancyqr-doc.pdf +2022/08/18 | 247208 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma +2022/08/18 | 245151 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma +2022/08/18 | 24207 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma +2022/08/18 | 2399360 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz +2022/08/18 | 23948 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma +2022/08/18 | 213847 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma +2022/08/18 | 209139 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma +2022/08/18 | 20433402 | systems/texlive/tlnet/install-tl-windows.exe +2022/08/18 | 203420 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma +2022/08/18 | 2023132 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma +2022/08/18 | 196213 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma +2022/08/18 | 18511 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma +2022/08/18 | 18382 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma +2022/08/18 | 181724 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma +2022/08/18 | 17582691 | systems/texlive/tlnet/tlpkg/texlive.tlpdb +2022/08/18 | 173177 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma +2022/08/18 | 169678 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma +2022/08/18 | 155431 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma +2022/08/18 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512 +2022/08/18 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512 +2022/08/18 | 1503053 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma +2022/08/18 | 1483018 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma +2022/08/18 | 1480825 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma +2022/08/18 | 145 | systems/texlive/tlnet/install-tl.zip.sha512 +2022/08/18 | 1458151 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma +2022/08/18 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 +2022/08/18 | 14433 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma +2022/08/18 | 140012 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma +2022/08/18 | 1364956 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma +2022/08/18 | 1364365 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma +2022/08/18 | 136388 | systems/texlive/tlnet/archive/tex4ht.doc.r64138.tar.xz +2022/08/18 | 135465 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma +2022/08/18 | 1323744 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma +2022/08/18 | 1274892 | systems/texlive/tlnet/archive/tex4ht.source.r64138.tar.xz +2022/08/18 | 1211724 | systems/texlive/tlnet/archive/profcollege.r64137.tar.xz +2022/08/18 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini +2022/08/18 | 1161753 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma +2022/08/18 | 115580 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma +2022/08/18 | 113500 | systems/texlive/tlnet/archive/texlive-scripts.r64133.tar.xz +2022/08/18 | 112980 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma +2022/08/18 | 112878 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma +2022/08/18 | 109651 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma +2022/08/18 | 107437 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma +2022/08/18 | 104920 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma +2022/08/18 | 103598 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma +2022/08/18 | 103246 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma +2022/08/18 | 10265 | graphics/pgf/contrib/fancyqr/fancyqr.sty +2022/08/18 | 10136040 | systems/texlive/tlnet/archive/profcollege.doc.r64137.tar.xz 2022/08/17 | 9955 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-color.pdf 2022/08/17 | 9945 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-czech-color.eps 2022/08/17 | 9931 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-czech.eps @@ -6,13 +243,11 @@ 2022/08/17 | 9661 | macros/luatex/generic/luavlna/luavlna-doc.tex 2022/08/17 | 9654 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-czech.eps 2022/08/17 | 95 | macros/latex/contrib/fithesis/style/mu/pharm.ins -2022/08/17 | 95467 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma 2022/08/17 | 9528 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-czech-color.eps 2022/08/17 | 9514 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-czech.eps 2022/08/17 | 93 | macros/latex/contrib/fithesis/style/mu/phil.ins 2022/08/17 | 93 | macros/latex/contrib/fithesis/style/mu/fsps.ins 2022/08/17 | 93 | macros/latex/contrib/fithesis/style/mu/econ.ins -2022/08/17 | 933 | systems/win32/miktex/tm/packages/pr.ini 2022/08/17 | 92320 | macros/luatex/generic/luavlna/luavlna-doc.pdf 2022/08/17 | 91 | macros/latex/contrib/fithesis/style/mu/sci.ins 2022/08/17 | 91 | macros/latex/contrib/fithesis/style/mu/ped.ins @@ -23,9 +258,7 @@ 2022/08/17 | 8995 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-czech-color.eps 2022/08/17 | 8981 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-czech.eps 2022/08/17 | 893 | macros/latex/contrib/fithesis/locale/english.ins -2022/08/17 | 88483 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma 2022/08/17 | 88348 | macros/latex/contrib/fithesis/fithesis.dtx -2022/08/17 | 8824 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma 2022/08/17 | 8817716 | macros/latex/contrib/profcollege/doc/ProfCollege-doc.pdf 2022/08/17 | 87 | macros/latex/contrib/fithesis/style/base.ins 2022/08/17 | 8732 | macros/latex/contrib/fithesis/style/mu/med.dtx @@ -33,34 +266,24 @@ 2022/08/17 | 867213 | macros/latex/contrib/profcollege/latex/ProfCollege.sty 2022/08/17 | 855220 | systems/texlive/tlnet/archive/proflycee.doc.r64130.tar.xz 2022/08/17 | 845 | macros/latex/contrib/fithesis/locale/czech.ins -2022/08/17 | 824994 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma -2022/08/17 | 81448 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma +2022/08/17 | 8111 | support/TeX4ht/source/mktex4ht-cnf.tex 2022/08/17 | 8082 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-czech-color.eps 2022/08/17 | 8068 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-czech.eps -2022/08/17 | 79062 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma 2022/08/17 | 7740 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-czech-color.eps 2022/08/17 | 7726 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-czech.eps -2022/08/17 | 77209 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma -2022/08/17 | 769650 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma 2022/08/17 | 767427 | macros/latex/contrib/fithesis/fithesis.pdf 2022/08/17 | 7560 | systems/texlive/tlnet/archive/pygmentex.r64131.tar.xz 2022/08/17 | 74997 | macros/latex/contrib/fithesis/style/mu/base.dtx -2022/08/17 | 74099 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma -2022/08/17 | 73684 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma -2022/08/17 | 73262 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma -2022/08/17 | 72978 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma 2022/08/17 | 7256 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-czech.eps 2022/08/17 | 7245 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-czech-color.eps +2022/08/17 | 7221 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl 2022/08/17 | 7188 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil-czech-color.eps 2022/08/17 | 7174 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil-czech.eps 2022/08/17 | 715568 | systems/texlive/tlnet/archive/pygmentex.doc.r64131.tar.xz -2022/08/17 | 70939 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma 2022/08/17 | 705 | macros/luatex/generic/luavlna/luavlna-predegrees.lua 2022/08/17 | 7015 | macros/latex/contrib/fithesis/style/mu/fsps.dtx 2022/08/17 | 6917 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-english-color.eps 2022/08/17 | 6905 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-english.eps -2022/08/17 | 682213 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma -2022/08/17 | 68146 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma 2022/08/17 | 6778 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-czech-color.eps 2022/08/17 | 6765 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-czech-color.eps 2022/08/17 | 6764 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-czech.eps @@ -69,39 +292,17 @@ 2022/08/17 | 6734 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-czech-color.eps 2022/08/17 | 6720 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-czech.eps 2022/08/17 | 662 | macros/latex/contrib/fithesis/style/base.dtx -2022/08/17 | 659852 | systems/win32/miktex/tm/packages/next/files.csv.lzma -2022/08/17 | 659342 | systems/win32/miktex/tm/packages/files.csv.lzma -2022/08/17 | 65130 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma -2022/08/17 | 63801 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma 2022/08/17 | 6364 | macros/latex/contrib/fithesis/style/mu/law.dtx -2022/08/17 | 63239 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma 2022/08/17 | 63156 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci.eps -2022/08/17 | 630433 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma -2022/08/17 | 62359 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma -2022/08/17 | 599105 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma +2022/08/17 | 617532 | support/TeX4ht/source/tex4ht-html4.tex 2022/08/17 | 595510 | macros/latex/contrib/fithesis/logo/mu/fithesis-base.eps 2022/08/17 | 588830 | macros/latex/contrib/exam.zip -2022/08/17 | 58633 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma -2022/08/17 | 5831896 | systems/texlive/tlnet/install-tl-unx.tar.gz -2022/08/17 | 57505 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma -2022/08/17 | 57501 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma -2022/08/17 | 57317 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma 2022/08/17 | 569182 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss.eps -2022/08/17 | 565081 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma -2022/08/17 | 55319 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma 2022/08/17 | 53503 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-color.eps -2022/08/17 | 527570 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma -2022/08/17 | 52627 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma -2022/08/17 | 52369 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma 2022/08/17 | 5215 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-english-color.eps -2022/08/17 | 52037 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma 2022/08/17 | 5201 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-english.eps 2022/08/17 | 5106 | macros/latex/contrib/fithesis/style/mu/phil.dtx -2022/08/17 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 -2022/08/17 | 48851 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma 2022/08/17 | 485896 | systems/texlive/tlnet/archive/lt3luabridge.doc.r64128.tar.xz -2022/08/17 | 481892 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma -2022/08/17 | 48037 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma 2022/08/17 | 4793 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-english-color.eps 2022/08/17 | 4779 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-english.eps 2022/08/17 | 46 | macros/luatex/generic/luavlna/luavlna.4ht @@ -114,12 +315,7 @@ 2022/08/17 | 4600 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-czech.pdf 2022/08/17 | 45934 | macros/latex/contrib/fithesis/logo/mu/fithesis-law.eps 2022/08/17 | 4591 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-english-color.eps -2022/08/17 | 45793 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma 2022/08/17 | 4577 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-english.eps -2022/08/17 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc -2022/08/17 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc -2022/08/17 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc -2022/08/17 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc 2022/08/17 | 4558 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-english-color.eps 2022/08/17 | 4544 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-english.eps 2022/08/17 | 4524 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-english-color.eps @@ -130,15 +326,9 @@ 2022/08/17 | 449 | macros/luatex/generic/luavlna/luavlna-csplain-langs.lua 2022/08/17 | 4398 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-czech-color.pdf 2022/08/17 | 4393 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-czech.pdf -2022/08/17 | 43888 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma -2022/08/17 | 43616 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma -2022/08/17 | 43527 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma 2022/08/17 | 4289 | macros/luatex/generic/luavlna/luavlna.tex 2022/08/17 | 420604 | systems/texlive/tlnet/archive/msu-thesis.doc.r64129.tar.xz -2022/08/17 | 419496 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64126.tar.xz 2022/08/17 | 4119 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-czech-color.pdf -2022/08/17 | 41189 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma -2022/08/17 | 41179 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma 2022/08/17 | 4113 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-czech.pdf 2022/08/17 | 41114 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-color.pdf 2022/08/17 | 41093 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm.pdf @@ -148,7 +338,6 @@ 2022/08/17 | 4002 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-czech.pdf 2022/08/17 | 4000 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-english.eps 2022/08/17 | 3953 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-english-color.eps -2022/08/17 | 394142 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma 2022/08/17 | 3939 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-english.eps 2022/08/17 | 3866 | macros/latex/contrib/fithesis/style/mu/ped.dtx 2022/08/17 | 38432 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-color.eps @@ -163,28 +352,17 @@ 2022/08/17 | 3776 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-czech.pdf 2022/08/17 | 3771 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-english-color.pdf 2022/08/17 | 3767 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-english.pdf -2022/08/17 | 37507 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma 2022/08/17 | 3742 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-english-color.eps 2022/08/17 | 3728 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-english.eps -2022/08/17 | 37050 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma 2022/08/17 | 3702 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-czech-color.pdf 2022/08/17 | 3697 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-czech.pdf 2022/08/17 | 3631 | macros/latex/contrib/fithesis/README.md 2022/08/17 | 3611 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-english-color.eps 2022/08/17 | 3597 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-english.eps -2022/08/17 | 35264 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma -2022/08/17 | 352464 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma -2022/08/17 | 35228 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma -2022/08/17 | 33861 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma 2022/08/17 | 3320 | macros/luatex/generic/luavlna/README.md 2022/08/17 | 3307 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-english-color.pdf 2022/08/17 | 3303 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-english.pdf -2022/08/17 | 325978 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma -2022/08/17 | 325483 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma -2022/08/17 | 32072 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma 2022/08/17 | 3189 | macros/latex/contrib/fithesis/style/mu/fss.dtx -2022/08/17 | 31585 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma -2022/08/17 | 31561 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma 2022/08/17 | 31390 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps.eps 2022/08/17 | 3131 | macros/luatex/generic/luavlna/luavlna-langno.lua 2022/08/17 | 3128 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil-english-color.pdf @@ -196,43 +374,27 @@ 2022/08/17 | 3069 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-english.pdf 2022/08/17 | 3068 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-english-color.pdf 2022/08/17 | 3063 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-english.pdf -2022/08/17 | 30570 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma 2022/08/17 | 30559 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ.eps 2022/08/17 | 3033 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-english-color.pdf 2022/08/17 | 3027 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-english.pdf 2022/08/17 | 29847 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-color.eps -2022/08/17 | 295943 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma 2022/08/17 | 2948 | systems/texlive/tlnet/archive/lt3luabridge.r64128.tar.xz 2022/08/17 | 29260 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi.eps 2022/08/17 | 2902 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-english-color.pdf 2022/08/17 | 289 | macros/luatex/generic/luavlna/luavlna-si.lua -2022/08/17 | 289560 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma 2022/08/17 | 2894 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-english.pdf -2022/08/17 | 289497 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma 2022/08/17 | 286 | macros/latex/contrib/fithesis/style/mu/base.ins -2022/08/17 | 286684 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma 2022/08/17 | 2852 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-english-color.pdf -2022/08/17 | 28509 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma 2022/08/17 | 2849 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-english.pdf 2022/08/17 | 28441 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil.eps -2022/08/17 | 2821670 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma 2022/08/17 | 2808 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-english-color.pdf 2022/08/17 | 2804 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-english.pdf 2022/08/17 | 279 | macros/latex/contrib/fithesis/fithesis.ins -2022/08/17 | 27464 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma 2022/08/17 | 2732 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-english-color.pdf 2022/08/17 | 2727 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-english.pdf -2022/08/17 | 270464 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma -2022/08/17 | 25516446 | systems/texlive/tlnet/install-tl.zip 2022/08/17 | 25396 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci.pdf 2022/08/17 | 25297 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-color.eps -2022/08/17 | 25118 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma -2022/08/17 | 247205 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma -2022/08/17 | 245151 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma 2022/08/17 | 24328 | macros/latex/contrib/fithesis/logo/mu/fithesis-sci-color.pdf -2022/08/17 | 24210 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma -2022/08/17 | 2397600 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz -2022/08/17 | 23949 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma 2022/08/17 | 23708 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil-color.eps 2022/08/17 | 23539 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped.eps 2022/08/17 | 23529 | macros/latex/contrib/fithesis/logo/mu/fithesis-med.eps @@ -240,76 +402,40 @@ 2022/08/17 | 2218015 | macros/latex/contrib/profcollege/doc/ProfCollege-doc.zip 2022/08/17 | 217 | macros/luatex/generic/luavlna/luavlna-presi.lua 2022/08/17 | 2175294 | install/macros/latex/contrib/fithesis.tds.zip -2022/08/17 | 213846 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma 2022/08/17 | 21247 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-color.eps -2022/08/17 | 209137 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma 2022/08/17 | 2056051 | macros/latex/contrib/fithesis.zip 2022/08/17 | 204 | macros/luatex/generic/luavlna/luavlna-sufdegrees.lua -2022/08/17 | 20436545 | systems/texlive/tlnet/install-tl-windows.exe -2022/08/17 | 203418 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma 2022/08/17 | 20324 | macros/latex/contrib/fithesis/locale/czech.dtx -2022/08/17 | 2023133 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma 2022/08/17 | 20058 | macros/latex/contrib/fithesis/locale/slovak.dtx -2022/08/17 | 196212 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma +2022/08/17 | 200199 | support/TeX4ht/source/ChangeLog 2022/08/17 | 19569 | macros/latex/contrib/fithesis/logo/mu/fithesis-med-color.eps 2022/08/17 | 19363 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped-color.eps -2022/08/17 | 18510 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma -2022/08/17 | 18382 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma 2022/08/17 | 18339 | macros/latex/contrib/fithesis/logo/mu/fithesis-law.pdf -2022/08/17 | 181723 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma 2022/08/17 | 1760 | macros/latex/contrib/fithesis/LICENSE.tex -2022/08/17 | 17582689 | systems/texlive/tlnet/tlpkg/texlive.tlpdb 2022/08/17 | 17579 | macros/latex/contrib/fithesis/logo/mu/fithesis-law-color.pdf -2022/08/17 | 173177 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma -2022/08/17 | 169677 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma 2022/08/17 | 15892 | macros/latex/contrib/fithesis/logo/mu/fithesis-base.pdf -2022/08/17 | 155431 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma -2022/08/17 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512 -2022/08/17 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512 2022/08/17 | 15264 | macros/latex/contrib/fithesis/locale/english.dtx 2022/08/17 | 15062 | macros/latex/contrib/fithesis/logo/mu/fithesis-base-color.pdf -2022/08/17 | 1503052 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma -2022/08/17 | 1482162 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma -2022/08/17 | 1481405 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma -2022/08/17 | 145 | systems/texlive/tlnet/install-tl.zip.sha512 2022/08/17 | 1459 | macros/luatex/generic/luavlna/luavlna.sty -2022/08/17 | 1458151 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma 2022/08/17 | 14512 | systems/texlive/tlnet/archive/proflycee.r64130.tar.xz -2022/08/17 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 -2022/08/17 | 14433 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma -2022/08/17 | 140012 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma -2022/08/17 | 1364924 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma -2022/08/17 | 1364461 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma -2022/08/17 | 135465 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma -2022/08/17 | 1323744 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma 2022/08/17 | 13150370 | macros/latex/contrib/profcollege.zip 2022/08/17 | 12838 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ.pdf 2022/08/17 | 12347 | macros/luatex/generic/luavlna/luavlna.lua 2022/08/17 | 12305 | macros/latex/contrib/fithesis/logo/mu/fithesis-econ-color.pdf 2022/08/17 | 12083 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil.pdf 2022/08/17 | 12007 | macros/latex/contrib/fithesis/style/mu/econ.dtx -2022/08/17 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini 2022/08/17 | 11709 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi-color.pdf 2022/08/17 | 11634 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps.pdf -2022/08/17 | 1161751 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma 2022/08/17 | 11596 | macros/latex/contrib/fithesis/logo/mu/fithesis-phil-color.pdf -2022/08/17 | 115580 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma 2022/08/17 | 11529 | macros/latex/contrib/fithesis/logo/mu/fithesis-fi.pdf 2022/08/17 | 11492 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss.pdf 2022/08/17 | 11356 | systems/texlive/tlnet/archive/msu-thesis.r64129.tar.xz -2022/08/17 | 113524 | systems/texlive/tlnet/archive/texlive-scripts.r64126.tar.xz -2022/08/17 | 112979 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma -2022/08/17 | 112877 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma +2022/08/17 | 1128043 | support/TeX4ht/source/tex4ht-4ht.tex 2022/08/17 | 11199 | macros/latex/contrib/fithesis/logo/mu/fithesis-fsps-color.pdf -2022/08/17 | 109650 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma 2022/08/17 | 10758 | macros/latex/contrib/fithesis/logo/mu/fithesis-fss-color.pdf -2022/08/17 | 107436 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma 2022/08/17 | 10494 | macros/latex/contrib/fithesis/logo/mu/fithesis-med.pdf -2022/08/17 | 104921 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma 2022/08/17 | 104682 | macros/luatex/generic/luavlna.zip 2022/08/17 | 10396 | macros/latex/contrib/fithesis/logo/mu/fithesis-ped.pdf -2022/08/17 | 103596 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma -2022/08/17 | 103245 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma 2022/08/17 | 101310 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm-color.eps 2022/08/17 | 101282 | macros/latex/contrib/fithesis/logo/mu/fithesis-pharm.eps 2022/08/16 | 9602 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual-en-library-misc.tex @@ -319,7 +445,6 @@ 2022/08/16 | 823 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual.tex 2022/08/16 | 784594 | macros/latex/contrib/pygmentex.zip 2022/08/16 | 773 | macros/latex/contrib/pygmentex/README -2022/08/16 | 7321 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl 2022/08/16 | 7188 | macros/latex/contrib/msu-thesis/samples/MSU-thesis-template.tex 2022/08/16 | 69999 | macros/latex/contrib/hereapplies.zip 2022/08/16 | 689500 | systems/texlive/tlnet/archive/xduts.doc.r64124.tar.xz @@ -390,7 +515,6 @@ 2022/08/15 | 736 | macros/latex/contrib/proflycee/doc/perroquet.csv 2022/08/15 | 707491 | macros/xetex/latex/xduts/xduts.pdf 2022/08/15 | 69340 | macros/xetex/latex/exam-zh/tex/exam-zh.cls -2022/08/15 | 63056 | systems/texlive/tlnet/archive/hereapplies.doc.r64117.tar.xz 2022/08/15 | 62157 | macros/latex/contrib/proflycee/tex/ProfLycee.sty 2022/08/15 | 62108 | macros/latex/contrib/elegantbook/elegantbook-cn.tex 2022/08/15 | 5868 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual-en-library-paths.ortho.tex @@ -402,7 +526,6 @@ 2022/08/15 | 431480 | systems/texlive/tlnet/archive/armtex.doc.r64119.tar.xz 2022/08/15 | 38903 | macros/latex/contrib/elegantbook/elegantbook-en.tex 2022/08/15 | 32 | macros/latex/contrib/proflycee/doc/basique.csv -2022/08/15 | 3216 | systems/texlive/tlnet/archive/hereapplies.r64117.tar.xz 2022/08/15 | 3061 | macros/latex/contrib/elegantbook/README.md 2022/08/15 | 2947940 | macros/latex/contrib/elegantbook.zip 2022/08/15 | 2861 | macros/xetex/latex/xduts/README.md @@ -712,7 +835,6 @@ 2022/08/12 | 9861 | macros/latex/contrib/ucs/data/uni-148.def 2022/08/12 | 9831 | macros/latex/contrib/ucs/data/uni-30.def 2022/08/12 | 9571 | macros/latex/contrib/ucs/config/pifont.ucf -2022/08/12 | 952476 | systems/texlive/tlnet/archive/tex4ht.r64099.tar.xz 2022/08/12 | 9515 | macros/latex/contrib/ucs/data/uni-37.def 2022/08/12 | 9504 | macros/latex/contrib/ucs/config/math.ucf 2022/08/12 | 9412 | macros/latex/contrib/ucs/data/uni-250.def @@ -980,7 +1102,6 @@ 2022/08/12 | 13691 | macros/latex/contrib/ucs/data/uni-114.def 2022/08/12 | 13690 | macros/latex/contrib/ucs/data/uni-105.def 2022/08/12 | 13680 | macros/latex/contrib/ucs/data/uni-107.def -2022/08/12 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64099.tar.xz 2022/08/12 | 1353 | macros/latex/contrib/ucs/data/uni-182.def 2022/08/12 | 13517 | dviware/dvisvgm/tests/data/Makefile.in 2022/08/12 | 13472 | macros/latex/contrib/ucs/data/uni-97.def @@ -1002,7 +1123,6 @@ 2022/08/12 | 12804 | macros/latex/contrib/ucs/data/uni-142.def 2022/08/12 | 12764 | macros/latex/contrib/ucs/data/uni-249.def 2022/08/12 | 12752 | macros/latex/contrib/ucs/data/uni-141.def -2022/08/12 | 1272264 | systems/texlive/tlnet/archive/tex4ht.source.r64099.tar.xz 2022/08/12 | 12677 | macros/latex/contrib/ucs/data/uni-151.def 2022/08/12 | 12660 | macros/latex/contrib/ucs/data/uni-120.def 2022/08/12 | 12651 | macros/latex/contrib/ucs/data/uni-115.def @@ -1062,147 +1182,3 @@ 2022/08/12 | 10122 | macros/latex/contrib/ucs/data/uni-147.def 2022/08/12 | 10028 | macros/latex/contrib/ucs/data/uni-48.def 2022/08/12 | 10001 | macros/latex/contrib/ucs/data/uni-146.def -2022/08/11 | 97323 | macros/latex/contrib/fixdif/fixdif.pdf -2022/08/11 | 962390 | systems/win32/miktex/tm/packages/postnotes__doc.tar.lzma -2022/08/11 | 9021 | systems/win32/miktex/tm/packages/ptex2pdf__doc.tar.lzma -2022/08/11 | 90135 | systems/win32/miktex/tm/packages/xecjk__source.tar.lzma -2022/08/11 | 8933 | systems/win32/miktex/tm/packages/projlib__doc.tar.lzma -2022/08/11 | 890197 | systems/win32/miktex/tm/packages/l3backend__doc.tar.lzma -2022/08/11 | 84332 | macros/latex/contrib/create-theorem/create-theorem-doc.pdf -2022/08/11 | 83300 | systems/texlive/tlnet/archive/luatexja.source.r64092.tar.xz -2022/08/11 | 8252 | systems/win32/miktex/tm/packages/texlogsieve__source.tar.lzma -2022/08/11 | 81419 | systems/win32/miktex/tm/packages/proflycee__source.tar.lzma -2022/08/11 | 79649 | systems/win32/miktex/tm/packages/tagpdf__source.tar.lzma -2022/08/11 | 7936874 | systems/win32/miktex/tm/packages/profcollege__doc.tar.lzma -2022/08/11 | 787 | macros/latex/contrib/fixdif/README.md -2022/08/11 | 7751 | systems/win32/miktex/tm/packages/customdice__source.tar.lzma -2022/08/11 | 74250 | systems/win32/miktex/tm/packages/texlogsieve__doc.tar.lzma -2022/08/11 | 7408 | systems/texlive/tlnet/archive/customdice.source.r64089.tar.xz -2022/08/11 | 7343 | systems/win32/miktex/tm/packages/postnotes.tar.lzma -2022/08/11 | 728710 | systems/win32/miktex/tm/packages/bodeplot__doc.tar.lzma -2022/08/11 | 717703 | systems/win32/miktex/tm/packages/proflycee__doc.tar.lzma -2022/08/11 | 68258 | systems/win32/miktex/tm/packages/olsak-misc.tar.lzma -2022/08/11 | 676 | macros/latex/contrib/create-theorem/README.md -2022/08/11 | 66837 | macros/latex/contrib/runcode/troubleshoot.pdf -2022/08/11 | 6679 | systems/win32/miktex/tm/packages/ocgx2__doc.tar.lzma -2022/08/11 | 65091 | systems/win32/miktex/tm/packages/siunitx.tar.lzma -2022/08/11 | 6489 | systems/win32/miktex/tm/packages/tikz-relay.tar.lzma -2022/08/11 | 642560 | systems/win32/miktex/tm/packages/europecv__doc.tar.lzma -2022/08/11 | 63738 | systems/win32/miktex/tm/packages/njuthesis__source.tar.lzma -2022/08/11 | 622691 | systems/win32/miktex/tm/packages/colortbl__doc.tar.lzma -2022/08/11 | 610698 | systems/win32/miktex/tm/packages/tikz-relay__doc.tar.lzma -2022/08/11 | 6105 | systems/win32/miktex/tm/packages/unbtex.tar.lzma -2022/08/11 | 6080 | systems/win32/miktex/tm/packages/bookcover.tar.lzma -2022/08/11 | 590455 | systems/win32/miktex/tm/packages/l3kernel__source.tar.lzma -2022/08/11 | 5725 | systems/win32/miktex/tm/packages/bodeplot.tar.lzma -2022/08/11 | 56780 | macros/latex/contrib/runcode/runcode.pdf -2022/08/11 | 5478 | systems/win32/miktex/tm/packages/unbtex__source.tar.lzma -2022/08/11 | 53831 | systems/win32/miktex/tm/packages/yfonts-otf.tar.lzma -2022/08/11 | 534525 | systems/win32/miktex/tm/packages/lt3rawobjects__doc.tar.lzma -2022/08/11 | 5236 | systems/win32/miktex/tm/packages/lt3luabridge__source.tar.lzma -2022/08/11 | 5065 | systems/win32/miktex/tm/packages/ptex2pdf.tar.lzma -2022/08/11 | 5040 | systems/win32/miktex/tm/packages/sillypage__source.tar.lzma -2022/08/11 | 492218 | systems/win32/miktex/tm/packages/gentombow__doc.tar.lzma -2022/08/11 | 487082 | systems/win32/miktex/tm/packages/lt3luabridge__doc.tar.lzma -2022/08/11 | 4685 | systems/win32/miktex/tm/packages/xsavebox__source.tar.lzma -2022/08/11 | 4620 | systems/win32/miktex/tm/packages/yfonts-otf__source.tar.lzma -2022/08/11 | 45480 | systems/win32/miktex/tm/packages/texlogsieve.tar.lzma -2022/08/11 | 4398 | systems/win32/miktex/tm/packages/bhcexam.tar.lzma -2022/08/11 | 4380 | systems/win32/miktex/tm/packages/xsavebox.tar.lzma -2022/08/11 | 43809 | systems/win32/miktex/tm/packages/tuda-ci.tar.lzma -2022/08/11 | 4168 | systems/texlive/tlnet/archive/bhcexam.r64093.tar.xz -2022/08/11 | 3977562 | systems/win32/miktex/tm/packages/tudscr__doc.tar.lzma -2022/08/11 | 396799 | systems/win32/miktex/tm/packages/europecv__source.tar.lzma -2022/08/11 | 3945 | systems/win32/miktex/tm/packages/colortbl.tar.lzma -2022/08/11 | 379622 | systems/win32/miktex/tm/packages/xsavebox__doc.tar.lzma -2022/08/11 | 3788 | systems/win32/miktex/tm/packages/biblatex-software.tar.lzma -2022/08/11 | 3668 | systems/win32/miktex/tm/packages/lt3rawobjects.tar.lzma -2022/08/11 | 3666626 | systems/win32/miktex/tm/packages/media9__doc.tar.lzma -2022/08/11 | 361526 | systems/win32/miktex/tm/packages/customdice__doc.tar.lzma -2022/08/11 | 360976 | systems/texlive/tlnet/archive/customdice.doc.r64089.tar.xz -2022/08/11 | 359746 | systems/win32/miktex/tm/packages/bhcexam__doc.tar.lzma -2022/08/11 | 359312 | systems/texlive/tlnet/archive/bhcexam.doc.r64093.tar.xz -2022/08/11 | 3584 | systems/win32/miktex/tm/packages/nimsticks__source.tar.lzma -2022/08/11 | 3549 | systems/win32/miktex/tm/packages/zlmtt.tar.lzma -2022/08/11 | 34279 | macros/latex/contrib/create-theorem/create-theorem-doc.tex -2022/08/11 | 33280 | systems/win32/miktex/tm/packages/l3backend__source.tar.lzma -2022/08/11 | 325945 | systems/win32/miktex/tm/packages/tudscr__source.tar.lzma -2022/08/11 | 3254072 | systems/texlive/tlnet/archive/luatexja.doc.r64092.tar.xz -2022/08/11 | 3206 | systems/win32/miktex/tm/packages/zlmtt__source.tar.lzma -2022/08/11 | 3179 | macros/latex/contrib/fixdif/fixdif.ins -2022/08/11 | 3145071 | systems/win32/miktex/tm/packages/animate__doc.tar.lzma -2022/08/11 | 31275 | systems/win32/miktex/tm/packages/postnotes__source.tar.lzma -2022/08/11 | 3116019 | systems/win32/miktex/tm/packages/luatexja__doc.tar.lzma -2022/08/11 | 3078 | systems/win32/miktex/tm/packages/lt3luabridge.tar.lzma -2022/08/11 | 30566 | systems/win32/miktex/tm/packages/animate__source.tar.lzma -2022/08/11 | 305383 | systems/win32/miktex/tm/packages/biblatex-software__doc.tar.lzma -2022/08/11 | 292032 | systems/win32/miktex/tm/packages/sillypage__doc.tar.lzma -2022/08/11 | 2863 | systems/win32/miktex/tm/packages/simplebnf.tar.lzma -2022/08/11 | 2821 | systems/win32/miktex/tm/packages/customdice.tar.lzma -2022/08/11 | 28135 | systems/win32/miktex/tm/packages/tagpdf.tar.lzma -2022/08/11 | 268190 | macros/latex/contrib/fixdif.zip -2022/08/11 | 2632 | systems/texlive/tlnet/archive/simplebnf.r64091.tar.xz -2022/08/11 | 2616 | systems/texlive/tlnet/archive/customdice.r64089.tar.xz -2022/08/11 | 25812 | systems/win32/miktex/tm/packages/projlib__source.tar.lzma -2022/08/11 | 25649 | systems/win32/miktex/tm/packages/hfutthesis.tar.lzma -2022/08/11 | 25424 | systems/win32/miktex/tm/packages/projlib.tar.lzma -2022/08/11 | 24749 | systems/win32/miktex/tm/packages/uni-titlepage__source.tar.lzma -2022/08/11 | 24078 | systems/win32/miktex/tm/packages/animate.tar.lzma -2022/08/11 | 23446 | macros/latex/contrib/fixdif/fixdif.dtx -2022/08/11 | 233590 | systems/win32/miktex/tm/packages/zlmtt__doc.tar.lzma -2022/08/11 | 2257 | systems/win32/miktex/tm/packages/simplebnf__source.tar.lzma -2022/08/11 | 222030 | systems/win32/miktex/tm/packages/xecjk.tar.lzma -2022/08/11 | 220662 | systems/win32/miktex/tm/packages/luatexja__source.tar.lzma -2022/08/11 | 210366 | systems/win32/miktex/tm/packages/optex.tar.lzma -2022/08/11 | 2084919 | systems/win32/miktex/tm/packages/profcollege__source.tar.lzma -2022/08/11 | 2038852 | systems/win32/miktex/tm/packages/optex__doc.tar.lzma -2022/08/11 | 199643 | support/TeX4ht/source/ChangeLog -2022/08/11 | 1990 | systems/win32/miktex/tm/packages/nimsticks.tar.lzma -2022/08/11 | 19403 | systems/win32/miktex/tm/packages/njuthesis.tar.lzma -2022/08/11 | 191980 | systems/texlive/tlnet/archive/luatexja.r64092.tar.xz -2022/08/11 | 191633 | systems/win32/miktex/tm/packages/luatexja.tar.lzma -2022/08/11 | 19106 | macros/latex/contrib/create-theorem/LICENSE -2022/08/11 | 19103 | systems/win32/miktex/tm/packages/colortbl__source.tar.lzma -2022/08/11 | 1907971 | systems/win32/miktex/tm/packages/tagpdf__doc.tar.lzma -2022/08/11 | 1900612 | systems/win32/miktex/tm/packages/media9.tar.lzma -2022/08/11 | 18941 | macros/latex/contrib/runcode/troubleshoot.tex -2022/08/11 | 1837729 | systems/win32/miktex/tm/packages/uni-titlepage__doc.tar.lzma -2022/08/11 | 1785916 | systems/win32/miktex/tm/packages/bookcover__doc.tar.lzma -2022/08/11 | 1782167 | systems/win32/miktex/tm/packages/media9__source.tar.lzma -2022/08/11 | 17397 | systems/win32/miktex/tm/packages/ocgx2.tar.lzma -2022/08/11 | 172729 | systems/win32/miktex/tm/packages/l3kernel.tar.lzma -2022/08/11 | 17189 | systems/win32/miktex/tm/packages/bodeplot__source.tar.lzma -2022/08/11 | 17120 | systems/win32/miktex/tm/packages/tikz-relay__source.tar.lzma -2022/08/11 | 16822 | macros/latex/contrib/fixdif/fixdif-zh-cn.tex -2022/08/11 | 163865 | systems/win32/miktex/tm/packages/tudscr.tar.lzma -2022/08/11 | 163015 | macros/latex/contrib/fixdif/fixdif-zh-cn.pdf -2022/08/11 | 1625928 | systems/win32/miktex/tm/packages/xecjk__doc.tar.lzma -2022/08/11 | 15769 | macros/latex/contrib/runcode/runcode.sty -2022/08/11 | 15636 | systems/win32/miktex/tm/packages/l3backend.tar.lzma -2022/08/11 | 15529 | systems/win32/miktex/tm/packages/europecv.tar.lzma -2022/08/11 | 15233 | systems/win32/miktex/tm/packages/bookcover__source.tar.lzma -2022/08/11 | 151428 | systems/texlive/tlnet/archive/simplebnf.doc.r64091.tar.xz -2022/08/11 | 150414 | systems/win32/miktex/tm/packages/simplebnf__doc.tar.lzma -2022/08/11 | 146626 | systems/win32/miktex/tm/packages/yfonts-otf__doc.tar.lzma -2022/08/11 | 1444792 | systems/win32/miktex/tm/packages/unbtex__doc.tar.lzma -2022/08/11 | 1395 | systems/win32/miktex/tm/packages/olsak-misc__doc.tar.lzma -2022/08/11 | 13662 | systems/win32/miktex/tm/packages/sillypage.tar.lzma -2022/08/11 | 13273 | systems/win32/miktex/tm/packages/gentombow__source.tar.lzma -2022/08/11 | 132560 | macros/latex/contrib/runcode.zip -2022/08/11 | 12 | macros/latex/contrib/create-theorem/DEPENDS.txt -2022/08/11 | 12832 | systems/win32/miktex/tm/packages/proflycee.tar.lzma -2022/08/11 | 1246141 | systems/win32/miktex/tm/packages/siunitx__doc.tar.lzma -2022/08/11 | 1212673 | systems/win32/miktex/tm/packages/profcollege.tar.lzma -2022/08/11 | 11889 | systems/win32/miktex/tm/packages/biblatex-software__source.tar.lzma -2022/08/11 | 1182741 | systems/win32/miktex/tm/packages/njuthesis__doc.tar.lzma -2022/08/11 | 116799 | systems/win32/miktex/tm/packages/siunitx__source.tar.lzma -2022/08/11 | 11673083 | systems/win32/miktex/tm/packages/l3kernel__doc.tar.lzma -2022/08/11 | 1133492 | systems/win32/miktex/tm/packages/tuda-ci__doc.tar.lzma -2022/08/11 | 11324 | macros/latex/contrib/runcode/runcode.tex -2022/08/11 | 11119 | systems/win32/miktex/tm/packages/gentombow.tar.lzma -2022/08/11 | 11077 | support/TeX4ht/source/tex4ht-html5.tex -2022/08/11 | 108622 | macros/latex/contrib/create-theorem.zip -2022/08/11 | 10761 | systems/win32/miktex/tm/packages/uni-titlepage.tar.lzma -2022/08/11 | 106565 | systems/win32/miktex/tm/packages/nimsticks__doc.tar.lzma -2022/08/11 | 10170 | systems/win32/miktex/tm/packages/lt3rawobjects__source.tar.lzma -2022/08/11 | 100673 | macros/latex/contrib/create-theorem/create-theorem.sty diff --git a/biblio/citation-style-language/CHANGELOG.md b/biblio/citation-style-language/CHANGELOG.md index 1c6b2df688..e351714b3a 100644 --- a/biblio/citation-style-language/CHANGELOG.md +++ b/biblio/citation-style-language/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.2.0] - 2022-08-18 + +### Added + +- The cite grouping, collapsing, and disambiguation features are now implemented. + +### Changed + +- The `citeproc` executable is renamed to `citeproc-lua` to avoid conflicts with other processor implementations. +- Package configuration can also be given in the package loading options. +- A warning is raised instead of and error in case of duplicate entry keys ([#14](https://github.com/zepinglee/citeproc-lua/issues/14)). + +### Fixed + +- Fix an infinite loop error when bib entry keys contain hyphens or underscores ([#18](https://github.com/zepinglee/citeproc-lua/issues/18)). +- Fix incorrect item position in note style ([#20](https://github.com/zepinglee/citeproc-lua/issues/20)). +- Fix compatibility with `\blockquote` of `csquotes` ([#21](https://github.com/zepinglee/citeproc-lua/issues/21)). +- Fix non-lowercase field names ([#22](https://github.com/zepinglee/citeproc-lua/issues/22)). + ## [v0.1.1] - 2022-03-21 ### Added @@ -27,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial CTAN release. -[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.1.1...HEAD +[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.2.0...HEAD +[v0.2.0]: https://github.com/zepinglee/citeproc-lua/compare/v0.1.1...0.2.0 [v0.1.1]: https://github.com/zepinglee/citeproc-lua/compare/v0.1.0...v0.1.1 [v0.1.0]: https://github.com/zepinglee/citeproc-lua/releases/tag/v0.1.0 diff --git a/biblio/citation-style-language/DEPENDS.txt b/biblio/citation-style-language/DEPENDS.txt new file mode 100644 index 0000000000..3d0edb73de --- /dev/null +++ b/biblio/citation-style-language/DEPENDS.txt @@ -0,0 +1,10 @@ +# The format of this file is described in https://www.tug.org/texlive/pkgcontrib.html#deps. + +hard luatex +hard l3kernel +hard l3packages +hard filehook +hard url +hard luaxml +hard lua-uca +hard lualibs diff --git a/biblio/citation-style-language/README.md b/biblio/citation-style-language/README.md index 74c27590c6..6ad5a87602 100644 --- a/biblio/citation-style-language/README.md +++ b/biblio/citation-style-language/README.md @@ -48,7 +48,7 @@ lualatex example.tex ```bash pdflatex example.tex -citeproc example.aux +citeproc-lua example.aux pdflatex example.tex ``` diff --git a/biblio/citation-style-language/american-chemical-society.csl b/biblio/citation-style-language/american-chemical-society.csl index bf75b2d8de..cde0e0c50f 100644 --- a/biblio/citation-style-language/american-chemical-society.csl +++ b/biblio/citation-style-language/american-chemical-society.csl @@ -21,7 +21,7 @@ The American Chemical Society style - 2021-05-22T12:00:00+00:00 + 2022-05-04T19:44:25-04:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -250,14 +250,17 @@ - - - - - - - - + + + + + + + + + + + diff --git a/biblio/citation-style-language/american-medical-association.csl b/biblio/citation-style-language/american-medical-association.csl index 44520822d0..b36bbb5488 100644 --- a/biblio/citation-style-language/american-medical-association.csl +++ b/biblio/citation-style-language/american-medical-association.csl @@ -27,7 +27,7 @@ The American Medical Association style as used in JAMA. Version 11 as per November-2019. - 2021-10-28T13:38:04+00:00 + 2022-03-17T08:48:24+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -86,7 +86,7 @@ - + @@ -247,6 +247,15 @@ + + + + + + + + + diff --git a/biblio/citation-style-language/american-sociological-association.csl b/biblio/citation-style-language/american-sociological-association.csl index 6657716cde..cf4ab0fc0d 100644 --- a/biblio/citation-style-language/american-sociological-association.csl +++ b/biblio/citation-style-language/american-sociological-association.csl @@ -16,7 +16,7 @@ The ASA style following the 6th edition of the Style Guide - 2020-09-18T10:38:12+00:00 + 2022-05-08T17:48:56-04:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -125,13 +125,25 @@ - - - + + + + + + + + + + + + - + + + + @@ -140,6 +152,12 @@ + + + + + + diff --git a/biblio/citation-style-language/apa.csl b/biblio/citation-style-language/apa.csl index 5ba01a9423..43a8e5932f 100644 --- a/biblio/citation-style-language/apa.csl +++ b/biblio/citation-style-language/apa.csl @@ -14,7 +14,7 @@ - 2022-01-31T09:43:56-05:00 + 2022-08-11T09:14:19-04:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License @@ -320,8 +320,8 @@ @@ -389,7 +389,7 @@ - @@ -470,7 +470,7 @@ - @@ -530,7 +530,7 @@ - @@ -1295,7 +1295,7 @@ - @@ -1553,14 +1553,14 @@ - + - + @@ -1568,6 +1568,20 @@ + + + + + + + + + + + @@ -1575,7 +1589,7 @@ - diff --git a/biblio/citation-style-language/chicago-author-date.csl b/biblio/citation-style-language/chicago-author-date.csl index 8c133354b3..f8c640cd7f 100644 --- a/biblio/citation-style-language/chicago-author-date.csl +++ b/biblio/citation-style-language/chicago-author-date.csl @@ -45,6 +45,30 @@ trans. + + + acedido a + + + + + editado por + ed. + por + traduzido por + trad. + editado e traduzido por + e + s.d + s.d. + em + em + por + + " + " + + diff --git a/biblio/citation-style-language/chicago-fullnote-bibliography.csl b/biblio/citation-style-language/chicago-fullnote-bibliography.csl index fb2f1ae943..da4969f115 100644 --- a/biblio/citation-style-language/chicago-fullnote-bibliography.csl +++ b/biblio/citation-style-language/chicago-fullnote-bibliography.csl @@ -34,7 +34,7 @@ Chicago format with full notes and bibliography - 2022-01-16T14:46:01-05:00 + 2017-10-12T12:00:00+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License diff --git a/biblio/citation-style-language/chicago-note-bibliography.csl b/biblio/citation-style-language/chicago-note-bibliography.csl index 6ec4a29534..83da96cb4d 100644 --- a/biblio/citation-style-language/chicago-note-bibliography.csl +++ b/biblio/citation-style-language/chicago-note-bibliography.csl @@ -34,7 +34,7 @@ Chicago format with short notes and bibliography - 2022-01-16T14:46:01-05:00 + 2017-10-12T12:00:00+00:00 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License diff --git a/biblio/citation-style-language/citation-style-language-doc.pdf b/biblio/citation-style-language/citation-style-language-doc.pdf index b4ccbe55d3..af42980a81 100644 Binary files a/biblio/citation-style-language/citation-style-language-doc.pdf and b/biblio/citation-style-language/citation-style-language-doc.pdf differ diff --git a/biblio/citation-style-language/citation-style-language-doc.tex b/biblio/citation-style-language/citation-style-language-doc.tex index 538aaae00a..1dda26c5fa 100644 --- a/biblio/citation-style-language/citation-style-language-doc.tex +++ b/biblio/citation-style-language/citation-style-language-doc.tex @@ -49,7 +49,7 @@ }% } -\date{2022-03-21 v0.1.1} +\date{2022-08-18 v0.2.0} \maketitle @@ -73,7 +73,7 @@ citations and bibliography according to the selected CSL style. A LaTeX package (\file{citation-style-language.sty}) is provided to communicate with the processor. Note that this project is in early development stage and some features of CSL -are not implemented yet (especially collapsing and disambiguation). +are not implemented yet. Comments, suggestions, and bug reports are welcome. @@ -131,14 +131,14 @@ The general procedure is similar to the traditional BibTeX workflow. \end{syntax} \end{function} -The behavior of the \pkg{citation-style-language} package is controlled by several key-value -options which can be set with the \cs{cslsetup} command. -For example, +Package options may be set when the package is loaded or at any later stage +with the \cs{cslsetup} command. +These two methods are equivalent. \begin{LaTeXdemo} - \cslsetup{ - style = apa, - locale = zh-CN, - } + \usepackage[style = apa]{citation-style-langugage} + % OR + \usepackage{citation-style-langugage} + \cslsetup{style = apa} \end{LaTeXdemo} \DescribeOption{style} @@ -278,13 +278,11 @@ recommended. If only one optional argument is provided, it is treated as \meta{postnote}. The \meta{postnote} is used as a page locator if it consists of only digits. -In other packages, several commands are provided for producing citations in -different styles such as \cs{citet}, \cs{citep}, \cs{parencite}, and -\cs{footnotecite}. -In \pkg{citation-style-language} package, however, the format of citations is -fixed as defined in CSL style and it is impossible to select another format -without modifying the \file{.csl} style file. -Note that narrative citation (like ``Doe (2018)'') will be supported in CSL 1.1. +For author-date styles, there are also narrative in-text citations where the +author names appear in running text and only dates are enclosed in parentheses +(\cs{citet} in \pkg{natbib} pacakge or \cs{textcite} in \pkg{biblatex}). +However such cite command in this pakcage is not available at the momment and +it will be implemented in the next release. \begin{function}{\cites} \begin{syntax} @@ -302,6 +300,17 @@ scanning process. The following example illustrates its usage. \cites[prefix = {See }, page = 6]{key1}[section = 2.3]{key2}\relax [Text] \end{LaTeXdemo} +\begin{function}{\nocite} + \begin{syntax} + \cs{nocite}\marg{keys} + \end{syntax} +\end{function} + +This command produces no output but makes the entries included in the +bibliography, which is the same in standard \LaTeX. +If the special key |*| is given (\cs{notecite\{*\}}), all the entries in the +database are included. + \begin{function}{\printbibliography} @@ -368,16 +377,11 @@ An error will be triggered if any of them is loaded together with \pkg{citation- The \pkg{citation-style-language} package is in early development stage and there are some issues with it. \begin{itemize} - \item The \pkg{citeproc-lua} has not implemented all the features of CSL, - especially disambiguation and collapsing. + \item The \pkg{citeproc-lua} has not implemented all the features of CSL. For detailed information of the coverage on the CSL standard test suite\footnote{\url{https://github.com/citation-style-language/test-suite}}, see \href{https://github.com/zepinglee/citeproc-lua/blob/main/test/citeproc-test.log}{citeproc-test.log} in the GitHub repository. - \item The processor is much slower than expected compared to other - reference engines. - This is because little care has been taken in the development so far. - Optimization is needed in the future. \item When used with \pkg{hyperref}, the citations are not correctly rendered as hyperlinks. \item The Unicode sorting method is provided by \pkg{lua-uca} package and diff --git a/biblio/citation-style-language/citation-style-language.sty b/biblio/citation-style-language/citation-style-language.sty index 546ac13eec..87165c4be2 100644 --- a/biblio/citation-style-language/citation-style-language.sty +++ b/biblio/citation-style-language/citation-style-language.sty @@ -9,9 +9,10 @@ \RequirePackage{expl3} \RequirePackage{xparse} -\ProvidesExplPackage {citation-style-language} {2022-01-22} {v0.1.0} +\ProvidesExplPackage {citation-style-language} {2022-08-18} {v0.2.0} {Citation Style Language for LaTeX} +\RequirePackage { l3keys2e } \RequirePackage { filehook } \RequirePackage { url } @@ -34,7 +35,7 @@ \__csl_load_check:n \sys_if_engine_luatex:T - { \lua_now:n { csl = require("csl") } } + { \lua_now:n { csl = require("citeproc-latex") } } \NewDocumentCommand \cslsetup { m } @@ -82,8 +83,11 @@ } +% Appends the cite key into \l__csl_cite_keys_seq and cite-items into +% \l__csl_cite_items_seq +% #1, #2: prenote/postnote +% #3: keys \cs_new:Npn \__csl_process_cite_items:nnn #1#2#3 - % #1,#2: prenote/postnote, #3: keys { \tl_if_novalue:nTF {#2} { @@ -120,25 +124,27 @@ { \prop_clear:N \l__csl_cite_item_prop \prop_put:Nnn \l__csl_cite_item_prop { id } {#3} + % \bool_if:T \l__csl_suppress_author_bool + % { \prop_put:Nnn \l__csl_cite_item_prop { suppress-author } { true } } + % \bool_if:T \l__csl_author_only_bool + % { \prop_put:Nnn \l__csl_cite_item_prop { author-only } { true } } \tl_if_empty:nF {#1} { \tl_set:Nn \l__csl_prefix_tl {#1} \tl_put_right:NV \l__csl_prefix_tl \l__csl_prefix_separator_tl - \prop_put:NnV \l__csl_cite_item_prop { prefix } { \l__csl_prefix_tl } + \prop_put:NnV \l__csl_cite_item_prop { prefix } \l__csl_prefix_tl } - \tl_if_in:nnTF {#2} { = } + \tl_if_empty:nF {#2} { - \keys_set:nn { csl / cite-item } {#2} - } - { - \tl_if_empty:nF {#2} + \tl_if_in:nnTF {#2} { = } + { \keys_set:nn { csl / cite-item } {#2} } { \regex_match:nnTF { \d+ } {#2} { \__csl_set_locator:nn { page } {#2} } { \tl_set:Nn \l__csl_suffix_tl {#2} \tl_put_left:NV \l__csl_suffix_tl \l__csl_suffix_separator_tl - \prop_put:NnV \l__csl_cite_item_prop { suffix } { \l__csl_suffix_tl } + \prop_put:NnV \l__csl_cite_item_prop { suffix } \l__csl_suffix_tl } } } @@ -202,20 +208,26 @@ \tl_new:N \l__csl_citation_info_tl \tl_new:N \l__csl_citation_tl \prop_new:N \g__csl_citations_prop +\prop_clear:N \l__csl_citation_prop \cs_new:Npn \__csl_make_citation:NN #1#2 % #1: \l__csl_cite_keys_seq % #2: \l__csl_cite_items_seq { + \prop_clear:N \l__csl_citation_prop \__csl_process_citation_id:NN \l__csl_citation_id_tl #1 + \prop_put:NnV \l__csl_citation_prop { citationID } \l__csl_citation_id_tl \tl_set:Nx \l__csl_cite_items_tl { \seq_use:Nn #2 { , } } - \exp_args:NNV \__csl_process_note_index:Nn \l__csl_note_index_tl \l__csl_citation_id_tl - \tl_set:Nx \l__csl_citation_info_tl + \prop_put:NnV \l__csl_citation_prop { citationItems } \l__csl_cite_items_tl + \__csl_get_note_index:N \l__csl_note_index_tl + \prop_put:Nnx \l__csl_citation_prop { properties } { noteIndex = { \l__csl_note_index_tl } } + \tl_clear:N \l__csl_citation_info_tl + \prop_map_inline:Nn \l__csl_citation_prop { - { \l__csl_citation_id_tl } - { \l__csl_cite_items_tl } - { \l__csl_note_index_tl } + \tl_if_empty:NF \l__csl_citation_info_tl + { \tl_put_right:Nn \l__csl_citation_info_tl { , } } + \tl_put_right:Nn \l__csl_citation_info_tl { ##1 = { ##2 } } } \exp_args:NV \__csl_write_aux_citation:n \l__csl_citation_info_tl \prop_get:NVNTF \g__csl_citations_prop \l__csl_citation_id_tl @@ -224,8 +236,9 @@ { \bool_if:NTF \l__csl_engine_initialized_bool { - \tl_set:Nx \l__csl_citation_tl + \tl_set:Nf \l__csl_citation_tl { \lua_now:e { csl.cite("\l__csl_citation_info_tl") } } + \exp_args:NV \__csl_print_citation:n \l__csl_citation_tl } { \exp_args:Nx \__csl_print_undefined_citation:n @@ -261,26 +274,18 @@ } -\int_new:N \l__csl_note_index_int -\int_gzero_new:N \g__csl_last_note_index_int -\int_gzero_new:N \g__csl_citation_note_count_int - -\cs_new:Npn \__csl_process_note_index:Nn #1#2 - % #1 \l__csl_note_index_tl - % #2: citationID +% Save the note number to \l__csl_note_index_tl +% TODO: multiple citations in a note +\cs_new:Npn \__csl_get_note_index:N #1 + % #1: \l__csl_note_index_tl { - \int_set_eq:Nc \l__csl_note_index_int { c@ \@mpfn } - \prop_if_in:NnTF \g__csl_citations_prop {#2} - { \int_incr:N \l__csl_note_index_int } - { \int_gincr:N \g__csl_citation_note_count_int } - \int_add:Nn \l__csl_note_index_int { \g__csl_citation_note_count_int } - \tl_set:Nx #1 { \int_use:N \l__csl_note_index_int } - \int_gset_eq:NN \g__csl_last_note_index_int \l__csl_note_index_int - \int_compare:nT { \l__csl_note_index_int < \g__csl_last_note_index_int } + \bool_if:NTF \l__csl_note_style_bool { - \int_gzero:N \g__csl_last_note_index_int - \int_gzero:N \g__csl_citation_note_count_int + \int_set_eq:Nc \l_tmpa_int { c@ \@mpfn } + \int_incr:N \l_tmpa_int + \tl_set:Nx #1 { \int_use:N \l_tmpa_int } } + { \tl_set:Nn #1 { 0 } } } @@ -294,6 +299,16 @@ } +% \cs_new:Npn \__csl_print_citation:n #1 +% % #1: citation text +% { +% \bool_if:NTF \l__csl_note_style_bool +% { +% \footnote {#1} +% } +% {#1} +% } + \cs_new:Npn \__csl_print_citation:n #1 % #1: "{}{}" { \__csl_print_citation_aux:nn #1 } @@ -330,17 +345,36 @@ \cs_new:Npn \__csl_no_cite:n #1 { - \__csl_if_preamble:TF + \seq_clear:N \l__csl_cite_keys_seq + \seq_clear:N \l__csl_cite_items_seq + \__csl_process_cite_items:nnn { } { } {#1} + \tl_set:Nx \l__csl_cite_items_tl + { \seq_use:Nn \l__csl_cite_items_seq { , } } + \tl_set:Nx \l__csl_citation_info_tl { - \AtBeginDocument - { \__csl_write_aux_citation:n { { nocite } {#1} { } } } + citationID = { @nocite } , + citationItems = { \l__csl_cite_items_tl } , + properties = { noteIndex = { 0 } } } - { \__csl_write_aux_citation:n { { nocite } {#1} { } } } + \exp_args:NV \__csl_no_cite_write_aux:n \l__csl_citation_info_tl \sys_if_engine_luatex:T { \lua_now:n { csl.nocite("#1") } } } +\cs_new:Npn \__csl_no_cite_write_aux:n #1 + { + \__csl_if_preamble:TF + { + \AtBeginDocument + { \exp_args:Nx \__csl_write_aux_citation:n { #1 } } + } + { + \exp_args:Nx \__csl_write_aux_citation:n { #1 } + } + } + + \prg_new_conditional:Nnn \__csl_if_preamble: { T , F , TF } { \if_meaning:w \@begindocumenthook \@undefined @@ -352,6 +386,7 @@ % Used in aux files to register cite items. +% #1: a citation object \cs_set:Npn \citation #1 { \sys_if_engine_luatex:T @@ -362,9 +397,9 @@ \cs_new:Npn \cslcite #1#2 { \bibcite {#1} {#2} - \if@filesw - \iow_now:Nn \@auxout { \bibcite {#1} {#2} } - \fi + % \if@filesw + % \iow_now:Nn \@auxout { \bibcite {#1} {#2} } + % \fi } @@ -444,6 +479,8 @@ entry-spacing = { 1 } , } +\ProcessKeysPackageOptions { csl } + \bool_new:N \l__csl_engine_initialized_bool \prop_set_from_keyval:Nn \l__csl_babel_locale_mapping_prop @@ -590,6 +627,7 @@ } \str_if_eq:eeT { \lua_now:n { tex.print(csl.initialized) } } { true } { \bool_set_true:N \l__csl_engine_initialized_bool } + \__csl_get_style_class: \@ifpackageloaded { hyperref } { \lua_now:n { csl.enable_linking() } } { } @@ -614,6 +652,21 @@ \fi } + +\str_new:N \l__csl_style_class_str +% In-text (including numeric or author-date) or note style +\bool_new:N \l__csl_note_style_bool + +\cs_new:Npn \__csl_get_style_class: { + \bool_if:NT \l__csl_engine_initialized_bool + { + \str_set:Nx \l__csl_style_class_str { \lua_now:n { csl.get_style_class() } } + \str_if_eq:VnT \l__csl_style_class_str { note } + { \bool_set_true:N \l__csl_note_style_bool } + } +} + + \clist_new:N \l__csl_options_clist \cs_new:Npn \__csl_write_aux_csl_options: @@ -972,6 +1025,19 @@ } +% csquotes + +\AtEndOfPackageFile* { csquotes } + { + \BlockquoteDisable + { + \cs_set_eq:NN \__csl_make_citation:NN \use_none:nn + % \sys_if_engine_luatex:T + % { \lua_now:n { csl.preview_mode = true } } + } + } + + % hyperref % The hyperref package also patches \bibcite but it cannot provide hyperlinks diff --git a/biblio/citation-style-language/citeproc b/biblio/citation-style-language/citeproc deleted file mode 100755 index 27663952bd..0000000000 --- a/biblio/citation-style-language/citeproc +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env texlua - --- --- Copyright (c) 2021-2022 Zeping Lee --- Released under the MIT license. --- Repository: https://github.com/zepinglee/citeproc-lua --- - -kpse.set_program_name("luatex") - -require("lualibs") -local citeproc = require("citeproc") -local util = require("citeproc-util") -local core = require("csl-core") - -local function getopt( arg, options ) - local tab = {} - for k, v in ipairs(arg) do - if string.sub( v, 1, 2) == "--" then - local x = string.find( v, "=", 1, true ) - if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 ) - else tab[ string.sub( v, 3 ) ] = true - end - elseif string.sub( v, 1, 1 ) == "-" then - local y = 2 - local l = string.len(v) - local jopt - while ( y <= l ) do - jopt = string.sub( v, y, y ) - if string.find( options, jopt, 1, true ) then - if y < l then - tab[ jopt ] = string.sub( v, y+1 ) - y = l - else - tab[ jopt ] = arg[ k + 1 ] - end - else - tab[ jopt ] = true - end - y = y + 1 - end - else - if tab.file then - error(string.format('Invalid argument "%s"', v)) - end - tab.file = v - end - - end - return tab -end - - -local function print_version() - io.write(string.format("CiteProc-Lua %s\n", citeproc.__VERSION__)) -end - - -local function print_help() - io.write("Usage: citeproc [options] auxname[.aux]\n") - io.write("Options:\n") - io.write(" -h, --help Print this message and exit.\n") - io.write(" -V, --version Print the version number and exit.\n") -end - - -local function convert_bib(path, output_path) - local contents = util.read_file(path) - local bib = citeproc.parse_bib(contents) - if not output_path then - output_path = string.gsub(path, "%.bib$", ".json") - end - local file = io.open(output_path, "w") - file:write(utilities.json.tojson(bib)) - file:write('\n') - file:close() -end - - - -local function read_aux_file(aux_file) - local bib_style = nil - local bib_files = {} - local citations = {} - local csl_options = {} - - local file = io.open(aux_file, "r") - if not file then - error(string.format('Failed to open "%s"', aux_file)) - return - end - for line in file:lines() do - local match - match = string.match(line, "^\\bibstyle%s*(%b{})") - if match then - bib_style = string.sub(match, 2, -2) - else - match = string.match(line, "^\\bibdata%s*(%b{})") - if match then - for _, bib in ipairs(util.split(string.sub(match, 2, -2), "%s*,%s*")) do - table.insert(bib_files, bib) - end - else - match = string.match(line, "^\\citation%s*(%b{})") - if match then - local citation = core.make_citation(string.sub(match, 2, -2)) - table.insert(citations, citation) - else - match = string.match(line, "^\\csloptions%s*(%b{})") - if match then - for key, value in string.gmatch(match, "([%w-]+)=(%w+)") do - csl_options[key] = value - end - end - end - end - end - end - file:close() - - return bib_style, bib_files, citations, csl_options -end - - -local function process_aux_file(aux_file) - if not util.endswith(aux_file, ".aux") then - aux_file = aux_file .. ".aux" - end - - local style_name, bib_files, citations, csl_options = read_aux_file(aux_file) - - local lang = csl_options.locale - - local engine = core.init(style_name, bib_files, lang) - if csl_options.linking == "true" then - engine:enable_linking() - end - local style_class = engine:get_style_class() - - local citation_strings = core.process_citations(engine, citations) - - local output_string = "" - - for _, citation in ipairs(citations) do - local citation_id = citation.citationID - if citation_id ~= "nocite" then - local citation_str = citation_strings[citation_id] - output_string = output_string .. string.format("\\cslcite{%s}{{%s}{%s}}\n", citation_id, style_class, citation_str) - end - end - - output_string = output_string .. "\n" - - local result = core.make_bibliography(engine) - output_string = output_string .. result - - local output_path = string.gsub(aux_file, "%.aux$", ".bbl") - local bbl_file = io.open(output_path, "w") - bbl_file:write(output_string) - bbl_file:close() -end - - -local function main() - local args = getopt(arg, "o") - - -- for k, v in pairs(args) do - -- print( k, v ) - -- end - - if args.V or args.version then - print_version() - return - elseif args.h or args.help then - print_help() - return - end - - if not args.file then - error("citeproc: Need exactly one file argument.\n") - end - - local path = args.file - - local output_path = args.o or args.output - if util.endswith(path, ".bib") then - convert_bib(path, output_path) - else - process_aux_file(path) - end - -end - -main() diff --git a/biblio/citation-style-language/citeproc-bib-data.json b/biblio/citation-style-language/citeproc-bib-data.json deleted file mode 100644 index 69e26e16c3..0000000000 --- a/biblio/citation-style-language/citeproc-bib-data.json +++ /dev/null @@ -1,2349 +0,0 @@ -{ - "description": "Bib CSL mapping", - "types": { - "archival": { - "csl": "collection", - "source": "jurabib.bst" - }, - "archive": { - "csl": "collection", - "notes": "New in CSL v1.0.2.", - "source": "gbt7714-numerical.bst" - }, - "article": { - "csl": "article-journal", - "notes": "May also be `article-magazine` or `article-newspaper` depending upon the field `entrysubtype`.", - "source": "bibtex" - }, - "artifactdataset": { - "csl": "dataset", - "source": "ACM-Reference-Format.bst" - }, - "artifactsoftware": { - "csl": "software", - "source": "ACM-Reference-Format.bst" - }, - "artwork": { - "csl": "graphic", - "source": "biblatex" - }, - "atlas": { - "csl": null, - "source": "ametsoc2014.bst" - }, - "audio": { - "csl": "song", - "notes": "CSL's `song` can be used for any audio recording (not only music).", - "source": "biblatex" - }, - "bachelor": { - "csl": "thesis", - "source": "dlfltxbbibtex.bst" - }, - "bibnote": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex" - }, - "book": { - "csl": "book", - "source": "bibtex" - }, - "bookinbook": { - "csl": "chapter", - "source": "biblatex" - }, - "booklet": { - "csl": "pamphlet", - "source": "bibtex" - }, - "brochure": { - "csl": "pamphlet", - "source": "thesnumb.bst" - }, - "cconference": { - "csl": null, - "source": "seuthesis.bst" - }, - "collection": { - "csl": "book", - "source": "biblatex" - }, - "comment": { - "csl": null, - "notes": "Special entry type for Scribe compatibility", - "source": "bibtex" - }, - "commentary": { - "csl": "book", - "notes": "Not supported.", - "source": "biblatex" - }, - "commented": { - "csl": null, - "source": "jurabib.bst" - }, - "conference": { - "alias": "inproceedings", - "csl": "paper-conference", - "source": "bibtex" - }, - "ctan": { - "csl": "software", - "source": "tugboat.bst" - }, - "customa": { - "csl": null, - "source": "biblatex" - }, - "customb": { - "csl": null, - "source": "biblatex" - }, - "customc": { - "csl": null, - "source": "biblatex" - }, - "customd": { - "csl": null, - "source": "biblatex" - }, - "custome": { - "csl": null, - "source": "biblatex" - }, - "customf": { - "csl": null, - "source": "biblatex" - }, - "database": { - "csl": "dataset", - "source": "gbt7714-numerical.bst" - }, - "dataset": { - "csl": "dataset", - "source": "biblatex" - }, - "dictionary": { - "csl": "book", - "source": "vancouver.bst" - }, - "docthesis": { - "csl": "thesis", - "source": "gost2003.bst" - }, - "electronic": { - "alias": "online", - "csl": "webpage", - "source": "biblatex" - }, - "eulegislation": { - "csl": "legislation", - "source": "bath.bst" - }, - "footnote": { - "csl": null, - "source": "apsrev4-2.bst" - }, - "game": { - "csl": "software", - "source": "ACM-Reference-Format.bst" - }, - "govpub": { - "csl": "regulation", - "source": "thesnumb.bst" - }, - "habthesis": { - "csl": "thesis", - "source": "bestpapers-export.bst" - }, - "heading": { - "csl": null, - "source": "bookdb.bst" - }, - "hidden": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "image": { - "csl": "graphic", - "source": "biblatex" - }, - "inbook": { - "csl": "chapter", - "source": "bibtex" - }, - "incollection": { - "csl": "chapter", - "source": "bibtex" - }, - "inloosecollection": { - "csl": "chapter", - "source": "resphilosophica.bst" - }, - "inpress": { - "csl": "article", - "notes": "Use for preprints, working papers, and similar works posted on a platform where some level of persistence or stewardship is expected (e.g. arXiv or other preprint repositories, working paper series).", - "source": "bjnano.bst" - }, - "inproceedings": { - "csl": "paper-conference", - "source": "bibtex" - }, - "inreference": { - "csl": "entry", - "notes": "May also be `entry`, `entry-dictionary` or `entry-encyclopedia`.", - "source": "biblatex" - }, - "inserialcollection": { - "csl": null, - "source": "asmejour.bst" - }, - "internet": { - "csl": "webpage", - "source": "IEEEtran.bst" - }, - "journalpart": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "journals": { - "csl": "periodical", - "source": "bestpapers-export.bst" - }, - "jurisdiction": { - "csl": null, - "notes": "\"Court decisions, court recordings, and similar things.\"", - "source": "biblatex" - }, - "jurthesis": { - "csl": "thesis", - "source": "jurabib.bst" - }, - "legal": { - "csl": "treaty", - "notes": "\"Legal documents such as treaties.\"", - "source": "biblatex" - }, - "legislation": { - "csl": "legislation", - "notes": "\"Laws, bills, legislative proposals, and similar things.\" May also be `bill`", - "source": "biblatex" - }, - "letter": { - "csl": "personal_communication", - "source": "biblatex" - }, - "lexicon": { - "csl": "book", - "source": "jurabib.bst" - }, - "majorthesis": { - "csl": "thesis", - "source": "achicago.bst" - }, - "manual": { - "csl": "report", - "nots": "CSL's `report` is also used for manuals and similar technical documentation (e.g. a software, instrument, or test manual).", - "source": "bibtex" - }, - "map": { - "csl": "map", - "source": "vancouver.bst" - }, - "mastersthesis": { - "alias": "thesis", - "csl": "thesis", - "source": "bibtex" - }, - "masterthesis": { - "csl": "thesis", - "source": "asaetr.bst" - }, - "minorthesis": { - "csl": "thesis", - "source": "achicago.bst" - }, - "misc": { - "csl": "document", - "notes": "New in CSL v1.0.2.", - "source": "bibtex" - }, - "monograph": { - "csl": "book", - "source": "gbt7714-numerical.bst" - }, - "monography": { - "csl": "book", - "source": "abntex2-alf.bst" - }, - "movie": { - "csl": "motion_picture", - "source": "biblatex" - }, - "music": { - "csl": "song", - "source": "biblatex" - }, - "mvbook": { - "csl": "book", - "source": "biblatex" - }, - "mvcollection": { - "csl": "book", - "source": "biblatex" - }, - "mvproceedings": { - "csl": "book", - "source": "biblatex" - }, - "mvreference": { - "csl": "book", - "source": "biblatex" - }, - "news": { - "csl": "article-newspaper", - "source": "seuthesix.bst" - }, - "newspaper": { - "csl": "article-newspaper", - "source": "gbt7714-numerical.bst" - }, - "online": { - "csl": "webpage", - "source": "biblatex" - }, - "other": { - "csl": null, - "source": "bjnano.bst" - }, - "patent": { - "csl": "patent", - "source": "biblatex" - }, - "performance": { - "csl": "performance", - "notes": "New in CSL v1.0.2.", - "source": "biblatex" - }, - "periodical": { - "csl": "periodical", - "notes": "New in CSL v1.0.2.", - "source": "biblatex" - }, - "phdthesis": { - "alias": "thesis", - "csl": "thesis", - "source": "bibtex" - }, - "preamble": { - "csl": null, - "notes": "Special entry type for inserting commands or text in the bbl", - "source": "bibtex" - }, - "preprint": { - "csl": "article", - "notes": "Use for preprints, working papers, and similar works posted on a platform where some level of persistence or stewardship is expected (e.g. arXiv or other preprint repositories, working paper series).", - "source": "gbt7714-numerical.bst" - }, - "presentation": { - "csl": "speech", - "notes": "A speech or other presentation (e.g. a paper, talk, poster, or symposium at a conference).", - "source": "apsrev4-2.bst" - }, - "proceedings": { - "csl": "book", - "source": "bibtex" - }, - "program": { - "csl": "software", - "source": "ChemCommun.bst" - }, - "reference": { - "csl": "book", - "source": "biblatex" - }, - "report": { - "csl": "report", - "source": "biblatex" - }, - "review": { - "csl": "review", - "notes": "\"A more specific variant of the `@article` type\"", - "source": "biblatex" - }, - "set": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex" - }, - "setup": { - "csl": null, - "source": "bestpapers-export.bst" - }, - "software": { - "csl": "software", - "notes": "New in CSL v1.0.2.", - "source": "biblatex" - }, - "standard": { - "csl": "standard", - "notes": "New in CSL v1.0.2.", - "source": "biblatex" - }, - "string": { - "csl": null, - "notes": "Special entry type for defining abbreviations", - "source": "bibtex" - }, - "suppbook": { - "csl": "chapter", - "notes": "lossy mapping; \"Supplemental material in a `@book`. This type is closely related to the @inbook entry type. While `@inbook` is primarily intended for a part of a book with its own title (e. g., a single essay in a collection of essays by the same author), this type is provided for elements such as prefaces, introductions, forewords, afterwords, etc. which often have a generic title only. Style guides may require such items to be formatted differently from other `@inbook` items.\"", - "source": "biblatex" - }, - "suppcollection": { - "csl": "chapter", - "notes": "lossy mapping; see `suppbook`", - "source": "biblatex" - }, - "suppperiodical": { - "csl": "article", - "notes": "see `article`", - "source": "biblatex" - }, - "techreport": { - "alias": "report", - "csl": "report", - "source": "bibtex" - }, - "techstandard": { - "csl": "standard", - "notes": "New in CSL v1.0.2.", - "source": "udesoftec.bst" - }, - "thesis": { - "csl": "thesis", - "source": "biblatex" - }, - "uklegislation": { - "csl": "legislation", - "source": "bath.bst" - }, - "unpublished": { - "csl": "manuscript", - "notes": "For unpublished works not made widely available or only hosted on personal websites, use manuscript", - "source": "bibtex" - }, - "video": { - "csl": "motion_picture", - "source": "biblatex" - }, - "webpage": { - "csl": "webpage", - "source": "IEEEtran.bst" - }, - "www": { - "alias": "online", - "csl": "webpage", - "source": "biblatex" - }, - "xdata": { - "csl": null, - "notes": "special item type: \"`@xdata` entries hold data which may be inherited by other entries using the xdata field. Entries of this type only serve as data containers; they may not be cited or added to the bibliography.\"", - "source": "biblatex" - } - }, - "fields": { - "abstract": { - "csl": "abstract", - "source": "biblatex", - "type": "literal" - }, - "account": { - "csl": null, - "source": "address-html.bst" - }, - "acronym": { - "csl": null, - "source": "export.bst" - }, - "add": { - "csl": null, - "source": "figbib.bst" - }, - "add1": { - "csl": null, - "source": "figbib1.bst" - }, - "addendum": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "address": { - "alias": "location", - "csl": "publisher-place", - "source": "bibtex", - "type": "literal" - }, - "adsurl": { - "csl": null, - "source": "mnras.bst" - }, - "advisor": { - "csl": null, - "source": "ACM-Reference-Format.bst" - }, - "afterword": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "name" - }, - "annotate": { - "csl": "note", - "source": "apacite.bst" - }, - "annotation": { - "csl": "note", - "notes": "Descriptive text or notes about an item (e.g. in an annotated bibliography)", - "source": "biblatex", - "type": "literal" - }, - "annotator": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "annote": { - "alias": "annotation", - "csl": "note", - "source": "biblatex", - "type": "literal" - }, - "annotelanguage": { - "csl": null, - "source": "bababbr3-fl.bst" - }, - "applicant": { - "csl": null, - "source": "seuthesix.bst" - }, - "archive": { - "csl": "archive", - "source": "apsrev4-2.bst" - }, - "archiveprefix": { - "alias": "eprinttype", - "csl": "archive", - "source": "biblatex", - "type": "literal" - }, - "archname": { - "csl": "archive", - "source": "jurabib.bst" - }, - "articleno": { - "csl": null, - "source": "ACM-Reference-Format.bst" - }, - "arxiv": { - "csl": null, - "source": "aomalpha.bst" - }, - "assignee": { - "csl": null, - "source": "vancouver.bst" - }, - "author": { - "csl": "author", - "source": "bibtex", - "type": "name" - }, - "authorcountry": { - "csl": null, - "source": "gost2003.bst" - }, - "authorfa": { - "csl": null, - "source": "asa-fa.bst" - }, - "authortype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "bibsource": { - "csl": null, - "source": "export.bst" - }, - "biburl": { - "csl": null, - "source": "export.bst" - }, - "binding": { - "csl": null, - "source": "bookdb.bst" - }, - "birthday": { - "csl": null, - "source": "adrbirthday.bst" - }, - "birthyear": { - "csl": null, - "source": "address-html.bst" - }, - "bookaddress": { - "csl": null, - "source": "apsrev4-2.bst" - }, - "bookauthor": { - "csl": "container-author", - "source": "biblatex", - "type": "name" - }, - "booklanguage": { - "csl": null, - "source": "gost2003.bst" - }, - "bookpages": { - "csl": null, - "source": "ACM-Reference-Format.bst" - }, - "bookpagination": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "booksubtitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "booktitle": { - "csl": "container-title", - "source": "bibtex", - "type": "literal" - }, - "booktitleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "caption": { - "csl": null, - "source": "figbib.bst" - }, - "caption1": { - "csl": null, - "source": "figbib1.bst" - }, - "cartographer": { - "csl": null, - "source": "vancouver.bst" - }, - "casenumber": { - "csl": null, - "source": "bath.bst" - }, - "category": { - "csl": null, - "source": "bookdb.bst" - }, - "cellular": { - "csl": null, - "source": "address-html.bst" - }, - "chair": { - "csl": null, - "source": "apacite.bst" - }, - "chapter": { - "csl": "chapter-number", - "source": "bibtex", - "type": "literal" - }, - "citedate": { - "csl": null, - "source": "seuthesis.bst" - }, - "city": { - "csl": null, - "source": "ACM-Reference-Format.bst" - }, - "coden": { - "csl": null, - "source": "is-abbrv.bst" - }, - "collaboration": { - "csl": null, - "source": "apsrev4-2.bst" - }, - "collator": { - "csl": null, - "source": "bookdb.bst" - }, - "commentator": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "compiler": { - "csl": null, - "source": "gost2003.bst" - }, - "condition": { - "csl": null, - "source": "bookdb.bst" - }, - "conference-location": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "conference-number": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "conference-year": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "copy": { - "csl": null, - "source": "bookdb.bst" - }, - "country": { - "csl": null, - "source": "adrbirthday.bst" - }, - "credits": { - "csl": null, - "source": "gost2003.bst" - }, - "crossref": { - "csl": null, - "notes": "Inherits data from a parent entry.", - "source": "biblatex", - "type": "entrykey" - }, - "ctrl-article-title": { - "csl": null, - "source": "achemso.bst" - }, - "ctrl-chapter-title": { - "csl": null, - "source": "achemso.bst" - }, - "ctrl-doi": { - "csl": null, - "source": "achemso.bst" - }, - "ctrl-etal-firstonly": { - "csl": null, - "source": "achemso.bst" - }, - "ctrl-etal-number": { - "csl": null, - "source": "achemso.bst" - }, - "ctrl-link-doi": { - "csl": null, - "source": "angew.bst" - }, - "ctrl-use-doi-all": { - "csl": null, - "source": "angew.bst" - }, - "ctrl-use-title": { - "csl": null, - "source": "achemso.bst" - }, - "dataset": { - "csl": null, - "source": "apalike-ejor.bst" - }, - "date": { - "csl": "issued", - "source": "biblatex", - "type": "date" - }, - "day": { - "csl": null, - "source": "apacite.bst" - }, - "dayfiled": { - "csl": null, - "source": "IEEEtran.bst" - }, - "definition": { - "csl": null, - "source": "glsplain.bst" - }, - "department": { - "csl": null, - "source": "ametsoc2014.bst" - }, - "description": { - "csl": null, - "source": "ChemCommun.bst" - }, - "designator": { - "csl": null, - "source": "thesnumb.bst" - }, - "dimensions": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "dissyear": { - "csl": null, - "source": "jurabib.bst" - }, - "doi": { - "csl": "DOI", - "source": "biblatex", - "type": "verbatim" - }, - "dticnumber": { - "csl": null, - "source": "thesnumb.bst" - }, - "dummy": { - "csl": null, - "source": "expcites.bst" - }, - "edition": { - "csl": "edition", - "source": "bibtex", - "type": "literal" - }, - "editor": { - "csl": "editor", - "source": "bibtex", - "type": "name" - }, - "editora": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "editoratype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "editorb": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "editorbtype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "editorc": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "editorctype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "editortype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "eid": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "email": { - "csl": null, - "source": "adrbirthday.bst" - }, - "endnumber": { - "csl": null, - "source": "seuthesis.bst" - }, - "endvolume": { - "csl": null, - "source": "seuthesis.bst" - }, - "endyear": { - "csl": null, - "source": "seuthesis.bst" - }, - "englishtitle": { - "csl": null, - "source": "apacite.bst" - }, - "entryset": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "entrykey" - }, - "entrysubtype": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "epilog": { - "csl": null, - "source": "iopart-num.bst" - }, - "eprint": { - "csl": null, - "notes": "Mapped to `PMID` if `eprinttype` is \"PubMed\".", - "source": "biblatex", - "type": "verbatim" - }, - "eprintclass": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "eprints": { - "csl": null, - "source": "databib.bst" - }, - "eprinttype": { - "csl": "archive", - "source": "biblatex", - "type": "literal" - }, - "erratumeid": { - "csl": null, - "source": "ChemCommun.bst" - }, - "erratumgermanpages": { - "csl": null, - "source": "ChemCommun.bst" - }, - "erratumnumpages": { - "csl": null, - "source": "ChemCommun.bst" - }, - "erratumpages": { - "csl": null, - "source": "ChemCommun.bst" - }, - "erratumvolume": { - "csl": null, - "source": "ChemCommun.bst" - }, - "erratumyear": { - "csl": null, - "source": "ChemCommun.bst" - }, - "eventdate": { - "csl": "event-date", - "source": "biblatex", - "type": "date" - }, - "eventtitle": { - "csl": "event-title", - "notes": "Supercedes `event` in CSL v1.0.2.", - "source": "biblatex", - "type": "literal" - }, - "eventtitleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "eventyear": { - "csl": null, - "source": "bath.bst" - }, - "execute": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "code" - }, - "faddress": { - "csl": null, - "source": "achicago.bst" - }, - "fakeset": { - "csl": null, - "source": "biblatex.bst" - }, - "fax": { - "csl": null, - "source": "adrbirthday.bst" - }, - "file": { - "csl": null, - "source": "biblatex", - "type": "verbatim" - }, - "firstkey": { - "csl": null, - "source": "apacite.bst" - }, - "fjournal": { - "csl": null, - "source": "ijmart.bst" - }, - "flanguage": { - "csl": null, - "source": "achicago.bst" - }, - "font": { - "csl": null, - "source": "nederlands.bst" - }, - "foreword": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "founder": { - "csl": null, - "source": "jurabib.bst" - }, - "fpublisher": { - "csl": null, - "source": "achicago.bst" - }, - "ftitle": { - "csl": null, - "source": "achicago.bst" - }, - "furtherresp": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "fyear": { - "csl": null, - "source": "achicago.bst" - }, - "gender": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "germanpages": { - "csl": null, - "source": "ChemCommun.bst" - }, - "group": { - "csl": null, - "source": "glsplain.bst" - }, - "heading": { - "csl": null, - "source": "glsplain.bst" - }, - "hereafter": { - "csl": null, - "source": "opcit.bst" - }, - "holder": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "howcited": { - "csl": null, - "source": "jox.bst" - }, - "howpublished": { - "csl": null, - "notes": "Check if a URL is contained.", - "source": "bibtex", - "type": "literal" - }, - "hyphenation": { - "alias": "langid", - "csl": "language", - "source": "biblatex", - "type": "literal" - }, - "ids": { - "csl": null, - "source": "biblatex", - "type": "entrykey" - }, - "illustrated": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "illustrations": { - "csl": null, - "source": "bookdb.bst" - }, - "illustrator": { - "csl": null, - "source": "achicago.bst" - }, - "indexsorttitle": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "indextitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "institution": { - "csl": "publisher", - "source": "bibtex", - "type": "literal" - }, - "introduction": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "intype": { - "csl": null, - "source": "IEEEtran.bst" - }, - "inventor": { - "csl": null, - "source": "vancouver.bst" - }, - "ipc": { - "csl": null, - "source": "gost2003.bst" - }, - "isan": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "isbn": { - "csl": "ISBN", - "source": "biblatex", - "type": "literal" - }, - "ismn": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "iso-abbreviation": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "iso-author-punctuation": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "iso-date-place": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "isrn": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "issn": { - "csl": "ISSN", - "source": "biblatex", - "type": "literal" - }, - "issue": { - "csl": "issue", - "source": "biblatex", - "type": "literal" - }, - "issuesubtitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "issuetitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "issuetitleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "iswc": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "jfmnumber": { - "csl": null, - "source": "aomalpha.bst" - }, - "journal": { - "alias": "journaltitle", - "csl": "container-title", - "source": "bibtex", - "type": "literal" - }, - "journalsubtitle": { - "csl": null, - "notes": "It should be concatenated to the `container-title`.", - "source": "biblatex", - "type": "literal" - }, - "journaltie": { - "csl": null, - "source": "tugboat.bst" - }, - "journaltitle": { - "csl": "container-title", - "source": "biblatex", - "type": "literal" - }, - "journaltitleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "juraauthor": { - "csl": null, - "source": "jox.bst" - }, - "juratitle": { - "csl": null, - "source": "jox.bst" - }, - "key": { - "alias": "sortkey", - "csl": null, - "notes": "Not supported.", - "source": "bibtex", - "type": "literal" - }, - "keywords": { - "csl": null, - "source": "biblatex", - "type": "keyword" - }, - "label": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "lang": { - "csl": null, - "source": "hustthesis.bst" - }, - "langid": { - "csl": "language", - "notes": "The language id of the bibliography entry. The identifier must be a language name known to the babel/polyglossia packages. It should be converted to ISO 639-1 language code in CSL.", - "source": "biblatex", - "type": "literal" - }, - "langidopts": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "language": { - "csl": null, - "notes": "The `language` field in `biblatex` has no special internal meaning thus it is used only when langid is missing.", - "source": "biblatex", - "type": "key" - }, - "lastaccessed": { - "csl": "accessed", - "source": "ACM-Reference-Format.bst" - }, - "lastchecked": { - "csl": "accessed", - "source": "apacite.bst" - }, - "lccn": { - "csl": null, - "source": "is-abbrv.bst" - }, - "library": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "lista": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "listb": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "listc": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "listd": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "liste": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "listf": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "littype": { - "csl": null, - "source": "seuthesix.bst" - }, - "location": { - "csl": "publisher-place", - "source": "biblatex", - "type": "literal" - }, - "madadurltest": { - "csl": null, - "source": "alpha-persian.bst" - }, - "main": { - "csl": null, - "source": "figbib.bst" - }, - "main1": { - "csl": null, - "source": "figbib1.bst" - }, - "mainsubtitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "maintitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "maintitleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "majorcode": { - "csl": null, - "source": "gost2003.bst" - }, - "marginnote": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "mark": { - "csl": null, - "source": "gbt7714-numerical.bst" - }, - "max.best.papers": { - "csl": null, - "source": "bestpapers-export.bst" - }, - "mcitetail": { - "csl": null, - "source": "apsrmpM.bst" - }, - "media": { - "csl": "medium", - "source": "gost2003.bst" - }, - "medium": { - "csl": "medium", - "source": "gbt7714-numerical.bst" - }, - "meeting": { - "csl": null, - "source": "amsra.bst" - }, - "misctitle": { - "csl": null, - "source": "aasjournal.bst" - }, - "miscyear": { - "csl": null, - "source": "seuthesis.bst" - }, - "mobile": { - "csl": null, - "source": "adrbirthday.bst" - }, - "modifydate": { - "csl": null, - "source": "seuthesis.bst" - }, - "month": { - "csl": null, - "notes": "Used only when `date` is empty.", - "source": "bibtex", - "type": "literal" - }, - "monthfiled": { - "csl": null, - "source": "IEEEtran.bst" - }, - "mrnumber": { - "csl": null, - "source": "amsplain.bst" - }, - "name": { - "csl": null, - "source": "adrbirthday.bst" - }, - "namea": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "nameaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "nameatype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "nameb": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "namebtype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "namec": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "namectype": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "nationality": { - "csl": null, - "source": "IEEEtran.bst" - }, - "nbirthday": { - "csl": null, - "source": "adrbirthday.bst" - }, - "newpage": { - "csl": null, - "source": "tugboat.bst" - }, - "newspaper": { - "csl": "container-title", - "source": "seuthesix.bst" - }, - "nickname": { - "csl": null, - "source": "address-html.bst" - }, - "nihms": { - "csl": null, - "source": "apacite.bst" - }, - "noed": { - "csl": null, - "source": "jox.bst" - }, - "normal": { - "csl": null, - "source": "bookdb.bst" - }, - "normalauthor": { - "csl": null, - "source": "seuthesis.bst" - }, - "normaleditor": { - "csl": null, - "source": "seuthesis.bst" - }, - "note": { - "csl": "note", - "source": "bibtex", - "type": "literal" - }, - "nowarning": { - "csl": null, - "source": "tugboat.bst" - }, - "number": { - "csl": "number", - "notes": "It is mapped to `issue` in `@ariticle` but to `number` in `@patent` or `@report`.", - "source": "bibtex", - "type": "literal" - }, - "numpages": { - "csl": null, - "source": "ACM-Reference-Format.bst" - }, - "oaddress": { - "csl": null, - "source": "achicago.bst" - }, - "options": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "option" - }, - "opublisher": { - "csl": null, - "source": "achicago.bst" - }, - "org-short": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "organization": { - "csl": "publisher", - "notes": "It is mapped to `author` (in `institution` property) if possible.", - "source": "bibtex", - "type": "literal" - }, - "origdate": { - "csl": "original-date", - "source": "biblatex", - "type": "date" - }, - "originaladdress": { - "csl": "original-publisher-place", - "source": "apacite.bst" - }, - "originalbooktitle": { - "csl": null, - "source": "apacite.bst" - }, - "originaledition": { - "csl": null, - "source": "apacite.bst" - }, - "originaleditor": { - "csl": null, - "source": "apacite.bst" - }, - "originaljournal": { - "csl": null, - "source": "apacite.bst" - }, - "originalnumber": { - "csl": null, - "source": "apacite.bst" - }, - "originalpages": { - "csl": null, - "source": "apacite.bst" - }, - "originalpublisher": { - "csl": "original-publisher", - "source": "apacite.bst" - }, - "originalvolume": { - "csl": null, - "source": "apacite.bst" - }, - "originalyear": { - "csl": "original-date", - "source": "apacite.bst" - }, - "origlanguage": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "origlocation": { - "csl": "original-publisher-place", - "source": "biblatex", - "type": "literal" - }, - "origpublisher": { - "csl": "original-publisher", - "source": "biblatex", - "type": "literal" - }, - "origtitle": { - "csl": "original-title", - "source": "biblatex", - "type": "literal" - }, - "oyear": { - "csl": null, - "source": "achicago.bst" - }, - "pagename": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "pages": { - "csl": "page", - "source": "bibtex", - "type": "range" - }, - "pagetotal": { - "csl": "number-of-pages", - "source": "biblatex", - "type": "literal" - }, - "pagination": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "paper": { - "csl": null, - "source": "IEEEtran.bst" - }, - "part": { - "csl": "part", - "source": "biblatex", - "type": "literal" - }, - "patentid": { - "csl": null, - "source": "seuthesis.bst" - }, - "pdf": { - "alias": "file", - "csl": null, - "source": "biblatex", - "type": "verbatim" - }, - "phone": { - "csl": null, - "source": "adrbirthday.bst" - }, - "pid": { - "csl": null, - "source": "seuthesix.bst" - }, - "pii": { - "csl": null, - "source": "amsra.bst" - }, - "pmcid": { - "csl": "PMCID", - "source": "apacite.bst" - }, - "prebibitem": { - "csl": null, - "source": "tugboat.bst" - }, - "preface": { - "csl": null, - "source": "bookdb.bst" - }, - "preprint": { - "csl": null, - "source": "amsra.bst" - }, - "presort": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "price": { - "csl": null, - "source": "is-abbrv.bst" - }, - "primaryclass": { - "alias": "eprintclass", - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "printing": { - "csl": null, - "source": "dk-abbrv.bst" - }, - "prioritycountry": { - "csl": null, - "source": "gost2003.bst" - }, - "prioritydate": { - "csl": null, - "source": "gost2003.bst" - }, - "prioritynumber": { - "csl": null, - "source": "gost2003.bst" - }, - "pseudonym": { - "csl": null, - "source": "jox.bst" - }, - "publication": { - "csl": null, - "source": "gost2003.bst" - }, - "publicationdate": { - "csl": null, - "source": "gost2003.bst" - }, - "publisher": { - "csl": "publisher", - "source": "bibtex", - "type": "literal" - }, - "pubmed": { - "csl": "PMID", - "source": "urlbst" - }, - "pubstate": { - "csl": null, - "source": "biblatex", - "type": "key" - }, - "related": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "entrykey" - }, - "relatedoptions": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "option" - }, - "relatedstring": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "relatedtype": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "reprinted-from": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "reprinted-text": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "reprinttitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "requestdate": { - "csl": null, - "source": "gost2003.bst" - }, - "requestnumber": { - "csl": null, - "source": "gost2003.bst" - }, - "responsible": { - "csl": null, - "source": "rusnat.bst" - }, - "review": { - "csl": null, - "source": "amsra.bst" - }, - "revision": { - "csl": null, - "source": "IEEEtran.bst" - }, - "school": { - "alias": "institution", - "csl": "publisher", - "source": "bibtex", - "type": "literal" - }, - "score": { - "csl": null, - "source": "bestpapers-export.bst" - }, - "section": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "series": { - "csl": "collection-title", - "source": "bibtex", - "type": "literal" - }, - "seriesedition": { - "csl": null, - "source": "achicago.bst" - }, - "short": { - "csl": null, - "source": "glsplain.bst" - }, - "shortarchive": { - "csl": null, - "source": "jurabib.bst" - }, - "shortauthor": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "shorteditor": { - "csl": null, - "source": "biblatex", - "type": "name" - }, - "shorthand": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "shorthandintro": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "shortjournal": { - "csl": "container-title-short", - "source": "biblatex", - "type": "literal" - }, - "shortseries": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "shortsubarchive": { - "csl": null, - "source": "jurabib.bst" - }, - "shorttitle": { - "csl": "title-short", - "source": "biblatex", - "type": "literal" - }, - "sig1": { - "csl": null, - "source": "jurabib.bst" - }, - "sig2": { - "csl": null, - "source": "jurabib.bst" - }, - "sig3": { - "csl": null, - "source": "jurabib.bst" - }, - "sig4": { - "csl": null, - "source": "jurabib.bst" - }, - "size": { - "csl": null, - "source": "bookdb.bst" - }, - "slaccitation": { - "csl": null, - "source": "apsrev4-2.bst" - }, - "sort-short": { - "csl": null, - "source": "glsplain.bst" - }, - "sort-word": { - "csl": null, - "source": "glsplain.bst" - }, - "sortas": { - "csl": null, - "source": "adrbirthday.bst" - }, - "sortkey": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "sortname": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "name" - }, - "sortshorthand": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "sorttitle": { - "csl": null, - "notes": "Not supported.", - "source": "biblatex", - "type": "literal" - }, - "sortyear": { - "csl": null, - "source": "biblatex", - "type": "integer" - }, - "source": { - "csl": null, - "source": "figbib.bst" - }, - "source1": { - "csl": null, - "source": "figbib1.bst" - }, - "specialitycode": { - "csl": null, - "source": "gost2003.bst" - }, - "ssedition": { - "csl": null, - "source": "jox.bst" - }, - "standard": { - "csl": null, - "source": "udesoftec.bst" - }, - "startnumber": { - "csl": null, - "source": "seuthesis.bst" - }, - "startvolume": { - "csl": null, - "source": "seuthesis.bst" - }, - "startyear": { - "csl": null, - "source": "seuthesis.bst" - }, - "state": { - "csl": null, - "source": "address-html.bst" - }, - "status": { - "csl": null, - "source": "amsra.bst" - }, - "stdcode": { - "csl": null, - "source": "seuthesix.bst" - }, - "stitle": { - "csl": null, - "source": "jox.bst" - }, - "street": { - "csl": null, - "source": "adrbirthday.bst" - }, - "subarchive": { - "csl": null, - "source": "jurabib.bst" - }, - "subtitle": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "symposium": { - "csl": null, - "source": "apacite.bst" - }, - "text": { - "csl": null, - "source": "apacite.bst" - }, - "timestamp": { - "csl": null, - "source": "export.bst" - }, - "title": { - "csl": "title", - "source": "bibtex", - "type": "literal" - }, - "titleaddon": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "titlenote": { - "csl": null, - "source": "aomalpha.bst" - }, - "totalpages": { - "csl": null, - "source": "aomalpha.bst" - }, - "transissue": { - "csl": null, - "source": "iopart-num.bst" - }, - "transjournal": { - "csl": null, - "source": "iopart-num.bst" - }, - "translation": { - "csl": null, - "source": "apsrev4-2.bst" - }, - "translator": { - "csl": "translator", - "source": "biblatex", - "type": "name" - }, - "transnumber": { - "csl": null, - "source": "iopart-num.bst" - }, - "transpages": { - "csl": null, - "source": "iopart-num.bst" - }, - "transsection": { - "csl": null, - "source": "iopart-num.bst" - }, - "transvolume": { - "csl": null, - "source": "iopart-num.bst" - }, - "transyear": { - "csl": null, - "source": "iopart-num.bst" - }, - "type": { - "csl": "genre", - "source": "bibtex", - "type": "key" - }, - "typeoflit": { - "csl": null, - "source": "seuthesis.bst" - }, - "umfnumber": { - "csl": null, - "source": "thesnumb.bst" - }, - "updated": { - "csl": null, - "source": "vancouver.bst" - }, - "url": { - "csl": "URL", - "source": "biblatex", - "type": "uri" - }, - "urlaccessdate": { - "csl": "accessed", - "source": "abntex2-alf.bst" - }, - "urldate": { - "csl": "accessed", - "source": "biblatex", - "type": "date" - }, - "urlnewline": { - "csl": null, - "source": "tugboat.bst" - }, - "urltype": { - "csl": null, - "source": "asmeconf.bst" - }, - "urlyear": { - "csl": null, - "source": "bath.bst" - }, - "urn": { - "csl": null, - "source": "export.bst" - }, - "usera": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "userb": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "userc": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "userd": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "usere": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "userf": { - "csl": null, - "source": "biblatex", - "type": "literal" - }, - "value": { - "csl": null, - "source": "bookdb.bst" - }, - "venue": { - "csl": "event-place", - "source": "biblatex", - "type": "literal" - }, - "verba": { - "csl": null, - "source": "biblatex", - "type": "verbatim" - }, - "verbb": { - "csl": null, - "source": "biblatex", - "type": "verbatim" - }, - "verbc": { - "csl": null, - "source": "biblatex", - "type": "verbatim" - }, - "version": { - "csl": "version", - "source": "biblatex", - "type": "literal" - }, - "versiontype": { - "csl": null, - "source": "asmeconf.bst" - }, - "volformat": { - "csl": null, - "source": "jox.bst" - }, - "volume": { - "csl": "volume", - "source": "bibtex", - "type": "integer" - }, - "volumes": { - "csl": "number-of-volumes", - "source": "biblatex", - "type": "integer" - }, - "volumetitle": { - "csl": null, - "source": "iopart-num.bst" - }, - "word": { - "csl": null, - "source": "vancouver.bst" - }, - "xdata": { - "csl": null, - "notes": "inherits fields from other items.", - "source": "biblatex", - "type": "entrykey" - }, - "xid": { - "csl": null, - "source": "amsra.bst" - }, - "xref": { - "csl": null, - "notes": "Establishes a parent-child relationship in biblatex, but without inheriting data => no need to parse this.", - "source": "biblatex", - "type": "entrykey" - }, - "year": { - "csl": null, - "notes": "Used only when `date` is empty.", - "source": "bibtex", - "type": "date" - }, - "year-presented": { - "csl": null, - "source": "abntex2-alf.bst" - }, - "yearcomp": { - "csl": null, - "source": "achicago.bst" - }, - "yearfiled": { - "csl": null, - "source": "IEEEtran.bst" - }, - "zblnumber": { - "csl": null, - "source": "aomalpha.bst" - }, - "zip": { - "csl": null, - "source": "adrbirthday.bst" - } - }, - "macros": { - "jan": { - "value": "1", - "notes": "Use numeric form for easy conversion.", - "source": "bibtex" - }, - "feb": { - "value": "2", - "source": "bibtex" - }, - "mar": { - "value": "3", - "source": "bibtex" - }, - "apr": { - "value": "4", - "source": "bibtex" - }, - "may": { - "value": "5", - "source": "bibtex" - }, - "jun": { - "value": "6", - "source": "bibtex" - }, - "jul": { - "value": "7", - "source": "bibtex" - }, - "aug": { - "value": "8", - "source": "bibtex" - }, - "sep": { - "value": "9", - "source": "bibtex" - }, - "oct": { - "value": "10", - "source": "bibtex" - }, - "nov": { - "value": "11", - "source": "bibtex" - }, - "dec": { - "value": "12", - "source": "bibtex" - }, - "acmcs": { - "value": "ACM Computing Surveys", - "source": "bibtex" - }, - "acta": { - "value": "Acta Informatica", - "source": "bibtex" - }, - "cacm": { - "value": "Communications of the ACM", - "source": "bibtex" - }, - "ibmjrd": { - "value": "IBM Journal of Research and Development", - "source": "bibtex" - }, - "ibmsj": { - "value": "IBM Systems Journal", - "source": "bibtex" - }, - "ieeese": { - "value": "IEEE Transactions on Software Engineering", - "source": "bibtex" - }, - "ieeetc": { - "value": "IEEE Transactions on Computers", - "source": "bibtex" - }, - "ieeetcad": { - "value": "IEEE Transactions on Computer-Aided Design of Integrated Circuits", - "source": "bibtex" - }, - "ipl": { - "value": "Information Processing Letters", - "source": "bibtex" - }, - "jacm": { - "value": "Journal of the ACM", - "source": "bibtex" - }, - "jcss": { - "value": "Journal of Computer and System Sciences", - "source": "bibtex" - }, - "scp": { - "value": "Science of Computer Programming", - "source": "bibtex" - }, - "sicomp": { - "value": "SIAM Journal on Computing", - "source": "bibtex" - }, - "tocs": { - "value": "ACM Transactions on Computer Systems", - "source": "bibtex" - }, - "tods": { - "value": "ACM Transactions on Database Systems", - "source": "bibtex" - }, - "tog": { - "value": "ACM Transactions on Graphics", - "source": "bibtex" - }, - "toms": { - "value": "ACM Transactions on Mathematical Software", - "source": "bibtex" - }, - "toois": { - "value": "ACM Transactions on Office Information Systems", - "source": "bibtex" - }, - "toplas": { - "value": "ACM Transactions on Programming Languages and Systems", - "source": "bibtex" - }, - "tcs": { - "value": "Theoretical Computer Science", - "source": "bibtex" - } - } -} diff --git a/biblio/citation-style-language/citeproc-bib-data.lua b/biblio/citation-style-language/citeproc-bib-data.lua new file mode 100644 index 0000000000..dafd3d05b4 --- /dev/null +++ b/biblio/citation-style-language/citeproc-bib-data.lua @@ -0,0 +1,1759 @@ +-- This file is generated from citeproc-bib-data.json by update-bib-date.py + +return { + ["description"] = "Bib CSL mapping", + ["types"] = { + ["archival"] = { + ["csl"] = "collection", + }, + ["archive"] = { + ["csl"] = "collection", + }, + ["article"] = { + ["csl"] = "article-journal", + }, + ["artifactdataset"] = { + ["csl"] = "dataset", + }, + ["artifactsoftware"] = { + ["csl"] = "software", + }, + ["artwork"] = { + ["csl"] = "graphic", + }, + ["atlas"] = { + ["csl"] = nil, + }, + ["audio"] = { + ["csl"] = "song", + }, + ["bachelor"] = { + ["csl"] = "thesis", + }, + ["bibnote"] = { + ["csl"] = nil, + }, + ["book"] = { + ["csl"] = "book", + }, + ["bookinbook"] = { + ["csl"] = "chapter", + }, + ["booklet"] = { + ["csl"] = "pamphlet", + }, + ["brochure"] = { + ["csl"] = "pamphlet", + }, + ["cconference"] = { + ["csl"] = nil, + }, + ["collection"] = { + ["csl"] = "book", + }, + ["comment"] = { + ["csl"] = nil, + }, + ["commentary"] = { + ["csl"] = "book", + }, + ["commented"] = { + ["csl"] = nil, + }, + ["conference"] = { + ["csl"] = "paper-conference", + }, + ["ctan"] = { + ["csl"] = "software", + }, + ["customa"] = { + ["csl"] = nil, + }, + ["customb"] = { + ["csl"] = nil, + }, + ["customc"] = { + ["csl"] = nil, + }, + ["customd"] = { + ["csl"] = nil, + }, + ["custome"] = { + ["csl"] = nil, + }, + ["customf"] = { + ["csl"] = nil, + }, + ["database"] = { + ["csl"] = "dataset", + }, + ["dataset"] = { + ["csl"] = "dataset", + }, + ["dictionary"] = { + ["csl"] = "book", + }, + ["docthesis"] = { + ["csl"] = "thesis", + }, + ["electronic"] = { + ["csl"] = "webpage", + }, + ["eulegislation"] = { + ["csl"] = "legislation", + }, + ["footnote"] = { + ["csl"] = nil, + }, + ["game"] = { + ["csl"] = "software", + }, + ["govpub"] = { + ["csl"] = "regulation", + }, + ["habthesis"] = { + ["csl"] = "thesis", + }, + ["heading"] = { + ["csl"] = nil, + }, + ["hidden"] = { + ["csl"] = nil, + }, + ["image"] = { + ["csl"] = "graphic", + }, + ["inbook"] = { + ["csl"] = "chapter", + }, + ["incollection"] = { + ["csl"] = "chapter", + }, + ["inloosecollection"] = { + ["csl"] = "chapter", + }, + ["inpress"] = { + ["csl"] = "article", + }, + ["inproceedings"] = { + ["csl"] = "paper-conference", + }, + ["inreference"] = { + ["csl"] = "entry", + }, + ["inserialcollection"] = { + ["csl"] = nil, + }, + ["internet"] = { + ["csl"] = "webpage", + }, + ["journalpart"] = { + ["csl"] = nil, + }, + ["journals"] = { + ["csl"] = "periodical", + }, + ["jurisdiction"] = { + ["csl"] = nil, + }, + ["jurthesis"] = { + ["csl"] = "thesis", + }, + ["legal"] = { + ["csl"] = "treaty", + }, + ["legislation"] = { + ["csl"] = "legislation", + }, + ["letter"] = { + ["csl"] = "personal_communication", + }, + ["lexicon"] = { + ["csl"] = "book", + }, + ["majorthesis"] = { + ["csl"] = "thesis", + }, + ["manual"] = { + ["csl"] = "report", + }, + ["map"] = { + ["csl"] = "map", + }, + ["mastersthesis"] = { + ["csl"] = "thesis", + }, + ["masterthesis"] = { + ["csl"] = "thesis", + }, + ["minorthesis"] = { + ["csl"] = "thesis", + }, + ["misc"] = { + ["csl"] = "document", + }, + ["monograph"] = { + ["csl"] = "book", + }, + ["monography"] = { + ["csl"] = "book", + }, + ["movie"] = { + ["csl"] = "motion_picture", + }, + ["music"] = { + ["csl"] = "song", + }, + ["mvbook"] = { + ["csl"] = "book", + }, + ["mvcollection"] = { + ["csl"] = "book", + }, + ["mvproceedings"] = { + ["csl"] = "book", + }, + ["mvreference"] = { + ["csl"] = "book", + }, + ["news"] = { + ["csl"] = "article-newspaper", + }, + ["newspaper"] = { + ["csl"] = "article-newspaper", + }, + ["online"] = { + ["csl"] = "webpage", + }, + ["other"] = { + ["csl"] = nil, + }, + ["patent"] = { + ["csl"] = "patent", + }, + ["performance"] = { + ["csl"] = "performance", + }, + ["periodical"] = { + ["csl"] = "periodical", + }, + ["phdthesis"] = { + ["csl"] = "thesis", + }, + ["preamble"] = { + ["csl"] = nil, + }, + ["preprint"] = { + ["csl"] = "article", + }, + ["presentation"] = { + ["csl"] = "speech", + }, + ["proceedings"] = { + ["csl"] = "book", + }, + ["program"] = { + ["csl"] = "software", + }, + ["reference"] = { + ["csl"] = "book", + }, + ["report"] = { + ["csl"] = "report", + }, + ["repository"] = { + ["csl"] = nil, + }, + ["review"] = { + ["csl"] = "review", + }, + ["set"] = { + ["csl"] = nil, + }, + ["setup"] = { + ["csl"] = nil, + }, + ["software"] = { + ["csl"] = "software", + }, + ["standard"] = { + ["csl"] = "standard", + }, + ["string"] = { + ["csl"] = nil, + }, + ["suppbook"] = { + ["csl"] = "chapter", + }, + ["suppcollection"] = { + ["csl"] = "chapter", + }, + ["suppperiodical"] = { + ["csl"] = "article", + }, + ["techreport"] = { + ["csl"] = "report", + }, + ["techstandard"] = { + ["csl"] = "standard", + }, + ["thesis"] = { + ["csl"] = "thesis", + }, + ["uklegislation"] = { + ["csl"] = "legislation", + }, + ["unpublished"] = { + ["csl"] = "manuscript", + }, + ["video"] = { + ["csl"] = "motion_picture", + }, + ["webpage"] = { + ["csl"] = "webpage", + }, + ["website"] = { + ["csl"] = nil, + }, + ["www"] = { + ["csl"] = "webpage", + }, + ["xdata"] = { + ["csl"] = nil, + }, + }, + ["fields"] = { + ["abstract"] = { + ["csl"] = "abstract", + ["type"] = "literal", + }, + ["account"] = { + ["csl"] = nil, + }, + ["acronym"] = { + ["csl"] = nil, + }, + ["add"] = { + ["csl"] = nil, + }, + ["add1"] = { + ["csl"] = nil, + }, + ["addendum"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["address"] = { + ["csl"] = "publisher-place", + ["type"] = "literal", + }, + ["adsurl"] = { + ["csl"] = nil, + }, + ["advisor"] = { + ["csl"] = nil, + }, + ["afterword"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["annotate"] = { + ["csl"] = "note", + }, + ["annotation"] = { + ["csl"] = "note", + ["type"] = "literal", + }, + ["annotator"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["annote"] = { + ["csl"] = "note", + ["type"] = "literal", + }, + ["annotelanguage"] = { + ["csl"] = nil, + }, + ["applicant"] = { + ["csl"] = nil, + }, + ["archive"] = { + ["csl"] = "archive", + }, + ["archiveprefix"] = { + ["csl"] = "archive", + ["type"] = "literal", + }, + ["archname"] = { + ["csl"] = "archive", + }, + ["articleno"] = { + ["csl"] = nil, + }, + ["arxiv"] = { + ["csl"] = nil, + }, + ["assignee"] = { + ["csl"] = nil, + }, + ["author"] = { + ["csl"] = "author", + ["type"] = "name", + }, + ["authorcountry"] = { + ["csl"] = nil, + }, + ["authorfa"] = { + ["csl"] = nil, + }, + ["authortype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["bibsource"] = { + ["csl"] = nil, + }, + ["biburl"] = { + ["csl"] = nil, + }, + ["binding"] = { + ["csl"] = nil, + }, + ["birthday"] = { + ["csl"] = nil, + }, + ["birthyear"] = { + ["csl"] = nil, + }, + ["bookaddress"] = { + ["csl"] = nil, + }, + ["bookauthor"] = { + ["csl"] = "container-author", + ["type"] = "name", + }, + ["booklanguage"] = { + ["csl"] = nil, + }, + ["bookpages"] = { + ["csl"] = nil, + }, + ["bookpagination"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["booksubtitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["booktitle"] = { + ["csl"] = "container-title", + ["type"] = "literal", + }, + ["booktitleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["caption"] = { + ["csl"] = nil, + }, + ["caption1"] = { + ["csl"] = nil, + }, + ["cartographer"] = { + ["csl"] = nil, + }, + ["casenumber"] = { + ["csl"] = nil, + }, + ["category"] = { + ["csl"] = nil, + }, + ["cellular"] = { + ["csl"] = nil, + }, + ["chair"] = { + ["csl"] = nil, + }, + ["chapter"] = { + ["csl"] = "chapter-number", + ["type"] = "literal", + }, + ["citedate"] = { + ["csl"] = nil, + }, + ["city"] = { + ["csl"] = nil, + }, + ["code"] = { + ["csl"] = nil, + }, + ["coden"] = { + ["csl"] = nil, + }, + ["collaboration"] = { + ["csl"] = nil, + }, + ["collator"] = { + ["csl"] = nil, + }, + ["commentator"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["commit"] = { + ["csl"] = nil, + }, + ["compiler"] = { + ["csl"] = nil, + }, + ["condition"] = { + ["csl"] = nil, + }, + ["conference-location"] = { + ["csl"] = nil, + }, + ["conference-number"] = { + ["csl"] = nil, + }, + ["conference-year"] = { + ["csl"] = nil, + }, + ["copy"] = { + ["csl"] = nil, + }, + ["country"] = { + ["csl"] = nil, + }, + ["credits"] = { + ["csl"] = nil, + }, + ["crossref"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["ctrl-article-title"] = { + ["csl"] = nil, + }, + ["ctrl-chapter-title"] = { + ["csl"] = nil, + }, + ["ctrl-doi"] = { + ["csl"] = nil, + }, + ["ctrl-etal-firstonly"] = { + ["csl"] = nil, + }, + ["ctrl-etal-number"] = { + ["csl"] = nil, + }, + ["ctrl-link-doi"] = { + ["csl"] = nil, + }, + ["ctrl-use-doi-all"] = { + ["csl"] = nil, + }, + ["ctrl-use-title"] = { + ["csl"] = nil, + }, + ["dataset"] = { + ["csl"] = nil, + }, + ["date"] = { + ["csl"] = "issued", + ["type"] = "date", + }, + ["day"] = { + ["csl"] = nil, + }, + ["dayfiled"] = { + ["csl"] = nil, + }, + ["definition"] = { + ["csl"] = nil, + }, + ["department"] = { + ["csl"] = nil, + }, + ["description"] = { + ["csl"] = nil, + }, + ["designator"] = { + ["csl"] = nil, + }, + ["dimensions"] = { + ["csl"] = nil, + }, + ["dissyear"] = { + ["csl"] = nil, + }, + ["doi"] = { + ["csl"] = "DOI", + ["type"] = "verbatim", + }, + ["dticnumber"] = { + ["csl"] = nil, + }, + ["dummy"] = { + ["csl"] = nil, + }, + ["edition"] = { + ["csl"] = "edition", + ["type"] = "literal", + }, + ["editor"] = { + ["csl"] = "editor", + ["type"] = "name", + }, + ["editora"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["editoratype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["editorb"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["editorbtype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["editorc"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["editorctype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["editortype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["eid"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["email"] = { + ["csl"] = nil, + }, + ["endnumber"] = { + ["csl"] = nil, + }, + ["endvolume"] = { + ["csl"] = nil, + }, + ["endyear"] = { + ["csl"] = nil, + }, + ["englishtitle"] = { + ["csl"] = nil, + }, + ["entryset"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["entrysubtype"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["epilog"] = { + ["csl"] = nil, + }, + ["eprint"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["eprintclass"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["eprints"] = { + ["csl"] = nil, + }, + ["eprinttype"] = { + ["csl"] = "archive", + ["type"] = "literal", + }, + ["erratumeid"] = { + ["csl"] = nil, + }, + ["erratumgermanpages"] = { + ["csl"] = nil, + }, + ["erratumnumpages"] = { + ["csl"] = nil, + }, + ["erratumpages"] = { + ["csl"] = nil, + }, + ["erratumvolume"] = { + ["csl"] = nil, + }, + ["erratumyear"] = { + ["csl"] = nil, + }, + ["eventdate"] = { + ["csl"] = "event-date", + ["type"] = "date", + }, + ["eventtitle"] = { + ["csl"] = "event-title", + ["type"] = "literal", + }, + ["eventtitleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["eventyear"] = { + ["csl"] = nil, + }, + ["execute"] = { + ["csl"] = nil, + ["type"] = "code", + }, + ["faddress"] = { + ["csl"] = nil, + }, + ["fakeset"] = { + ["csl"] = nil, + }, + ["fax"] = { + ["csl"] = nil, + }, + ["file"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["firstkey"] = { + ["csl"] = nil, + }, + ["fjournal"] = { + ["csl"] = nil, + }, + ["flanguage"] = { + ["csl"] = nil, + }, + ["font"] = { + ["csl"] = nil, + }, + ["foreword"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["founder"] = { + ["csl"] = nil, + }, + ["fpublisher"] = { + ["csl"] = nil, + }, + ["ftitle"] = { + ["csl"] = nil, + }, + ["furtherresp"] = { + ["csl"] = nil, + }, + ["fyear"] = { + ["csl"] = nil, + }, + ["gender"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["germanpages"] = { + ["csl"] = nil, + }, + ["group"] = { + ["csl"] = nil, + }, + ["heading"] = { + ["csl"] = nil, + }, + ["hereafter"] = { + ["csl"] = nil, + }, + ["holder"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["howcited"] = { + ["csl"] = nil, + }, + ["howpublished"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["hyphenation"] = { + ["csl"] = "language", + ["type"] = "literal", + }, + ["ids"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["illustrated"] = { + ["csl"] = nil, + }, + ["illustrations"] = { + ["csl"] = nil, + }, + ["illustrator"] = { + ["csl"] = nil, + }, + ["indexsorttitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["indextitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["institution"] = { + ["csl"] = "publisher", + ["type"] = "literal", + }, + ["introduction"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["intype"] = { + ["csl"] = nil, + }, + ["inventor"] = { + ["csl"] = nil, + }, + ["ipc"] = { + ["csl"] = nil, + }, + ["isan"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["isbn"] = { + ["csl"] = "ISBN", + ["type"] = "literal", + }, + ["ismn"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["iso-abbreviation"] = { + ["csl"] = nil, + }, + ["iso-author-punctuation"] = { + ["csl"] = nil, + }, + ["iso-date-place"] = { + ["csl"] = nil, + }, + ["isrn"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["issn"] = { + ["csl"] = "ISSN", + ["type"] = "literal", + }, + ["issue"] = { + ["csl"] = "issue", + ["type"] = "literal", + }, + ["issuesubtitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["issuetitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["issuetitleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["iswc"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["jfmnumber"] = { + ["csl"] = nil, + }, + ["journal"] = { + ["csl"] = "container-title", + ["type"] = "literal", + }, + ["journalsubtitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["journaltie"] = { + ["csl"] = nil, + }, + ["journaltitle"] = { + ["csl"] = "container-title", + ["type"] = "literal", + }, + ["journaltitleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["juraauthor"] = { + ["csl"] = nil, + }, + ["juratitle"] = { + ["csl"] = nil, + }, + ["key"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["keywords"] = { + ["csl"] = nil, + ["type"] = "keyword", + }, + ["label"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["lang"] = { + ["csl"] = nil, + }, + ["langid"] = { + ["csl"] = "language", + ["type"] = "literal", + }, + ["langidopts"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["language"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["lastaccessed"] = { + ["csl"] = "accessed", + }, + ["lastchecked"] = { + ["csl"] = "accessed", + }, + ["lccn"] = { + ["csl"] = nil, + }, + ["library"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["lista"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["listb"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["listc"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["listd"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["liste"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["listf"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["littype"] = { + ["csl"] = nil, + }, + ["location"] = { + ["csl"] = "publisher-place", + ["type"] = "literal", + }, + ["madadurltest"] = { + ["csl"] = nil, + }, + ["main"] = { + ["csl"] = nil, + }, + ["main1"] = { + ["csl"] = nil, + }, + ["mainsubtitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["maintitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["maintitleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["majorcode"] = { + ["csl"] = nil, + }, + ["marginnote"] = { + ["csl"] = nil, + }, + ["mark"] = { + ["csl"] = nil, + }, + ["max.best.papers"] = { + ["csl"] = nil, + }, + ["mcitetail"] = { + ["csl"] = nil, + }, + ["media"] = { + ["csl"] = "medium", + }, + ["medium"] = { + ["csl"] = "medium", + }, + ["meeting"] = { + ["csl"] = nil, + }, + ["misctitle"] = { + ["csl"] = nil, + }, + ["miscyear"] = { + ["csl"] = nil, + }, + ["mobile"] = { + ["csl"] = nil, + }, + ["modifydate"] = { + ["csl"] = nil, + }, + ["month"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["monthfiled"] = { + ["csl"] = nil, + }, + ["mrnumber"] = { + ["csl"] = nil, + }, + ["name"] = { + ["csl"] = nil, + }, + ["namea"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["nameaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["nameatype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["nameb"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["namebtype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["namec"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["namectype"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["nationality"] = { + ["csl"] = nil, + }, + ["nbirthday"] = { + ["csl"] = nil, + }, + ["newpage"] = { + ["csl"] = nil, + }, + ["newspaper"] = { + ["csl"] = "container-title", + }, + ["nickname"] = { + ["csl"] = nil, + }, + ["nihms"] = { + ["csl"] = nil, + }, + ["noed"] = { + ["csl"] = nil, + }, + ["nolink"] = { + ["csl"] = nil, + }, + ["normal"] = { + ["csl"] = nil, + }, + ["normalauthor"] = { + ["csl"] = nil, + }, + ["normaleditor"] = { + ["csl"] = nil, + }, + ["note"] = { + ["csl"] = "note", + ["type"] = "literal", + }, + ["nowarning"] = { + ["csl"] = nil, + }, + ["number"] = { + ["csl"] = "number", + ["type"] = "literal", + }, + ["numpages"] = { + ["csl"] = nil, + }, + ["oaddress"] = { + ["csl"] = nil, + }, + ["options"] = { + ["csl"] = nil, + ["type"] = "option", + }, + ["opublisher"] = { + ["csl"] = nil, + }, + ["org-short"] = { + ["csl"] = nil, + }, + ["organization"] = { + ["csl"] = "publisher", + ["type"] = "literal", + }, + ["origdate"] = { + ["csl"] = "original-date", + ["type"] = "date", + }, + ["originaladdress"] = { + ["csl"] = "original-publisher-place", + }, + ["originalbooktitle"] = { + ["csl"] = nil, + }, + ["originaledition"] = { + ["csl"] = nil, + }, + ["originaleditor"] = { + ["csl"] = nil, + }, + ["originaljournal"] = { + ["csl"] = nil, + }, + ["originalnumber"] = { + ["csl"] = nil, + }, + ["originalpages"] = { + ["csl"] = nil, + }, + ["originalpublisher"] = { + ["csl"] = "original-publisher", + }, + ["originalvolume"] = { + ["csl"] = nil, + }, + ["originalyear"] = { + ["csl"] = "original-date", + }, + ["origlanguage"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["origlocation"] = { + ["csl"] = "original-publisher-place", + ["type"] = "literal", + }, + ["origpublisher"] = { + ["csl"] = "original-publisher", + ["type"] = "literal", + }, + ["origtitle"] = { + ["csl"] = "original-title", + ["type"] = "literal", + }, + ["oyear"] = { + ["csl"] = nil, + }, + ["pagename"] = { + ["csl"] = nil, + }, + ["pages"] = { + ["csl"] = "page", + ["type"] = "range", + }, + ["pagetotal"] = { + ["csl"] = "number-of-pages", + ["type"] = "literal", + }, + ["pagination"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["paper"] = { + ["csl"] = nil, + }, + ["part"] = { + ["csl"] = "part", + ["type"] = "literal", + }, + ["patentid"] = { + ["csl"] = nil, + }, + ["pdf"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["phone"] = { + ["csl"] = nil, + }, + ["pid"] = { + ["csl"] = nil, + }, + ["pii"] = { + ["csl"] = nil, + }, + ["pmcid"] = { + ["csl"] = "PMCID", + }, + ["prebibitem"] = { + ["csl"] = nil, + }, + ["preface"] = { + ["csl"] = nil, + }, + ["preprint"] = { + ["csl"] = nil, + }, + ["presort"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["price"] = { + ["csl"] = nil, + }, + ["primaryclass"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["printing"] = { + ["csl"] = nil, + }, + ["prioritycountry"] = { + ["csl"] = nil, + }, + ["prioritydate"] = { + ["csl"] = nil, + }, + ["prioritynumber"] = { + ["csl"] = nil, + }, + ["pseudonym"] = { + ["csl"] = nil, + }, + ["publication"] = { + ["csl"] = nil, + }, + ["publicationdate"] = { + ["csl"] = nil, + }, + ["publisher"] = { + ["csl"] = "publisher", + ["type"] = "literal", + }, + ["pubmed"] = { + ["csl"] = "PMID", + }, + ["pubstate"] = { + ["csl"] = nil, + ["type"] = "key", + }, + ["related"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["relatedoptions"] = { + ["csl"] = nil, + ["type"] = "option", + }, + ["relatedstring"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["relatedtype"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["reprinted-from"] = { + ["csl"] = nil, + }, + ["reprinted-text"] = { + ["csl"] = nil, + }, + ["reprinttitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["requestdate"] = { + ["csl"] = nil, + }, + ["requestnumber"] = { + ["csl"] = nil, + }, + ["responsible"] = { + ["csl"] = nil, + }, + ["review"] = { + ["csl"] = nil, + }, + ["revision"] = { + ["csl"] = nil, + }, + ["school"] = { + ["csl"] = "publisher", + ["type"] = "literal", + }, + ["score"] = { + ["csl"] = nil, + }, + ["section"] = { + ["csl"] = nil, + }, + ["series"] = { + ["csl"] = "collection-title", + ["type"] = "literal", + }, + ["seriesedition"] = { + ["csl"] = nil, + }, + ["short"] = { + ["csl"] = nil, + }, + ["shortarchive"] = { + ["csl"] = nil, + }, + ["shortauthor"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["shorteditor"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["shorthand"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["shorthandintro"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["shortjournal"] = { + ["csl"] = "container-title-short", + ["type"] = "literal", + }, + ["shortseries"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["shortsubarchive"] = { + ["csl"] = nil, + }, + ["shorttitle"] = { + ["csl"] = "title-short", + ["type"] = "literal", + }, + ["shorturl"] = { + ["csl"] = nil, + }, + ["sig1"] = { + ["csl"] = nil, + }, + ["sig2"] = { + ["csl"] = nil, + }, + ["sig3"] = { + ["csl"] = nil, + }, + ["sig4"] = { + ["csl"] = nil, + }, + ["size"] = { + ["csl"] = nil, + }, + ["slaccitation"] = { + ["csl"] = nil, + }, + ["sort-short"] = { + ["csl"] = nil, + }, + ["sort-word"] = { + ["csl"] = nil, + }, + ["sortas"] = { + ["csl"] = nil, + }, + ["sortkey"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["sortname"] = { + ["csl"] = nil, + ["type"] = "name", + }, + ["sortshorthand"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["sorttitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["sortyear"] = { + ["csl"] = nil, + ["type"] = "integer", + }, + ["source1"] = { + ["csl"] = nil, + }, + ["specialitycode"] = { + ["csl"] = nil, + }, + ["ssedition"] = { + ["csl"] = nil, + }, + ["standard"] = { + ["csl"] = nil, + }, + ["startnumber"] = { + ["csl"] = nil, + }, + ["startvolume"] = { + ["csl"] = nil, + }, + ["startyear"] = { + ["csl"] = nil, + }, + ["state"] = { + ["csl"] = nil, + }, + ["status"] = { + ["csl"] = nil, + }, + ["stdcode"] = { + ["csl"] = nil, + }, + ["stitle"] = { + ["csl"] = nil, + }, + ["street"] = { + ["csl"] = nil, + }, + ["subarchive"] = { + ["csl"] = nil, + }, + ["subtitle"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["symposium"] = { + ["csl"] = nil, + }, + ["text"] = { + ["csl"] = nil, + }, + ["timestamp"] = { + ["csl"] = nil, + }, + ["title"] = { + ["csl"] = "title", + ["type"] = "literal", + }, + ["titleaddon"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["titlenote"] = { + ["csl"] = nil, + }, + ["totalpages"] = { + ["csl"] = nil, + }, + ["transissue"] = { + ["csl"] = nil, + }, + ["transjournal"] = { + ["csl"] = nil, + }, + ["translation"] = { + ["csl"] = nil, + }, + ["translator"] = { + ["csl"] = "translator", + ["type"] = "name", + }, + ["transnumber"] = { + ["csl"] = nil, + }, + ["transpages"] = { + ["csl"] = nil, + }, + ["transsection"] = { + ["csl"] = nil, + }, + ["transvolume"] = { + ["csl"] = nil, + }, + ["transyear"] = { + ["csl"] = nil, + }, + ["type"] = { + ["csl"] = "genre", + ["type"] = "key", + }, + ["typeoflit"] = { + ["csl"] = nil, + }, + ["umfnumber"] = { + ["csl"] = nil, + }, + ["updated"] = { + ["csl"] = nil, + }, + ["url"] = { + ["csl"] = "URL", + ["type"] = "uri", + }, + ["urlaccessdate"] = { + ["csl"] = "accessed", + }, + ["urldate"] = { + ["csl"] = "accessed", + ["type"] = "date", + }, + ["urlnewline"] = { + ["csl"] = nil, + }, + ["urltype"] = { + ["csl"] = nil, + }, + ["urlyear"] = { + ["csl"] = nil, + }, + ["urn"] = { + ["csl"] = nil, + }, + ["usera"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["userb"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["userc"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["userd"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["usere"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["userf"] = { + ["csl"] = nil, + ["type"] = "literal", + }, + ["value"] = { + ["csl"] = nil, + }, + ["venue"] = { + ["csl"] = "event-place", + ["type"] = "literal", + }, + ["verba"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["verbb"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["verbc"] = { + ["csl"] = nil, + ["type"] = "verbatim", + }, + ["version"] = { + ["csl"] = "version", + ["type"] = "literal", + }, + ["versiontype"] = { + ["csl"] = nil, + }, + ["volformat"] = { + ["csl"] = nil, + }, + ["volume"] = { + ["csl"] = "volume", + ["type"] = "integer", + }, + ["volumes"] = { + ["csl"] = "number-of-volumes", + ["type"] = "integer", + }, + ["volumetitle"] = { + ["csl"] = nil, + }, + ["word"] = { + ["csl"] = nil, + }, + ["xdata"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["xid"] = { + ["csl"] = nil, + }, + ["xref"] = { + ["csl"] = nil, + ["type"] = "entrykey", + }, + ["year"] = { + ["csl"] = nil, + ["type"] = "date", + }, + ["year-presented"] = { + ["csl"] = nil, + }, + ["yearcomp"] = { + ["csl"] = nil, + }, + ["yearfiled"] = { + ["csl"] = nil, + }, + ["zblnumber"] = { + ["csl"] = nil, + }, + ["zip"] = { + ["csl"] = nil, + }, + }, + ["macros"] = { + ["jan"] = { + ["value"] = "1", + }, + ["feb"] = { + ["value"] = "2", + }, + ["mar"] = { + ["value"] = "3", + }, + ["apr"] = { + ["value"] = "4", + }, + ["may"] = { + ["value"] = "5", + }, + ["jun"] = { + ["value"] = "6", + }, + ["jul"] = { + ["value"] = "7", + }, + ["aug"] = { + ["value"] = "8", + }, + ["sep"] = { + ["value"] = "9", + }, + ["oct"] = { + ["value"] = "10", + }, + ["nov"] = { + ["value"] = "11", + }, + ["dec"] = { + ["value"] = "12", + }, + ["acmcs"] = { + ["value"] = "ACM Computing Surveys", + }, + ["acta"] = { + ["value"] = "Acta Informatica", + }, + ["cacm"] = { + ["value"] = "Communications of the ACM", + }, + ["ibmjrd"] = { + ["value"] = "IBM Journal of Research and Development", + }, + ["ibmsj"] = { + ["value"] = "IBM Systems Journal", + }, + ["ieeese"] = { + ["value"] = "IEEE Transactions on Software Engineering", + }, + ["ieeetc"] = { + ["value"] = "IEEE Transactions on Computers", + }, + ["ieeetcad"] = { + ["value"] = "IEEE Transactions on Computer-Aided Design of Integrated Circuits", + }, + ["ipl"] = { + ["value"] = "Information Processing Letters", + }, + ["jacm"] = { + ["value"] = "Journal of the ACM", + }, + ["jcss"] = { + ["value"] = "Journal of Computer and System Sciences", + }, + ["scp"] = { + ["value"] = "Science of Computer Programming", + }, + ["sicomp"] = { + ["value"] = "SIAM Journal on Computing", + }, + ["tocs"] = { + ["value"] = "ACM Transactions on Computer Systems", + }, + ["tods"] = { + ["value"] = "ACM Transactions on Database Systems", + }, + ["tog"] = { + ["value"] = "ACM Transactions on Graphics", + }, + ["toms"] = { + ["value"] = "ACM Transactions on Mathematical Software", + }, + ["toois"] = { + ["value"] = "ACM Transactions on Office Information Systems", + }, + ["toplas"] = { + ["value"] = "ACM Transactions on Programming Languages and Systems", + }, + ["tcs"] = { + ["value"] = "Theoretical Computer Science", + }, + }, +} \ No newline at end of file diff --git a/biblio/citation-style-language/citeproc-bib.lua b/biblio/citation-style-language/citeproc-bib.lua index b99ba955dd..7e24b1a6a5 100644 --- a/biblio/citation-style-language/citeproc-bib.lua +++ b/biblio/citation-style-language/citeproc-bib.lua @@ -16,24 +16,12 @@ local bib = {} -require("lualibs") local unicode = require("unicode") +bib.bib_data = require("citeproc-bib-data") local util = require("citeproc-util") -local path = "citeproc-bib-data.json" -if kpse then - path = kpse.find_file(path) -end -if path then - local contents = util.read_file(path) - if not contents then - error(string.format('Failed to find "%s"', path)) - end - bib.bib_data = utilities.json.tolua(contents) -end - function bib.parse(contents) local items = {} for item_contents in string.gmatch(contents, "(@%w+%b{})") do @@ -80,9 +68,9 @@ end function bib.parse_fields(contents) local fields = {} local field_patterns = { - "^(%w+)%s*=%s*(%b{}),?%s*(.-)$", - '^(%w+)%s*=%s*"([^"]*)",?%s*(.-)$', - "^(%w+)%s*=%s*(%w+),?%s*(.-)$", + "^([^,}%s]+)%s*=%s*(%b{}),?%s*(.-)$", + '^([^,}%s]+)%s*=%s*"([^"]*)",?%s*(.-)$', + "^([^,}%s]+)%s*=%s*(%w+),?%s*(.-)$", } while #contents > 0 do @@ -91,6 +79,7 @@ function bib.parse_fields(contents) for pattern_index, pattern in ipairs(field_patterns) do field, value, rest = string.match(contents, pattern) if value then + field = string.lower(field) if pattern_index == 1 then -- Strip braces "{}" value = string.sub(value, 2, -2) @@ -106,10 +95,14 @@ function bib.parse_fields(contents) end end fields[field] = value - contents = rest break end end + if value then + contents = rest + else + break + end end return fields end diff --git a/biblio/citation-style-language/citeproc-cli.lua b/biblio/citation-style-language/citeproc-cli.lua new file mode 100644 index 0000000000..97a51d724c --- /dev/null +++ b/biblio/citation-style-language/citeproc-cli.lua @@ -0,0 +1,196 @@ + +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local cli = {} + + +require("lualibs") +local citeproc = require("citeproc") +local util = require("citeproc-util") +local core = require("citeproc-latex-core") + + +local function getopt( arg, options ) + local tab = {} + for k, v in ipairs(arg) do + if string.sub( v, 1, 2) == "--" then + local x = string.find( v, "=", 1, true ) + if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 ) + else tab[ string.sub( v, 3 ) ] = true + end + elseif string.sub( v, 1, 1 ) == "-" then + local y = 2 + local l = string.len(v) + local jopt + while ( y <= l ) do + jopt = string.sub( v, y, y ) + if string.find( options, jopt, 1, true ) then + if y < l then + tab[ jopt ] = string.sub( v, y+1 ) + y = l + else + tab[ jopt ] = arg[ k + 1 ] + end + else + tab[ jopt ] = true + end + y = y + 1 + end + else + if tab.file then + error(string.format('Invalid argument "%s"', v)) + end + tab.file = v + end + + end + return tab +end + + +local function print_version() + io.write(string.format("citeproc-lua %s\n", citeproc.__VERSION__)) +end + + +local function print_help() + io.write("Usage: citeproc-lua [options] auxname[.aux]\n") + io.write("Options:\n") + io.write(" -h, --help Print this message and exit.\n") + io.write(" -V, --version Print the version number and exit.\n") +end + + +local function convert_bib(path, output_path) + local contents = util.read_file(path) + local bib = citeproc.parse_bib(contents) + if not output_path then + output_path = string.gsub(path, "%.bib$", ".json") + end + local file = io.open(output_path, "w") + file:write(utilities.json.tojson(bib)) + file:write('\n') + file:close() +end + + + +local function read_aux_file(aux_file) + local bib_style = nil + local bib_files = {} + local citations = {} + local csl_options = {} + + local file = io.open(aux_file, "r") + if not file then + error(string.format('Failed to open "%s"', aux_file)) + return + end + for line in file:lines() do + local match + match = string.match(line, "^\\bibstyle%s*(%b{})") + if match then + bib_style = string.sub(match, 2, -2) + else + match = string.match(line, "^\\bibdata%s*(%b{})") + if match then + for _, bib in ipairs(util.split(string.sub(match, 2, -2), "%s*,%s*")) do + table.insert(bib_files, bib) + end + else + match = string.match(line, "^\\citation%s*(%b{})") + if match then + local citation = core.make_citation(string.sub(match, 2, -2)) + table.insert(citations, citation) + else + match = string.match(line, "^\\csloptions%s*(%b{})") + if match then + for key, value in string.gmatch(match, "([%w-]+)=(%w+)") do + csl_options[key] = value + end + end + end + end + end + end + file:close() + + return bib_style, bib_files, citations, csl_options +end + + +local function process_aux_file(aux_file) + if not util.endswith(aux_file, ".aux") then + aux_file = aux_file .. ".aux" + end + + local style_name, bib_files, citations, csl_options = read_aux_file(aux_file) + + local lang = csl_options.locale + + local engine = core.init(style_name, bib_files, lang) + if csl_options.linking == "true" then + engine:enable_linking() + end + local style_class = engine:get_style_class() + + local citation_strings = core.process_citations(engine, citations) + + local output_string = "" + + for _, citation in ipairs(citations) do + local citation_id = citation.citationID + if citation_id ~= "@nocite" then + local citation_str = citation_strings[citation_id] + output_string = output_string .. string.format("\\cslcite{%s}{{%s}{%s}}\n", citation_id, style_class, citation_str) + end + end + + output_string = output_string .. "\n" + + local result = core.make_bibliography(engine) + output_string = output_string .. result + + local output_path = string.gsub(aux_file, "%.aux$", ".bbl") + local bbl_file = io.open(output_path, "w") + bbl_file:write(output_string) + bbl_file:close() +end + + +function cli.main() + local args = getopt(arg, "o") + + -- for k, v in pairs(args) do + -- print( k, v ) + -- end + + if args.V or args.version then + print_version() + return + elseif args.h or args.help then + print_help() + return + end + + if not args.file then + error("citeproc: Need exactly one file argument.\n") + end + + local path = args.file + + local output_path = args.o or args.output + if util.endswith(path, ".bib") then + convert_bib(path, output_path) + else + process_aux_file(path) + end + +end + + +return cli diff --git a/biblio/citation-style-language/citeproc-context.lua b/biblio/citation-style-language/citeproc-context.lua new file mode 100644 index 0000000000..08be158bcf --- /dev/null +++ b/biblio/citation-style-language/citeproc-context.lua @@ -0,0 +1,335 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local context = {} + +local LocalizedQuotes = require("citeproc-output").LocalizedQuotes + +local util = require("citeproc-util") + + +local Context = { + reference = nil, + format = nil, + cite_id = nil, + style = nil, + locale = nil, + name_citation = nil, + names_delimiter = nil, + + position = nil, + + disamb_pass = nil, + + cite = nil, + bib_number = nil, + + in_bibliography = false, + sort_key = nil, + + year_suffix = nil, +} + +function Context:new() + local o = { + in_bibliography = false + } + setmetatable(o, self) + self.__index = self + return o +end + +function Context:get_variable(name, form) + local variable_type = util.variable_types[name] + if variable_type == "number" then + return self:get_number(name) + -- elseif variable_type == "date" then + -- return self:get_date(name) + elseif variable_type == "name" then + return self:get_name(name) + else + return self:get_ordinary(name, form) + end +end + +function Context:get_number(name) + if name == "locator" then + return self.cite.locator + elseif name == "citation-number" then + -- return self.bib_number + return self.reference["citation-number"] + elseif name == "first-reference-note-number" then + return self.cite["first-reference-note-number"] + elseif name == "page-first" then + return self.page_first(self.reference.page) + else + return self.reference[name] + end +end + +function Context:get_ordinary(name, form) + local res = nil + local variable_name = name + if form and form ~= "long" then + variable_name = variable_name .. "-" .. form + end + + if variable_name == "locator" or variable_name == "label" then + res = self.cite[variable_name] + else + res = self.reference[variable_name] + end + if res then + return res + end + + if variable_name == "container-title-short" then + res = self.reference["journalAbbreviation"] + if res then + return res + end + end + + if form then + res = self.reference[name] + if res then + return res + end + end + + -- if name == "title-short" or name == "container-title-short" then + -- variable_name = string.gsub(name, "%-short$", "") + -- res = self.reference[variable_name] + -- end + + return res +end + +-- TODO: optimize: process only once +-- TODO: organize the name parsing code +function Context:get_name(variable_name) + local names = self.reference[variable_name] + if names then + for _, name in ipairs(names) do + if name.family == "" then + name.family = nil + end + if name.given == "" then + name.given = nil + end + if name.given and not name.family then + name.family = name.given + name.given = nil + end + self:parse_name_suffix(name) + self:split_ndp_family(name) + -- self:split_given_ndp(name) + self:split_given_dp(name) + end + end + return names +end + +function Context:parse_name_suffix(name) + if not name.suffix and name.family and string.match(name.family, ",") then + local words = util.split(name.family, ",%s*") + name.suffix = words[#words] + name.family = table.concat(util.slice(words, 1, -2), ", ") + end + if not name.suffix and name.given and string.match(name.given, ",") then + -- Split name suffix: magic_NameSuffixNoComma.txt + -- "John, III" => given: "John", suffix: "III" + local words = util.split(name.given, ",%s*") + name.suffix = words[#words] + name.given = table.concat(util.slice(words, 1, -2), ", ") + end +end + +function Context:split_ndp_family(name) + if name["non-dropping-particle"] or not name.family then + return + end + if util.startswith(name.family, '"') and util.endswith(name.family, '"') then + -- Stop parsing family name if surrounded by quotation marks + -- bugreports_parseName.txt + name.family = string.gsub(name.family, '"', "") + return + end + local ndp_parts = {} + local family_parts = {} + local parts = util.split(name.family) + for i, part in ipairs(parts) do + local ndp, family + -- d'Aubignac + ndp, family = string.match(part, "^(%l')(.+)$") + if ndp and family then + table.insert(ndp_parts, ndp) + parts[i] = family + else + ndp, family = string.match(part, "^(%l’)(.+)$") + if ndp and family then + table.insert(ndp_parts, ndp) + parts[i] = family + else + -- al-Aswānī + ndp, family = string.match(part, "^(%l+%-)(.+)$") + if ndp and family then + table.insert(ndp_parts, ndp) + parts[i] = family + elseif i < #parts and util.is_lower(part) then + table.insert(ndp_parts, part) + end + end + end + if ndp or i == #parts then + for j = i, #parts do + table.insert(family_parts, parts[j]) + end + break + end + if not util.is_lower(part) then + for j = i, #parts do + table.insert(family_parts, parts[j]) + end + break + end + end + if #ndp_parts > 0 then + name["non-dropping-particle"] = table.concat(ndp_parts, " ") + name.family = table.concat(family_parts, " ") + end +end + +function Context:split_given_dp(name) + if name["dropping-particle"] or not name.given then + return + end + local dp_parts = {} + local given_parts = {} + local parts = util.split(name.given) + for i = #parts, 1, -1 do + local part = parts[i] + if i == 1 or not util.is_lower(part) then + for j = 1, i do + table.insert(given_parts, parts[j]) + end + break + end + -- name_ParsedDroppingParticleWithApostrophe.txt + -- given: "François Hédelin d'" => + -- given: "François Hédelin", dropping-particle: "d'" + if string.match(part, "^%l+'?$") or string.match(part, "^%l+’$") then + table.insert(dp_parts, 1, part) + end + end + if #dp_parts > 0 then + name["dropping-particle"] = table.concat(dp_parts, " ") + name.given = table.concat(given_parts, " ") + end +end + +-- function Context:split_given_ndp(name) +-- if name["non-dropping-particle"] or not name.given then +-- return +-- end + +-- if not (string.match(name.given, "%l'$") or string.match(name.given, "%l’$")) then +-- return +-- end + +-- local words = util.split(name.given) +-- if #words < 2 then +-- return +-- end +-- local last_word = words[#words] +-- if util.endswith(last_word, "'") or util.endswith(last_word, util.unicode["apostrophe"]) then +-- name["non-dropping-particle"] = last_word +-- name.given = table.concat(util.slice(words, 1, -2), " ") +-- end +-- util.debug(name) +-- end + +function Context:get_localized_date(form) + return self.locale.dates[form] +end + +function Context:get_macro(name) + local res = self.style.macros[name] + if not res then + util.error(string.format('Undefined macro "%s"', name)) + end + return res +end + +function Context:get_simple_term(name, form, plural) + assert(self.locale) + return self.locale:get_simple_term(name, form, plural) +end + +function Context:get_localized_quotes() + return LocalizedQuotes:new( + self:get_simple_term("open-quote"), + self:get_simple_term("close-quote"), + self:get_simple_term("open-inner-quote"), + self:get_simple_term("close-inner-quote"), + self.locale.style_options.punctuation_in_quote + ) +end + +function Context.page_first(page) + local page_first = util.split(page, "%s*[&,-]%s*")[1] + return util.split(page_first, util.unicode["en dash"])[1] +end + +-- https://docs.citationstyles.org/en/stable/specification.html#non-english-items +function Context:is_english() + local language = self:get_variable("language") + if util.startswith(self.engine.lang, "en") then + if not language or util.startswith(language, "en") then + return true + else + return false + end + else + if language and util.startswith(language, "en") then + return true + else + return false + end + end +end + + +local IrState = {} + +function IrState:new(style, cite_id, cite, reference) + local o = { + macro_stack = {}, + suppressed = {}, + } + setmetatable(o, self) + self.__index = self + return o +end + +function IrState:push_macro(macro_name) + for _, name in ipairs(macro_name) do + if name == macro_name then + util.error(string.format('Recursive macro "%s".', macro_name)) + end + table.insert(self.macro_stack, macro_name) + end +end + +function IrState:pop_macro(macro_name) + table.remove(self.macro_stack) +end + + +context.Context = Context +context.IrState = IrState + +return context diff --git a/biblio/citation-style-language/citeproc-element.lua b/biblio/citation-style-language/citeproc-element.lua index 0e7abf87e3..197616cb08 100644 --- a/biblio/citation-style-language/citeproc-element.lua +++ b/biblio/citation-style-language/citeproc-element.lua @@ -6,410 +6,536 @@ local element = {} -local unicode = require("unicode") +local SeqIr = require("citeproc-ir-node").SeqIr + +local InlineElement = require("citeproc-output").InlineElement +local Micro = require("citeproc-output").Micro -local richtext = require("citeproc-richtext") local util = require("citeproc-util") local Element = { - default_options = {}, + element_name = nil, + children = nil, + element_type_map = {}, } -function Element:new () - local o = {} +function Element:new(element_name) + local o = { + element_name = element_name or self.element_name, + } setmetatable(o, self) self.__index = self return o end -Element.option_type = { - ["et-al-min"] = "integer", - ["et-al-use-first"] = "integer", - ["et-al-subsequent-min"] = "integer", - ["et-al-subsequent-use-first"] = "integer", - ["near-note-distance"] = "integer", - ["line-spacing"] = "integer", - ["entry-spacing"] = "integer", - ["names-min"] = "integer", - ["names-use-first"] = "integer", - ["limit-day-ordinals-to-day-1"] = "boolean", - ["punctuation-in-quote"] = "boolean", - ["et-al-use-last"] = "boolean", - ["initialize"] = "boolean", - ["initialize-with-hyphen"] = "boolean", - ["disambiguate-add-names"] = "boolean", - ["disambiguate-add-givenname"] = "boolean", - ["disambiguate-add-year-suffix"] = "boolean", - ["hanging-indent"] = "boolean", - ["names-use-last"] = "boolean", - ["quotes"] = "boolean", - ["strip-periods"] = "boolean", -} +function Element:derive(element_name, default_options) + local o = { + element_name = element_name or self.element_name, + children = nil, + } -Element.inheritable_options = { - -- Style - ["initialize-with-hyphen"] = true, - ["page-range-format"] = true, - ["demote-non-dropping-particle"] = true, - -- Citation - ["disambiguate-add-givenname"] = true, - ["givenname-disambiguation-rule"] = true, - ["disambiguate-add-names"] = true, - ["disambiguate-add-year-suffix"] = true, - ["cite-group-delimiter"] = true, - ["collapse"] = true, - ["year-suffix-delimiter"] = true, - ["after-collapse-delimiter"] = true, - ["near-note-distance"] = true, - -- Bibliography - ["second-field-align"] = true, -- for use in layout - ["subsequent-author-substitute"] = true, - ["subsequent-author-substitute-rule"] = true, - -- Date - ["date-parts"] = true, - -- Names - ["and"] = true, - ["delimiter-precedes-et-al"] = true, - ["delimiter-precedes-last"] = true, - ["et-al-min"] = true, - ["et-al-use-first"] = true, - ["et-al-use-last"] = true, - ["et-al-subsequent-min"] = true, - ["et-al-subsequent-use-first"] = true, - ["names-min"] = true, - ["names-use-first"] = true, - ["names-use-last"] = true, - ["initialize-with"] = true, - ["name-as-sort-order"] = true, - ["sort-separator"] = true, - ["name-form"] = true, - ["name-delimiter"] = true, - ["names-delimiter"] = true, -} + if default_options then + for key, value in pairs(default_options) do + o[key] = value + end + end + + Element.element_type_map[element_name] = o + setmetatable(o, self) + self.__index = self + return o +end -function Element:render (item, context) - self:debug_info(context) - context = self:process_context(context) - return self:render_children(item, context) +function Element:from_node(node, parent) + local o = self:new() + o.element_name = self.element_name or node:get_element_name() + return o end -function Element:render_children (item, context) - local output = {} - for i, child in ipairs(self:get_children()) do - if child:is_element() then - if child.render == nil then - local element_name = child:get_element_name() - util.warning("Unkown type \"" .. element_name .. "\"") - end - local str = child:render(item, context) - table.insert(output, str) - end +function Element:set_attribute(node, attribute) + local value = node:get_attribute(attribute) + if value then + local key = string.gsub(attribute, "%-" , "_") + self[key] = value end - return self:concat(output, context) end -function Element:set_base_class (node) - if node:is_element() then - local org_meta_table = getmetatable(node) - setmetatable(node, {__index = function (_, key) - if self[key] then - return self[key] - else - return org_meta_table[key] - end - end}) +function Element:set_bool_attribute(node, attribute) + local value = node:get_attribute(attribute) + if value == "true" then + local key = string.gsub(attribute, "%-" , "_") + self[key] = true + elseif value == "false" then + local key = string.gsub(attribute, "%-" , "_") + self[key] = false end end -function Element:debug_info (context, debug) - -- debug = true - if debug then - local text = "" - local level = 0 - if context and context.level then - level = context.level + 1 - end - text = text .. string.rep(" ", 2 * level) - text = text .. self:get_element_name() - local attrs = {} - if self._attr then - for attr, value in pairs(self._attr) do - table.insert(attrs, attr .. "=\"" .. value .. "\"") - end - text = text .. "[" .. table.concat(attrs, " ") .. "]" - end - io.stderr:write(text .. "\n") +function Element:set_number_attribute(node, attribute) + local value = node:get_attribute(attribute) + if value then + local key = string.gsub(attribute, "%-" , "_") + self[key] = tonumber(value) end end -function Element:get_child (type) - for _, child in ipairs(self:get_children()) do - if child:get_element_name() == type then - return child +function Element:process_children_nodes(node) + if not self.children then + self.children = {} + end + for _, child in ipairs(node:get_children()) do + if child:is_element() then + local element_name = child:get_element_name() + local element_type = self.element_type_map[element_name] or Element + local child_element = element_type:from_node(child, self) + table.insert(self.children, child_element) end end - return nil -end -function Element:get_style () - local style = self:root_node().style - assert(style ~= nil) - return style end -function Element:get_engine () - local engine = self:root_node().engine - assert(engine ~= nil) - return engine +function Element:build_ir(engine, state, context) + return self:build_children_ir(engine, state, context) end -function Element:process_context (context) - local state = { - -- The `build` table is directly passed to new context. - build = context.build or {}, - -- The `option` table is copied. - options = {}, - -- Other items in `context` is copied. - } - for key, value in pairs(self.default_options) do - state.options[key] = value - end - if context then - local element_name = self:get_element_name() - for key, value in pairs(context) do - if key == "options" then - for k, v in pairs(context.options) do - if self.inheritable_options[k] then - state.options[k] = v - if element_name == "name" then - if k == "name-form" then - state.options["form"] = v - end - if k == "name-delimiter" then - state.options["delimiter"] = v - end - elseif element_name == "names" then - if k == "names-delimiter" then - state.options["delimiter"] = v - end - end - end +function Element:build_children_ir(engine, state, context) + local child_irs = {} + local ir_sort_key + local group_var = "plain" + if self.children then + for _, child_element in ipairs(self.children) do + local child_ir = child_element:build_ir(engine, state, context) + if child_ir then + if child_ir.sort_key ~= nil then + ir_sort_key = child_ir.sort_key end - else - state[key] = value + if child_ir.group_var == "important" then + group_var = "important" + end + table.insert(child_irs, child_ir) end end - if state.level then - state.level = state.level + 1 - else - state.level = 0 - end end - if self._attr then - for key, value in pairs(self._attr) do - if self.option_type[key] == "integer" then - value = tonumber(value) - elseif self.option_type[key] == "boolean" then - value = (value == "true") - end - state.options[key] = value - end + local ir = SeqIr:new(child_irs, self) + ir.sort_key = ir_sort_key + ir.group_var = group_var + if #child_irs == 0 then + ir.group_var = "missing" + else + ir.group_var = group_var end - return state + return ir end -function Element:get_option (key, context) - assert(context ~= nil) - return context.options[key] -end - -function Element:get_locale_option (key) - local locales = self:get_style():get_locales() - for i, locale in ipairs(locales) do - local option = locale:get_option(key) - if option ~= nil then - return option - end +-- Used in cs:group and cs:macro +function Element:build_group_ir(engine, state, context) + if not self.children then + return nil end - return nil -end + local irs = {} + local name_count + local ir_sort_key + local group_var = "plain" + + for _, child_element in ipairs(self.children) do + -- util.debug(child_element.element_name) + local child_ir = child_element:build_ir(engine, state, context) + -- util.debug(child_ir) + -- util.debug(child_ir.group_var) + + if child_ir then + -- cs:group and its child elements are suppressed if + -- a) at least one rendering element in cs:group calls a variable (either + -- directly or via a macro), and + -- b) all variables that are called are empty. This accommodates + -- descriptive cs:text and `cs:label` elements. + local child_group_var = child_ir.group_var + if child_group_var == "important" then + group_var = "important" + elseif child_group_var == "missing" and child_ir._type ~= "YearSuffix" then + if group_var == "plain" then + group_var = "missing" + end + end -function Element:get_variable (item, name, context) - if context.suppressed_variables and context.suppressed_variables[name] then - return nil - else - local res = item[name] - if type(res) == "table" and res._type == "RichText" then - -- TODO: should be deep copy - res = res:shallow_copy() - end + if child_ir.name_count then + if not name_count then + name_count = 0 + end + name_count = name_count + child_ir.name_count + end - if res and res ~= "" then - if context.suppress_subsequent_variables then - context.suppressed_variables[name] = true + if child_ir.sort_key ~= nil then + ir_sort_key = child_ir.sort_key end + + table.insert(irs, child_ir) end - return res end -end -function Element:get_macro (name) - local query = string.format("macro[name=\"%s\"]", name) - local macro = self:root_node():query_selector(query)[1] - if not macro then - error(string.format("Failed to find %s.", query)) + -- A non-empty nested cs:group is treated as a non-empty variable for the + -- puropses of determining suppression of the outer cs:group. + if #irs > 0 and group_var == "plain" then + group_var = "important" end - return macro -end -function Element:get_term (name, form, number, gender) - return self:get_style():get_term(name, form, number, gender) -end + local ir = SeqIr:new(irs, self) + ir.name_count = name_count + ir.sort_key = ir_sort_key + ir.group_var = group_var --- Formatting -function Element:escape (str, context) - return str - -- return self:get_engine().formatter.text_escape(str) + return ir end -function Element:format(text, context) - if not text or text == "" then - return nil +function Element:render_text_inlines(str, context) + if str == "" then + return {} end - if text._type ~= "RichText" then - text = richtext.new(text) + + str = self:apply_strip_periods(str) + -- TODO: try links + + local output_format = context.format + local localized_quotes = nil + if self.quotes then + localized_quotes = context:get_localized_quotes() end - local attributes = { + + local inlines = InlineElement:parse(str, context) + local is_english = context:is_english() + output_format:apply_text_case(inlines, self.text_case, is_english) + inlines = {Micro:new(inlines)} + inlines = output_format:with_format(inlines, self.formatting) + inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes) + return output_format:with_display(inlines, self.display) +end + +function Element:set_formatting_attributes(node) + for _, attribute in ipairs({ "font-style", "font-variant", "font-weight", "text-decoration", "vertical-align", - } - for _, attribute in ipairs(attributes) do - local value = context.options[attribute] + }) do + local value = node:get_attribute(attribute) if value then - if text.formats[attribute] then - local new = richtext.new() - new.contents = {text} - text = new + if not self.formatting then + self.formatting = {} end - text:add_format(attribute, value) + self.formatting[attribute] = value end end - return text end --- Affixes -function Element:wrap (str, context) - if not str or str == "" then - return nil - end - local prefix = context.options["prefix"] - local suffix = context.options["suffix"] - local res = str - if prefix and prefix ~= "" then - local linkable = false - local variable_name = context.options["variable"] - if variable_name == "DOI" or variable_name == "PMID" or variable_name == "PMCID" then - linkable = true +function Element:set_affixes_attributes(node) + for _, attribute in ipairs({"prefix", "suffix"}) do + local value = node:get_attribute(attribute) + if value then + if not self.affixes then + self.affixes = {} + end + self.affixes[attribute] = value end - if variable_name == "URL" or (linkable and not string.match(prefix, "^https?://")) then - res:add_format(variable_name, "true") + end +end + +function Element:get_delimiter_attribute(node) + self:set_attribute(node, "delimiter") +end + +function Element:set_display_attribute(node) + self:set_attribute(node, "display") +end + +function Element:set_quotes_attribute(node) + self:set_bool_attribute(node, "quotes") +end + +function Element:set_strip_periods_attribute(node) + self:set_bool_attribute(node, "strip-periods") +end + +function Element:set_text_case_attribute(node) + self:set_attribute(node, "text-case") +end + +-- function Element:apply_formatting(ir) +-- local attributes = { +-- "font_style", +-- "font_variant", +-- "font_weight", +-- "text_decoration", +-- "vertical_align", +-- } +-- for _, attribute in ipairs(attributes) do +-- local value = self[attribute] +-- if value then +-- if not ir.formatting then +-- ir.formatting = {} +-- end +-- ir.formatting[attribute] = value +-- end +-- end +-- return ir +-- end + +function Element:apply_affixes(ir) + if ir then + if self.prefix then + ir.prefix = self.prefix end - res = richtext.concat(prefix, res) - if linkable and string.match(prefix, "^https?://") then - res:add_format("URL", "true") + if self.suffix then + ir.suffix = self.suffix end end - if suffix and suffix ~= "" then - res = richtext.concat(res, suffix) + return ir +end + +function Element:apply_delimiter(ir) + if ir and ir.children then + ir.delimiter = self.delimiter end - return res + return ir end --- Delimiters -function Element:concat (strings, context) - local delimiter = context.options["delimiter"] - return richtext.concat_list(strings, delimiter) +function Element:apply_display(ir) + ir.display = self.display + return ir end --- Display -function Element:display(text, context) - if not text then - return text - end - local value = context.options["display"] - if not value then - return text +function Element:apply_quotes(ir) + if ir and self.quotes then + ir.quotes = true + ir.children = {ir} + ir.open_quote = nil + ir.close_quote = nil + ir.open_inner_quote = nil + ir.close_inner_quote = nil + ir.punctuation_in_quote = false end - if type(text) == "string" then - text = richtext.new(text) + return ir +end + +function Element:apply_strip_periods(str) + local res = str + if str and self.strip_periods then + res = string.gsub(str, "%.", "") end - text:add_format("display", value) - return text + return res end --- Quotes -function Element:quote (str, context) - if not str then - return nil + +function Element:format_number(number, variable, form, context) + -- if not util.is_numeric(number) then + -- number = string.gsub(number, "\\%-", "-") + -- return number + -- end + number = util.strip(number) + if variable == "locator" then + variable = context:get_variable("label") end - if context.sorting then - return str + form = form or "numeric" + local number_part_list = self:split_number_parts(number, context) + -- { + -- {"1", "", " & "} + -- {"5", "8", ", "} + -- } + -- util.debug(number_part_list) + + for _, number_parts in ipairs(number_part_list) do + if form == "roman" then + self:format_roman_number_parts(number_parts) + elseif form == "ordinal" or form == "long-ordinal" then + local gender = context.locale:get_number_gender(variable) + self:format_ordinal_number_parts(number_parts, form, gender, context) + elseif number_parts[2] ~= "" and variable == "page" then + local page_range_format = context.style.page_range_format + self:format_page_range(number_parts, page_range_format) + else + self:format_numeric_number_parts(number_parts) + end end - if not str._type == "RichText" then - str = richtext.new(str) + + local range_delimiter = util.unicode["en dash"] + if variable == "page" then + local page_range_delimiter = context:get_simple_term("page-range-delimiter") + if page_range_delimiter then + range_delimiter = page_range_delimiter + end end - local quotes = context.options["quotes"] or false - if quotes then - str:add_format("quotes", "true") + + local res = "" + for _, number_parts in ipairs(number_part_list) do + res = res .. number_parts[1] + if number_parts[2] ~= "" then + res = res .. range_delimiter + res = res .. number_parts[2] + end + res = res .. number_parts[3] end - return str + return res + end --- Strip periods -function Element:strip_periods (str, context) - if not str then - return nil +function Element:split_number_parts(number, context) + -- number = string.gsub(number, util.unicode["en dash"], "-") + local and_symbol + and_symbol = context.locale:get_simple_term("and", "symbol") + if and_symbol then + and_symbol = " " .. and_symbol .. " " end - if str._type ~= "RichText" then - str = richtext.new(str) + local number_part_list = {} + for _, tuple in ipairs(util.split_multiple(number, "%s*[,&]%s*", true)) do + local single_number, delim = table.unpack(tuple) + delim = util.strip(delim) + if delim == "," then + delim = ", " + elseif delim == "&" then + delim = and_symbol or " & " + end + local start = single_number + local stop = "" + local splits = util.split(start, "%s*%-%s*") + if #splits == 2 then + start, stop = table.unpack(splits) + if util.endswith(start, "\\") then + start = string.sub(start, 1, -2) + start = start .. "-" .. stop + stop = "" + end + -- if string.match(start, "^%a*%d+%a*$") and string.match(stop, "^%a*%d+%a*$") then + -- if s + table.insert(number_part_list, {start, stop, delim}) + -- else + -- table.insert(number_part_list, {start .. "-" .. stop, "", delim}) + -- end + else + table.insert(number_part_list, {start, stop, delim}) + end end - local strip_periods = context.options["strip-periods"] - if strip_periods then - str:strip_periods() + return number_part_list +end + +function Element:format_roman_number_parts(number_parts) + for i = 1, 2 do + local part = number_parts[i] + if string.match(part, "%d+") then + number_parts[i] = util.convert_roman(tonumber(part)) + end end - return str end --- Text-case -function Element:case (text, context) - if not text or text == "" then - return nil +function Element:format_ordinal_number_parts(number_parts, form, gender, context) + for i = 1, 2 do + local part = number_parts[i] + if string.match(part, "%d+") then + local number = tonumber(part) + if form == "long-ordinal" and number >= 1 and number <= 10 then + number_parts[i] = context:get_simple_term(string.format("long-ordinal-%02d", number)) + else + local suffix = context.locale:get_ordinal_term(number, gender) + if suffix then + number_parts[i] = number_parts[i] .. suffix + end + end + end + end +end + +function Element:format_numeric_number_parts(number_parts) + -- if number_parts[2] ~= "" then + -- local first_prefix = string.match(number_parts[1], "^(.-)%d+") + -- local second_prefix = string.match(number_parts[2], "^(.-)%d+") + -- if first_prefix == second_prefix then + -- number_parts[1] = number_parts[1] .. "-" .. number_parts[2] + -- number_parts[2] = "" + -- end + -- end +end + +-- https://docs.citationstyles.org/en/stable/specification.html#appendix-v-page-range-formats +function Element:format_page_range(number_parts, page_range_format) + local start = number_parts[1] + local stop = number_parts[2] + local start_prefix, start_num = string.match(start, "^(.-)(%d+)$") + local stop_prefix, stop_num = string.match(stop, "^(.-)(%d+)$") + if start_prefix ~= stop_prefix then + -- Not valid range: "n11564-1568" -> "n11564-1568" + -- 110-N6 + -- N110-P5 + number_parts[1] = start .. "-" .. stop + number_parts[2] = "" + return end - if text._type ~= "RichText" then - text = richtext.new(text) + + if not page_range_format then + return end - local text_case = context.options["text-case"] - if not text_case then - return text + if page_range_format == "chicago-16" then + stop = self:_format_range_chicago_16(start_num, stop_num) + elseif page_range_format == "chicago-15" then + stop = self:_format_range_chicago_15(start_num, stop_num) + elseif page_range_format == "expanded" then + stop = stop_prefix .. self:_format_range_expanded(start_num, stop_num) + elseif page_range_format == "minimal" then + stop = self:_format_range_minimal(start_num, stop_num) + elseif page_range_format == "minimal-two" then + stop = self:_format_range_minimal(start_num, stop_num, 2) end - if text_case == "title" then - -- title case conversion only affects English-language items - local language = context.item["language"] - if not language then - language = self:get_style():get_attribute("default-locale") or "en-US" - end - if not util.startswith(language, "en") then - return text + number_parts[2] = stop +end + +function Element:_format_range_chicago_16(start, stop) + if #start < 3 or string.sub(start, -2) == "00" then + return self:_format_range_expanded(start, stop) + elseif string.sub(start, -2, -2) == "0" then + return self:_format_range_minimal(start, stop) + else + return self:_format_range_minimal(start, stop, 2) + end + return stop +end + +function Element:_format_range_chicago_15(start, stop) + if #start < 3 or string.sub(start, -2) == "00" then + return self:_format_range_expanded(start, stop) + else + local changed_digits = self:_format_range_minimal(start, stop) + if string.sub(start, -2, -2) == "0" then + return changed_digits + elseif #start == 4 and #changed_digits == 3 then + return self:_format_range_expanded(start, stop) + else + return self:_format_range_minimal(start, stop, 2) end end - text:add_format("text-case", text_case) - return text + return stop end +function Element:_format_range_expanded(start, stop) + -- Expand "1234–56" -> "1234–1256" + if #start <= #stop then + return stop + end + return string.sub(start, 1, #start - #stop) .. stop +end + +function Element:_format_range_minimal(start, stop, threshold) + threshold = threshold or 1 + if #start < #stop then + return stop + end + local offset = #start - #stop + for i = 1, #stop - threshold do + local j = i + offset + if string.sub(stop, i, i) ~= string.sub(start, j, j) then + return string.sub(stop, i) + end + end + return string.sub(stop, -threshold) +end element.Element = Element diff --git a/biblio/citation-style-language/citeproc-engine.lua b/biblio/citation-style-language/citeproc-engine.lua index 33a5a08e08..2e9f4c4a61 100644 --- a/biblio/citation-style-language/citeproc-engine.lua +++ b/biblio/citation-style-language/citeproc-engine.lua @@ -8,17 +8,29 @@ local engine = {} local dom = require("luaxml-domobject") -local richtext = require("citeproc-richtext") -local element = require("citeproc-element") local nodes = require("citeproc-nodes") -local formats = require("citeproc-formats") +local Style = require("citeproc-node-style").Style +local Locale = require("citeproc-node-locale").Locale +local Context = require("citeproc-context").Context +local IrState = require("citeproc-context").IrState +local Rendered = require("citeproc-ir-node").Rendered +local YearSuffix = require("citeproc-ir-node").YearSuffix +-- local OutputFormat = require("citeproc-output").OutputFormat +local LatexWriter = require("citeproc-output").LatexWriter +local HtmlWriter = require("citeproc-output").HtmlWriter +local SortStringFormat = require("citeproc-output").SortStringFormat +local DisamStringFormat = require("citeproc-output").DisamStringFormat +local InlineElement = require("citeproc-output").InlineElement +local Micro = require("citeproc-output").Micro +local Formatted = require("citeproc-output").Formatted +local PlainText = require("citeproc-output").PlainText local util = require("citeproc-util") local CiteProc = {} -function CiteProc.new (sys, style, lang, force_lang) - if sys == nil then +function CiteProc.new(sys, style, lang, force_lang) + if not sys then error("\"citeprocSys\" required") end if sys.retrieveLocale == nil then @@ -28,213 +40,1579 @@ function CiteProc.new (sys, style, lang, force_lang) error("\"citeprocSys.retrieveItem\" required") end local o = {} + + o.style = Style:parse(style) + + o.sys = sys + o.locales = {} + o.system_locales = {} + + o.lang = o.style.default_locale + if not o.lang or force_lang then + o.lang = lang or "en-US" + end + + o.output_format = LatexWriter:new() + + o.opt = { + -- Similar to citeproc-js's development_extensions.wrap_url_and_doi + url_link = false, + doi_link = false, -- also applied to PMID and PMCID + title_link = false, + } + o.registry = { - citations = {}, -- A map - citation_strings = {}, -- A list - registry = {}, -- A map - reflist = {}, -- A list + citations_by_id = {}, -- A map + citation_list = {}, -- A list + citations_by_item_id = {}, -- A map from item id to a map of citations + registry = {}, -- A map of bibliographic meta data + reflist = {}, -- list of cited ids + uncited_list = {}, previous_citation = nil, requires_sorting = false, + longest_label = "", + maxoffset = 0, } - o.sys = sys - o.system_locales = {} + o.cite_first_note_numbers = {} + o.cite_last_note_numbers = {} + o.note_citations_map = {} + + o.tainted_item_ids = {} + + o.disam_irs = {} + -- { , , ... } + + o.cite_irs_by_output = {} + -- { + -- ["Roe, J"] = {}, + -- ["Doe, J"] = {, }, + -- ["Doe, John"] = {}, + -- ["Doe, Jack"] = {}, + -- } + + o.person_names = {} + o.person_names_by_output = {} + + setmetatable(o, { __index = CiteProc }) + return o +end + +function CiteProc:updateItems(ids) + self.registry.reflist = {} + self.registry.registry = {} + self.person_names = {} + self.person_names_by_output = {} + self.disam_irs = {} + self.cite_irs_by_output = {} + + local cite_items = {} + local loaded_ids = {} + + for _, id in ipairs(ids) do + table.insert(cite_items, {id = id}) + loaded_ids[id] = true + end + for _, id in ipairs(self.registry.uncited_list) do + if not loaded_ids[id] then + table.insert(cite_items, {id = id}) + loaded_ids[id] = true + end + end + + -- TODO: optimize this + self:makeCitationCluster(cite_items) + + self.registry.previous_citation = nil + self.cite_first_note_numbers = {} + self.cite_last_note_numbers = {} + self.note_citations_map = {} +end + +function CiteProc:updateUncitedItems(uncited_ids) + -- self.registry.reflist = {} + self.registry.registry = {} + self.registry.uncited_list = {} + self.person_names = {} + self.person_names_by_output = {} + self.disam_irs = {} + self.cite_irs_by_output = {} + + local cite_items = {} + local loaded_ids = {} + + for _, id in ipairs(self.registry.reflist) do + if not loaded_ids[id] then + table.insert(cite_items, {id = id}) + loaded_ids[id] = true + end + end + self.registry.reflist = {} + + for _, id in ipairs(uncited_ids) do + if not loaded_ids[id] then + table.insert(cite_items, {id = id}) + loaded_ids[id] = true + end + end + + loaded_ids = {} + for _, id in ipairs(uncited_ids) do + if not loaded_ids[id] then + table.insert(self.registry.uncited_list, id) + loaded_ids[id] = true + end + end + + -- TODO: optimize this + self:makeCitationCluster(cite_items) + + self.registry.previous_citation = nil + self.cite_first_note_numbers = {} + self.cite_last_note_numbers = {} + self.note_citations_map = {} +end + +function CiteProc:processCitationCluster(citation, citationsPre, citationsPost) + -- util.debug(citation) + -- util.debug(citationsPre) + -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt + if not citation.properties then + citation.properties = {} + end + if not citation.properties.noteIndex then + citation.properties.noteIndex = 0 + end + + -- Registor citation + self.registry.citations_by_id[citation.citationID] = citation + + local citation_note_pairs = {} + util.extend(citation_note_pairs, citationsPre) + table.insert(citation_note_pairs, {citation.citationID, citation.properties.noteIndex}) + util.extend(citation_note_pairs, citationsPost) + -- util.debug(citation_note_pairs) + + local citations_by_id = {} + local citation_list = {} + for _, pair in ipairs(citation_note_pairs) do + local citation_id, note_number = table.unpack(pair) + local citation_ = self.registry.citations_by_id[citation_id] + if not citation_ then + util.error("Citation not in registry.") + end + citations_by_id[citation_.citationID] = citation_ + table.insert(citation_list, citation_) + end + self.registry.citations_by_id = citations_by_id + self.registry.citation_list = citation_list + + -- update self.registry.citations_by_item_id + local item_ids = {} + self.registry.citations_by_item_id = {} + for _, citation_ in ipairs(citation_list) do + for _, cite_item in ipairs(citation_.citationItems) do + if not self.registry.citations_by_item_id[cite_item.id] then + self.registry.citations_by_item_id[cite_item.id] = {} + table.insert(item_ids, cite_item.id) + end + self.registry.citations_by_item_id[cite_item.id][citation_.citationID] = true + end + end + self:updateItems(item_ids) + + local params = { + bibchange = false, + citation_errors = {}, + } + local output = {} + + local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs) + -- util.debug(tainted_citation_ids) + + -- params.bibchange = #tainted_citation_ids > 0 + for citation_id, _ in pairs(tainted_citation_ids) do + local citation_ = self.registry.citations_by_id[citation_id] + + local citation_index = citation_.citation_index + local citation_str = self:build_citation_str(citation_) + table.insert(output, {citation_index, citation_str, citation_id}) + end + + -- util.debug(output) + return {params, output} +end + +-- A variant of processCitationCluster() for easy use with LaTeX. +-- It should be run after refreshing the registry (updateItems()) with all items +function CiteProc:process_citation(citation) + -- util.debug(citation) + -- util.debug(citationsPre) + -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt + if not citation.properties then + citation.properties = {} + end + if not citation.properties.noteIndex then + citation.properties.noteIndex = 0 + end + + -- Registor citation + self.registry.citations_by_id[citation.citationID] = citation + + table.insert(self.registry.citation_list, citation) + + local citation_note_pairs = {} + for _, citation_ in ipairs(self.registry.citation_list) do + table.insert(citation_note_pairs, {citation_.citationID, citation_.properties.noteIndex}) + end + + -- update self.registry.citations_by_item_id + for _, cite_item in ipairs(citation.citationItems) do + if not self.registry.citations_by_item_id[cite_item.id] then + self.registry.citations_by_item_id[cite_item.id] = {} + end + self.registry.citations_by_item_id[cite_item.id][citation.citationID] = true + end + + -- self:updateItems(item_ids) + for i, cite_item in ipairs(citation.citationItems) do + cite_item.id = tostring(cite_item.id) + self:get_item(cite_item.id) + end + + local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs) + local citation_str = self:build_citation_str(citation) + + return citation_str +end + + +function CiteProc:get_tainted_citaion_ids(citation_note_pairs) + local tainted_citation_ids = {} + + self.cite_first_note_numbers = {} + self.cite_last_note_numbers = {} + self.note_citations_map = {} + -- { + -- 1 = {"citation-1", "citation-2"}, + -- 2 = {"citation-2"}, + -- } + + local previous_citation + for citation_index, pair in ipairs(citation_note_pairs) do + local citation_id, note_number = table.unpack(pair) + -- util.debug(citation_id) + local citation = self.registry.citations_by_id[citation_id] + citation.properties.noteIndex = note_number + citation.citation_index = citation_index + + local tainted = false + + local previous_cite + for _, cite_item in ipairs(citation.citationItems) do + tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation) + self.cite_last_note_numbers[cite_item.id] = note_number + previous_cite = cite_item + end + + if tainted then + tainted_citation_ids[citation.citationID] = true + end + + if not self.note_citations_map[note_number] then + self.note_citations_map[note_number] = {} + end + table.insert(self.note_citations_map[note_number], citation.citationID) + previous_citation = citation + end + + -- Update tainted citation ids because of citation-number's change + -- The self.tainted_item_ids were added in the sort_bibliography() procedure. + for item_id, _ in pairs(self.tainted_item_ids) do + if self.registry.citations_by_item_id[item_id] then + for citation_id, _ in pairs(self.registry.citations_by_item_id[item_id]) do + tainted_citation_ids[citation_id] = true + end + end + end + + return tainted_citation_ids +end + +function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation) + local position = util.position_map["first"] + + local first_reference_note_number = self.cite_first_note_numbers[cite_item.id] + if first_reference_note_number then + position = util.position_map["subsequent"] + else + self.cite_first_note_numbers[cite_item.id] = note_number + end + + local preceding_cite_item = self:get_preceding_cite_item(cite_item, previous_cite, previous_citation, note_number) + if preceding_cite_item then + position = self:_get_cite_position(cite_item, preceding_cite_item) + end + + local near_note = false + local last_note_number = self.cite_last_note_numbers[cite_item.id] + if last_note_number then + local note_distance = note_number - last_note_number + if note_distance <= self.style.citation.near_note_distance then + near_note = true + end + end + + local tainted = false + if cite_item.position_level ~= position then + tainted = true + cite_item.position_level = position + end + if cite_item["first-reference-note-number"] ~= first_reference_note_number then + tainted = true + cite_item["first-reference-note-number"] = first_reference_note_number + end + if cite_item.near_note ~= near_note then + tainted = true + cite_item.near_note = near_note + end + return tainted +end + +-- Find the preceding cite referencing the same item +function CiteProc:get_preceding_cite_item(cite_item, previous_cite, previous_citation, note_number) + if previous_cite then + -- a. the current cite immediately follows on another cite, within the same + -- citation, that references the same item + if cite_item.id == previous_cite.id then + return previous_cite + end + elseif previous_citation then + -- (hidden) The previous citation is the only one in the previous note. + -- See also + -- https://github.com/citation-style-language/documentation/issues/121 + -- position_IbidWithMultipleSoloCitesInBackref.txt + -- b. the current cite is the first cite in the citation, and the previous + -- citation consists of a single cite referencing the same item + local previous_note_number = previous_citation.properties.noteIndex + local num_previous_note_citations = #self.note_citations_map[previous_note_number] + if (previous_note_number == note_number - 1 and num_previous_note_citations == 1) + or previous_note_number == note_number then + if #previous_citation.citationItems == 1 then + previous_cite = previous_citation.citationItems[1] + if previous_cite.id == cite_item.id then + return previous_cite + end + end + end + end + return nil +end + +function CiteProc:_get_cite_position(item, preceding_cite) + if preceding_cite.locator then + if item.locator then + if item.locator == preceding_cite.locator and item.label == preceding_cite.label then + return util.position_map["ibid"] + else + return util.position_map["ibid-with-locator"] + end + else + return util.position_map["subsequent"] + end + else + if item.locator then + return util.position_map["ibid-with-locator"] + else + return util.position_map["ibid"] + end + end +end + +function CiteProc:build_citation_str(citation) + -- util.debug(citation.citationID) + local items = {} + for i, cite_item in ipairs(citation.citationItems) do + cite_item.id = tostring(cite_item.id) + -- util.debug(cite_item.id) + + -- Use "page" as locator label if missing + -- label_PluralWithAmpersand.txt + if cite_item.locator and not cite_item.label then + cite_item.label = "page" + end + + table.insert(items, cite_item) + end + + if self.registry.requires_sorting then + self:sort_bibliography() + end + + local citation_str = self:build_cluster(items) + return citation_str +end + +function CiteProc:build_cluster(citation_items) + local output_format = self.output_format + local irs = {} + citation_items = self:sorted_citation_items(citation_items) + for _, cite_item in ipairs(citation_items) do + local ir = self:build_fully_disambiguated_ir(cite_item, output_format) + table.insert(irs, ir) + end + + if self.style.citation.cite_grouping then + irs = self:group_cites(irs) + else + local citation_collapse = self.style.citation.collapse + if citation_collapse == "year" or citation_collapse == "year-suffix" or + citation_collapse == "year-suffix-ranged" then + irs = self:group_cites(irs) + end + end + + if self.style.citation.collapse then + self:collapse_cites(irs) + end + + -- Capitalize first + for i, ir in ipairs(irs) do + -- local layout_prefix + -- local layout_affixes = self.style.citation.layout.affixes + -- if layout_affixes then + -- layout_prefix = layout_affixes.prefix + -- end + local prefix = citation_items[i].prefix + if prefix then + if prefix and string.match(prefix, "[.!?]%s*$") and #util.split(util.strip(prefix)) > 1 then + ir:capitalize_first_term() + end + else + local delimiter = self.style.citation.layout.delimiter + if i == 1 or not delimiter or string.match(delimiter, "[.!?]%s*$") then + ir:capitalize_first_term() + end + end + end + + -- util.debug(irs) + + local citation_delimiter = self.style.citation.layout.delimiter + local citation_stream = {} + + local context = Context:new() + context.engine = self + context.style = self.style + context.area = self.style.citation + context.in_bibliography = false + context.locale = self:get_locale(self.lang) + context.name_inheritance = self.style.citation.name_inheritance + context.format = output_format + + local previous_ir + for i, ir in ipairs(irs) do + local cite_prefix = citation_items[i].prefix + local cite_suffix = citation_items[i].suffix + if not ir.collapse_suppressed then + local ir_inlines = ir:flatten(output_format) + if #ir_inlines > 0 then + -- Make sure ir_inlines has outputs contents. + -- collapse_AuthorCollapseNoDateSorted.txt + if previous_ir then + if previous_ir.own_delimiter then + table.insert(citation_stream, PlainText:new(previous_ir.own_delimiter)) + elseif citation_delimiter and not (cite_prefix and util.startswith(cite_prefix, ",")) then + table.insert(citation_stream, PlainText:new(citation_delimiter)) + end + end + + if cite_prefix then + table.insert(citation_stream, Micro:new(InlineElement:parse(cite_prefix, context))) + end + + -- util.debug(ir) + util.extend(citation_stream, ir_inlines) + previous_ir = ir + + if cite_suffix then + table.insert(citation_stream, Micro:new(InlineElement:parse(cite_suffix, context))) + end + end + end + end + -- util.debug(citation_stream) + + if context.area.layout.affixes then + local affixes = context.area.layout.affixes + if affixes.prefix then + table.insert(citation_stream, 1, PlainText:new(affixes.prefix)) + end + if affixes.suffix then + table.insert(citation_stream, PlainText:new(affixes.suffix)) + end + end + -- util.debug(citation_stream) + + if #citation_stream > 0 and context.area.layout.formatting then + citation_stream = {Formatted:new(citation_stream, context.area.layout.formatting)} + end + + if #citation_stream == 0 then + citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")} + end + + -- util.debug(citation_stream) + local str = output_format:output(citation_stream, context) + str = util.strip(str) + + return str +end + +function CiteProc:sorted_citation_items(items) + local citation_sort = self.style.citation.sort + if not citation_sort then + return items + end + + local state = IrState:new() + local context = Context:new() + context.engine = self + context.style = self.style + context.area = self.style.citation + context.in_bibliography = false + context.locale = self:get_locale(self.lang) + context.name_inheritance = self.style.citation.name_inheritance + context.format = SortStringFormat:new() + -- context.id = id + context.cite = nil + -- context.reference = self:get_item(id) + + items = citation_sort:sort(items, state, context) + return items +end + +function CiteProc:build_fully_disambiguated_ir(cite_item, output_format) + local cite_ir = self:build_ambiguous_ir(cite_item, output_format) + -- util.debug(cite_ir) + cite_ir = self:disambiguate_add_givenname(cite_ir) + cite_ir = self:disambiguate_add_names(cite_ir) + cite_ir = self:disambiguate_conditionals(cite_ir) + cite_ir = self:disambiguate_add_year_suffix(cite_ir) + return cite_ir +end + +function CiteProc:build_ambiguous_ir(cite_item, output_format) + local state = IrState:new(self.style) + cite_item.id = tostring(cite_item.id) + local context = Context:new() + context.engine = self + context.style = self.style + context.area = self.style.citation + context.locale = self:get_locale(self.lang) + context.name_inheritance = self.style.citation.name_inheritance + context.format = output_format + context.id = cite_item.id + context.cite = cite_item + -- context.reference = self:get_item(cite_item.id) + context.reference = self.registry.registry[cite_item.id] + + local ir + if context.reference then + ir = self.style.citation:build_ir(self, state, context) + else + -- The warning has been given in the retrieving procedure. + -- util.warning(string.format('Failed to find the entry "%s" in database.', cite_item.id)) + ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self) + end + + ir.cite_item = cite_item + ir.reference = context.reference + ir.ir_index = #self.disam_irs + 1 + table.insert(self.disam_irs, ir) + ir.is_ambiguous = false + ir.disam_level = 0 + + -- Formattings like font-style are ignored for disambiguation. + local disam_format = DisamStringFormat:new() + local inlines = ir:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + ir.disam_str = disam_str + + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + + for ir_index, ir_ in pairs(self.cite_irs_by_output[disam_str]) do + if ir_.cite_item.id ~= cite_item.id then + ir.is_ambiguous = true + break + end + end + self.cite_irs_by_output[disam_str][ir.ir_index] = ir + + return ir +end + +function CiteProc:disambiguate_add_givenname(cite_ir) + if self.style.citation.disambiguate_add_givenname then + -- util.debug("disambiguate_add_givenname: " .. cite_ir.cite_item.id) + + local gn_disam_rule = self.style.citation.givenname_disambiguation_rule + if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then + cite_ir = self:disambiguate_add_givenname_all_names(cite_ir) + elseif gn_disam_rule == "primary-name" or gn_disam_rule == "primary-name-with-initials" then + cite_ir = self:disambiguate_add_givenname_primary_name(cite_ir) + elseif gn_disam_rule == "by-cite" then + cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir) + end + end + return cite_ir +end + +-- TODO: reorganize this code +function CiteProc:disambiguate_add_givenname_all_names(cite_ir) + -- util.debug("disambiguate_add_givenname_all_names: " .. cite_ir.cite_item.id) + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + + -- util.debug(cite_ir.disam_str) + + for _, person_name_ir in ipairs(cite_ir.person_name_irs) do + local name_output = person_name_ir.name_output + -- util.debug(name_output) + + if not person_name_ir.person_name_index then + person_name_ir.person_name_index = #self.person_names + 1 + table.insert(self.person_names, person_name_ir) + end + + if not self.person_names_by_output[name_output] then + self.person_names_by_output[name_output] = {} + end + self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir + + local ambiguous_name_irs = {} + local ambiguous_same_output_irs = {} + + for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + -- util.debug(person_name_ir.name_output) + -- util.debug(person_name_ir.full_name) + -- util.debug(#ambiguous_name_irs) + -- util.debug(person_name_ir.disam_variants_index) + -- util.debug(person_name_ir.disam_variants) + + while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do + if #ambiguous_name_irs == 0 then + break + end + + for _, pn_ir in ipairs(ambiguous_same_output_irs) do + -- expand one name + if pn_ir.disam_variants_index < #pn_ir.disam_variants then + pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 + pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] + pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] + + if not self.person_names_by_output[pn_ir.name_output] then + self.person_names_by_output[pn_ir.name_output] = {} + end + self.person_names_by_output[pn_ir.name_output][pn_ir.person_name_index] = pn_ir + end + end + + -- util.debug(person_name_ir.name_output) + + -- update ambiguous_name_irs and ambiguous_same_output_irs + ambiguous_name_irs = {} + ambiguous_same_output_irs = {} + for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + -- util.debug(pn_ir.full_name .. ": " .. pn_ir.name_output) + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + end + end + + -- update cite_ir output + local disam_format = DisamStringFormat:new() + local inlines = cite_ir:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + cite_ir.disam_str = disam_str + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + self.cite_irs_by_output[disam_str][cite_ir.ir_index] = cite_ir + + -- update ambiguous_cite_irs and ambiguous_same_output_irs + local ambiguous_cite_irs = {} + for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + -- util.debug(ir_.cite_item.id) + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + end + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + end + + return cite_ir +end + +function CiteProc:disambiguate_add_givenname_primary_name(cite_ir) + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + local person_name_ir = cite_ir.person_name_irs[1] + local name_output = person_name_ir.name_output + -- util.debug(name_output) + + if not person_name_ir.person_name_index then + person_name_ir.person_name_index = #self.person_names + 1 + table.insert(self.person_names, person_name_ir) + end + if not self.person_names_by_output[name_output] then + self.person_names_by_output[name_output] = {} + end + self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir + + local ambiguous_name_irs = {} + local ambiguous_same_output_irs = {} + + for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + for _, name_variant in ipairs(person_name_ir.disam_variants) do + if #ambiguous_name_irs == 0 then + break + end + + for _, pn_ir in ipairs(ambiguous_same_output_irs) do + -- expand one name + if pn_ir.disam_variants_index < #pn_ir.disam_variants then + pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 + pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] + pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] + + if not self.person_names_by_output[pn_ir.name_output] then + self.person_names_by_output[pn_ir.name_output] = {} + end + self.person_names_by_output[pn_ir.name_output][person_name_ir.person_name_index] = person_name_ir + end + end + + -- update ambiguous_name_irs and ambiguous_same_output_irs + ambiguous_name_irs = {} + ambiguous_same_output_irs = {} + for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + end + + return cite_ir +end + +function CiteProc:disambiguate_add_givenname_by_cite(cite_ir) + if not cite_ir.is_ambiguous then + return cite_ir + end + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + + for _, ir_ in ipairs(self.disam_irs) do + -- util.debug(ir_.cite_item.id) + -- util.debug(ir_.disam_str) + end + + local disam_format = DisamStringFormat:new() + + local ambiguous_cite_irs = {} + local ambiguous_same_output_irs = {} + + for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + for i, person_name_ir in ipairs(cite_ir.person_name_irs) do + -- util.debug(person_name_ir.name_output) + -- util.debug(person_name_ir.disam_variants) + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + break + end + + -- util.debug(person_name_ir.disam_variants) + while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do + -- util.debug(person_name_ir.name_output) + + local is_different_name = false + for _, ir_ in ipairs(ambiguous_cite_irs) do + if ir_.person_name_irs[i] then + if ir_.person_name_irs[i].full_name ~= person_name_ir.full_name then + -- util.debug(ir_.cite_item.id) + is_different_name = true + break + end + end + end + -- util.debug(is_different_name) + if not is_different_name then + break + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + -- util.debug(ir_.cite_item.id) + local person_name_ir_ = ir_.person_name_irs[i] + if person_name_ir_ then + if person_name_ir_.disam_variants_index < #person_name_ir_.disam_variants then + person_name_ir_.disam_variants_index = person_name_ir_.disam_variants_index + 1 + local disam_variant = person_name_ir_.disam_variants[person_name_ir_.disam_variants_index] + person_name_ir_.name_output = disam_variant + -- util.debug(disam_variant) + person_name_ir_.inlines = person_name_ir_.disam_inlines[disam_variant] + -- Update cite ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + ir_.disam_str = disam_str + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + end + + -- update ambiguous_cite_irs and ambiguous_same_output_irs + ambiguous_cite_irs = {} + ambiguous_same_output_irs = {} + for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + -- util.debug(ir_.cite_item.id) + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + -- util.debug(#ambiguous_cite_irs) + + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + return cite_ir + end + + end + end + + return cite_ir +end + +local function find_first_name_ir(ir) + if ir._type == "NameIr" then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + local name_ir = find_first_name_ir(child) + if name_ir then + return name_ir + end + end + end + return nil +end + +function CiteProc:disambiguate_add_names(cite_ir) + if not self.style.citation.disambiguate_add_names then + return cite_ir + end + + if not cite_ir.name_ir then + cite_ir.name_ir = find_first_name_ir(cite_ir) + end + local name_ir = cite_ir.name_ir + + if not cite_ir.is_ambiguous then + return cite_ir + end + + if not name_ir or not name_ir.et_al_abbreviation then + return cite_ir + end + + -- util.debug("disambiguate_add_names: " .. cite_ir.cite_item.id) + + if name_ir then + -- util.debug(cite_ir.disam_str) + -- util.debug(cite_ir.name_ir.full_name_str) + -- util.debug(cite_ir.is_ambiguous) + end + + local disam_format = DisamStringFormat:new() + + while cite_ir.is_ambiguous do + if #cite_ir.name_ir.hidden_name_irs == 0 then + break + end + + local ambiguous_cite_irs = {} + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + -- util.debug(#ambiguous_same_output_irs) + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + break + end + + -- check if the cite can be (fully) disambiguated by adding names + local can_be_disambuguated = false + for _, ir_ in ipairs(ambiguous_cite_irs) do + if ir_.name_ir.full_name_str ~= cite_ir.name_ir.full_name_str then + can_be_disambuguated = true + break + end + end + -- util.debug(can_be_disambuguated) + if not can_be_disambuguated then + break + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + local added_person_name_ir = ir_.name_ir.name_inheritance:expand_one_name(ir_.name_ir) + if added_person_name_ir then + -- util.debug("Updated: " .. ir_.cite_item.id) + table.insert(ir_.person_name_irs, added_person_name_ir) + + if not added_person_name_ir.person_name_index then + added_person_name_ir.person_name_index = #self.person_names + 1 + table.insert(self.person_names, added_person_name_ir) + end + local name_output = added_person_name_ir.name_output + if not self.person_names_by_output[name_output] then + self.person_names_by_output[name_output] = {} + end + self.person_names_by_output[name_output][added_person_name_ir.person_name_index] = added_person_name_ir + + -- Update ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + -- util.debug(disam_str) + ir_.disam_str = disam_str + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + + -- util.debug("disambiguate_add_givenname") + + if self.style.citation.disambiguate_add_givenname then + local gn_disam_rule = self.style.citation.givenname_disambiguation_rule + if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then + cite_ir = self:disambiguate_add_givenname_all_names(cite_ir) + elseif gn_disam_rule == "by-cite" then + cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir) + end + end + + cite_ir.is_ambiguous = self:check_ambiguity(cite_ir) + + for _, ir_ in ipairs(self.disam_irs) do + -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) + end + + end + + + return cite_ir +end - if type(style) == "string" then - o.csl = dom.parse(style) - else - o.csl = style +function CiteProc:collect_irs_with_disambiguate_branch(ir) + local irs_with_disambiguate_branch = {} + if ir.children then + for i, child_ir in ipairs(ir.children) do + if child_ir.disambiguate_branch_ir then + table.insert(irs_with_disambiguate_branch, child_ir) + elseif child_ir.children then + util.extend(irs_with_disambiguate_branch, + self:collect_irs_with_disambiguate_branch(child_ir)) + end + end end - o.csl:traverse_elements(CiteProc.set_base_class) - o.csl:root_node().engine = o - o.style = o.csl:get_path("style")[1] - o.style.lang = lang - o.csl:root_node().style = o.style + return irs_with_disambiguate_branch +end - o.style:set_lang(lang, force_lang) +function CiteProc:disambiguate_conditionals(cite_ir) + -- util.debug(cite_ir) - o.formatter = formats.latex - o.linking_enabled = false + cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir) - setmetatable(o, { __index = CiteProc }) - return o + local disam_format = DisamStringFormat:new() + + while cite_ir.is_ambiguous do + if #cite_ir.irs_with_disambiguate_branch == 0 then + break + end + + -- util.debug(cite_ir.cite_item.id) + -- util.debug(cite_ir.disam_str) + + -- update ambiguous_same_output_irs + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + if #ir_.irs_with_disambiguate_branch > 0 then + -- Disambiguation is incremental + -- disambiguate_IncrementalExtraText.txt + local condition_ir = ir_.irs_with_disambiguate_branch[1] + condition_ir.children[1] = condition_ir.disambiguate_branch_ir + condition_ir.group_var = condition_ir.disambiguate_branch_ir.group_var + table.remove(ir_.irs_with_disambiguate_branch, 1) + -- disambiguate_DisambiguateTrueReflectedInBibliography.txt + ir_.reference.disambiguate = true + + -- Update ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) + ir_.disam_str = disam_str + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + + cite_ir.is_ambiguous = self:check_ambiguity(cite_ir) + -- util.debug(cite_ir.is_ambiguous) + for _, ir_ in ipairs(self.disam_irs) do + -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) + end + + end + return cite_ir end -function CiteProc:updateItems (ids) - self.registry.reflist = {} - self.registry.registry = {} - for _, id in ipairs(ids) do - self:get_item(id) +function CiteProc:check_ambiguity(cite_ir) + for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + return true + end end + return false end -function CiteProc:updateUncitedItems(ids) - for _, id in ipairs(ids) do - if not self.registry.registry[id] then - self:get_item(id) +function CiteProc:get_same_output_irs(cite_ir) + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) end end - -- TODO: disambiguation + return ambiguous_same_output_irs end -function CiteProc:processCitationCluster(citation, citationsPre, citationsPost) - -- citation = { - -- citationID = "CITATION-3", - -- citationItems = { - -- { id = "ITEM-1" }, - -- { id = "ITEM-2" }, - -- }, - -- properties = { - -- noteIndex = 3, - -- }, - -- } - -- citationsPre = { - -- {"CITATION-1", 1}, - -- {"CITATION-2", 2}, - -- } - -- citationsPost = { - -- {"CITATION-4", 4}, - -- } - -- returns = { - -- { - -- bibchange = true, - -- citation_errors = {}, - -- }, - -- { - -- { 2, "[1,2]", "CITATION-3" } - -- } - -- } - self.registry.citations[citation.citationID] = citation +function CiteProc:disambiguate_add_year_suffix(cite_ir) + if not cite_ir.is_ambiguous or not self.style.citation.disambiguate_add_year_suffix then + return cite_ir + end - local items = {} + local same_output_irs = self:get_same_output_irs(cite_ir) - for _, cite_item in ipairs(citation.citationItems) do - cite_item.id = tostring(cite_item.id) - local position_first = (self.registry.registry[cite_item.id] == nil) - local item_data = self:get_item(cite_item.id) + table.sort(same_output_irs, function (a, b) + -- return a.ir_index < b.ir_index + return a.reference["citation-number"] < b.reference["citation-number"] + end) - if item_data then - -- Create a wrapper of the orignal item from registry so that - -- it may hold different `locator` or `position` values for cites. - local item = setmetatable({}, {__index = function (_, key) - if cite_item[key] then - return cite_item[key] - else - return item_data[key] + local year_suffix_number = 0 + -- util.debug(cite_ir) + + local disam_format = DisamStringFormat:new() + + for _, ir_ in ipairs(same_output_irs) do + ir_.reference.year_suffix_number = nil + end + + for _, ir_ in ipairs(same_output_irs) do + -- print(ir_.cite_item.id) + -- print(ir_.reference) + if not ir_.reference.year_suffix_number then + year_suffix_number = year_suffix_number + 1 + ir_.reference.year_suffix_number = year_suffix_number + ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number) + end + + if not ir_.year_suffix_irs then + ir_.year_suffix_irs = self:collect_year_suffix_irs(ir_) + if #ir_.year_suffix_irs == 0 then + -- By default, the year-suffix is appended the first year rendered through cs:date + local year_ir = self:find_first_year_ir(ir_) + -- util.debug(year_ir) + if year_ir then + local year_suffix_ir = YearSuffix:new({}, self.style.citation) + table.insert(year_ir.children, year_suffix_ir) + table.insert(ir_.year_suffix_irs, year_suffix_ir) end - end}) + end + end + + for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do + year_suffix_ir.inlines = {PlainText:new(ir_.reference["year-suffix"])} + year_suffix_ir.year_suffix_number = ir_.reference.year_suffix_number + year_suffix_ir.group_var = "important" + end - if not item.position and position_first then - item.position = util.position_map["first"] + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) + ir_.disam_str = disam_str + if not self.cite_irs_by_output[disam_str] then + self.cite_irs_by_output[disam_str] = {} + end + self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + + end + + cite_ir.is_ambiguous = false + + return cite_ir +end + +function CiteProc:collect_year_suffix_irs(ir) + local year_suffix_irs = {} + if ir.children then + for i, child_ir in ipairs(ir.children) do + if child_ir._type == "YearSuffix" then + table.insert(year_suffix_irs, child_ir) + elseif child_ir.children then + util.extend(year_suffix_irs, + self:collect_year_suffix_irs(child_ir)) end + end + end + return year_suffix_irs +end - local first_reference_note_number = nil - for _, pre_citation in ipairs(citationsPre) do - pre_citation = self.registry.citations[pre_citation[1]] - for _, pre_cite_item in ipairs(pre_citation.citationItems) do - if pre_cite_item.id == cite_item.id then - first_reference_note_number = pre_citation.properties.noteIndex - end - break - end - if first_reference_note_number then - break - end +function CiteProc:find_first_year_ir(ir) + if ir.is_year then + return ir + end + if ir.children then + for _, child_ir in ipairs(ir.children) do + local year_ir = self:find_first_year_ir(child_ir) + if year_ir then + return year_ir + end + end + end + return nil +end + +function CiteProc:render_year_suffix(year_suffix_number) + if year_suffix_number <= 0 then + return nil + end + local year_suffix = "" + while year_suffix_number > 0 do + local i = (year_suffix_number - 1) % 26 + year_suffix = string.char(i + 97) .. year_suffix + year_suffix_number = (year_suffix_number - 1) // 26 + end + -- util.debug(year_suffix) + return year_suffix +end + +local function find_first(ir, check) + if check(ir) then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + local target_ir = find_first(child, check) + if target_ir then + return target_ir + end + end + end + return nil +end + +function CiteProc:group_cites(irs) + local disam_format = DisamStringFormat:new() + for _, ir in ipairs(irs) do + local first_names_ir = ir.first_names_ir + if not first_names_ir then + first_names_ir = find_first(ir, function (ir_) + return ir_._element == "names" + end) + if first_names_ir then + local inlines = first_names_ir:flatten(disam_format) + first_names_ir.disam_str = disam_format:output(inlines, context) end - item["first-reference-note-number"] = first_reference_note_number + ir.first_names_ir = first_names_ir + end + end - table.insert(items, item) + local irs_by_name = {} + local name_list = {} + + for _, ir in ipairs(irs) do + local name_str = "" + if ir.first_names_ir then + name_str = ir.first_names_ir.disam_str + end + if not irs_by_name[name_str] then + irs_by_name[name_str] = {} + table.insert(name_list, name_str) end + table.insert(irs_by_name[name_str], ir) end - if #citationsPre > 0 then - local previous_citation_id = citationsPre[#citationsPre][1] - local previous_citation = self.registry.citations[previous_citation_id] - self.registry.previous_citation = previous_citation + local grouped = {} + for _, name_str in ipairs(name_list) do + local irs_with_same_name = irs_by_name[name_str] + for i, ir in ipairs(irs_with_same_name) do + if i < #irs_with_same_name then + ir.own_delimiter = self.style.citation.cite_group_delimiter + end + table.insert(grouped, ir) + end end + return grouped +end - if self.registry.requires_sorting then - self:sort_bibliography() +function CiteProc:collapse_cites(irs) + if self.style.citation.collapse == "citation-number" then + self:collapse_cites_by_citation_number(irs) + elseif self.style.citation.collapse == "year" then + self:collapse_cites_by_year(irs) + elseif self.style.citation.collapse == "year-suffix" then + self:collapse_cites_by_year_suffix(irs) + elseif self.style.citation.collapse == "year-suffix-ranged" then + self:collapse_cites_by_year_suffix_ranged(irs) end +end - local params = { - bibchange = false, - citation_errors = {}, - } +function CiteProc:collapse_cites_by_citation_number(irs) + local cite_groups = {} + local current_group = {} + local previous_citation_number + for i, ir in ipairs(irs) do + local citation_number + local only_citation_number_ir = self:get_only_citation_number(ir) + if only_citation_number_ir then + -- Other irs like locators are not rendered. + -- collapse_CitationNumberRangesWithAffixesGrouped.txt + citation_number = only_citation_number_ir.citation_number + end + if i == 1 then + table.insert(current_group, ir) + elseif citation_number and previous_citation_number and + previous_citation_number + 1 == citation_number then + table.insert(current_group, ir) + else + table.insert(cite_groups, current_group) + current_group = {ir} + end + previous_citation_number = citation_number + end + table.insert(cite_groups, current_group) - local citation_id_note_list = {} - for _, citation_id_note in ipairs(citationsPre) do - table.insert(citation_id_note_list, citation_id_note) + for _, cite_group in ipairs(cite_groups) do + if #cite_group >= 3 then + cite_group[1].own_delimiter = util.unicode["en dash"] + for i = 2, #cite_group - 1 do + cite_group[i].collapse_suppressed = true + end + cite_group[#cite_group].own_delimiter = self.style.citation.after_collapse_delimiter + end + end +end + +function CiteProc:get_only_citation_number(ir) + if ir.citation_number then + return ir end - local note_index = 0 - if citation.properties and citation.properties.noteIndex then - note_index = citation.properties.noteIndex + if not ir.children then + return nil end - table.insert(citation_id_note_list, {citation.citationID, note_index}) - for _, citation_id_note in ipairs(citationsPost) do - table.insert(citation_id_note_list, citation_id_note) + local only_citation_number_ir + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" then + local citation_number_ir = self:get_only_citation_number(child) + if citation_number_ir then + if only_citation_number_ir then + return nil + else + only_citation_number_ir = citation_number_ir + end + else + return false + end + end end + return only_citation_number_ir +end - local citation_id_cited = {} - for _, citation_id_note in ipairs(citation_id_note_list) do - citation_id_cited[citation_id_note[1]] = true +function CiteProc:collapse_cites_by_year(irs) + local cite_groups = {{}} + local previous_name_str + for i, ir in ipairs(irs) do + local name_str + if ir.first_names_ir then + name_str = ir.first_names_ir.disam_str + end + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif name_str and name_str == previous_name_str then + -- ir.fist_names_ir was set in the cite grouping stage + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) + else + table.insert(cite_groups, {ir}) + end + previous_name_str = name_str end - for citation_id, _ in pairs(self.registry.citations) do - if not citation_id_cited[citation_id] then - self.registry.citations[citation_id] = nil - self.registry.citation_strings[citation_id] = nil + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 1 then + for i, cite_ir in ipairs(cite_group) do + if i > 1 and cite_ir.first_names_ir then + cite_ir.first_names_ir.collapse_suppressed = true + end + if i == #cite_group then + cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter + elseif i < #cite_group then + -- The delimiter depends on the citation > sort. + -- https://github.com/citation-style-language/test-suite/issues/39#issuecomment-687901688 + if cite_ir.cite_item.locator then + -- Special hack for + cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter + elseif self.style.citation.cite_grouping then + if self.style.citation.sort then + cite_ir.own_delimiter = self.style.citation.cite_group_delimiter + else + cite_ir.own_delimiter = self.style.citation.layout.delimiter + end + else + if self.style.citation.sort then + cite_ir.own_delimiter = self.style.citation.cite_group_delimiter + else + -- disambiguate_YearCollapseWithInstitution.txt + -- disambiguate_InitializeWithButNoDisambiguation.txt ? + cite_ir.own_delimiter = self.style.citation.layout.delimiter + end + end + end + end end end +end - local output = {} +local function find_rendered_year_suffix(ir) + if ir._type == "YearSuffix" then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" then + local year_suffix = find_rendered_year_suffix(child) + if year_suffix then + return year_suffix + end + end + end + end + return nil +end - for i, citation_id_note in ipairs(citation_id_note_list) do - local citation_id = citation_id_note[1] - -- local note_index = citation_id_note[2] - if citation_id == citation.citationID then - local context = { - build = {}, - engine = self, - } - local citation_str = self.style:render_citation(items, context) - - self.registry.citation_strings[citation_id] = citation_str - table.insert(output, {i - 1, citation_str, citation_id}) +function CiteProc:collapse_cites_by_year_suffix(irs) + self:collapse_cites_by_year(irs) + -- Group by disam_str + -- The year-suffix is ommitted in DisamStringFormat + local cite_groups = {{}} + local previous_ir + local previous_year_suffix + for i, ir in ipairs(irs) do + local year_suffix = find_rendered_year_suffix(ir) + ir.rendered_year_suffix_ir = year_suffix + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif year_suffix and previous_ir.disam_str == ir.disam_str and previous_year_suffix then + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) else - -- TODO: correct note_index - -- TODO: update other citations after disambiguation - local citation_str = self.registry.citation_strings[citation_id] - if self.registry.citation_strings[citation_id] ~= citation_str then - params.bibchange = true - self.registry.citation_strings[citation_id] = citation_str - table.insert(output, {i - 1, citation_str, citation_id}) + table.insert(cite_groups, {ir}) + end + previous_ir = ir + previous_year_suffix = year_suffix + end + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 1 then + for i, cite_ir in ipairs(cite_group) do + if i > 1 then + -- cite_ir.children = {cite_ir.rendered_year_suffix_ir} + -- Set the collapse_suppressed flag rather than removing the child irs. + -- This leaves the disamb ir structure unchanged. + self:suppress_ir_except_child(cite_ir, cite_ir.rendered_year_suffix_ir) + end + if i < #cite_group then + if self.style.citation.cite_grouping then + -- In the current citeproc-js impplementation, explicitly set + -- cite-group-delimiter takes precedence over year-suffix-delimiter. + -- May be changed in the future. + -- https://github.com/citation-style-language/test-suite/issues/50 + cite_ir.own_delimiter = self.style.citation.cite_group_delimiter + else + cite_ir.own_delimiter = self.style.citation.year_suffix_delimiter + end + elseif i == #cite_group then + cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter + end end end end +end - return {params, output} +function CiteProc:suppress_ir_except_child(ir, target) + if ir == target then + ir.collapse_suppressed = false + return false + end + ir.collapse_suppressed = true + if ir.children then + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" and not child.collapse_suppressed then + if not self:suppress_ir_except_child(child, target) then + ir.collapse_suppressed = false + end + end + end + end + return ir.collapse_suppressed +end + +function CiteProc:collapse_cites_by_year_suffix_ranged(irs) + self:collapse_cites_by_year_suffix(irs) + -- Group by disam_str + local cite_groups = {{}} + local previous_ir + local previous_year_suffix + for i, ir in ipairs(irs) do + local year_suffix_ir = find_rendered_year_suffix(ir) + ir.rendered_year_suffix_ir = year_suffix_ir + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif year_suffix_ir and previous_ir.disam_str == ir.disam_str and previous_year_suffix and + year_suffix_ir.year_suffix_number == previous_year_suffix.year_suffix_number + 1 then + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) + else + table.insert(cite_groups, {ir}) + end + previous_ir = ir + previous_year_suffix = year_suffix_ir + end + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 2 then + for i, cite_ir in ipairs(cite_group) do + if i == 1 then + cite_ir.own_delimiter = util.unicode["en dash"] + elseif i < #cite_group then + cite_ir.collapse_suppressed = true + end + end + end + end end -function CiteProc:makeCitationCluster (citation_items) +function CiteProc:makeCitationCluster(citation_items) local items = {} - for _, cite_item in ipairs(citation_items) do + for i, cite_item in ipairs(citation_items) do cite_item.id = tostring(cite_item.id) - local position_first = (self.registry.registry[cite_item.id] == nil) local item_data = self:get_item(cite_item.id) -- Create a wrapper of the orignal item from registry so that -- it may hold different `locator` or `position` values for cites. - local item = setmetatable({}, {__index = function (_, key) - if cite_item[key] then - return cite_item[key] - else - return item_data[key] + local item = setmetatable(cite_item, {__index = item_data}) + + -- Use "page" as locator label if missing + -- label_PluralWithAmpersand.txt + if item.locator and not item.label then + item.label = "page" + end + + item.position_level = util.position_map["first"] + if self.cite_first_note_numbers[cite_item.id] then + item.position_level = util.position_map["subsequent"] + else + self.cite_first_note_numbers[cite_item.id] = 0 + end + + local preceding_cite + if i == 1 then + local previous_citation = self.registry.previous_citation + if previous_citation then + if #previous_citation.citationItems == 1 and previous_citation.citationItems[1].id == item.id then + preceding_cite = previous_citation.citationItems[1] + end end - end}) + elseif citation_items[i - 1].id == item.id then + preceding_cite = citation_items[i - 1] + end - if not item.position and position_first then - item.position = util.position_map["first"] + if preceding_cite then + item.position_level = self:_get_cite_position(item, preceding_cite) end + table.insert(items, item) end @@ -242,134 +1620,252 @@ function CiteProc:makeCitationCluster (citation_items) self:sort_bibliography() end - local context = { - build = {}, - engine=self, - } - local res = self.style:render_citation(items, context) + local res = self:build_cluster(items) + + -- local context = { + -- build = {}, + -- engine=self, + -- } + -- local res = self.style:render_citation(items, context) + self.registry.previous_citation = { citationID = "pseudo-citation", citationItems = items, properties = { - noteIndex = 1, + noteIndex = 0, } } return res end function CiteProc:makeBibliography() - local items = {} + if not self.style.bibliography then + return {{}, {}} + end + + local output_format = self.output_format + + local res = {} + + self.registry.longest_label = "" + self.registry.maxoffset = 0 + + for _, id in ipairs(self:get_sorted_refs()) do + local ref = self.registry.registry[id] + + local state = IrState:new() + local context = Context:new() + context.engine = self + context.style = self.style + context.area = self.style.bibliography + context.in_bibliography = true + context.locale = self:get_locale(self.lang) + context.name_inheritance = self.style.bibliography.name_inheritance + context.format = output_format + context.id = id + context.cite = nil + context.reference = self:get_item(id) + + local ir = self.style.bibliography:build_ir(self, state, context) + -- util.debug(ir) + ir.reference = context.reference + + -- Add year-suffix + self:add_bibliography_year_suffix(ir) + + -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt + if ir then + local flat = ir:flatten(output_format) + local str = output_format:output_bibliography_entry(flat, context) + table.insert(res, str) + end + end + + local bib_start = self.output_format.markups["bibstart"] + local bib_end = self.output_format.markups["bibend"] + if type(bib_start) == "function" then + bib_start = bib_start(self) + end + if type(bib_end) == "function" then + bib_end = bib_end(self) + end + + local params = { + hangingindent = self.style.bibliography.hanging_indent, + ["second-field-align"] = self.style.bibliography.second_field_align ~= nil, + linespacing = self.style.bibliography.line_spacing, + entryspacing = self.style.bibliography.entry_spacing, + maxoffset = self.registry.maxoffset, + bibstart = bib_start, + bibend = bib_end, + entry_ids = util.clone(self.registry.reflist), + } + + return {params, res} +end +function CiteProc:get_sorted_refs() if self.registry.requires_sorting then self:sort_bibliography() end + return self.registry.reflist +end - for _, id in ipairs(self.registry.reflist) do - local item = self.registry.registry[id] - table.insert(items, item) +function CiteProc:add_bibliography_year_suffix(ir) + if not ir.reference.year_suffix_number then + return end - local context = { - build = {}, - engine=self, - } - local res = self.style:render_biblography(items, context) - return res + local year_suffix_number = ir.reference.year_suffix_number + + if not ir.year_suffix_irs then + ir.year_suffix_irs = self:collect_year_suffix_irs(ir) + if #ir.year_suffix_irs == 0 then + local year_ir = self:find_first_year_ir(ir) + -- util.debug(year_ir) + if year_ir then + local year_suffix_ir = YearSuffix:new({}, self.style.citation) + table.insert(year_ir.children, year_suffix_ir) + table.insert(ir.year_suffix_irs, year_suffix_ir) + end + end + end + + for _, year_suffix_ir in ipairs(ir.year_suffix_irs) do + year_suffix_ir.inlines = {PlainText:new(ir.reference["year-suffix"])} + year_suffix_ir.group_var = "important" + end + + end -function CiteProc:set_formatter(format) - self.formatter = formats[format] +function CiteProc:set_output_format(format) + if format == "latex" then + self.output_format = LatexWriter:new() + elseif format == "html" then + self.output_format = HtmlWriter:new() + end end function CiteProc:enable_linking() - self.linking_enabled = true + self.opt.url_link = true + self.opt.doi_link = true end function CiteProc:disable_linking() - self.linking_enabled = false + self.opt.url_link = false + self.opt.doi_link = false end -function CiteProc.set_base_class (node) - if node:is_element() then - local name = node:get_element_name() - local element_class = nodes[name] - if element_class then - element_class:set_base_class(node) - else - element.Element:set_base_class(node) +function CiteProc.create_element_tree(node) + local element_name = node:get_element_name() + local element_class = nodes[element_name] + local el = nil + if element_class then + el = element_class:from_node(node) + end + if el then + for i, child in ipairs(node:get_children()) do + if child:is_element() then + local child_element = CiteProc.create_element_tree(child) + if child_element then + if not el.children then + el.children = {} + end + table.insert(el.children, child_element) + end + end end end + return el end -function CiteProc:get_style_class() - return self.style:get_attribute("class") or "in-text" -end - -function CiteProc:get_item (id) +function CiteProc:get_item(id) local item = self.registry.registry[id] if not item then item = self:_retrieve_item(id) if not item then return nil end + item = self:process_extra_note(item) table.insert(self.registry.reflist, id) item["citation-number"] = #self.registry.reflist self.registry.registry[id] = item self.registry.requires_sorting = true end - local res = {} - setmetatable(res, {__index = item}) - return res + -- local res = {} + -- setmetatable(res, {__index = item}) + -- return res + return item end -function CiteProc:_retrieve_item (id) +function CiteProc:_retrieve_item(id) -- Retrieve, copy, and normalize local res = {} local item = self.sys.retrieveItem(id) if not item then - util.warning(string.format('Failed to retrieve item "%s"', id)) + util.warning(string.format('Failed to find entry "%s"', id)) return nil end item.id = tostring(item.id) for key, value in pairs(item) do - if key == "title" then - value = self.normalize_string(value) - end res[key] = value end - if res["page"] and not res["page-first"] then - local page_first = util.split(res["page"], "%s*[&,-]%s*")[1] - page_first = util.split(page_first, util.unicode["en dash"])[1] - res["page-first"] = page_first - end + -- if res["page"] and not res["page-first"] then + -- local page_first = util.split(res["page"], "%s*[&,-]%s*")[1] + -- page_first = util.split(page_first, util.unicode["en dash"])[1] + -- res["page-first"] = page_first + -- end return res end -function CiteProc.normalize_string (str) - if not str or str == "" then - return str - end - -- French punctuation spacing - if type(str) == "string" then - str = string.gsub(str, " ;", util.unicode["narrow no-break space"] .. ";") - str = string.gsub(str, " %?", util.unicode["narrow no-break space"] .. "?") - str = string.gsub(str, " !", util.unicode["narrow no-break space"] .. "!") - str = string.gsub(str, " »", util.unicode["narrow no-break space"] .. "»") - str = string.gsub(str, "« ", "«" .. util.unicode["narrow no-break space"]) +-- TODO: Nomalize all inputs +function CiteProc:process_extra_note(item) + if item.note then + local note_fields = {} + for _, line in ipairs(util.split(item.note, "%s*\r?\n%s*")) do + -- util.debug(line) + local splits = util.split(line, ":%s+", 1) + -- util.debug(splits) + if #splits == 2 then + local field, value = table.unpack(splits) + -- util.debug(field) + + local variable_type = util.variable_types[field] + if not item[field] or field == "type" or variable_type == "date" then + if variable_type == "number" then + item[field] = value + elseif variable_type == "date" then + item[field] = util.parse_iso_date(value) + elseif variable_type == "name" then + if not note_fields[field] then + note_fields[field] = {} + end + table.insert(note_fields[field], util.parse_extra_name(value)) + else + item[field] = value + end + end + end + end + for field, value in pairs(note_fields) do + item[field] = value + end end - -- local text = str - local text = richtext.new(str) - return text + return item end function CiteProc:sort_bibliography() -- Sort the items in registry according to the `sort` in `bibliography.` -- This will update the `citation-number` of each item. - local bibliography_sort = self.style:get_path("style bibliography sort")[1] + local bibliography_sort = nil + if self.style.bibliography and self.style.bibliography.sort then + bibliography_sort = self.style.bibliography.sort + end if not bibliography_sort then return end @@ -378,44 +1874,119 @@ function CiteProc:sort_bibliography() table.insert(items, self.registry.registry[id]) end - local context = { - engine = self, - style = self.style, - mode = "bibliography", - } - context = self.style:process_context(context) - context = self.style:get_path("style bibliography")[1]:process_context(context) + local state = IrState:new() + local context = Context:new() + context.engine = self + context.style = self.style + context.area = self.style.bibliography + context.in_bibliography = true + context.locale = self:get_locale(self.lang) + context.name_inheritance = self.style.bibliography.name_inheritance + context.format = SortStringFormat:new() + -- context.id = id + context.cite = nil + -- context.reference = self:get_item(id) - bibliography_sort:sort(items, context) + bibliography_sort:sort(items, state, context) self.registry.reflist = {} + self.tainted_item_ids = {} for i, item in ipairs(items) do + if item["citation-number"] ~= i then + self.tainted_item_ids[item.id] = true + end item["citation-number"] = i - table.insert(self.registry.reflist, item.id) + self.registry.reflist[i] = item.id end self.registry.requires_sorting = false end -function CiteProc:get_system_locale (lang) +function CiteProc:get_locale(lang) + if string.len(lang) == 2 then + lang = util.primary_dialects[lang] or lang + end + local locale = self.locales[lang] + if locale then + return locale + else + return self:get_merged_locales(lang) + end +end + +function CiteProc:get_merged_locales(lang) + local fall_back_locales = {} + + local language = string.sub(lang, 1, 2) + local primary_dialect = util.primary_dialects[language] + + -- 1. In-style cs:locale elements + -- i. `xml:lang` set to chosen dialect, “de-AT” + table.insert(fall_back_locales, self.style.locales[lang]) + + -- ii. `xml:lang` set to matching language, “de” (German) + if language and language ~= lang then + table.insert(fall_back_locales, self.style.locales[language]) + end + + -- iii. `xml:lang` not set + table.insert(fall_back_locales, self.style.locales["@generic"]) + + -- 2. Locale files + -- iv. `xml:lang` set to chosen dialect, “de-AT” + if lang then + table.insert(fall_back_locales, self:get_system_locale(lang)) + end + + -- v. `xml:lang` set to matching primary dialect, “de-DE” (Standard German) + -- (only applicable when the chosen locale is a secondary dialect) + if primary_dialect and primary_dialect ~= lang then + table.insert(fall_back_locales, self:get_system_locale(primary_dialect)) + end + + -- vi. `xml:lang` set to “en-US” (American English) + if lang ~= "en-US" and primary_dialect ~= "en-US" then + table.insert(fall_back_locales, self:get_system_locale("en-US")) + end + + -- Merge locales + + local locale = Locale:new() + for i = #fall_back_locales, 1, -1 do + local fall_back_locale = fall_back_locales[i] + locale:merge(fall_back_locale) + end + + self.locales[lang] = locale + return locale +end + +function CiteProc:get_system_locale(lang) local locale = self.system_locales[lang] - if not locale then - locale = self.sys.retrieveLocale(lang) - if not locale then - util.warning(string.format("Failed to retrieve locale \"%s\"", lang)) - return nil - end - if type(locale) == "string" then - locale = dom.parse(locale) - end - locale:traverse_elements(self.set_base_class) - locale = locale:get_path("locale")[1] - locale:root_node().engine = self - locale:root_node().style = self.style - self.system_locales[lang] = locale + if locale then + return locale + end + + local locale_str = self.sys.retrieveLocale(lang) + if not locale_str then + util.warning(string.format("Failed to retrieve locale \"%s\"", lang)) + return nil end + local locale_xml = dom.parse(locale_str) + local root_element = locale_xml:get_path("locale")[1] + locale = Locale:from_node(root_element) + self.system_locales[lang] = locale return locale end +function CiteProc:get_style_class() + if self.style and self.style.class then + return self.style.class + else + return nil + end +end + + engine.CiteProc = CiteProc return engine diff --git a/biblio/citation-style-language/citeproc-formats.lua b/biblio/citation-style-language/citeproc-formats.lua deleted file mode 100644 index fd9621936c..0000000000 --- a/biblio/citation-style-language/citeproc-formats.lua +++ /dev/null @@ -1,211 +0,0 @@ --- --- Copyright (c) 2021-2022 Zeping Lee --- Released under the MIT license. --- Repository: https://github.com/zepinglee/citeproc-lua --- - -local util = require("citeproc-util") - - -local formats = {} - -formats.html = { - ["text_escape"] = function (str) - str = string.gsub(str, "%&", "&") - str = string.gsub(str, "<", "<") - str = string.gsub(str, ">", ">") - for char, sub in pairs(util.superscripts) do - str = string.gsub(str, char, "" .. sub .. "") - end - return str - end, - ["bibstart"] = "
\n", - ["bibend"] = "
", - ["@font-style/italic"] = "%s", - ["@font-style/oblique"] = "%s", - ["@font-style/normal"] = '%s', - ["@font-variant/small-caps"] = '%s', - ["@font-variant/normal"] = '%s', - ["@font-weight/bold"] = "%s", - ["@font-weight/normal"] = '%s', - ["@font-weight/light"] = false, - ["@text-decoration/none"] = '%s', - ["@text-decoration/underline"] = '%s', - ["@vertical-align/sup"] = "%s", - ["@vertical-align/sub"] = "%s", - ["@vertical-align/baseline"] = '%s', - ["@quotes/true"] = function (str, context) - local open_quote = context.style:get_term("open-quote"):render(context) - local close_quote = context.style:get_term("close-quote"):render(context) - return open_quote .. str .. close_quote - end, - ["@quotes/inner"] = function (str, context) - local open_quote = context.style:get_term("open-inner-quote"):render(context) - local close_quote = context.style:get_term("close-inner-quote"):render(context) - return open_quote .. str .. close_quote - end, - ["@bibliography/entry"] = function (str, context) - return '
' .. str .. "
\n" - end, - ["@display/block"] = function (str, state) - return '\n\n
' .. str .. "
\n" - end, - ["@display/left-margin"] = function (str, state) - return '\n
' .. str .. "
" - end, - ["@display/right-inline"] = function (str, state) - str = util.rstrip(str) - return '
' .. str .. "
\n " - end, - ["@display/indent"] = function (str, state) - return '
' .. str .. "
\n " - end, - ["@URL/true"] = function (str, state) - if state.engine.linking_enabled then - return string.format('%s', str, str) - else - return str - end - end, - ["@DOI/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://doi.org/" .. str; - end - return string.format('%s', href, str) - else - return str - end - end, - ["@PMID/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://www.ncbi.nlm.nih.gov/pubmed/" .. str; - end - return string.format('%s', href, str) - else - return str - end - end, - ["@PMCID/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. str; - end - return string.format('%s', href, str) - else - return str - end - end, -} - -formats.latex = { - ["text_escape"] = function (str) - str = str:gsub("\\", "\\textbackslash") - str = str:gsub("#", "\\#") - str = str:gsub("%$", "\\$") - str = str:gsub("%%", "\\%%") - str = str:gsub("&", "\\&") - str = str:gsub("{", "\\{") - str = str:gsub("}", "\\}") - str = str:gsub("_", "\\_") - str = str:gsub(util.unicode["no-break space"], "~") - for char, sub in pairs(util.superscripts) do - str = string.gsub(str, char, "\\textsuperscript{" .. sub .. "}") - end - return str - end, - ["bibstart"] = function (context) - return string.format("\\begin{thebibliography}{%s}\n", context.build.longest_label) - end, - ["bibend"] = "\\end{thebibliography}", - ["@font-style/normal"] = "{\\normalshape %s}", - ["@font-style/italic"] = "\\emph{%s}", - ["@font-style/oblique"] = "\\textsl{%s}", - ["@font-variant/normal"] = "{\\normalshape %s}", - ["@font-variant/small-caps"] = "\\textsc{%s}", - ["@font-weight/normal"] = "\\fontseries{m}\\selectfont %s", - ["@font-weight/bold"] = "\\textbf{%s}", - ["@font-weight/light"] = "\\fontseries{l}\\selectfont %s", - ["@text-decoration/none"] = false, - ["@text-decoration/underline"] = "\\underline{%s}", - ["@vertical-align/sup"] = "\\textsuperscript{%s}", - ["@vertical-align/sub"] = "\\textsubscript{%s}", - ["@vertical-align/baseline"] = false, - ["@quotes/true"] = function (str, context) - local open_quote = context.style:get_term("open-quote"):render(context) - local close_quote = context.style:get_term("close-quote"):render(context) - return open_quote .. str .. close_quote - end, - ["@quotes/inner"] = function (str, context) - local open_quote = context.style:get_term("open-inner-quote"):render(context) - local close_quote = context.style:get_term("close-inner-quote"):render(context) - return open_quote .. str .. close_quote - end, - ["@bibliography/entry"] = function (str, context) - if not string.match(str, "\\bibitem") then - str = "\\bibitem{".. context.item.id .. "}\n" .. str - end - return str .. "\n" - end, - ["@display/block"] = function (str, state) - return str - end, - ["@display/left-margin"] = function (str, state) - if #str > #state.build.longest_label then - state.build.longest_label = str - end - if string.match(str, "%]") then - str = "{" .. str .. "}" - end - return string.format("\\bibitem[%s]{%s}\n", str, state.item.id) - end, - ["@display/right-inline"] = function (str, state) - return str - end, - ["@display/indent"] = function (str, state) - return str - end, - ["@URL/true"] = function (str, state) - return "\\url{" .. str .. "}" - end, - ["@DOI/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://doi.org/" .. str; - end - return string.format("\\href{%s}{%s}", href, str) - else - return str - end - end, - ["@PMID/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://www.ncbi.nlm.nih.gov/pubmed/" .. str; - end - return string.format("\\href{%s}{%s}", href, str) - else - return str - end - end, - ["@PMCID/true"] = function (str, state) - if state.engine.linking_enabled then - local href = str - if not string.match(href, "^https?://") then - href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. str; - end - return string.format("\\href{%s}{%s}", href, str) - else - return str - end - end, -} - - -return formats diff --git a/biblio/citation-style-language/citeproc-ir-node.lua b/biblio/citation-style-language/citeproc-ir-node.lua new file mode 100644 index 0000000000..01cca4abf6 --- /dev/null +++ b/biblio/citation-style-language/citeproc-ir-node.lua @@ -0,0 +1,153 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local irnode = {} + +local util = require("citeproc-util") + + +local IrNode = { + _element = nil, + _type = "IrNode", + _base_class = "IrNode", + text = nil, + formatting = nil, + affixes = nil, + children = nil, + delimiter = nil, +} + +function IrNode:new(children, element) + local o = { + _element = element.element_name, + _type = self._type, + children = children, + group_var = "plain", + } + + o.group_var = "missing" + for _, child_ir in ipairs(children) do + if child_ir.group_var == "important" then + o.group_var = "important" + break + elseif child_ir.group_var == "plain" then + o.group_var = "plain" + end + end + + o.person_name_irs = {} + if children then + for _, child in ipairs(children) do + if child.person_name_irs then + util.extend(o.person_name_irs, child.person_name_irs) + end + end + end + + setmetatable(o, self) + self.__index = self + return o +end + +function IrNode:derive(type) + local o = { + _type = type, + } + setmetatable(o, self) + self.__index = self + return o +end + +function IrNode:flatten(format) + return format:flatten_ir(self) +end + +function IrNode:capitalize_first_term() + -- util.debug(self) + if self._type == "Rendered" and self.element and (self.element.term == "ibid" or self.element.term == "and") then + self.inlines[1]:capitalize_first_term() + elseif self._type == "SeqIr" and self.children[1] then + self.children[1]:capitalize_first_term() + end +end + + + +local Rendered = IrNode:derive("Rendered") + +function Rendered:new(inlines, element) + local o = { + _element = element.element_name, + _type = self._type, + element = element, -- required for capitalizing first term + inlines = inlines, + group_var = "plain", + } + + setmetatable(o, self) + self.__index = self + return o +end + + +local YearSuffix = IrNode:derive("YearSuffix") + +function YearSuffix:new(inlines, element) + local o = { + _element = element.element_name, + _type = self._type, + element = element, + inlines = inlines, + group_var = "plain", + } + + setmetatable(o, self) + self.__index = self + return o +end + + +local NameIr = IrNode:derive("NameIr") + + +local PersonNameIr = IrNode:derive("PersonNameIr") + +function PersonNameIr:new(inlines, element) + local o = { + _element = element.element_name, + _type = self._type, + inlines = inlines, + group_var = "plain", + } + setmetatable(o, self) + self.__index = self + return o +end + + +local SeqIr = IrNode:derive("SeqIr") + +-- function SeqIr:new(children) +-- o = IrNode.new(self, children) +-- local o = { +-- children = children, +-- group_var = "plain", +-- } +-- setmetatable(o, self) +-- self.__index = self +-- return o +-- end + + + +irnode.IrNode = IrNode +irnode.Rendered = Rendered +irnode.YearSuffix = YearSuffix +irnode.NameIr = NameIr +irnode.PersonNameIr = PersonNameIr +irnode.SeqIr = SeqIr + +return irnode diff --git a/biblio/citation-style-language/citeproc-latex-core.lua b/biblio/citation-style-language/citeproc-latex-core.lua new file mode 100644 index 0000000000..0be2381797 --- /dev/null +++ b/biblio/citation-style-language/citeproc-latex-core.lua @@ -0,0 +1,302 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local core = {} + +local citeproc = require("citeproc") +local util = citeproc.util +require("lualibs") + + +core.locale_file_format = "csl-locales-%s.xml" +core.uncited_ids = {} +core.uncite_all_items = false + +function core.error(message) + if luatexbase then + luatexbase.module_error("csl", message) + else + util.error(message) + end +end + +function core.warning(message) + if luatexbase then + luatexbase.module_warning("csl", message) + else + util.warning(message) + end +end + +function core.info(message) + if luatexbase then + luatexbase.module_info("csl", message) + else + util.info(message) + end +end + + +function core.read_file(file_name, ftype, file_info) + file_info = file_info or "file" + local path = kpse.find_file(file_name, ftype) + if not path then + if ftype and not util.endswith(file_name, ftype) then + file_name = file_name .. ftype + end + core.error(string.format('Cannot find %s "%s"', file_info, file_name)) + return nil + end + local file = io.open(path, "r") + if not file then + core.error(string.format('Cannot open %s "%s"', file_info, path)) + return nil + end + local contents = file:read("*a") + file:close() + return contents +end + + +local function read_data_file(data_file) + local file_name = data_file + local extension = nil + local contents = nil + + if util.endswith(data_file, ".json") then + extension = ".json" + contents = core.read_file(data_file, nil, "database file") + elseif util.endswith(data_file, ".bib") then + extension = ".bib" + contents = core.read_file(data_file, "bib", "database file") + else + local path = kpse.find_file(data_file .. ".json") + if path then + file_name = data_file .. ".json" + extension = ".json" + contents = core.read_file(data_file .. ".json", nil, "database file") + else + path = kpse.find_file(data_file, "bib") + if path then + file_name = data_file .. ".bib" + extension = ".bib" + contents = core.read_file(data_file, "bib", "database file") + else + core.error(string.format('Cannot find database file "%s"', data_file .. ".json")) + end + end + end + + local csl_items = nil + + if extension == ".json" then + csl_items = utilities.json.tolua(contents) + elseif extension == ".bib" then + csl_items = citeproc.parse_bib(contents) + end + + return file_name, csl_items +end + + +local function read_data_files(data_files) + local bib = {} + for _, data_file in ipairs(data_files) do + local file_name, csl_items = read_data_file(data_file) + + -- TODO: parse bib entries on demand + for _, item in ipairs(csl_items) do + local id = item.id + if bib[id] then + core.warning(string.format('Duplicate entry key "%s" in "%s".', id, file_name)) + else + bib[id] = item + end + end + end + return bib +end + + + +function core.make_citeproc_sys(data_files) + core.bib = read_data_files(data_files) + local citeproc_sys = { + retrieveLocale = function (lang) + local locale_file_format = core.locale_file_format or "locales-%s.xml" + local filename = string.format(locale_file_format, lang) + return core.read_file(filename) + end, + retrieveItem = function (id) + local res = core.bib[id] + -- if not res then + -- core.warning(string.format('Failed to find entry "%s".', id)) + -- end + return res + end + } + + return citeproc_sys +end + +function core.init(style_name, data_files, lang) + if style_name == "" or #data_files == 0 then + return nil + end + local style = core.read_file(style_name .. ".csl", nil, "style file") + if not style then + core.error(string.format('Failed to load style "%s.csl"', style_name)) + return nil + end + + local force_lang = nil + if lang and lang ~= "" then + force_lang = true + else + lang = nil + end + + local citeproc_sys = core.make_citeproc_sys(data_files) + local engine = citeproc.new(citeproc_sys, style, lang, force_lang) + return engine +end + +local function parse_latex_seq(s) + local t = {} + for item in string.gmatch(s, "(%b{})") do + item = string.sub(item, 2, -2) + table.insert(t, item) + end + return t +end + +local function parse_latex_prop(s) + local t = {} + for key, value in string.gmatch(s, "([%w%-]+)%s*=%s*(%b{})") do + value = string.sub(value, 2, -2) + if value == "true" then + value = true + elseif value == "false" then + value = false + end + t[key] = value + end + return t +end + +function core.make_citation(citation_info) + -- `citation_info`: "citationID={ITEM-1@2},citationItems={{id={ITEM-1},label={page},locator={6}}},properties={noteIndex={3}}" + local citation = parse_latex_prop(citation_info) + assert(citation.citationID) + assert(citation.citationItems) + assert(citation.properties) + + citation.citationItems = parse_latex_seq(citation.citationItems) + + for i, item in ipairs(citation.citationItems) do + citation.citationItems[i] = parse_latex_prop(item) + end + + citation.properties = parse_latex_prop(citation.properties) + local note_index = citation.properties.noteIndex + if not note_index or note_index == "" then + citation.properties.noteIndex = 0 + elseif type(note_index) == "string" and string.match(note_index, "^%d+$") then + citation.properties.noteIndex = tonumber(note_index) + else + util.error(string.format('Invalid note index "%s".', note_index)) + end + + return citation +end + + +function core.process_citations(engine, citations) + local citations_pre = {} + + core.update_uncited_items(engine, citations) + local citation_strings = {} + + for _, citation in ipairs(citations) do + if citation.citationID ~= "@nocite" then + local citation_str = engine:process_citation(citation) + citation_strings[citation.citationID] = citation_str + + table.insert(citations_pre, {citation.citationID, citation.properties.noteIndex}) + end + end + + return citation_strings +end + + +function core.update_uncited_items(engine, citations) + -- util.debug(core.uncite_all_items) + if core.uncite_all_items then + -- \nocite{*} + for id, _ in pairs(core.bib) do + table.insert(core.uncited_ids, id) + end + else + for _, citation in ipairs(citations) do + if citation.citationID == "@nocite" then + for _, cite_item in ipairs(citation.citationItems) do + table.insert(core.uncited_ids, cite_item.id) + if cite_item.id == "*" then + for id, _ in pairs(core.bib) do + table.insert(core.uncited_ids, id) + end + else + table.insert(core.uncited_ids, cite_item.id) + end + end + end + end + end + engine:updateUncitedItems(core.uncited_ids) +end + + +function core.make_bibliography(engine) + local result = engine:makeBibliography() + + local params = result[1] + local bib_items = result[2] + + local res = "" + + local bib_options = "" + if params["hangingindent"] then + bib_options = bib_options .. "\n hanging-indent = true," + end + if params["linespacing"] then + bib_options = bib_options .. string.format("\n line-spacing = %d,", params["linespacing"]) + end + if params["entryspacing"] then + bib_options = bib_options .. string.format("\n entry-spacing = %d,", params["entryspacing"]) + end + + if bib_options ~= "" then + bib_options = "\\cslsetup{" .. bib_options .. "\n}\n\n" + res = res .. bib_options + end + + if params.bibstart then + res = res .. params.bibstart + end + + for _, bib_item in ipairs(bib_items) do + res = res .. "\n" .. bib_item + end + + if params.bibend then + res = res .. "\n" .. params.bibend + end + return res +end + + +return core diff --git a/biblio/citation-style-language/citeproc-latex.lua b/biblio/citation-style-language/citeproc-latex.lua new file mode 100644 index 0000000000..19348635b0 --- /dev/null +++ b/biblio/citation-style-language/citeproc-latex.lua @@ -0,0 +1,150 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local csl = {} + +local citeproc = require("citeproc") +local util = citeproc.util +require("lualibs") +local core = require("citeproc-latex-core") + + +csl.initialized = "false" +csl.id_list = {} +csl.id_map = {} -- Boolean map for checking if id in list +csl.uncited_id_list = {} +csl.uncited_id_map = {} +csl.citations_pre = {} + +csl.preview_mode = false -- Whether to use citeproc:preview_citation + + +function csl.error(str) + luatexbase.module_error("csl", str) +end +function csl.warning(str) + luatexbase.module_warning("csl", str) +end +function csl.info(str) + luatexbase.module_info("csl", str) +end + + +function csl.init(style_name, bib_files, lang) + bib_files = util.split(util.strip(bib_files), "%s*,%s*") + + csl.engine = core.init(style_name, bib_files, lang) + + if csl.engine then + csl.initialized = "true" + else + return + end + + -- csl.init is called via \AtBeginDocument and it's executed after + -- loading .aux file. The csl.id_list are already registered. + csl.engine:updateItems(csl.id_list) + + if core.uncite_all_items then + for id, _ in pairs(core.bib) do + if not csl.uncited_id_map[id] then + table.insert(csl.uncited_id_list, id) + csl.uncited_id_map[id] = true + end + end + end + csl.engine:updateUncitedItems(csl.uncited_id_list) + csl.style_class = csl.engine:get_style_class() +end + + +function csl.get_style_class() + tex.sprint(csl.style_class) +end + + +function csl.register_citation_info(citation_info) + local citation = core.make_citation(citation_info) + for _, cite_item in ipairs(citation.citationItems) do + if not csl.id_map[cite_item.id] then + table.insert(csl.id_list, cite_item.id) + csl.id_map[cite_item.id] = true + end + end +end + + +function csl.enable_linking() + csl.engine:enable_linking() +end + + +function csl.cite(citation_info) + -- "citationID={ITEM-UNAVAILABLE@1},citationItems={{id={ITEM-UNAVAILABLE}}},properties={noteIndex={1}}" + -- util.debug(citation_info) + if not csl.engine then + csl.error("CSL engine is not initialized.") + end + + local citation = core.make_citation(citation_info) + + local citation_str + if csl.preview_mode then + -- TODO: preview mode in first pass of \blockquote of csquotes + -- citation_str = csl.engine:preview_citation(citation) + citation_str = "" + else + citation_str = csl.engine:process_citation(citation) + end + + tex.sprint(string.format("{%s}{%s}", csl.style_class, citation_str)) + -- tex.sprint(citation_str) + + table.insert(csl.citations_pre, {citation.citationID, citation.properties.noteIndex}) +end + + +function csl.nocite(ids_string) + local uncited_ids = util.split(ids_string, "%s*,%s*") + for _, uncited_id in ipairs(uncited_ids) do + if uncited_id == "*" then + if csl.engine then + for id, _ in pairs(core.bib) do + if not csl.uncited_id_map[id] then + table.insert(csl.uncited_id_list, id) + csl.uncited_id_map[id] = true + end + end + csl.engine:updateUncitedItems(csl.uncited_id_list) + else + core.uncite_all_items = true + end + else + if not csl.uncited_id_map[uncited_id] then + table.insert(csl.uncited_id_list, uncited_id) + csl.uncited_id_map[uncited_id] = true + if csl.engine then + csl.engine:updateUncitedItems(csl.uncited_id_list) + end + end + end + end +end + + +function csl.bibliography() + if not csl.engine then + csl.error("CSL engine is not initialized.") + return + end + + local result = core.make_bibliography(csl.engine) + + tex.print(util.split(result, "\n")) +end + + +return csl diff --git a/biblio/citation-style-language/citeproc-lua.1 b/biblio/citation-style-language/citeproc-lua.1 new file mode 100644 index 0000000000..c8fe3c3d68 --- /dev/null +++ b/biblio/citation-style-language/citeproc-lua.1 @@ -0,0 +1,26 @@ +.TH citeproc-lua 1 "0.1.1" +.SH NAME +citeproc-lua \- make CSL citations and bibliography for LaTeX +.SH SYNOPSIS +.B citeproc-lua +.RB [] +.IR auxname [ \fB.aux\fP ] +.SH DESCRIPTION +The citeproc engine is a Citation Style Lanugage processor for LaTeX. +It reads the auxiliary +.RB ( .aux ) +file +.I auxname +and creates a bibliography +.RB ( .bbl ) +file +.SH OPTIONS +.IP \-h ", " \-\-help +Prints the usage and exits. +.IP \-V ", " \-\-version +Prints the version number and exits. +.SH AUTHOR +Zeping Lee +.PP +Please log issues on the GitHub homepage: +https://github.com/zepinglee/citeproc-lua/issues. diff --git a/biblio/citation-style-language/citeproc-lua.lua b/biblio/citation-style-language/citeproc-lua.lua new file mode 100755 index 0000000000..c670866813 --- /dev/null +++ b/biblio/citation-style-language/citeproc-lua.lua @@ -0,0 +1,12 @@ +#!/usr/bin/env texlua + +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +kpse.set_program_name("luatex") + +local cli = require("citeproc-cli") +cli.main() diff --git a/biblio/citation-style-language/citeproc-node-choose.lua b/biblio/citation-style-language/citeproc-node-choose.lua index e48731406c..3a4fc3f79e 100644 --- a/biblio/citation-style-language/citeproc-node-choose.lua +++ b/biblio/citation-style-language/citeproc-node-choose.lua @@ -6,123 +6,299 @@ local choose = {} -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local SeqIr = require("citeproc-ir-node").SeqIr local util = require("citeproc-util") +-- [Choose](https://docs.citationstyles.org/en/stable/specification.html#choose) +local Choose = Element:derive("choose") -local Choose = element.Element:new() +function Choose:from_node(node) + local o = Choose:new() + o.children = {} + o:process_children_nodes(node) + if #o.children == 0 then + return nil + end + return o +end + +function Choose:build_ir(engine, state, context) + local branch + local branch_ir + for _, child in ipairs(self.children) do + if child:evaluate_conditions(engine, state, context) then + branch = child + branch_ir = child:build_ir(engine, state, context) + break + end + end + + if not branch_ir then + branch_ir = SeqIr:new({}, self) + branch_ir.group_var = "missing" + end + + local ir = SeqIr:new({branch_ir}, self) + ir.group_var = branch_ir.group_var + ir.name_count = branch_ir.name_count + ir.sort_key = branch_ir.sort_key + + if not context.disambiguate then + context.disambiguate = true -function Choose:render (item, context) - self:debug_info(context) - context = self:process_context(context) - for i, child in ipairs(self:get_children()) do - if child:is_element() then - local result, status = child:render(item, context) - if status then - return result + for _, child in ipairs(self.children) do + if child:evaluate_conditions(engine, state, context) then + if child ~= branch then + ir.disambiguate_branch_ir = child:build_ir(engine, state, context) + end + break end end + + context.disambiguate = false end - return nil + + return ir end -local If = element.Element:new() +local Condition = { + condition = nil, + value = nil, + match_type = "all", +} + +function Condition:new(condition, value, match_type) + local o = { + condition = condition, + value = value, + match_type = match_type or "all" + } + setmetatable(o, self) + self.__index = self + return o +end -If.render = function (self, item, context) - self:debug_info(context) - context = self:process_context(context) - local results = {} - local variable_names = context.options["is-numeric"] - if variable_names then - for _, variable_name in ipairs(util.split(variable_names)) do - local variable = self:get_variable(item, variable_name, context) - table.insert(results, util.is_numeric(variable)) - end +local If = Element:derive("if", { + conditions = nil, + match = "all" +}) + +function If:new() + local o = Element.new(self) + o.conditions = {} + o.match = "all" + return o +end + +function If:from_node(node) + local o = If:new() + -- TODO: disambiguate + -- o:set_bool_attribute(node, "disambiguate") + + o:add_conditions(node, "disambiguate") + o:add_conditions(node, "is-numeric") + o:add_conditions(node, "is-uncertain-date") + o:add_conditions(node, "locator") + o:add_conditions(node, "position") + o:add_conditions(node, "type") + o:add_conditions(node, "variable") + + o.match = node:get_attribute("match") + + o:process_children_nodes(node) + + return o +end + +function If:add_conditions(node, attribute) + local values = node:get_attribute(attribute) + if not values then + return + end + for _, value in ipairs(util.split(values)) do + local condition = Condition:new(attribute, value, self.match) + table.insert(self.conditions, condition) end +end - variable_names = context.options["is-uncertain-date"] - if variable_names then - for _, variable_name in ipairs(util.split(variable_names)) do - local variable = self:get_variable(item, variable_name, context) - table.insert(results, util.is_uncertain_date(variable)) - end +function If:build_children_ir(engine, state, context) + if not self.children then + return nil end + local irs = {} + local name_count + local ir_sort_key + local group_var = "plain" - local locator_types = context.options["locator"] - if locator_types then - for _, locator_type in ipairs(util.split(locator_types)) do - local locator_label = item.label or "page" - local res = locator_label == locator_type - if locator_type == "sub-verbo" then - res = locator_label == "sub-verbo" or locator_label == "sub verbo" + for _, child_element in ipairs(self.children) do + local child_ir = child_element:build_ir(engine, state, context) + + if child_ir then + local child_group_var = child_ir.group_var + if child_group_var == "important" then + group_var = "important" + elseif child_group_var == "missing" and child_ir._type ~= "YearSuffix" then + if group_var == "plain" then + group_var = "missing" + end end - table.insert(results, res) - end - end - local positions = context.options["position"] - if positions then - for _, position in ipairs(util.split(positions)) do - local res = false - if context.mode == "citation" then - if position == "first" then - res = (item.position == util.position_map["first"]) - elseif position == "near-note" then - res = item["near-note"] ~= nil and item["near-note"] ~= false - else - res = (item.position >= util.position_map[position]) + if child_ir.name_count then + if not name_count then + name_count = 0 end + name_count = name_count + child_ir.name_count + end + + if child_ir.sort_key ~= nil then + ir_sort_key = child_ir.sort_key end - table.insert(results, res) + + -- The condition can be simplified + table.insert(irs, child_ir) end end - local type_names = context.options["type"] - if type_names then - for _, type_name in ipairs(util.split(type_names)) do - table.insert(results, item["type"] == type_name) + if #irs == 0 then + group_var = "missing" + end + + local ir = SeqIr:new(irs, self) + ir.name_count = name_count + ir.sort_key = ir_sort_key + ir.group_var = group_var + return ir +end + +function If:build_ir(engine, state, context) + if not self:evaluate_conditions(engine, state, context) then + return nil + end + + local ir = self:build_children_ir(engine, state, context) + if not ir then + return nil + end + + ir.should_inherit_delim = true + return ir +end + +function If:evaluate_conditions(engine, state, context) + local res = false + -- util.debug(self.conditions) + for _, condition in ipairs(self.conditions) do + if self:evaluate_condition(condition, state, context) then + if self.match == "any" then + return true + elseif self.match == "none" then + return false + end + else + if self.match == "all" then + return false + end end end + if self.match == "any" then + return false + else + return true + end +end + +function If:evaluate_condition(condition, state, context) + -- util.debug(context.cite) + -- util.debug(condition) + if condition.condition == "disambiguate" then + return context.disambiguate or (context.in_bibliography and context.reference.disambiguate) - variable_names = context.options["variable"] - if variable_names then - for _, variable_name in ipairs(util.split(variable_names)) do - local variable = self:get_variable(item, variable_name, context) - local res = (variable ~= nil and variable ~= "") - table.insert(results, res) + elseif condition.condition == "is-numeric" then + local variable = context:get_variable(condition.value) + return util.is_numeric(variable) + + elseif condition.condition == "is-uncertain-date" then + local variable = context:get_variable(condition.value) + -- TODO + return self:is_uncertain_date(variable) + + elseif condition.condition == "locator" then + local locator_label = context:get_variable("label") + if locator_label == "sub verbo" then + locator_label = "sub-verbo" + end + return locator_label == condition.value + + elseif condition.condition == "position" then + return self:check_position(condition.value, context) + + elseif condition.condition == "type" then + local item_type = context:get_variable("type") + return item_type == condition.value + + elseif condition.condition == "variable" then + local var = condition.value + if var == "locator" or var == "label" then + if context.in_bibliography then + return false + else + return context.cite[var] ~= nil + end + else + local res = context.reference[var] ~= nil + return res end end +end + +function If:is_uncertain_date(variable) + if variable == nil then + return false + end + local circa = variable["circa"] + return circa and circa ~= "" +end - local match = context.options["match"] or "all" - local status = false - if match == "any" then - status = util.any(results) - elseif match == "none" then - status = not util.any(results) - else - status = util.all(results) +function If:check_position(position, context) + -- util.debug(context.cite) + if context.in_bibliography then + return false end - if status then - return self:render_children(item, context), status + + local position_level = context.cite.position or context.cite.position_level or 0 + -- context.cite.position is for hacking in debugging + -- bugreports_DemoPageFullCiteCruftOnSubsequent.txt + if position == "first" then + return (position_level == util.position_map["first"]) + elseif position == "near-note" then + return context.cite["near-note"] or context.cite.near_note else - return nil, false + return (position_level >= util.position_map[position]) end end -local ElseIf = If:new() +local ElseIf = If:derive("else-if") + +local Else = Element:derive("else") -local Else = element.Element:new() +function Else:from_node(node) + local o = Else:new() + o:process_children_nodes(node) + return o +end -Else.render = function (self, item, context) - self:debug_info(context) - context = self:process_context(context) - return self:render_children(item, context), true +function Else:evaluate_conditions(engine, state, context) + return true end +Else.build_children_ir = If.build_children_ir +Else.build_ir = If.build_ir + choose.Choose = Choose choose.If = If diff --git a/biblio/citation-style-language/citeproc-node-date.lua b/biblio/citation-style-language/citeproc-node-date.lua index e10c1a9ac2..7ec1f439b8 100644 --- a/biblio/citation-style-language/citeproc-node-date.lua +++ b/biblio/citation-style-language/citeproc-node-date.lua @@ -6,376 +6,481 @@ local date_module = {} -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local IrNode = require("citeproc-ir-node").IrNode +local Rendered = require("citeproc-ir-node").Rendered +local SeqIr = require("citeproc-ir-node").SeqIr +local PlainText = require("citeproc-output").PlainText local util = require("citeproc-util") -local Date = element.Element:new() +-- [Date](https://docs.citationstyles.org/en/stable/specification.html#date) +local Date = Element:derive("date") -function Date:render (item, context) - self:debug_info(context) - context = self:process_context(context) +function Date:from_node(node) + local o = Date:new() - if context.sorting then - return self:render_sort_key(item, context) + o:set_attribute(node, "variable") + o:set_attribute(node, "form") + o:set_attribute(node, "date-parts") + + if o.form and not o.date_parts then + o.date_parts = "year-month-day" end - local variable_name = context.options["variable"] + o:get_delimiter_attribute(node) + o:set_formatting_attributes(node) + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_text_case_attribute(node) - local is_locale_date - if variable_name then - context.variable = variable_name - is_locale_date = false - else - variable_name = context.variable - is_locale_date = true + o.children = {} + o:process_children_nodes(node) + + for _, date_part in ipairs(o.children) do + o[date_part.name] = date_part end - local date = self:get_variable(item, variable_name, context) - if not date then - return nil + return o +end + +function Date:build_ir(engine, state, context) + local variable + if not state.suppressed[self.variable] then + variable = context:get_variable(self.variable) + end + + if not variable then + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir end - local res = nil - local form = context.options["form"] - if form and not is_locale_date then - for _, date_part in ipairs(self:query_selector("date-part")) do - local name = date_part:get_attribute("name") - if not context.date_part_attributes then - context.date_part_attributes = {} + local ir + + if variable["date-parts"] and #variable["date-parts"] > 0 then + + -- TODO: move input normlization in one place + for i = 1, 2 do + if variable["date-parts"][i] then + for j = 1, 3 do + local variabel_part = variable["date-parts"][i][j] + if variabel_part == 0 or variabel_part == "" then + variable["date-parts"][i][j] = nil + else + variable["date-parts"][i][j] = tonumber(variabel_part) + end + end end - if not context.date_part_attributes[name] then - context.date_part_attributes[name] = {} + end + if variable["season"] and not variable["date-parts"][1][2] then + variable["date-parts"][1][2] = 20 + tonumber(variable["season"]) + end + + variable = variable["date-parts"] + if self.form then + ir = self:build_localized_date_ir(variable, engine, state, context) + else + ir = self:build_independent_date_ir(variable, engine, state, context) + end + ir.affixes = self.affixes + + elseif variable["literal"] then + local inlines = self:render_text_inlines(variable["literal"], context) + ir = Rendered:new(inlines, self) + ir.group_var = "important" + + elseif variable["raw"] then + local inlines = self:render_text_inlines(variable["raw"], context) + ir = Rendered:new(inlines, self) + ir.group_var = "important" + + end + + if not ir then + -- date_LiteralFailGracefullyIfNoValue.txt + ir = Rendered:new({}, self) + if context.sort_key then + ir.sort_key = false + end + ir.group_var = "missing" + return ir + end + + if ir.group_var == "important" then + -- Suppress substituted name variable + if state.name_override and not context.sort_key then + state.suppressed[self.variable] = true + end + end + + if context.sort_key then + ir.sort_key = self:render_sort_key(engine, state, context) + end + + return ir +end + +function Date:build_independent_date_ir(variable, engine, state, context) + -- else + -- local literal = variable["literal"] + -- if literal then + -- res = literal + -- else + -- local raw = variable["raw"] + -- if raw then + -- res = raw + -- end + -- end + + return self:build_date_parts(self.children, variable, self.delimiter, engine, state, context) +end + +function Date:build_localized_date_ir(variable, engine, state, context) + local date_part_mask = {} + for _, part in ipairs(util.split(self.date_parts or "year-month-day", "%-")) do + date_part_mask[part] = true + end + -- local date_parts = {} + -- for _, date_part in ipairs(self.children) do + -- date_parts[date_part.name] = date_part + -- end + local localized_date = context:get_localized_date(self.form) + local date_parts = {} + for _, date_part in ipairs(localized_date.children) do + if date_part_mask[date_part.name] then + date_part = date_part:copy() + local local_date_part = self[date_part.name] + if local_date_part then + local_date_part:override(date_part) end + table.insert(date_parts, date_part) + end + end + return self:build_date_parts(date_parts, variable, localized_date.delimiter, engine, state, context) +end - for attr, value in pairs(date_part._attr) do - if attr ~= name then - context.date_part_attributes[name][attr] = value - end +function Date:build_date_parts(date_parts, variable, delimiter, engine, state, context) + if #variable >= 2 then + return self:build_date_range(date_parts, variable, delimiter, engine, state, context) + elseif #variable == 1 then + return self:build_single_date(date_parts, variable[1], delimiter, engine, state, context) + end +end + +function Date:build_single_date(date_parts, single_date, delimiter, engine, state, context) + local irs = {} + for _, date_part in ipairs(date_parts) do + local part_ir = date_part:build_ir(single_date, engine, state, context) + table.insert(irs, part_ir) + end + + local ir = SeqIr:new(irs, self) + ir.delimiter = self.delimiter + + -- return Rendered:new(inlines, self) + return ir +end + +local date_part_index = { + year = 1, + month = 2, + day = 3, +} + +function Date:build_date_range(date_parts, variable, delimiter, engine, state, context) + local first, second = variable[1], variable[2] + local diff_level = 4 + for _, date_part in ipairs(date_parts) do + local part_index = date_part_index[date_part.name] + if first[part_index] and first[part_index] ~= second[part_index] then + if part_index < diff_level then + diff_level = part_index end end - res = self:get_locale_date(context, form):render(item, context) - else - if not date["date-parts"] or #date["date-parts"] == 0 then - local literal = date["literal"] - if literal then - res = literal + end + + local irs = {} + + local range_part_queue = {} + local range_delimiter + for i, date_part in ipairs(date_parts) do + local part_index = date_part_index[date_part.name] + if part_index == diff_level then + range_delimiter = date_part.range_delimiter or util.unicode["en dash"] + end + if first[part_index] then + if part_index >= diff_level then + table.insert(range_part_queue, date_part) else - local raw = date["raw"] - if raw then - res = raw + if #range_part_queue > 0 then + table.insert(irs, self:build_date_range_parts(range_part_queue, variable, + delimiter, engine, state, context, range_delimiter)) + range_part_queue = {} end - end - - else - if #date["date-parts"] == 1 then - res = self:_render_single_date(date, context) - elseif #date["date-parts"] == 2 then - res = self:_render_date_range(date, context) + table.insert(irs, date_part:build_ir(first, engine, state, context)) end end end + if #range_part_queue > 0 then + table.insert(irs, self:build_date_range_parts(range_part_queue, variable, + delimiter, engine, state, context, range_delimiter)) + end - table.insert(context.variable_attempt, res ~= nil) + local ir = SeqIr:new(irs, self) + ir.delimiter = delimiter - res = self:format(res, context) - res = self:wrap(res, context) - return res + return ir end -function Date:get_locale_date(context, form) - local date = nil - local style = context.style - local query = string.format("date[form=\"%s\"]", form) - for _, locale in ipairs(style:get_locales()) do - date = locale:query_selector(query)[1] - if date then - break +function Date:build_date_range_parts(range_part_queue, variable, delimiter, engine, state, context, range_delimiter) + local irs = {} + local first, second = variable[1], variable[2] + + local date_part_irs = {} + for i, diff_part in ipairs(range_part_queue) do + -- if delimiter and i > 1 then + -- table.insert(date_part_irs, PlainText:new(delimiter)) + -- end + if i == #range_part_queue then + table.insert(date_part_irs, diff_part:build_ir(first, engine, state, context, "suffix")) + else + table.insert(date_part_irs, diff_part:build_ir(first, engine, state, context)) end end - if not date then - error(string.format("Failed to find '%s'", query)) + local range_part_ir = SeqIr:new(date_part_irs, self) + range_part_ir.delimiter = delimiter + table.insert(irs, range_part_ir) + + table.insert(irs, Rendered:new({PlainText:new(range_delimiter)}, self)) + + date_part_irs = {} + for i, diff_part in ipairs(range_part_queue) do + if i == 1 then + table.insert(date_part_irs, diff_part:build_ir(second, engine, state, context, "prefix")) + else + table.insert(date_part_irs, diff_part:build_ir(second, engine, state, context)) + end end - return date + range_part_ir = SeqIr:new(date_part_irs, self) + range_part_ir.delimiter = delimiter + table.insert(irs, range_part_ir) + + local ir = SeqIr:new(irs, self) + + return ir end -function Date:render_sort_key (item, context) - local variable_name = context.options["variable"] - local date = self:get_variable(item, variable_name, context) - if not date or not date["date-parts"] then - return nil +function Date:render_sort_key(engine, state, context) + local date = context:get_variable(self.variable) + if not date then + return false end + if not date["date-parts"] then + if self.literal then + return "1" .. self.literal + else + return false + end + end + local show_parts = { year = false, month = false, day = false, } - if self:get_attribute("form") then - local date_parts = self:get_attribute("date-parts") or "year-month-day" - for _, dp_name in ipairs(util.split(date_parts, "%-")) do + if self.form then + for _, dp_name in ipairs(util.split(self.date_parts, "%-")) do show_parts[dp_name] = true end else - for _, child in ipairs(self:query_selector("date-part")) do - show_parts[child:get_attribute("name")] = true + for _, date_part in ipairs(self.children) do + show_parts[date_part.name] = true end end local res = "" - for _, date_parts in ipairs(date["date-parts"]) do + for _, range_part in ipairs(date["date-parts"]) do + if res ~= "" then + res = res .. "/" + end for i, dp_name in ipairs({"year", "month", "day"}) do - local value = date_parts[i] - if not value or not show_parts[dp_name] then - value = 0 + local value = 0 + if show_parts[dp_name] and range_part[i] then + value = range_part[i] end if i == 1 then res = res .. string.format("%05d", value + 10000) else - res = res .. string.format("%02d", value) + res = res .. "-" .. string.format("%02d", value) end end end return res end -function Date:_render_single_date (date, context) - local show_parts = self:_get_show_parts(context) - local output = {} - for _, child in ipairs(self:query_selector("date-part")) do - if show_parts[child:get_attribute("name")] then - table.insert(output, child:render(date, context)) - end +-- [Date-part](https://docs.citationstyles.org/en/stable/specification.html#date-part) +local DatePart = Element:derive("date-part") + +function DatePart:from_node(node) + local o = DatePart:new() + o:set_attribute(node, "name") + o:set_attribute(node, "form") + if o.name == "month" then + o:set_strip_periods_attribute(node) end - return self:concat(output, context) + o:set_formatting_attributes(node) + o:set_text_case_attribute(node) + o:set_attribute(node, "range-delimiter") + o:set_affixes_attributes(node) + return o end -function Date:_render_date_range (date, context) - local show_parts = self:_get_show_parts(context) - local part_index = {} - - local largest_diff_part = nil - for i, name in ipairs({"year", "month", "day"}) do - part_index[name] = i - local part_value1 = date["date-parts"][1][i] - if show_parts[name] and part_value1 then - if not largest_diff_part then - largest_diff_part = name - end - end +function DatePart:build_ir(single_date, engine, state, context, suppressed_affix) + local text + if self.name == "year" then + text = self:render_year(single_date[1], engine, state, context) + elseif self.name == "month" then + text = self:render_month(single_date[2], engine, state, context) + elseif self.name == "day" then + text = self:render_day(single_date[3], single_date[2], engine, state, context) end - local date_parts = {} - for _, date_part in ipairs(self:query_selector("date-part")) do - if show_parts[date_part:get_attribute("name")] then - table.insert(date_parts, date_part) - end + if not text then + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir end - local diff_begin = 0 - local diff_end = #date_parts - local range_delimiter = nil - - for i, date_part in ipairs(date_parts) do - local name = date_part:get_attribute("name") - if name == largest_diff_part then - range_delimiter = date_part:get_attribute("range-delimiter") - if not range_delimiter then - range_delimiter = util.unicode["en dash"] - end - end - - local index = part_index[name] - local part_value1 = date["date-parts"][1][index] - local part_value2 = date["date-parts"][2][index] - if part_value1 and part_value1 ~= part_value2 then - if diff_begin == 0 then - diff_begin = i - end - diff_end = i - end + local inlines = {PlainText:new(text)} + local output_format = context.format + if not context.is_english then + print(debug.traceback()) end + local is_english = context:is_english() + output_format:apply_text_case(inlines, self.text_case, is_english) + inlines = output_format:with_format(inlines, self.formatting) - local same_prefix = {} - local range_begin = {} - local range_end = {} - local same_suffix = {} + local ir = Rendered:new(inlines, self) + ir.group_var = "important" - local no_suffix_context = self:process_context(context) - no_suffix_context.options["suffix"] = nil - - for i, date_part in ipairs(date_parts) do - local res = nil - if i == diff_end then - res = date_part:render(date, no_suffix_context, true) - else - res = date_part:render(date, context) - end - if i < diff_begin then - table.insert(same_prefix, res) - elseif i <= diff_end then - table.insert(range_begin, res) - table.insert(range_end, date_part:render(date, context, false, true)) - else - table.insert(same_suffix, res) - end + if self.name == "year" then + ir = SeqIr:new({ir}, self) + ir.is_year = true end - local prefix_output = self:concat(same_prefix, context) or "" - local range_begin_output = self:concat(range_begin, context) or "" - local range_end_output = self:concat(range_end, context) or "" - local suffix_output = self:concat(same_suffix, context) - local range_output = range_begin_output .. range_delimiter .. range_end_output - - local res = self:concat({prefix_output, range_output, suffix_output}, context) + ir.affixes = util.clone(self.affixes) + if ir.affixes and suppressed_affix then + ir.affixes[suppressed_affix] = nil + end - return res + return ir end -function Date:_get_show_parts (context) - local show_parts = {} - local date_parts = context.options["date-parts"] or "year-month-day" - for _, date_part in ipairs(util.split(date_parts, "%-")) do - show_parts[date_part] = true +function DatePart:render_day(day, month, engine, state, context) + if not day or day == "" then + return nil + end + day = tonumber(day) + if day < 1 or day > 31 then + return nil + end + local form = self.form or "numeric" + if form == "ordinal" then + local limit_day_1 = context.locale.style_options.limit_day_ordinals_to_day_1 + if limit_day_1 and day > 1 then + form = "numeric" + end + end + if form == "numeric-leading-zeros" then + return string.format("%02d", day) + elseif form == "ordinal" then + -- When the “day” date-part is rendered in the “ordinal” form, the ordinal + -- gender is matched against that of the month term. + local gender = context.locale:get_number_gender(string.format("month-%02d", month)) + local suffix = context.locale:get_ordinal_term(day, gender) + return tostring(day) .. suffix + else -- numeric + return tostring(day) end - return show_parts end - -local DatePart = element.Element:new() - -DatePart.render = function (self, date, context, last_range_begin, range_end) - self:debug_info(context) - context = self:process_context(context) - local name = context.options["name"] - local range_delimiter = context.options["range-delimiter"] or false - - -- The attributes set on cs:date-part elements of a cs:date with form - -- attribute override those specified for the localized date formats - if context.date_part_attributes then - local context_attributes = context.date_part_attributes[name] - if context_attributes then - for attr, value in pairs(context_attributes) do - context.options[attr] = value +function DatePart:render_month(month, engine, state, context) + if not month or month == "" then + return nil + end + month = tonumber(month) + if not month or month < 1 or month > 24 then + return nil + end + local form = self.form or "long" + local res + if form == "long" or form == "short" then + if month >= 1 and month <= 12 then + res = context:get_simple_term(string.format("month-%02d", month), form) + else + local season = month % 4 + if season == 0 then + season = 4 end + res = context:get_simple_term(string.format("season-%02d", season)) end + elseif form == "numeric-leading-zeros" then + res = string.format("%02d", month) + else + res = tostring(month) end + return self:apply_strip_periods(res) +end - if last_range_begin then - context.options["suffix"] = "" +function DatePart:render_year(year, engine, state, context) + if not year or year == "" then + return nil end - - local date_parts_index = 1 - if range_end then - date_parts_index = 2 + year = tonumber(year) + if year == 0 then + return nil end - - local res = nil - if name == "day" then - local day = date["date-parts"][date_parts_index][3] - if not day then - return nil - end - day = tonumber(day) - -- range open - if day == 0 then - return nil - end - local form = context.options["form"] or "numeric" - - if form == "ordinal" then - local option = self:get_locale_option("limit-day-ordinals-to-day-1") - if option and option ~= "false" and day > 1 then - form = "numeric" - end - end - if form == "numeric" then - res = tostring(day) - elseif form == "numeric-leading-zeros" then - -- TODO: day == nil? - if not day then - return nil - end - res = string.format("%02d", day) - elseif form == "ordinal" then - res = util.to_ordinal(day) + local form = self.form or "long" + if form == "long" then + if year < 0 then + return tostring(-year) .. context:get_simple_term("bc") + elseif year < 1000 then + return tostring(year) .. context:get_simple_term("ad") + else + return tostring(year) end + elseif form == "short" then + return string.sub(tostring(year), -2) + end +end - elseif name == "month" then - local form = context.options["form"] or "long" - - local month = date["date-parts"][date_parts_index][2] - if month then - month = tonumber(month) - -- range open - if month == 0 then - return nil +function DatePart:copy() + local o = {} + for key, value in pairs(self) do + if type(value) == "table" then + o[key] = {} + for k, v in pairs(value) do + o[key][k] = v end + else + o[key] = value end + end + setmetatable(o, DatePart) + return o +end - if form == "long" or form == "short" then - local term_name = nil - if month then - if month >= 1 and month <= 12 then - term_name = string.format("month-%02d", month) - elseif month >= 13 and month <= 24 then - local season = month % 4 - if season == 0 then - season = 4 - end - term_name = string.format("season-%02d", season) - else - util.warning("Invalid month value") - return nil - end - else - local season = date["season"] - if season then - season = tonumber(season) - term_name = string.format("season-%02d", season) - else - return nil - end - end - res = self:get_term(term_name, form):render(context) - elseif form == "numeric" then - res = tostring(month) - elseif form == "numeric-leading-zeros" then - -- TODO: month == nil? - if not month then - return nil - end - res = string.format("%02d", month) - end - res = self:strip_periods(res, context) - - elseif name == "year" then - local year = date["date-parts"][date_parts_index][1] - if year then - year = tonumber(year) - -- range open - if year == 0 then - return nil - end - local form = context.options["form"] or "long" - if form == "long" then - year = tonumber(year) - if year < 0 then - res = tostring(-year) .. self:get_term("bc"):render(context) - elseif year < 1000 then - res = tostring(year) .. self:get_term("ad"):render(context) - else - res = tostring(year) - end - elseif form == "short" then - res = string.sub(tostring(year), -2) +function DatePart:override(localized_date_part) + for key, value in pairs(self) do + if type(value) == "table" and localized_date_part[key] then + for k, v in pairs(value) do + localized_date_part[key][k] = v end + else + localized_date_part[key] = value end end - res = self:case(res, context) - res = self:format(res, context) - res = self:wrap(res, context) - res = self:display(res, context) - return res end diff --git a/biblio/citation-style-language/citeproc-node-group.lua b/biblio/citation-style-language/citeproc-node-group.lua index 51969212ba..81132a77dc 100644 --- a/biblio/citation-style-language/citeproc-node-group.lua +++ b/biblio/citation-style-language/citeproc-node-group.lua @@ -6,33 +6,36 @@ local group = {} -local element = require("citeproc-element") +local SeqIr = require("citeproc-ir-node").SeqIr +local Element = require("citeproc-element").Element local util = require("citeproc-util") -local Group = element.Element:new() +local Group = Element:derive("group") -function Group:render (item, context) - self:debug_info(context) - context = self:process_context(context) +function Group:from_node(node) + local o = Group:new() + o:get_delimiter_attribute(node) + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_formatting_attributes(node) - local num_variable_attempt = #context.variable_attempt + o:process_children_nodes(node) - local res = self:render_children(item, context) + return o +end - if #context.variable_attempt > num_variable_attempt then - if not util.any(util.slice(context.variable_attempt, num_variable_attempt + 1)) then - res = nil - end +function Group:build_ir(engine, state, context) + local ir = self:build_group_ir(engine, state, context) + if ir then + ir.delimiter = self.delimiter + ir.formatting = util.clone(self.formatting) + ir.affixes = util.clone(self.affixes) + ir.display = self.display end - - res = self:format(res, context) - res = self:wrap(res, context) - res = self:display(res, context) - return res + return ir end - group.Group = Group return group diff --git a/biblio/citation-style-language/citeproc-node-label.lua b/biblio/citation-style-language/citeproc-node-label.lua index 828f5e9122..27cafa2f18 100644 --- a/biblio/citation-style-language/citeproc-node-label.lua +++ b/biblio/citation-style-language/citeproc-node-label.lua @@ -6,104 +6,88 @@ local label = {} -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local IrNode = require("citeproc-ir-node").IrNode +local Rendered = require("citeproc-ir-node").Rendered +local PlainText = require("citeproc-output").PlainText local util = require("citeproc-util") -local Label = element.Element:new() +-- [Label](https://docs.citationstyles.org/en/stable/specification.html#label) +local Label = Element:derive("label") -function Label:render (item, context) - self:debug_info(context) - context = self:process_context(context) +Label.form = "long" +Label.plural = "contextual" - local variable_name - if context.names_element then - -- The `variable` attribute of names may hold multiple roles. - -- Each of them may call `Label:render()` to render the term. - -- When used in `names` element, the role name is the first argument - -- and the item is accessed via `context.item`. - -- Bad design - -- TODO: Redesign the arguments of render() - variable_name = item - else - variable_name = context.options["variable"] - end - - local form = context.options["form"] - local plural = context.options["plural"] or "contextual" +function Label:from_node(node) + local o = Label:new() + o:set_attribute(node, "variable") + o:set_attribute(node, "form") + o:set_attribute(node, "plural") + o:set_affixes_attributes(node) + o:set_formatting_attributes(node) + o:set_text_case_attribute(node) + o:set_strip_periods_attribute(node) + return o +end - if not context.names_element then - local variable_type = util.variable_types[variable_name] - -- variable must be or one of the number variables. - if variable_type ~= "number" then - return nil - end - -- The term is only rendered if the selected variable is non-empty - local variable = item[variable_name] - if not variable then - return nil - end - if type(variable) == "string" then - if not (string.match(variable, "^%d") or util.is_numeric(variable)) then - return nil - end - end - end +function Label:build_ir(engine, state, context) + -- local variable = context:get_variable(self.variable, self.form) - local term - if variable_name == "locator" then - local locator_type = item.label or "page" - term = self:get_term(locator_type, form) - else - term = self:get_term(variable_name, form) + local is_plural = false + if self.plural == "always" then + is_plural = true + elseif self.plural == "never" then + is_plural = false + elseif self.plural == "contextual" then + is_plural = self:_is_variable_plural(self.variable, context) end - local res = nil - if term then - if plural == "contextual" and self:_is_plural(variable_name, context) or plural == "always" then - res = term:render(context, true) - else - res = term:render(context, false) + local variable = self.variable + if variable == "locator" then + variable = context:get_variable("label") or "page" + if variable == "sub verbo" then + -- bugreports_MovePunctuationInsideQuotesForLocator.txt + variable = "sub-verbo" end - - res = self:strip_periods(res, context) - res = self:case(res, context) - res = self:format(res, context) - res = self:wrap(res, context) end - return res + local text = context:get_simple_term(variable, self.form, is_plural) + if not text or text == "" then + return nil + end + + local inlines = self:render_text_inlines(text, context) + return Rendered:new(inlines, self) end -function Label:_is_plural (variable_name, context) - local variable_type = util.variable_types[variable_name] - -- Don't use self:get_variable here - local variable = context.item[variable_name] - local res = false +function Label:_is_variable_plural(variable, context) + local value = context:get_variable(variable) + if not value then + return false + end + local variable_type = util.variable_types[variable] if variable_type == "name" then - -- Label inside `names` - res = #variable > 1 - + return #variable > 1 elseif variable_type == "number" then - if util.startswith(variable_name, "number-of-") then - res = tonumber(variable) > 1 + if util.startswith(variable, "number-of-") then + return tonumber(value) > 1 else - variable = tostring(variable) - variable = string.gsub(variable, "\\%-", "") - if #util.split(variable, "%s*[,&-]%s*") > 1 then - -- check if contains multiple numbers - -- "i–ix": true - -- res = string.match(tostring(variable), "%d+%D+%d+") ~= nil - res = true - elseif string.match(variable, "%Aand%A") or string.match(variable, "%Aet%A") then - res = true - else - res = false + value = tostring(value) + -- label_CollapsedPageNumberPluralDetection.txt + -- 327\-30 => single + value = string.gsub(value, "\\%-", "") + if string.match(value, "[,&-]") then + return true + elseif string.match(value, util.unicode["en dash"]) then + return true + elseif string.match(value, "%Wand%W") then + return true + elseif string.match(value, "%Wet%W") then + return true end end - else - util.warning("Invalid attribute \"variable\".") end - return res + return false end diff --git a/biblio/citation-style-language/citeproc-node-layout.lua b/biblio/citation-style-language/citeproc-node-layout.lua index 944ed883a3..168f923b08 100644 --- a/biblio/citation-style-language/citeproc-node-layout.lua +++ b/biblio/citation-style-language/citeproc-node-layout.lua @@ -6,229 +6,56 @@ local layout = {} -local richtext = require("citeproc-richtext") -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local SeqIr = require("citeproc-ir-node").SeqIr local util = require("citeproc-util") -local Layout = element.Element:new() +local Layout = Element:derive("layout") -function Layout:render (items, context) - self:debug_info(context) +function Layout:from_node(node) + local o = Layout:new() + o:set_affixes_attributes(node) + o:set_formatting_attributes(node) + o:get_delimiter_attribute(node) - context.items = items + o:process_children_nodes(node) - -- When used within cs:citation, the delimiter attribute may be used to specify a delimiter for cites within a citation. - -- Thus the processing of context is put after render_children(). - if context.mode == "citation" then - if context.options["collapse"] == "citation-number" then - context.build.item_citation_numbers = {} - context.build.item_citation_number_text = {} - end - elseif context.mode == "bibliography" then - context.build.longest_label = "" - context.build.preceding_first_rendered_names = nil - context = self:process_context(context) - end - - local output = {} - local previous_cite = nil - for _, item in ipairs(items) do - - context.item = item - context.variable_attempt = {} - context.suppressed_variables = {} - context.suppress_subsequent_variables = false - if context.mode == "bibliography" then - context.build.first_rendered_names = {} - end - - if not item.position then - item.position = self:_get_position(item, previous_cite, context) - end - - local first = nil - local second = {} - local element_index = 0 - for _, child in ipairs(self:get_children()) do - if child:is_element() then - element_index = element_index + 1 - local text = child:render(item, context) - if element_index == 1 then - first = text - else - table.insert(second, text) - end - end - end - second = self:concat(second, context) - - if context.mode == "bibliography" then - if first and context.options["prefix"] then - first = richtext.new(context.options["prefix"]) .. first - end - if second and context.options["suffix"] then - second = second .. richtext.new(context.options["suffix"]) - end - end - - local res = nil - if context.options["second-field-align"] == "flush" then - if first then - first:add_format("display", "left-margin") - res = first - end - if second then - second:add_format("display", "right-inline") - if res then - res = richtext.concat(res, second) - else - res = second - end - end - else - res = self:concat({first, second}, context) - end - - if context.mode == "citation" then - if res and item["prefix"] then - res = richtext.new(item["prefix"]) .. res - end - if res and item["suffix"] then - res = res .. richtext.new(item["suffix"]) - end - elseif context.mode == "bibliography" then - if not res then - res = richtext.new("[CSL STYLE ERROR: reference with no printed form.]") - end - res = self:wrap(res, context) - -- util.debug(text) - res = res:render(context.engine.formatter, context) - res = context.engine.formatter["@bibliography/entry"](res, context) - end - table.insert(output, res) - previous_cite = item - end - - if context.mode == "citation" then - if next(output) == nil then - return "[CSL STYLE ERROR: reference with no printed form.]" - end - - context = self:process_context(context) - local res - if context.options["collapse"] then - res = self:_collapse_citations(output, context) - else - res = self:concat(output, context) - end - res = self:wrap(res, context) - res = self:format(res, context) - if res then - -- util.debug(res) - res = res:render(context.engine.formatter, context) - end - return res - - else - local params = { - maxoffset = #context.build.longest_label, - } - - return {params, output} - end + return o end -function Layout:_get_position (item, previous_cite, context) - local engine = context.engine - if not engine.registry.registry[item.id] then - return util.position_map["first"] +function Layout:build_ir(engine, state, context) + local ir = self:build_children_ir(engine, state, context) + if not ir then + return nil end - - local position = util.position_map["subsequent"] - -- Find the preceding cite referencing the same item - local preceding_cite = nil - if previous_cite then - -- a. the current cite immediately follows on another cite - if item.id == previous_cite.id then - preceding_cite = previous_cite - end - elseif engine.registry.previous_citation then - -- b. first cite in the citation and previous citation exists - for _, cite in ipairs(engine.registry.previous_citation.citationItems) do - if item.id == cite.id then - preceding_cite = cite - break - end - end - end - - if preceding_cite then - if preceding_cite.locator then - -- Preceding cite does have a locator - if item.locator then - if item.locator == preceding_cite.locator then - position = util.position_map["ibid"] - else - position = util.position_map["ibid-with-locator"] + if context.in_bibliography then + ir.delimiter = self.delimiter + + -- Move affixes of `bibliography > layout` into the right-inline element + -- bugreports_SmallCapsEscape.txt + local has_right_inline = false + if self.affixes or self.formatting then + for i, child_ir in ipairs(ir.children) do + if child_ir.display == "right-inline" then + has_right_inline = true + local right_inline_with_affixes = SeqIr:new({child_ir}, self) + right_inline_with_affixes.affixes = util.clone(self.affixes) + right_inline_with_affixes.formatting = util.clone(self.formatting) + child_ir.display = nil + right_inline_with_affixes.display = "right-inline" + ir.children[i] = right_inline_with_affixes + break end - else - -- the current cite lacks a locator - position = util.position_map["subsequent"] - end - else - -- Preceding cite does not have a locator - if item.locator then - position = util.position_map["ibid-with-locator"] - else - position = util.position_map["ibid"] end end - end - return position -end - -function Layout:_collapse_citations(output, context) - if context.options["collapse"] == "citation-number" then - assert(#output == #context.items) - local citation_numbers = {} - for i, item in ipairs(context.items) do - citation_numbers[i] = context.build.item_citation_numbers[item.id] or 0 + if not has_right_inline then + ir.affixes = util.clone(self.affixes) + ir.formatting = util.clone(self.formatting) end - - local collapsed_output = {} - local citation_number_range_delimiter = util.unicode["en dash"] - local index = 1 - while index <= #citation_numbers do - local stop_index = index + 1 - if output[index] == context.build.item_citation_number_text[index] then - while stop_index <= #citation_numbers do - if output[stop_index] ~= context.build.item_citation_number_text[stop_index] then - break - end - if citation_numbers[stop_index - 1] + 1 ~= citation_numbers[stop_index] then - break - end - stop_index = stop_index + 1 - end - end - - if stop_index >= index + 3 then - local range_text = output[index] .. citation_number_range_delimiter .. output[stop_index - 1] - table.insert(collapsed_output, range_text) - else - for i = index, stop_index - 1 do - table.insert(collapsed_output, output[i]) - end - end - - index = stop_index - end - - return self:concat(collapsed_output, context) end - return self:concat(output, context) + return ir end diff --git a/biblio/citation-style-language/citeproc-node-locale.lua b/biblio/citation-style-language/citeproc-node-locale.lua index 92a604992c..0e8cdd0787 100644 --- a/biblio/citation-style-language/citeproc-node-locale.lua +++ b/biblio/citation-style-language/citeproc-node-locale.lua @@ -6,127 +6,227 @@ local locale = {} -local element = require("citeproc-element") - +local Element = require("citeproc-element").Element +local util = require("citeproc-util") + + +local Locale = Element:derive("locale") + +function Locale:new() + local o = { + xml_lang = nil, + terms = {}, + ordinal_terms = nil, + dates = {}, + style_options = { + -- limit_day_ordinals_to_day_1 = false, + -- punctuation_in_quote = false, + }, + } + setmetatable(o, self) + self.__index = self + return o +end -local Locale = element.Element:new() +function Locale:from_node(node) + local o = Locale:new() + o.xml_lang = node:get_attribute("xml:lang") + o:process_children_nodes(node) + + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "terms" then + o.terms = child.terms_map + o.ordinal_terms = child.ordinal_terms + elseif element_name == "date" then + o.dates[child.form] = child + end + end -function Locale:get_option (key) - local query = string.format("style-options[%s]", key) - local option = self:query_selector(query)[1] - if option then - local value = option:get_attribute(key) - if self.option_type[key] == "integer" then - value = tonumber(value) - elseif self.option_type[key] == "boolean" then - value = (value == "true") - end - return value - else - return nil + for _, child in ipairs(node:get_children()) do + if child:is_element() and child:get_element_name() == "style-options" then + local style_options = child + o.style_options.limit_day_ordinals_to_day_1 = util.to_boolean( + style_options:get_attribute("limit-day-ordinals-to-day-1") + ) + o.style_options.punctuation_in_quote = util.to_boolean( + style_options:get_attribute("punctuation-in-quote") + ) + end end -end -function Locale:get_term (name, form, number, gender) + return o +end - if form == "long" then - form = nil +function Locale:merge(other) + for key, value in pairs(other.terms) do + self.terms[key] = value end - - local match_last - local match_last_two - local match_whole - if number then - assert(type(number) == "number") - match_last = string.format("%s-%02d", name, number % 10) - match_last_two = string.format("%s-%02d", name, number % 100) - match_whole = string.format("%s-%02s", name, number) + -- https://docs.citationstyles.org/en/stable/specification.html#ordinal-suffixes + -- The “ordinal” term, and “ordinal-00” through “ordinal-99” terms, behave + -- differently from other terms when it comes to Locale Fallback. + -- Whereas other terms can be (re)defined individually, (re)defining any of + -- the ordinal terms through cs:locale replaces all previously defined + -- ordinal terms. + if other.ordinal_terms then + self.ordinal_terms = other.ordinal_terms + end + for key, value in pairs(other.dates) do + self.dates[key] = value end + for key, value in pairs(other.style_options) do + if value ~= nil then + self.style_options[key] = value + end + end + return self +end - local res = nil - for _, term in ipairs(self:query_selector("term")) do - -- Use get_path? - local match_name = name - - if number then - local term_match = term:get_attribute("last-two-digits") - if term_match == "whole-number" then - match_name = match_whole - elseif term_match == "last-two-digits" then - match_name = match_last_two - elseif number < 10 then - -- "13" can match only "ordinal-13" not "ordinal-03" - -- It is sliced to "3" in a later checking pass. - match_name = match_last_two +Locale.form_fallbacks = { + ["verb-short"] = {"verb-short", "verb", "long"}, + ["verb"] = {"verb", "long"}, + ["symbol"] = {"symbol", "short", "long"}, + ["short"] = {"short", "long"}, + ["long"] = {"long"}, +} + +-- Keep in sync with Terms:from_node +function Locale:get_simple_term(name, form, plural) + form = form or "long" + for _, fallback_form in ipairs(self.form_fallbacks[form]) do + local key = name + if form ~= "long" then + if not key then print(debug.traceback()) end + key = key .. "/form-" .. fallback_form + end + local term = self.terms[key] + if term then + if plural then + return term.multiple or term.text else - match_name = match_last + return term.single or term.text end end + end + return nil +end - local term_name = term:get_attribute("name") - local term_form = term:get_attribute("form") - if term_form == "long" then - term_form = nil - end - local term_gender = term:get_attribute("gender-form") +function Locale:get_ordinal_term(number, gender) + -- TODO: match and gender - if term_name == match_name and term_form == form and term_gender == gender then - return term - end + local keys = {} + if gender then + if number < 100 then + table.insert(keys, string.format("ordinal-%02d/gender-form-%s/match-whole-number", number, gender)) + end + table.insert(keys, string.format("ordinal-%02d/gender-form-%s/match-last-two-digits", number % 100, gender)) + table.insert(keys, string.format("ordinal-%02d/gender-form-%s/match-last-digit", number % 10, gender)) end - -- Fallback - if form == "verb-sort" then - return self:get_term(name, "verb") - elseif form == "symbol" then - return self:get_term(name, "short") - elseif form == "verb" then - return self:get_term(name, "long") - elseif form == "short" then - return self:get_term(name, "long") + if number < 100 then + table.insert(keys, string.format("ordinal-%02d/match-whole-number", number)) end - - if number and number > 10 then - return self:get_term(name, nil, number % 10, gender) + table.insert(keys, string.format("ordinal-%02d/match-last-two-digits", number % 100)) + table.insert(keys, string.format("ordinal-%02d/match-last-digit", number % 10)) + table.insert(keys, "ordinal") + + for _, key in ipairs(keys) do + local term = self.ordinal_terms[key] + if term then + return term.text + end end + return nil +end - if gender then - return self:get_term(name, nil, number, nil) +function Locale:get_number_gender(name) + local term = self.terms[name] + if term and term.gender then + return term.gender + else + return nil end +end - if number then - return self:get_term(name, nil, nil, nil) - end - return nil +local Terms = Element:derive("terms") + +function Terms:new() + local o = Element.new(self) + o.element_name = "terms" + o.children = {} + o.terms_map = {} + o.ordinal_terms = nil + return o end -local Term = element.Element:new() +function Terms:from_node(node) + local o = Terms:new() + o:process_children_nodes(node) + for _, term in ipairs(o.children) do + local form = term.form + local gender = term.gender + local gender_form = term.gender_form + local match + if util.startswith(term.name, "ordinal-0") then + match = term.match or "last-digit" + elseif util.startswith(term.name, "ordinal-") then + match = term.match or "last-two-digits" + end -function Term:render (context, is_plural) - self:debug_info(context) - context = self:process_context(context) + local key = term.name + if form then + key = key .. '/form-' .. form + end + -- if gender then + -- key = key .. '/gender-' .. gender + -- end + if gender_form then + key = key .. '/gender-form-' .. gender_form + end + if match then + key = key .. '/match-' .. match + end - local output = { - single = self:get_text(), - } - for _, child in ipairs(self:get_children()) do - if child:is_element() then - output[child:get_element_name()] = self:escape(child:get_text()) + if term.name == "ordinal" or util.startswith(term.name, "ordinal-") then + if not o.ordinal_terms then + o.ordinal_terms = {} + end + o.ordinal_terms[key] = term + else + o.terms_map[key] = term end end - local res = output.single - if is_plural then - if output.multiple then - res = output.multiple + return o +end + + +local Term = Element:derive("term") + +function Term:from_node(node) + local o = Term:new() + + o.name = node:get_attribute("name") + o.form = node:get_attribute("form") + o.match = node:get_attribute("match") + o.gender = node:get_attribute("gender") + o.gender_form = node:get_attribute("gender-form") + o.text = node:get_text() + for _, child in ipairs(node:get_children()) do + if child:is_element() then + local element_name = child:get_element_name() + if element_name == "single" then + o.single = child:get_text() + o.text = o.single + elseif element_name == "multiple" then + o.multiple = child:get_text() + end end end - if res == "" then - return nil - end - return res + + return o end diff --git a/biblio/citation-style-language/citeproc-node-names.lua b/biblio/citation-style-language/citeproc-node-names.lua index 72096a58f9..2f02367eea 100644 --- a/biblio/citation-style-language/citeproc-node-names.lua +++ b/biblio/citation-style-language/citeproc-node-names.lua @@ -8,320 +8,839 @@ local names_module = {} local unicode = require("unicode") -local richtext = require("citeproc-richtext") -local element = require("citeproc-element") +local IrNode = require("citeproc-ir-node").IrNode +local NameIr = require("citeproc-ir-node").NameIr +local PersonNameIr = require("citeproc-ir-node").PersonNameIr +local SeqIr = require("citeproc-ir-node").SeqIr +local Rendered = require("citeproc-ir-node").Rendered +local InlineElement = require("citeproc-output").InlineElement +local PlainText = require("citeproc-output").PlainText +local SortStringFormat = require("citeproc-output").SortStringFormat + +local Element = require("citeproc-element").Element local util = require("citeproc-util") -local Name = element.Element:new() - -Name.default_options = { - ["delimiter"] = ", ", - ["delimiter-precedes-et-al"] = "contextual", - ["delimiter-precedes-last"] = "contextual", - ["et-al-min"] = nil, - ["et-al-use-first"] = nil, - ["et-al-subsequent-min"] = nil, - ["et-al-subsequent-use-first "] = nil, - ["et-al-use-last"] = false, - ["form"] = "long", - ["initialize"] = true, - ["initialize-with"] = false, - ["name-as-sort-order"] = false, - ["sort-separator"] = ", ", - ["prefix"] = "", - ["suffix"] = "", -} - -function Name:render (names, context) - self:debug_info(context) - context = self:process_context(context) - - local and_ = context.options["and"] - local delimiter = context.options["delimiter"] - local delimiter_precedes_et_al = context.options["delimiter-precedes-et-al"] - local delimiter_precedes_last = context.options["delimiter-precedes-last"] - local et_al_min = context.options["et-al-min"] - local et_al_use_first = context.options["et-al-use-first"] - local et_al_subsequent_min = context.options["et-al-subsequent-min"] - local et_al_subsequent_use_first = context.options["et-al-subsequent-use-first "] - local et_al_use_last = context.options["et-al-use-last"] - - -- sorting - if context.options["names-min"] then - et_al_min = context.options["names-min"] - end - if context.options["names-use-first"] then - et_al_use_first = context.options["names-use-first"] - end - if context.options["names-use-last"] ~= nil then - et_al_use_last = context.options["names-use-last"] - end - - local form = context.options["form"] - - local et_al_truncate = et_al_min and et_al_use_first and #names >= et_al_min - local et_al_last = et_al_use_last and et_al_use_first <= et_al_min - 2 - - if form == "count" then - if et_al_truncate then - return et_al_use_first +local Names = Element:derive("names") +local Name = Element:derive("name", { + delimiter = ", ", + delimiter_precedes_et_al = "contextual", + delimiter_precedes_last = "contextual", + et_al_use_last = false, + form = "long", + initialize = true, + sort_separator = ", ", +}) +local NamePart = Element:derive("name-part") +local EtAl = Element:derive("et-al") +local Substitute = Element:derive("substitute") + + +-- [Names](https://docs.citationstyles.org/en/stable/specification.html#names) +function Names:new() + local o = Element.new(self) + o.name = nil + o.et_al = nil + o.substitute = nil + o.label = nil + return o +end + +function Names:from_node(node) + local o = Names:new() + o:set_attribute(node, "variable") + o.name = nil + o.et_al = nil + o.substitute = nil + o.label = nil + o.children = {} + o:process_children_nodes(node) + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "name" then + o.name = child + elseif element_name == "et-al" then + o.et_al = child + elseif element_name == "substitute" then + o.substitute = child + elseif element_name == "label" then + o.label = child + if o.name then + child.after_name = true + end else - return #names + util.warning(string.format('Unkown element "{}".', element_name)) end end + o:get_delimiter_attribute(node) + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_formatting_attributes(node) + o:set_text_case_attribute(node) + return o +end - local output = nil +function Names:build_ir(engine, state, context) + -- names_inheritance: names and name attributes inherited from cs:style + -- and cs:citaiton or cs:bibliography + -- name_override: names, name, et-al, label elements inherited in substitute element + local names_inheritance = Names:new() + names_inheritance.delimiter = context.name_inheritance.names_delimiter - local res = nil - local inverted = false + names_inheritance.variable = self.variable - for i, name in ipairs(names) do - if et_al_truncate and i > et_al_use_first then - if et_al_last then - if i == #names then - output = richtext.concat(output, delimiter) - output = output .. util.unicode["horizontal ellipsis"] - output = output .. " " - res = self:render_single_name(name, i, context) - output = output .. res - end - else - if not self:_check_delimiter(delimiter_precedes_et_al, i, inverted) then - delimiter = " " - end - if output then - output = richtext.concat_list({output, context.et_al:render(context)}, delimiter) + for _, attr in ipairs({"delimiter", "affixes", "formatting", "display"}) do + if self[attr] then + names_inheritance[attr] = util.clone(self[attr]) + elseif state.name_override and state.name_override[attr] then + names_inheritance[attr] = util.clone(state.name_override[attr]) + end + end + + if self.name then + names_inheritance.name = util.clone(context.name_inheritance) + for key, value in pairs(self.name) do + names_inheritance.name[key] = util.clone(value) + end + else + if state.name_override then + names_inheritance.name = util.clone(state.name_override.name) + else + names_inheritance.name = util.clone(context.name_inheritance) + end + end + + if self.et_al then + names_inheritance.et_al = util.clone(self.et_al) + elseif state.name_override then + names_inheritance.et_al = util.clone(state.name_override.et_al) + else + names_inheritance.et_al = EtAl:new() + end + + if self.label then + names_inheritance.label = util.clone(self.label) + elseif state.name_override then + names_inheritance.label = util.clone(state.name_override.label) + end + + if context.cite then + local position_level = context.cite.position or context.cite.position_level + if position_level and position_level >= util.position_map["subsequent"] then + if names_inheritance.name.et_al_subsequent_min then + names_inheritance.name.et_al_min = names_inheritance.name.et_al_subsequent_min + end + if names_inheritance.name.et_al_subsequent_use_first then + names_inheritance.name.et_al_use_first = names_inheritance.name.et_al_subsequent_use_first + end + end + end + + local irs = {} + local num_names = 0 + -- util.debug(self.name) + + -- The names element may not hold a variable attribute. + -- substitute_SubstituteOnlyOnceString.txt + if names_inheritance.variable then + for _, variable in ipairs(util.split(names_inheritance.variable)) do + local name_ir = names_inheritance.name:build_ir(variable, names_inheritance.et_al, names_inheritance.label, engine, state, context) + if name_ir and names_inheritance.name.form == "count" then + num_names = num_names + name_ir.name_count + end + if name_ir and name_ir.group_var ~= "missing" then + table.insert(irs, name_ir) + end + end + end + + if names_inheritance.name.form == "count" then + if num_names > 0 then + local ir = Rendered:new({PlainText:new(tostring(num_names))}, self) + ir.name_count = num_names + ir.group_var = "important" + ir = NameIr:new({ir}, self) + ir.name_count = num_names + ir.group_var = "important" + -- util.debug(ir) + return ir + end + else + if #irs > 0 then + local ir = SeqIr:new(irs, self) + ir.group_var = "important" + ir.delimiter = names_inheritance.delimiter + ir.formatting = util.clone(names_inheritance.formatting) + ir.affixes = util.clone(names_inheritance.affixes) + ir.display = names_inheritance.display + return ir + end + end + + if self.substitute then + local new_state = util.clone(state) + new_state.name_override = names_inheritance + for _, substitution in ipairs(self.substitute.children) do + local ir = substitution:build_ir(engine, new_state, context) + if ir and ir.group_var ~= "missing" then + if not ir.person_name_irs or #ir.person_name_irs == 0 then + -- In case of a in + local name_count = ir.name_count + ir = NameIr:new({ir}, self) + ir.name_count = name_count -- sort_AguStyle.txt + ir.group_var = "important" end - break + return ir end - else - if i > 1 then - if i == #names and context.options["and"] then - if self:_check_delimiter(delimiter_precedes_last, i, inverted) then - output = richtext.concat(output, delimiter) - else - output = output .. " " - end - local and_term = "" - if context.options["and"] == "text" then - and_term = self:get_term("and"):render(context) - elseif context.options["and"] == "symbol" then - and_term = self:escape("&") - end - output = output .. and_term .. " " + end + end + + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir + +end + + +function Names:substitute_single_field(result, context) + if not result then + return nil + end + if context.build.first_rendered_names and #context.build.first_rendered_names == 0 then + context.build.first_rendered_names[1] = result + end + result = self:substitute_names(result, context) + return result +end + +function Names:substitute_names(result, context) + if not context.build.first_rendered_names then + return result + end + local name_strings = {} + local match_all + + if #context.build.first_rendered_names > 0 then + match_all = true + else + match_all = false + end + for i, text in ipairs(context.build.first_rendered_names) do + local str = text:render(context.engine.formatter, context) + name_strings[i] = str + if context.build.preceding_first_rendered_names and str ~= context.build.preceding_first_rendered_names[i] then + match_all = false + end + end + + if context.build.preceding_first_rendered_names then + local sub_str = context.options["subsequent-author-substitute"] + local sub_rule = context.options["subsequent-author-substitute-rule"] + + if sub_rule == "complete-all" then + if match_all then + if sub_str == "" then + result = nil else - output = richtext.concat(output, delimiter) + result.contents = {sub_str} end end - res, inverted = self:render_single_name(name, i, context) - if res and res ~= "" then - res = richtext.new(res) - if context.build.first_rendered_names then - table.insert(context.build.first_rendered_names, res) + elseif sub_rule == "complete-each" then + -- In-place substitution + if match_all then + for _, text in ipairs(context.build.first_rendered_names) do + text.contents = {sub_str} end + result = self:concat(context.build.first_rendered_names, context) + end - if output then - output = richtext.concat(output, res) + elseif sub_rule == "partial-each" then + for i, text in ipairs(context.build.first_rendered_names) do + if name_strings[i] == context.build.preceding_first_rendered_names[i] then + text.contents = {sub_str} else - output = res + break end end + result = self:concat(context.build.first_rendered_names, context) + + elseif sub_rule == "partial-first" then + if name_strings[1] == context.build.preceding_first_rendered_names[1] then + context.build.first_rendered_names[1].contents = {sub_str} + end + result = self:concat(context.build.first_rendered_names, context) end end - local ret = self:format(output, context) - ret = self:wrap(ret, context) - return ret + if #context.build.first_rendered_names > 0 then + context.build.first_rendered_names = nil + end + context.build.preceding_first_rendered_names = name_strings + return result end -function Name:_check_delimiter (delimiter_attribute, index, inverted) - -- `delimiter-precedes-et-al` and `delimiter-precedes-last` - if delimiter_attribute == "always" then - return true - elseif delimiter_attribute == "never" then - return false - elseif delimiter_attribute == "contextual" then - if index > 2 then - return true - else - return false - end - elseif delimiter_attribute == "after-inverted-name" then - if inverted then - return true - else - return false +-- [Name](https://docs.citationstyles.org/en/stable/specification.html#name) +function Name:new() + local o = Element.new(self, "name") + + o.family = NamePart:new("family") + o.given = NamePart:new("given") + return o +end + +function Name:from_node(node) + local o = Name:new() + o:set_attribute(node, "and") + o:get_delimiter_attribute(node) + o:set_attribute(node, "delimiter-precedes-et-al") + o:set_attribute(node, "delimiter-precedes-last") + o:set_number_attribute(node, "et-al-min") + o:set_number_attribute(node, "et-al-use-first") + o:set_number_attribute(node, "et-al-subsequent-min") + o:set_number_attribute(node, "et-al-subsequent-use-first") + o:set_bool_attribute(node, "et-al-use-last") + o:set_attribute(node, "form") + o:set_bool_attribute(node, "initialize") + o:set_attribute(node, "initialize-with") + o:set_attribute(node, "name-as-sort-order") + o:set_attribute(node, "sort-separator") + o:set_affixes_attributes(node) + o:set_formatting_attributes(node) + o:process_children_nodes(node) + for _, child in ipairs(o.children) do + if child.name == "family" then + o.family = child + elseif child.name == "given" then + o.given = child end end - return false + if not o.family then + o.family = NamePart:new() + o.family.name = "family" + end + if not o.given then + o.given = NamePart:new() + o.family.name = "given" + end + return o end -function Name:render_single_name (name, index, context) - local form = context.options["form"] - local initialize = context.options["initialize"] - local initialize_with = context.options["initialize-with"] - local name_as_sort_order = context.options["name-as-sort-order"] - if context.sorting then - name_as_sort_order = "all" + +function Name:build_ir(variable, et_al, label, engine, state, context) + -- Returns NameIR + local names + if not state.suppressed[variable] then + names = context:get_variable(variable) + end + if not names then + return nil end - local sort_separator = context.options["sort-separator"] - local demote_non_dropping_particle = context.options["demote-non-dropping-particle"] + if context.sort_key then + self.delimiter = " " + self.name_as_sort_order = "all" + if context.sort_key.names_min then + self.et_al_min = context.sort_key.names_min + end + if context.sort_key.names_use_first then + self.et_al_use_first = context.sort_key.names_use_first + end + if context.sort_key.names_use_last then + self.et_al_use_last = context.sort_key.names_use_last + end + et_al = nil + label = nil + end - -- TODO: make it a module - local function _strip_quotes(str) - if str then - str = string.gsub(str, '"', "") - str = string.gsub(str, "'", util.unicode["apostrophe"]) + local et_al_abbreviation = self.et_al_min and self.et_al_use_first and #names >= self.et_al_min + local use_last = et_al_abbreviation and self.et_al_use_last and self.et_al_use_first <= self.et_al_min - 2 + + if self.form == "count" then + local count + if et_al_abbreviation then + count = self.et_al_use_first + else + count = #names end - return str + local ir = Rendered:new({PlainText:new(tostring(count))}, {}) + ir.name_count = count + ir.group_var = "important" + return ir end - local family = _strip_quotes(name["family"]) or "" - local given = _strip_quotes(name["given"]) or "" - local dp = _strip_quotes(name["dropping-particle"]) or "" - local ndp = _strip_quotes(name["non-dropping-particle"]) or "" - local suffix = _strip_quotes(name["suffix"]) or "" - local literal = _strip_quotes(name["literal"]) or "" + -- TODO: only build names as needed + local full_name_irs = {} + local full_name_str = "" + for i, name_var in ipairs(names) do + local person_name_ir = self:build_person_name_ir(name_var, i == 1, context) + table.insert(full_name_irs, person_name_ir) - if family == "" then - family = literal - if family == "" then - family = given - given = "" + local name_variants = person_name_ir.disam_variants + if full_name_str ~= "" then + full_name_str = full_name_str .. " " end - if family ~= "" then - return family - else - error("Name not avaliable") + full_name_str = full_name_str .. name_variants[#name_variants] + end + + local person_name_irs -- TODO: rename to rendered_name_irs + local hidden_name_irs + if et_al_abbreviation then + person_name_irs = util.slice(full_name_irs, 1, self.et_al_use_first) + hidden_name_irs = util.slice(full_name_irs, self.et_al_use_first + 1, #full_name_irs) + if use_last then + table.insert(person_name_irs, full_name_irs[#full_name_irs]) + table.remove(hidden_name_irs, #hidden_name_irs) + end + else + person_name_irs = util.slice(full_name_irs, 1, #full_name_irs) + hidden_name_irs = {} + end + + + local and_term_ir + if not context.sort_key then + -- sort_WithAndInOneEntry.txt + local and_term + if self["and"] == "text" then + and_term = context.locale:get_simple_term("and") + elseif self["and"] == "symbol" then + and_term = "&" + end + if and_term then + and_term_ir = Rendered:new({PlainText:new(and_term .. " ")}, {}) end end - if initialize_with then - given = self:initialize(given, initialize_with, context) + local et_al_ir + if et_al and et_al_abbreviation and not use_last then + et_al_ir = et_al:build_ir(engine, state, context) end - local demote_ndp = false -- only active when form == "long" - if demote_non_dropping_particle == "display-and-sort" or - demote_non_dropping_particle == "sort-only" and context.sorting then - demote_ndp = true - else -- demote_non_dropping_particle == "never" - demote_ndp = false + local irs = self:join_person_name_irs(person_name_irs, and_term_ir, et_al_ir, use_last) + + local ir = NameIr:new(irs, self) + + ir.name_inheritance = self + ir.name_variable = names + ir.and_term_ir = and_term_ir + ir.et_al_ir = et_al_ir + ir.et_al_abbreviation = et_al_abbreviation + ir.use_last = use_last + + ir.full_name_irs = full_name_irs + ir.full_name_str = full_name_str + ir.person_name_irs = person_name_irs + ir.hidden_name_irs = hidden_name_irs + + -- etal_UseZeroFirst.txt: et-al-use-first="0" + if #irs == 0 then + ir.group_var = "missing" + return ir + else + ir.group_var = "important" end - local family_name_part = nil - local given_name_part = nil - for _, child in ipairs(self:get_children()) do - if child:is_element() and child:get_element_name() == "name-part" then - local name_part = child:get_attribute("name") - if name_part == "family" then - family_name_part = child - elseif name_part == "given" then - given_name_part = child + ir.formatting = self.formatting + ir.affixes = self.affixes + + irs = {ir} + + if label then + local is_plural = (label.plural == "always" or (label.plural == "contextual" and #names > 1)) + local label_term = context.locale:get_simple_term(variable, label.form, is_plural) + if label_term and label_term ~= "" then + local inlines = label:render_text_inlines(label_term, context) + local label_ir = Rendered:new(inlines, label) + if label.after_name then + table.insert(irs, label_ir) + else + table.insert(irs, 1, label_ir) end end end - local res = nil - local inverted = false - if form == "long" then - local order - local suffix_separator = sort_separator - if not util.has_romanesque_char(name["family"]) then - order = {family, given} - inverted = true - sort_separator = "" - elseif name_as_sort_order == "all" or (name_as_sort_order == "first" and index == 1) then - - -- "Alan al-One" - local hyphen_parts = util.split(family, "%-", 1) - if #hyphen_parts > 1 then - local particle - particle, family = table.unpack(hyphen_parts) - particle = particle .. "-" - ndp = richtext.concat(ndp, particle) + ir = SeqIr:new(irs, self) + + -- Suppress substituted name variable + if state.name_override and not context.sort_key then + state.suppressed[variable] = true + end + + return ir +end + +function Name:build_person_name_ir(name, is_first, context) + local is_latin = util.has_romanesque_char(name.family) + local is_inverted = (name.family and name.family ~= "" and is_latin and + (self.name_as_sort_order == "all" or (self.name_as_sort_order == "first" and is_first))) + + local inlines = self:render_person_name(name, is_first, is_latin, is_inverted, context) + local person_name_ir = PersonNameIr:new(inlines, self) + + person_name_ir.is_inverted = is_inverted + + local output_format = SortStringFormat:new() + person_name_ir.name_output = output_format:output(inlines) + person_name_ir.disam_variants_index = 1 + + person_name_ir.disam_variants = {person_name_ir.name_output} + person_name_ir.disam_inlines = {inlines} + + if context.area.disambiguate_add_givenname and not context.sort_key then + local disam_name = util.clone(self) + if disam_name.form == "short" then + disam_name.form = "long" + if disam_name.initialize and disam_name.initialize_with then + local name_inlines = disam_name:render_person_name(name, is_first, is_latin, is_inverted, context) + local disam_variant = output_format:output(name_inlines) + local last_variant = person_name_ir.disam_variants[#person_name_ir.disam_variants] + if disam_variant ~= last_variant then + table.insert(person_name_ir.disam_variants, disam_variant) + person_name_ir.disam_inlines[disam_variant] = name_inlines + end end + end - if family_name_part then - family = family_name_part:format_name_part(family, context) - ndp = family_name_part:format_name_part(ndp, context) + local givenname_disambiguation_rule = context.area.givenname_disambiguation_rule + local only_initials = (givenname_disambiguation_rule == "all-names-with-initials" or + givenname_disambiguation_rule == "primary-name-with-initials") + if disam_name.initialize and not only_initials then + disam_name.initialize = false + local name_inlines = disam_name:render_person_name(name, is_first, is_latin, is_inverted, context) + local disam_variant = output_format:output(name_inlines) + local last_variant = person_name_ir.disam_variants[#person_name_ir.disam_variants] + if disam_variant ~= last_variant then + table.insert(person_name_ir.disam_variants, disam_variant) + person_name_ir.disam_inlines[disam_variant] = name_inlines end - if given_name_part then - given = given_name_part:format_name_part(given, context) - dp = family_name_part:format_name_part(dp, context) + end + + context.sort_key = true + local full_name_inlines = disam_name:render_person_name(name, is_first, is_latin, is_inverted, context) + -- full_name is used for comparison in disambiguation + person_name_ir.full_name = output_format:output(full_name_inlines) + context.sort_key = false + end + + return person_name_ir +end + +function Name:render_person_name(name, is_first, is_latin, is_inverted, context) + -- Return: inlines + -- TODO + local is_sort = context.sort_key + local demote_ndp = (context.style.demote_non_dropping_particle == "display-and-sort" or + (is_sort and context.style.demote_non_dropping_particle == "sort-only")) + + local name_part_tokens = self:get_display_order(name, self.form, is_latin, is_sort, is_inverted, demote_ndp) + -- util.debug(name) + -- util.debug(name_part_tokens) + + local inlines = {} + for i, token in ipairs(name_part_tokens) do + if token == "family" or token == "ndp-family" or token == "dp-ndp-family-suffix" then + local family_inlines = self:render_family(name, token, context) + util.extend(inlines, family_inlines) + + elseif token == "given" or token == "given-dp" or token == "given-dp-ndp" then + local given_inlines = self:render_given(name, token, context) + util.extend(inlines, given_inlines) + + elseif token == "dp" or token == "dp-ndp" then + local particle_inlines = self:render_particle(name, token, context) + util.extend(inlines, particle_inlines) + + elseif token == "suffix" then + local text = name.suffix or "" + util.extend(inlines, InlineElement:parse(text, context)) + + elseif token == "literal" then + local literal_inlines = self.family:format_text_case(name.literal, context) + util.extend(inlines, literal_inlines) + + elseif token == "space" then + table.insert(inlines, PlainText:new(" ")) + + elseif token == "wide-space" then + table.insert(inlines, PlainText:new(" ")) + + elseif token == "sort-separator" then + table.insert(inlines, PlainText:new(self.sort_separator)) + end + end + -- util.debug(inlines) + return inlines +end + +-- Name-part Order +-- https://docs.citationstyles.org/en/stable/specification.html#name-part-order +function Name:get_display_order(name, form, is_latin, is_sort, is_inverted, demote_ndp) + if is_sort then + if not name.family then + -- The literal is compared with the literal + if self.form == "long" then + return {"literal", "wide-space", "wide-space", "wide-space"} + else + return {"literal", "wide-space"} end + end + if not is_latin then + if form == "long" and name.given then + return {"family", "given"} + else + return {"family"} + end + end + + if self.form == "long" then if demote_ndp then - given = richtext.concat_list({given, dp, ndp}, " ") + return {"family", "wide-space", "dp-ndp", "wide-space", "given", "wide-space", "suffix"} else - family = richtext.concat_list({ndp, family}, " ") - given = richtext.concat_list({given, dp}, " ") + return {"ndp-family", "wide-space", "dp", "wide-space", "given", "wide-space", "suffix"} end + else + if demote_ndp then + return {"family", "wide-space", "dp-ndp"} + else + return {"ndp-family", "wide-space", "dp"} + end + end + end + + if not name.family then + if name.literal then + return {"literal"} + else + util.error("Invalid name") + end + end + + if not is_latin then + if form == "long" and name.given then + return {"family", "given"} + else + return {"family"} + end + end - if family_name_part then - family = family_name_part:wrap_name_part(family, context) + if form == "short" then + return {"ndp-family"} + end + + local ndp = name["non-dropping-particle"] + local dp = name["dropping-particle"] + + local name_part_tokens = {"family"} + if name.given then + if is_inverted then + if demote_ndp then + name_part_tokens = {"family", "sort-separator", "given-dp-ndp"} + else + name_part_tokens = {"ndp-family", "sort-separator", "given-dp"} end - if given_name_part then - given = given_name_part:wrap_name_part(given, context) + else + name_part_tokens = {"given", "space", "dp-ndp-family-suffix"} + end + else + if is_inverted then + if demote_ndp then + if ndp or dp then + name_part_tokens = {"family", "sort-separator", "dp-ndp"} + else + name_part_tokens = {"family"} + end + else + name_part_tokens = {"ndp-family"} end + else + name_part_tokens = {"dp-ndp-family-suffix"} + end + end - order = {family, given, suffix} - inverted = true + if name.suffix and is_inverted then + if is_inverted or name["comma-suffix"] then + table.insert(name_part_tokens, "sort-separator") + table.insert(name_part_tokens, "suffix") + elseif string.match(name.suffix, "^%p") then + table.insert(name_part_tokens, "sort-separator") + table.insert(name_part_tokens, "suffix") else - if family_name_part then - family = family_name_part:format_name_part(family, context) - ndp = family_name_part:format_name_part(ndp, context) + table.insert(name_part_tokens, "space") + table.insert(name_part_tokens, "suffix") + end + end + + return name_part_tokens +end + +function Name:render_family(name, token, context) + local inlines = {} + local name_part + + if token == "dp-ndp-family-suffix" then + local dp_part = name["dropping-particle"] + if dp_part then + name_part = dp_part + local dp_inlines = self.given:format_text_case(dp_part, context) + util.extend(inlines, dp_inlines) + end + end + + if token == "dp-ndp-family-suffix" or token == "ndp-family" then + local ndp_part = name["non-dropping-particle"] + if ndp_part then + if context.sort_key then + ndp_part = self:format_sort_particle(ndp_part) end - if given_name_part then - given = given_name_part:format_name_part(given, context) - dp = family_name_part:format_name_part(dp, context) + if #inlines > 0 then + table.insert(inlines, PlainText:new(" ")) end + name_part = ndp_part + local ndp_inlines = self.family:format_text_case(ndp_part, context) + util.extend(inlines, ndp_inlines) + end + end - family = richtext.concat_list({dp, ndp, family}, " ") - if name["comma-suffix"] then - suffix_separator = ", " + local family = name.family + if context.sort_key then + -- Remove brackets for sorting: sort_NameVariable.txt + family = string.gsub(family, "[%[%]]", "") + end + + local family_inlines = self.family:format_text_case(family, context) + if #inlines > 0 then + if not string.match(name_part, "^%l'$") and + not string.match(name_part, "^%l’$") and + not util.endswith(name_part, "-") then + table.insert(inlines, PlainText:new(" ")) + end + end + util.extend(inlines, family_inlines) + + if token == "dp-ndp-family-suffix" then + local suffix_part = name.suffix + if suffix_part then + if name["comma-suffix"] or util.startswith(suffix_part, "!") then + -- force use sort-separator exclamation prefix: magic_NameSuffixWithComma.txt + -- "! Jr." => "Jr." + table.insert(inlines, PlainText:new(self.sort_separator)) + suffix_part = string.gsub(suffix_part, "^%p%s*", "") else - suffix_separator = " " + table.insert(inlines, PlainText:new(" ")) end - family = richtext.concat_list({family, suffix}, suffix_separator) + table.insert(inlines, PlainText:new(suffix_part)) + end + end - if family_name_part then - family = family_name_part:wrap_name_part(family, context) - end - if given_name_part then - given = given_name_part:wrap_name_part(given, context) - end + inlines = self.family:affixed(inlines) + return inlines +end + +function Name:render_given(name, token, context) + local given = name.given - order = {given, family} - sort_separator = " " + if context.sort_key then + -- The empty given name is needed for evaluate the sort key. + if not given then + return {PlainText:new("")} end - res = richtext.concat_list(order, sort_separator) + -- Remove brackets for sorting: sort_NameVariable.txt + given = string.gsub(given, "[%[%]]", "") + end - elseif form == "short" then - if family_name_part then - family = family_name_part:format_name_part(family, context) - ndp = family_name_part:format_name_part(ndp, context) + if self.initialize_with then + given = self:initialize_name(given, self.initialize_with, context.style.initialize_with_hyphen) + end + local inlines = self.given:format_text_case(given, context) + + if token == "given-dp" or token == "given-dp-ndp" then + local name_part = name["dropping-particle"] + if name_part then + table.insert(inlines, PlainText:new(" ")) + local dp_inlines = self.given:format_text_case(name_part, context) + util.extend(inlines, dp_inlines) end - family = util.concat({ndp, family}, " ") - if family_name_part then - family = family_name_part:wrap_name_part(family, context) + end + + if token == "given-dp-ndp" then + local name_part = name["non-dropping-particle"] + if name_part then + table.insert(inlines, PlainText:new(" ")) + local ndp_inlines = self.family:format_text_case(name_part, context) + util.extend(inlines, ndp_inlines) + end + end + + inlines = self.given:affixed(inlines) + return inlines +end + +-- sort_LeadingApostropheOnNameParticle.txt +-- "’t " => "t" +function Name:format_sort_particle(particle) + particle = string.gsub(particle, "^'", "") + particle = string.gsub(particle, "^’", "") + return particle +end + +function Name:render_particle(name, token, context) + local inlines = {} + + local dp_part = name["dropping-particle"] + if dp_part then + dp_part = self:format_sort_particle(dp_part) + local dp_inlines = self.given:format_text_case(dp_part, context) + util.extend(inlines, dp_inlines) + end + + if token == "dp-ndp" then + local ndp_part = name["non-dropping-particle"] + if ndp_part then + if #inlines > 0 then + table.insert(inlines, PlainText:new(" ")) end - res = family - else - error(string.format('Invalid attribute form="%s" of "name".', form)) + ndp_part = self:format_sort_particle(ndp_part) + local ndp_inlines = self.family:format_text_case(ndp_part, context) + util.extend(inlines, ndp_inlines) + end + end + + return inlines +end + +function Name:_check_delimiter(delimiter_attribute, num_first_names, inverted) + -- `delimiter-precedes-et-al` and `delimiter-precedes-last` + if delimiter_attribute == "always" then + return true + elseif delimiter_attribute == "never" then + return false + elseif delimiter_attribute == "contextual" then + if num_first_names > 1 then + return true + else + return false + end + elseif delimiter_attribute == "after-inverted-name" then + if inverted then + return true + else + return false + end end - return res, inverted + return false end -function Name:initialize (given, terminator, context) +-- TODO: initialize name with markups +-- name_InTextMarkupInitialize.txt +-- name_InTextMarkupNormalizeInitials.txt +function Name:initialize_name(given, with, initialize_with_hyphen) if not given or given == "" then return "" end - local initialize = context.options["initialize"] - if context.options["initialize-with-hyphen"] == false then + if initialize_with_hyphen == false then given = string.gsub(given, "-", " ") end @@ -367,9 +886,9 @@ function Name:initialize (given, terminator, context) name_list[i-1] = name_list[i-1] .. " " end elseif is_abbreviation then - name_list[i] = name .. terminator + name_list[i] = name .. with else - if initialize then + if self.initialize then if util.is_upper(name) then name = first_letter else @@ -389,7 +908,7 @@ function Name:initialize (given, terminator, context) end name = abbreviation end - name_list[i] = name .. terminator + name_list[i] = name .. with else name_list[i] = name .. " " end @@ -412,287 +931,176 @@ function Name:initialize (given, terminator, context) end -local NamePart = element.Element:new() - -function NamePart:format_name_part(name_part, context) - context = self:process_context(context) - local res = self:case(name_part, context) - res = self:format(res, context) - return res -end - -function NamePart:wrap_name_part(name_part, context) - context = self:process_context(context) - local res = self:wrap(name_part, context) - return res -end - - -local EtAl = element.Element:new() - -EtAl.default_options = { - term = "et-al", -} - -EtAl.render = function (self, context) - self:debug_info(context) - context = self:process_context(context) - local res = self:get_term(context.options["term"]):render(context) - res = self:format(res, context) - return res -end - - -local Substitute = element.Element:new() +function Name:join_person_name_irs(rendered_name_irs, and_term_ir, et_al_ir, use_last) + local first_items = rendered_name_irs + local last_item + if et_al_ir then + first_items = rendered_name_irs + last_item = et_al_ir + elseif #rendered_name_irs > 1 then + first_items = util.slice(rendered_name_irs, 1, #rendered_name_irs - 1) + last_item = rendered_name_irs[#rendered_name_irs] + end -function Substitute:render (item, context) - self:debug_info(context) + local irs = {} - if context.suppressed_variables then - -- true in layout, not in sort - context.suppress_subsequent_variables = true + for i, person_name_ir in ipairs(first_items) do + if i > 1 then + table.insert(irs, Rendered:new({PlainText:new(self.delimiter)}, self)) + end + table.insert(irs, person_name_ir) end - for i, child in ipairs(self:get_children()) do - if child:is_element() then - local result = child:render(item, context) - if result and result ~= "" then - return result + if last_item then + if use_last then + local delimiter = self.delimiter .. util.unicode["horizontal ellipsis"] .. " " + table.insert(irs, Rendered:new({PlainText:new(delimiter)}, self)) + table.insert(irs, last_item) + elseif et_al_ir then + if #first_items > 0 then + local inverted = first_items[#first_items].is_inverted + local use_delimiter = self:_check_delimiter(self.delimiter_precedes_et_al, #first_items, inverted) + if use_delimiter then + table.insert(irs, Rendered:new({PlainText:new(self.delimiter)}, self)) + elseif not et_al_ir.starts_with_cjk then + -- name_EtAlWithCombined.txt + table.insert(irs, Rendered:new({PlainText:new(" ")}, self)) + end + table.insert(irs, last_item) end + else + local inverted = first_items[#first_items].is_inverted + local use_delimiter = self:_check_delimiter(self.delimiter_precedes_last, #first_items, inverted) + if use_delimiter or not and_term_ir then + table.insert(irs, Rendered:new({PlainText:new(self.delimiter)}, self)) + else + table.insert(irs, Rendered:new({PlainText:new(" ")}, self)) + end + if and_term_ir and not et_al_ir then + table.insert(irs, and_term_ir) + end + table.insert(irs, last_item) end end - return nil -end - -local Names = element.Element:new() - -function Names:render (item, context) - self:debug_info(context) - context = self:process_context(context) + return irs +end - local names_delimiter = context.options["names-delimiter"] - if names_delimiter then - context.options["delimiter"] = names_delimiter +-- For use in disambiguate-add-names +function Name:expand_one_name(name_ir) + local rendered_name_irs = name_ir.person_name_irs + local hidden_name_irs = name_ir.hidden_name_irs + if #hidden_name_irs == 0 then + return nil end - - -- Inherit attributes of parent `names` element - local names_element = context.names_element - if names_element then - for key, value in pairs(names_element._attr) do - context.options[key] = value - end - for key, value in pairs(self._attr) do - context.options[key] = value - end + local person_name_ir_to_add = hidden_name_irs[1] + if name_ir.use_last then + table.insert(rendered_name_irs, #rendered_name_irs, person_name_ir_to_add) else - context.names_element = self - context.variable = context.options["variable"] - end - - local name, et_al, label - -- The position of cs:label relative to cs:name determines the order of - -- the name and label in the rendered text. - local label_position = nil - for _, child in ipairs(self:get_children()) do - if child:is_element() then - local element_name = child:get_element_name() - if element_name == "name" then - name = child - if label then - label_position = "before" - end - elseif element_name == "et-al" then - et_al = child - elseif element_name == "label" then - label = child - if name then - label_position = "after" - end - end - end + table.insert(rendered_name_irs, person_name_ir_to_add) end - if label_position then - context.label_position = label_position - else - label_position = context.label_position or "after" + table.remove(hidden_name_irs, 1) + if #hidden_name_irs == 0 then + if name_ir.et_al_abbreviation then + name_ir.et_al_abbreviation = false + end + if name_ir.use_last then + name_ir.use_last = false + end end - -- local name = self:get_child("name") - if not name then - name = context.name_element + local and_term_ir = name_ir.and_term_ir + local et_al_ir + if name_ir.et_al_abbreviation then + et_al_ir = name_ir.et_al_ir end - if not name then - name = self:create_element("name", {}, self) - Name:set_base_class(name) - end - context.name_element = name + local use_last = name_ir.use_last - -- local et_al = self:get_child("et-al") - if not et_al then - et_al = context.et_al - end - if not et_al then - et_al = self:create_element("et-al", {}, self) - EtAl:set_base_class(et_al) - end - context.et_al = et_al + name_ir.children = self:join_person_name_irs(rendered_name_irs, and_term_ir, et_al_ir, use_last) + return person_name_ir_to_add +end - -- local label = self:get_child("label") - if label then - context.label = label - else - label = context.label - end - - local sub_str = nil - if context.mode == "bibliography" and not context.sorting then - sub_str = context.options["subsequent-author-substitute"] - -- if sub_str and #context.build.preceding_first_rendered_names == 0 then - -- context.rendered_names = {} - -- else - -- sub_str = nil - -- context.rendered_names = nil - -- end - end - - local variable_names = context.options["variable"] or context.variable - local ret = nil - - if variable_names then - local output = {} - local num_names = 0 - for _, role in ipairs(util.split(variable_names)) do - local names = self:get_variable(item, role, context) - - table.insert(context.variable_attempt, names ~= nil) - - if names then - local res = name:render(names, context) - if res then - if type(res) == "number" then -- name[form="count"] - num_names = num_names + res - elseif label and not context.sorting then - -- drop name label in sorting - local label_result = label:render(role, context) - if label_result then - if label_position == "before" then - res = richtext.concat(label_result, res) - else - res = richtext.concat(res, label_result) - end - end - end - end - table.insert(output, res) - end - end - if num_names > 0 then - ret = tostring(num_names) - else - ret = self:concat(output, context) - if ret and sub_str and context.build.first_rendered_names then - ret = self:substitute_names(ret, context) - end - end +-- [Name-part](https://docs.citationstyles.org/en/stable/specification.html#name-part-formatting) +function NamePart:new(name) + local o = Element.new(self) + o.name = name + return o +end + +function NamePart:from_node(node) + local o = NamePart:new() + o:set_attribute(node, "name") + o:set_formatting_attributes(node) + o:set_text_case_attribute(node) + o:set_affixes_attributes(node) + return o +end + +function NamePart:format_text_case(text, context) + local output_format = context.format + local inlines = InlineElement:parse(text, context) + local is_english = context:is_english() + if not output_format then + print(debug.traceback()) end + output_format:apply_text_case(inlines, self.text_case, is_english) - if ret then - ret = self:format(ret, context) - ret = self:wrap(ret, context) - ret = self:display(ret, context) - return ret - else - local substitute = self:get_child("substitute") - if substitute then - ret = substitute:render(item, context) + inlines = output_format:with_format(inlines, self.formatting) + return inlines +end + +function NamePart:affixed(inlines) + if self.affixes then + if self.affixes.prefix then + table.insert(inlines, 1, PlainText:new(self.affixes.prefix)) end - if ret and sub_str then - ret = self:substitute_single_field(ret, context) + if self.affixes.suffix then + table.insert(inlines, PlainText:new(self.affixes.suffix)) end - return ret end + return inlines end -function Names:substitute_single_field(result, context) - if not result then - return nil - end - if context.build.first_rendered_names and #context.build.first_rendered_names == 0 then - context.build.first_rendered_names[1] = result - end - result = self:substitute_names(result, context) - return result -end -function Names:substitute_names(result, context) - if not context.build.first_rendered_names then - return result - end - local name_strings = {} - local match_all +-- [Et-al](https://docs.citationstyles.org/en/stable/specification.html#et-al) +EtAl.term = "et-al" - if #context.build.first_rendered_names > 0 then - match_all = true - else - match_all = false +function EtAl:from_node(node) + local o = EtAl:new() + o:set_attribute(node, "term") + o:set_formatting_attributes(node) + return o +end + +function EtAl:build_ir(engine, state, context) + local term = context.locale:get_simple_term(self.term) + if not term then + return term end - for i, text in ipairs(context.build.first_rendered_names) do - local str = text:render(context.engine.formatter, context) - name_strings[i] = str - if context.build.preceding_first_rendered_names and str ~= context.build.preceding_first_rendered_names[i] then - match_all = false - end + local inlines = InlineElement:parse(term, context) + if #inlines == 0 then + return nil end - if context.build.preceding_first_rendered_names then - local sub_str = context.options["subsequent-author-substitute"] - local sub_rule = context.options["subsequent-author-substitute-rule"] + inlines = context.format:with_format(inlines, self.formatting) - if sub_rule == "complete-all" then - if match_all then - if sub_str == "" then - result = nil - else - result.contents = {sub_str} - end - end + local ir = Rendered:new(inlines, self) - elseif sub_rule == "complete-each" then - -- In-place substitution - if match_all then - for _, text in ipairs(context.build.first_rendered_names) do - text.contents = {sub_str} - end - result = self:concat(context.build.first_rendered_names, context) - end - - elseif sub_rule == "partial-each" then - for i, text in ipairs(context.build.first_rendered_names) do - if name_strings[i] == context.build.preceding_first_rendered_names[i] then - text.contents = {sub_str} - else - break - end - end - result = self:concat(context.build.first_rendered_names, context) - - elseif sub_rule == "partial-first" then - if name_strings[1] == context.build.preceding_first_rendered_names[1] then - context.build.first_rendered_names[1].contents = {sub_str} - end - result = self:concat(context.build.first_rendered_names, context) - end + if util.is_cjk_char(utf8.codepoint(term, 1)) then + ir.starts_with_cjk = true end - if #context.build.first_rendered_names > 0 then - context.build.first_rendered_names = nil - end - context.build.preceding_first_rendered_names = name_strings - return result + return ir end +function Substitute:from_node(node) + local o = Substitute:new() + o:process_children_nodes(node) + return o +end + + names_module.Names = Names names_module.Name = Name names_module.NamePart = NamePart diff --git a/biblio/citation-style-language/citeproc-node-number.lua b/biblio/citation-style-language/citeproc-node-number.lua index ba096157aa..4ef308e3d0 100644 --- a/biblio/citation-style-language/citeproc-node-number.lua +++ b/biblio/citation-style-language/citeproc-node-number.lua @@ -6,97 +6,63 @@ local number_module = {} -local element = require("citeproc-element") -local util = require("citeproc-util") - - -local Number = element.Element:new() - -function Number:render (item, context) - self:debug_info(context) - context = self:process_context(context) - local variable = context.options["variable"] - local content = self:get_variable(item, variable, context) +local Element = require("citeproc-element").Element - table.insert(context.variable_attempt, content ~= nil) +local Rendered = require("citeproc-ir-node").Rendered - if not content then - return nil - end - - local numbers = {} - local punct_list = {} - local last_position = 1 - for number, punct, pos in string.gmatch(content, "(.-)%s*([-,&])%s*()") do - table.insert(numbers, number) - table.insert(punct_list, punct) - last_position = pos - end - table.insert(numbers, string.sub(content, last_position)) +local util = require("citeproc-util") - local res = "" - for i, number in ipairs(numbers) do - local punct = punct_list[i] - number = self:_format_single_number(number, context) - res = res .. number - if punct == "-" then - res = res .. punct - elseif punct == "," then - res = res .. punct .. " " - elseif punct == "&" then - res = res .. " " .. punct .. " " - end - end +local Number = Element:derive("number") - res = self:case(res, context) - res = self:wrap(res, context) - res = self:display(res, context) +function Number:new(node) + local o = { + element_name = "number", + form = "numeric", + } + setmetatable(o, self) + self.__index = self + return o +end - return res +function Number:from_node(node) + local o = Number:new() + o:set_attribute(node, "variable") + o:set_attribute(node, "form") + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_formatting_attributes(node) + o:set_text_case_attribute(node) + return o end -function Number:_format_single_number(number, context) - local form = context.options["form"] or "numeric" - if form == "numeric" or not string.match(number, "^%d+$") then - return number +function Number:build_ir(engine, state, context) + local number + if not state.suppressed[self.variable] then + number = context:get_variable(self.variable, self.form) end - number = tonumber(number) - if form == "ordinal" or form == "long-ordinal" then - return self:_format_oridinal(number, form, context) - elseif form == "roman" then - return util.convert_roman(number) + if not number then + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir end -end - -function Number:_format_oridinal(number, form, context) - assert(type(number) == "number") - local variable = context.options["variable"] - if form == "long-ordinal" then - if number < 1 or number > 10 then - form = "ordinal" - end + if type(number) == "number" then + number = tostring(number) + number = self:format_number(number, self.variable, self.form, context) + elseif util.is_numeric(number) then + number = self:format_number(number, self.variable, self.form, context) end - local gender = nil - local term = self:get_term(variable) - if term then - gender = term:get_attribute("gender") - end + local inlines = self:render_text_inlines(number, context) + local ir = Rendered:new(inlines, self) + ir.group_var = "important" - term = self:get_term(form, nil, number, gender) - local res = term:render(context) - if form == "ordinal" then - if res then - return tostring(number) .. res - else - res = tostring(number) - end - else - return res + -- Suppress substituted name variable + if state.name_override and not context.sort_key then + state.suppressed[self.variable] = true end - return res + return ir end diff --git a/biblio/citation-style-language/citeproc-node-sort.lua b/biblio/citation-style-language/citeproc-node-sort.lua index 1b82d21b55..876c3257da 100644 --- a/biblio/citation-style-language/citeproc-node-sort.lua +++ b/biblio/citation-style-language/citeproc-node-sort.lua @@ -8,57 +8,80 @@ local sort = {} local unicode = require("unicode") -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local Date = require("citeproc-node-date").Date local names = require("citeproc-node-names") -local date = require("citeproc-node-date") +local InlineElement = require("citeproc-output").InlineElement local util = require("citeproc-util") -local Sort = element.Element:new() +-- [Sorting](https://docs.citationstyles.org/en/stable/specification.html#sorting) +local Sort = Element:derive("sort") -function Sort:sort (items, context) +function Sort:from_node(node) + local o = Sort:new() + o.children = {} + + o:process_children_nodes(node) + o.sort_directions = {} + for i, key in ipairs(o.children) do + o.sort_directions[i] = (key.sort ~= "descending") + end + table.insert(o.sort_directions, true) + + return o +end + +function Sort:sort(items, state, context) -- key_map = { -- id1 = {key1, key2, ...}, -- id2 = {key1, key2, ...}, -- ... -- } - context.variable_attempt = {} - local key_map = {} - local sort_directions = {} + local sort_directions = self.sort_directions -- true: ascending -- false: descending - if not Sort.collator_obj then - local lang = context.style.lang + if not Sort.collator then + local lang = context.engine.lang local language = string.sub(lang, 1, 2) -- It's 6 seconds slower to run the whole test-suite if these package -- loading statements are put in the header. - local ducet = require("lua-uca.lua-uca-ducet") - local collator = require("lua-uca.lua-uca-collator") - local languages = require("lua-uca.lua-uca-languages") - local collator_obj = collator.new(ducet) - if languages[language] then - Sort.collator_obj = languages[language](collator_obj) - else - util.warning(string.format('Lcoale "%s" is not supported.', lang)) + local uca_ducet = require("lua-uca.lua-uca-ducet") + local uca_collator = require("lua-uca.lua-uca-collator") + Sort.collator = uca_collator.new(uca_ducet) + if language ~= "en" then + local uca_languages = require("lua-uca.lua-uca-languages") + if uca_languages[language] then + Sort.collator = uca_languages[language](Sort.collator) + else + util.warning(string.format('Locale "%s" is not provided by lua-uca. The sorting order may be incorrect.', lang)) + end end end - for _, item in ipairs(items) do - if not key_map[item.id] then - key_map[item.id] = {} - - context.item = item - for i, key in ipairs(self:query_selector("key")) do - if sort_directions[i] == nil then - local direction = (key:get_attribute("sort") ~= "descending") - sort_directions[i] = direction - end - local value = key:render(item, context) - table.insert(key_map[item.id], value) + -- TODO: optimize: use cached values for fixed keys + for i, item in ipairs(items) do + key_map[item.id] = {} + + context.id = item.id + context.cite = item + context.reference = context.engine.registry.registry[item.id] + + for j, key in ipairs(self.children) do + if context.reference then + context.sort_key = key + local key_str = key:eval(context.engine, state, context) + key_map[item.id][j] = key_str + else + -- The entry is missing + key_map[item.id][j] = false end end + -- To preserve the original order of items with same sort keys + -- sort_NameImplicitSortOrderAndForm.txt + table.insert(key_map[item.id], i) end -- util.debug(key_map) @@ -66,6 +89,7 @@ function Sort:sort (items, context) local function compare_entry(item1, item2) return self.compare_entry(key_map, sort_directions, item1, item2) end + -- util.debug(items) table.sort(items, compare_entry) return items @@ -80,8 +104,8 @@ function Sort.compare(value1, value2) end function Sort.compare_strings(str1, str2) - if Sort.collator_obj then - return Sort.collator_obj:compare_strings(str1, str2) + if Sort.collator then + return Sort.collator:compare_strings(str1, str2) else return str1 < str2 end @@ -109,77 +133,98 @@ function Sort.compare_entry(key_map, sort_directions, item1, item2) end end -local Key = element.Element:new() - -function Key:render (item, context) - context = self:process_context(context) - context.options["name-as-sort-order"] = "all" - context.sorting = true - local variable = self:get_attribute("variable") - local res = nil - if variable then - context.variable = variable - local variable_type = util.variable_types[variable] +local Key = Element:derive("key") + +function Key:new() + local o = Element.new(self) + Key.sort = "ascending" + return o +end + +function Key:from_node(node) + local o = Key:new() + o:set_attribute(node, "sort") + o:set_attribute(node, "variable") + o:set_attribute(node, "macro") + o:set_number_attribute(node, "names-min") + o:set_number_attribute(node, "names-use-first") + o:set_bool_attribute(node, "names-use-last") + return o +end + +function Key:eval(engine, state, context) + local res + if self.variable then + local variable_type = util.variable_types[self.variable] if variable_type == "name" then - res = self:_render_name(item, context) + res = self:eval_name(engine, state, context) elseif variable_type == "date" then - res = self:_render_date(item, context) + res = self:eval_date(context) elseif variable_type == "number" then - res = item[variable] + local value = context:get_variable(self.variable) + if type(value) == "string" and string.match(value, "%s+") then + value = tonumber(value) + end + res = value else - res = item[variable] + res = context:get_variable(self.variable) + if type(res) == "string" then + local inlines = InlineElement:parse(res, context) + res = context.format:output(inlines) + end end - else - local macro = self:get_attribute("macro") - if macro then - res = self:get_macro(macro):render(item, context) + elseif self.macro then + local macro = context:get_macro(self.macro) + state:push_macro(self.macro) + local ir = macro:build_ir(engine, state, context) + state:pop_macro(self.macro) + if ir.name_count then + return ir.name_count + elseif ir.sort_key ~= nil then + return ir.sort_key end + local output_format = context.format + local inlines = ir:flatten(output_format) + -- util.debug(inlines) + local str = output_format:output(inlines) + return str end if res == nil then + -- make table.insert(_, nil) work res = false - elseif type(res) == "table" and res._type == "RichText" then - res = res:render(nil, context) - end - if type(res) == "string" then - res = self._normalize_string(res) end return res end -function Key:_render_name (item, context) - if not self.names then - self.names = self:create_element("names", {}, self) - names.Names:set_base_class(self.names) - self.names:set_attribute("variable", context.options["variable"]) - self.names:set_attribute("form", "long") +function Key:eval_name(engine, state, context) + if not self.name_inheritance then + self.name_inheritance = util.clone(context.name_inheritance) end - local res = self.names:render(item, context) - return res + local name = context:get_variable(self.variable) + if not name then + return false + end + local ir = self.name_inheritance:build_ir(self.variable, nil, nil, engine, state, context) + if ir.name_count then + -- name count + return ir.name_count + end + local output_format = context.format + local inlines = ir:flatten(output_format) + + local str = output_format:output(inlines) + + return str end -function Key:_render_date (item, context) +function Key:eval_date(context) if not self.date then - self.date = self:create_element("date", {}, self) - date.Date:set_base_class(self.date) - self.date:set_attribute("variable", context.options["variable"]) - self.date:set_attribute("form", "numeric") + self.date = Date:new() + self.date.variable = self.variable + self.date.form = "numeric" + self.date.date_parts = "year-month-day" end - local res = self.date:render(item, context) - return res -end -function Key._normalize_string(str) - str = unicode.utf8.lower(str) - str = string.gsub(str, "[%[%]]", "") - local words = {} - for _, word in ipairs(util.split(str, " ")) do - -- TODO: strip leading prepositions - -- remove leading apostrophe on name particle - word = string.gsub(word, "^" .. util.unicode["apostrophe"], "") - table.insert(words, word) - end - str = table.concat(words, " ") - str = string.gsub(str, util.unicode["apostrophe"], "'") - return str + return self.date:render_sort_key(context.engine, nil, context) end diff --git a/biblio/citation-style-language/citeproc-node-style.lua b/biblio/citation-style-language/citeproc-node-style.lua index 2fb10cc79c..5c387bd6c8 100644 --- a/biblio/citation-style-language/citeproc-node-style.lua +++ b/biblio/citation-style-language/citeproc-node-style.lua @@ -4,15 +4,126 @@ -- Repository: https://github.com/zepinglee/citeproc-lua -- -local style = {} +local style_module = {} -local element = require("citeproc-element") +local dom = require("luaxml-domobject") + +local Element = require("citeproc-element").Element +local IrNode = require("citeproc-ir-node").IrNode +local Rendered = require("citeproc-ir-node").Rendered +local SeqIr = require("citeproc-ir-node").SeqIr +local PlainText = require("citeproc-output").PlainText +local DisamStringFormat = require("citeproc-output").DisamStringFormat local util = require("citeproc-util") -local Style = element.Element:new() +local Style = Element:derive("style") + +function Style:new() + local o = { + children = {}, + macros = {}, + locales = {}, + initialize_with_hyphen = true, + demote_non_dropping_particle = "display-and-sort", + } + setmetatable(o, self) + self.__index = self + return o +end + +function Style:parse(xml_str) + local csl_xml = dom.parse(xml_str) + if not csl_xml then + error("Failed to parse CSL style.") + end + local style_node = csl_xml:get_path("style")[1] + if not csl_xml then + error('Element "style" not found.') + end + return Style:from_node(style_node) +end + +local function make_name_inheritance(name, node) + name:set_attribute(node, "and") + name:set_attribute(node, "delimiter-precedes-et-al") + name:set_attribute(node, "delimiter-precedes-last") + name:set_number_attribute(node, "et-al-min") + name:set_number_attribute(node, "et-al-use-first") + name:set_number_attribute(node, "et-al-subsequent-min") + name:set_number_attribute(node, "et-al-subsequent-use-first") + name:set_bool_attribute(node, "et-al-use-last") + name:set_bool_attribute(node, "initialize") + name:set_attribute(node, "initialize-with") + name:set_attribute(node, "name-as-sort-order") + name:set_attribute(node, "sort-separator") + local delimiter = node:get_attribute("name-delimiter") + if delimiter then + name.delimiter = delimiter + end + local form = node:get_attribute("name-form") + if form then + name.form = form + end + local names_delimiter = node:get_attribute("names-delimiter") + if names_delimiter then + name.names_delimiter = names_delimiter + end +end + +function Style:from_node(node) + local o = Style:new() + + o:set_attribute(node, "class") + o:set_attribute(node, "default-locale") + o:set_attribute(node, "version") + + -- Global Options + o.initialize_with_hyphen = true + o:set_bool_attribute(node, "initialize-with-hyphen") + o:set_attribute(node, "page-range-format") + o:set_attribute(node, "demote-non-dropping-particle") + + -- Inheritable Name Options + -- https://docs.citationstyles.org/en/stable/specification.html#inheritable-name-options + o.name_inheritance = require("citeproc-node-names").Name:new() + make_name_inheritance(o.name_inheritance, node) + + if o.page_range_format == "chicago" then + if o.version < "1.1" then + o.page_range_format = "chicago-15" + else + o.page_range_format = "chicago-16" + end + end + + o.macros = {} + o.locales = {} + + o.children = {} + o:process_children_nodes(node) + + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "info" then + o.info = child + elseif element_name == "citation" then + o.citation = child + elseif element_name == "bibliography" then + o.bibliography = child + elseif element_name == "macro" then + o.macros[child.name] = child + elseif element_name == "locale" then + local xml_lang = child.xml_lang or "@generic" + o.locales[xml_lang] = child + end + end + + return o +end + -Style.default_options = { +Style._default_options = { ["initialize-with-hyphen"] = true, ["page-range-format"] = nil, ["demote-non-dropping-particle"] = "display-and-sort", @@ -29,171 +140,385 @@ function Style:set_lang(lang, force_lang) end end -function Style:render_citation (items, context) - self:debug_info(context) - context = self:process_context(context) - context.style = self - local citation = self:get_child("citation") - return citation:render(items, context) -end -function Style:render_biblography (items, context) - self:debug_info(context) - context = self:process_context(context) - context.style = self - local bibliography = self:get_child("bibliography") - return bibliography:render(items, context) -end +local Info = Element:derive("info") + + +function Info:from_node(node) + local o = Info:new() + + -- o.authors = nil + -- o.contributors = nil + o.categories = {} + o.id = nil + -- o.issn = nil + -- o.eissn = nil + -- o.issnl = nil + o.links = { + independent_parent = nil, + } + -- o.published = nil + -- o.rights = nil + -- o.summary = nil + o.title = nil + -- o.title_short = nil + o.updated = nil + + for _, child in ipairs(node:get_children()) do + if child:is_element() then + local element_name = child:get_element_name() + if element_name == "category" then + local citation_format = child:get_attribute("citation-format") + if citation_format then + o.categories.citation_format = citation_format + end + + elseif element_name == "id" then + o.id = child:get_text() + + elseif element_name == "link" then + local href = child:get_attribute("href") + local rel = child:get_attribute("rel") + if href and rel == "independent-parent" then + o.links.independent_parent = href + end + + elseif element_name == "title" then + o.title = child:get_text() + + elseif element_name == "updated" then + o.updated = child:get_text() + + end + end + end -function Style:get_version () - return self:get_attribute("version") + return o end -function Style:get_locales() - if not self.locale_dict then - self.locale_dict = {} - end - local locales = self.locale_dict[self.lang] - if not locales then - locales = self:get_locale_list(self.lang) - self.locale_dict[self.lang] = locales - end - return locales -end -function Style:get_locale_list (lang) - assert(lang ~= nil) - local language = string.sub(lang, 1, 2) - local primary_dialect = util.primary_dialects[language] - if not primary_dialect then - -- util.warning(string.format("Failed to find primary dialect of \"%s\"", language)) - end - local locale_list = {} +local Citation = Element:derive("citation", { + givenname_disambiguation_rule = "by-cite", + -- https://github.com/citation-style-language/schema/issues/338 + -- The cite_group_delimiter may be changed to inherit the delimiter in citaion > layout. + cite_group_delimiter = ", ", + near_note_distance = 5, +}) - -- 1. In-style cs:locale elements - -- i. `xml:lang` set to chosen dialect, “de-AT” - if lang == language then - lang = primary_dialect - end - table.insert(locale_list, self:get_in_style_locale(lang)) +function Citation:from_node(node, style) - -- ii. `xml:lang` set to matching language, “de” (German) - if language and language ~= lang then - table.insert(locale_list, self:get_in_style_locale(language)) - end + local o = Citation:new() + o.children = {} - -- iii. `xml:lang` not set - table.insert(locale_list, self:get_in_style_locale(nil)) + o:process_children_nodes(node) - -- 2. Locale files - -- iv. `xml:lang` set to chosen dialect, “de-AT” - if lang then - table.insert(locale_list, self:get_engine():get_system_locale(lang)) + -- o.layouts = nil -- CSL-M extension + + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "layout" then + o.layout = child + elseif element_name == "sort" then + o.sort = child + end end - -- v. `xml:lang` set to matching primary dialect, “de-DE” (Standard German) - -- (only applicable when the chosen locale is a secondary dialect) - if primary_dialect and primary_dialect ~= lang then - table.insert(locale_list, self:get_engine():get_system_locale(primary_dialect)) + -- Disambiguation + o:set_bool_attribute(node, "disambiguate-add-givenname") + o:set_attribute(node, "givenname-disambiguation-rule") + o:set_bool_attribute(node, "disambiguate-add-names") + o:set_bool_attribute(node, "disambiguate-add-year-suffix") + + -- Cite Grouping + o:set_attribute(node, "cite-group-delimiter") + -- In the current citeproc-js implementation and test suite, + -- cite grouping is activated by setting the cite-group-delimiter + -- attribute or the collapse attributes on cs:citation. + -- It may be changed to an independent procedure. + -- https://github.com/citation-style-language/schema/issues/338 + if node:get_attribute("cite-group-delimiter") then + o.cite_grouping = true + else + o.cite_grouping = false end - -- vi. `xml:lang` set to “en-US” (American English) - if lang ~= "en-US" and primary_dialect ~= "en-US" then - table.insert(locale_list, self:get_engine():get_system_locale("en-US")) + + -- Cite Collapsing + o:set_attribute(node, "collapse") + o:set_attribute(node, "year-suffix-delimiter") + if not o.year_suffix_delimiter then + o.year_suffix_delimiter = o.layout.delimiter + end + o:set_attribute(node, "after-collapse-delimiter") + if not o.after_collapse_delimiter then + o.after_collapse_delimiter = o.layout.delimiter end - return locale_list -end + -- Note Distance + o:set_number_attribute(node, "near-note-distance") -function Style:get_in_style_locale (lang) - for _, locale in ipairs(self:query_selector("locale")) do - if locale:get_attribute("xml:lang") == lang then - return locale + local name_inheritance = require("citeproc-node-names").Name:new() + for key, value in pairs(style.name_inheritance) do + if value ~= nil then + name_inheritance[key] = value end end - return nil + make_name_inheritance(name_inheritance, node) + o.name_inheritance = name_inheritance + + -- update_mode = "plain" or "numeric" or "position" (or "both"?) + + return o end -function Style:get_term (...) - for _, locale in ipairs(self:get_locales()) do - local res = locale:get_term(...) - if res then - return res - end +function Citation:build_ir(engine, state, context) + if not self.layout then + util.error("Missing citation layout.") end - return nil + return self.layout:build_ir(engine, state, context) end -local Citation = element.Element:new() +local Bibliography = Element:derive("bibliography", { + hanging_indent = false, + line_spacing = 1, + entry_spacing = 1, + subsequent_author_substitute_rule = "complete-all" +}) + +function Bibliography:from_node(node, style) + local o = Bibliography:new() + o.children = {} -function Citation:render (items, context) - self:debug_info(context) - context = self:process_context(context) + o:process_children_nodes(node) - context.mode = "citation" - context.citation = self + -- o.layouts = nil -- CSL-M extension - local sort = self:get_child("sort") - if sort then - sort:sort(items, context) + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "layout" then + o.layout = child + elseif element_name == "sort" then + o.sort = child + end end - local layout = self:get_child("layout") - return layout:render(items, context) + -- Whitespace + o:set_bool_attribute(node, "hanging-indent") + o:set_attribute(node, "second-field-align") + o:set_number_attribute(node, "line-spacing") + o:set_number_attribute(node, "entry-spacing") + + -- Reference Grouping + o:set_attribute(node, "subsequent-author-substitute") + o:set_attribute(node, "subsequent-author-substitute-rule") + + local name_inheritance = require("citeproc-node-names").Name:new() + for key, value in pairs(style.name_inheritance) do + if value ~= nil then + name_inheritance[key] = value + end + end + make_name_inheritance(name_inheritance, node) + o.name_inheritance = name_inheritance + + return o end +function Bibliography:build_ir(engine, state, context) + if not self.layout then + util.error("Missing bibliography layout.") + end + local ir = self.layout:build_ir(engine, state, context) + -- util.debug(ir) + if self.second_field_align == "flush" and #ir.children >= 2 then + ir.children[1].display = "left-margin" + local right_inline_ir = SeqIr:new(util.slice(ir.children, 2), self) + right_inline_ir.display = "right-inline" + if ir.affixes then + right_inline_ir.affixes = ir.affixes + right_inline_ir.formatting = ir.formatting + ir.affixes = nil + ir.formatting = nil + end + ir.children = {ir.children[1], right_inline_ir} + end -local Bibliography = element.Element:new() + if self.subsequent_author_substitute then + self:substitute_subsequent_authors(engine, ir) + end -Bibliography.default_options = { - ["hanging-indent"] = false, - ["second-field-align"] = nil, - ["line-spacing"] = 1, - ["entry-spacing"] = 1, - ["subsequent-author-substitute"] = nil, - ["subsequent-author-substitute-rule"] = "complete-all", -} + if not ir then + ir = Rendered:new(PlainText:new("[CSL STYLE ERROR: reference with no printed form.]"), self) + end + return ir +end -function Bibliography:render (items, context) - self:debug_info(context) - context = self:process_context(context) - -- util.debug(context) +function Bibliography:substitute_subsequent_authors(engine, ir) + ir.first_name_ir = self:find_first_name_ir(ir) -- should be a SeqIr wiht _element = "names" + if not ir.first_name_ir then + engine.previous_bib_names_ir = nil + return + end + if self.subsequent_author_substitute_rule == "complete-all" then + self:substitute_subsequent_authors_complete_all(engine, ir) + elseif self.subsequent_author_substitute_rule == "complete-each" then + self:substitute_subsequent_authors_complete_each(engine, ir) + elseif self.subsequent_author_substitute_rule == "partial-each" then + self:substitute_subsequent_authors_partial_each(engine, ir) + elseif self.subsequent_author_substitute_rule == "partial-first" then + self:substitute_subsequent_authors_partial_first(engine, ir) + end + engine.previous_bib_names_ir = ir.first_name_ir +end - context.mode = "bibliography" - context.bibliography = self +function Bibliography:find_first_name_ir(ir) + if ir._type == "NameIr" then + return ir + elseif ir.children then + for _, child_ir in ipairs(ir.children) do + local first_name_ir = self:find_first_name_ir(child_ir) + if first_name_ir then + return first_name_ir + end + end + end + return nil +end - -- Already sorted in CiteProc:sort_bibliography() +function Bibliography:substitute_subsequent_authors_complete_all(engine, ir) + local bib_names_str = "" - local layout = self:get_child("layout") - local res = layout:render(items, context) + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + if bib_names_str ~= "" then + bib_names_str = bib_names_str .. " " + end + local name_variants = person_name_ir.disam_variants + bib_names_str = bib_names_str .. name_variants[#name_variants] + end + else + -- In case of a in + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) + end + ir.first_name_ir.bib_names_str = bib_names_str + + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + -- the output of label is not substituted + -- util.debug(ir.first_name_ir) + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end +end - local params = res[1] +function Bibliography:substitute_subsequent_authors_complete_each(engine, ir) + local bib_names_str = "" - params.entryspacing = context.options["entry-spacing"] - params.linespacing = context.options["line-spacing"] - params.hangingindent = context.options["hanging-indent"] - params["second-field-align"] = context.options["second-field-align"] - for _, key in ipairs({"bibstart", "bibend"}) do - local value = context.engine.formatter[key] - if type(value) == "function" then - value = value(context) + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + if bib_names_str ~= "" then + bib_names_str = bib_names_str .. " " + end + local name_variants = person_name_ir.disam_variants + bib_names_str = bib_names_str .. name_variants[#name_variants] end - params[key] = value + else + -- In case of a in + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) end + ir.first_name_ir.bib_names_str = bib_names_str + + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + person_name_ir.inlines = {PlainText:new(text)} + end + else + -- In case of a in + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end + end +end - params.bibliography_errors = {} - params.entry_ids = {} - for _, item in ipairs(items) do - table.insert(params.entry_ids, item.id) +function Bibliography:substitute_subsequent_authors_partial_each(engine, ir) + local bib_names_str = "" + + if #ir.first_name_ir.person_name_irs > 0 then + if engine.previous_bib_names_ir then + for i, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + local prev_name_ir = engine.previous_bib_names_ir.person_names[i] + if prev_name_ir then + local prev_name_variants = prev_name_ir.disam_variants + local prev_full_name_str = prev_name_variants[#prev_name_variants] + local name_variants = person_name_ir.disam_variants + local full_name_str = name_variants[#name_variants] + if prev_full_name_str == full_name_str then + local text = self.subsequent_author_substitute + person_name_ir.inlines = {PlainText:new(text)} + else + break + end + end + end + end + else + -- In case of a in + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) + ir.first_name_ir.bib_names_str = bib_names_str + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end end +end + +function Bibliography:substitute_subsequent_authors_partial_first(engine, ir) +end + +local Macro = Element:derive("macro") - return res +function Macro:from_node(node) + local o = Macro:new() + o.children = {} + o:set_attribute(node, "name") + o:process_children_nodes(node) + return o end -style.Style = Style -style.Citation = Citation -style.Bibliography = Bibliography +function Macro:build_ir(engine, state, context) + local ir = self:build_group_ir(engine, state, context) + return ir +end + + +style_module.Style = Style +style_module.Citation = Citation +style_module.Bibliography = Bibliography -return style +return style_module diff --git a/biblio/citation-style-language/citeproc-node-text.lua b/biblio/citation-style-language/citeproc-node-text.lua index e26a480438..fae3535013 100644 --- a/biblio/citation-style-language/citeproc-node-text.lua +++ b/biblio/citation-style-language/citeproc-node-text.lua @@ -4,224 +4,196 @@ -- Repository: https://github.com/zepinglee/citeproc-lua -- -local text = {} +local text_module = {} -local element = require("citeproc-element") -local richtext = require("citeproc-richtext") +local Element = require("citeproc-element").Element +local Rendered = require("citeproc-ir-node").Rendered +local YearSuffix = require("citeproc-ir-node").YearSuffix +local PlainText = require("citeproc-output").PlainText +local Linked = require("citeproc-output").Linked local util = require("citeproc-util") -local Text = element.Element:new() - -function Text:render (item, context) - self:debug_info(context) - context = self:process_context(context) +-- [Text](https://docs.citationstyles.org/en/stable/specification.html#text) +local Text = Element:derive("text", { + -- Default attributes + variable = nil, + form = "long", + macro = nil, + term = nil, + plural = false, + value = nil, + -- Style behavior + formatting = nil, + affixes = nil, + delimiter = nil, + display = nil, + quotes = false, + strip_periods = false, + text_case = nil, +}) + +function Text:from_node(node) + local o = Text:new() + o:set_attribute(node, "variable") + o:set_attribute(node, "form") + o:set_attribute(node, "macro") + o:set_attribute(node, "term") + o:set_bool_attribute(node, "plural") + o:set_attribute(node, "value") + + o:set_formatting_attributes(node) + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_quotes_attribute(node) + o:set_strip_periods_attribute(node) + o:set_text_case_attribute(node) + return o +end - local res = nil +function Text:build_ir(engine, state, context) + local ir = nil + if self.variable then + ir = self:build_variable_ir(engine, state, context) + elseif self.macro then + ir = self:build_macro_ir(engine, state, context) + elseif self.term then + ir = self:build_term_ir(engine, state, context) + elseif self.value then + ir = self:build_value_ir(engine, state, context) + end + return ir +end - local variable = nil - local variable_name = self:get_attribute("variable") - if variable_name then - local form = self:get_attribute("form") - if form == "short" then - variable = self:get_variable(item, variable_name .. "-" .. form, context) - end - if not variable then - variable = self:get_variable(item, variable_name, context) - end - if variable then - res = variable - if type(res) == "number" then - res = tostring(res) - end - if variable_name == "page" or variable_name == "locator" then - res = util.lstrip(res) - res = self:_format_page(res, context) - end - end +function Text:build_variable_ir(engine, state, context) + local variable = self.variable + local text - table.insert(context.variable_attempt, res ~= nil) + if variable == "year-suffix" then + return self:build_year_suffix_ir(engine, state, context) end - local macro_name = self:get_attribute("macro") - if macro_name then - local macro = self:get_macro(macro_name) - res = macro:render(item, context) + if not state.suppressed[variable] then + text = context:get_variable(variable, self.form) end - local term_name = self:get_attribute("term") - if term_name then - local form = self:get_attribute("form") - - local term = self:get_term(term_name, form) - if term then - res = term:render(context) - end + if not text then + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir end - - local value = self:get_attribute("value") - if value then - res = value - res = self:escape(res) + if type(text) == "number" then + text = tostring(text) end - - if type(res) == "string" and res ~= "" then - res = richtext.new(res) + if util.variable_types[variable] == "number" then + text = self:format_number(text, variable, "numeric", context) end - if res and variable_name == "URL" then - res:add_format("URL", "true") + local inlines + if (variable == "URL" and engine.opt.url_link) or + (variable == "DOI" and engine.opt.doi_link) or + (variable == "PMID" and engine.opt.doi_link) or + (variable == "PMID" and engine.opt.doi_link) then + inlines = self:render_linked(engine, state, context, variable, text) + else + inlines = self:render_text_inlines(text, context) end - res = self:strip_periods(res, context) - res = self:case(res, context) - res = self:format(res, context) - res = self:quote(res, context) - res = self:wrap(res, context) - res = self:display(res, context) + local ir = Rendered:new(inlines, self) + ir.group_var = "important" - if variable_name == "citation-number" then - res = self:_process_citation_number(variable, res, context) + if variable == "citation-number" then + ir.citation_number = context.reference["citation-number"] end - return res -end - - -function Text:_process_citation_number(citation_number, res, context) - if context.mode == "citation" and not context.sorting and context.options["collapse"] == "citation-number" then - context.build.item_citation_numbers[context.item.id] = citation_number - if type(res) == "string" then - res = richtext.new(res) - end - table.insert(context.build.item_citation_number_text, res) + -- Suppress substituted name variable + if state.name_override and not context.sort_key then + state.suppressed[variable] = true end - return res -end - -function Text:_format_page (page, context) - local res = nil - - local page_range_delimiter = self:get_term("page-range-delimiter"):render(context) or util.unicode["en dash"] - local page_range_format = context.options["page-range-format"] - if page_range_format == "chicago" then - if self:get_style():get_version() >= "1.1" then - page_range_format = "chicago-16" - else - page_range_format = "chicago-15" - end - end + return ir +end - local last_position = 1 - local page_parts = {} - local punct_list = {} - for part, punct, pos in string.gmatch(page, "(.-)%s*([,&])%s*()") do - table.insert(page_parts, part) - table.insert(punct_list, punct) - last_position = pos - end - table.insert(page_parts, string.sub(page, last_position)) - - res = "" - for i, part in ipairs(page_parts) do - res = res .. self:_format_range(part, page_range_format, page_range_delimiter) - local punct = punct_list[i] - if punct then - if punct == "&" then - res = res .. " " .. punct .. " " - else - res = res .. punct .. " " - end +function Text:render_linked(engine, state, context, variable, text) + local href + if variable == "URL" then + href = text + elseif self.affixes and self.affixes.prefix then + if string.match(self.affixes.prefix, "https?://") then + text = self.affixes.prefix .. text + self.affixes.prefix = nil + href = text end - end - res = self:escape(res) - return res + elseif variable == "DOI" then + href = "https://doi.org/" .. text + elseif variable == "PMID" then + href = "https://www.ncbi.nlm.nih.gov/pubmed/" .. text + elseif variable == "PMCID" then + href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. text + end + local inlines = {Linked:new(text, href)} + local output_format = context.format + local localized_quotes = nil + if self.quotes then + localized_quotes = context:get_localized_quotes() + end + inlines = output_format:with_format(inlines, self.formatting) + inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes) + return output_format:with_display(inlines, self.display) end -function Text:_format_range (str, format, range_delimiter) - local start, delimiter, stop = string.match(str, "(%w+)%s*(%-+)%s*(%S*)") - if not stop or stop == "" then - return string.gsub(str, "\\%-", "-") - end - - - local start_prefix, start_num = string.match(start, "(.-)(%d*)$") - local stop_prefix, stop_num = string.match(stop, "(.-)(%d*)$") - - if start_prefix ~= stop_prefix then - -- Not valid range: "n11564-1568" -> "n11564-1568" - -- 110-N6 - -- N110-P5 - return start .. delimiter .. stop - end - - if format == "chicago-16" then - stop = self:_format_range_chicago_16(start_num, stop_num) - elseif format == "chicago-15" then - stop = self:_format_range_chicago_15(start_num, stop_num) - elseif format == "expanded" then - stop = stop_prefix .. self:_format_range_expanded(start_num, stop_num) - elseif format == "minimal" then - stop = self:_format_range_minimal(start_num, stop_num) - elseif format == "minimal-two" then - stop = self:_format_range_minimal(start_num, stop_num, 2) +function Text:build_year_suffix_ir(engine, state, context) + local text = context:get_variable(self.variable, self.form) + local group_var + if text then + group_var = "important" + else + text = "" + group_var = "missing" end - return start .. range_delimiter .. stop -end - -function Text:_format_range_chicago_16(start, stop) - if #start < 3 or string.sub(start, -2) == "00" then - return self:_format_range_expanded(start, stop) - elseif string.sub(start, -2, -2) == "0" then - return self:_format_range_minimal(start, stop) - else - return self:_format_range_minimal(start, stop, 2) + local ir = YearSuffix:new({PlainText:new(text)}, self) + ir.group_var = group_var + ir.affixes = util.clone(self.affixes) + ir.display = self.display + ir.formatting = util.clone(self.formatting) + if self.quotes then + ir.quotes = context:get_localized_quotes() end - return stop + return ir end -function Text:_format_range_chicago_15(start, stop) - if #start < 3 or string.sub(start, -2) == "00" then - return self:_format_range_expanded(start, stop) - else - local changed_digits = self:_format_range_minimal(start, stop) - if string.sub(start, -2, -2) == "0" then - return changed_digits - elseif #start == 4 and #changed_digits == 3 then - return self:_format_range_expanded(start, stop) - else - return self:_format_range_minimal(start, stop, 2) +function Text:build_macro_ir(engine, state, context) + local macro = context:get_macro(self.macro) + state:push_macro(self.macro) + local ir = macro:build_ir(engine, state, context) + state:pop_macro(self.macro) + if ir then + ir.affixes = util.clone(self.affixes) + ir.display = self.display + ir.formatting = util.clone(self.formatting) + if self.quotes then + ir.quotes = context:get_localized_quotes() end end - return stop + return ir end -function Text:_format_range_expanded(start, stop) - -- Expand "1234–56" -> "1234–1256" - if #start <= #stop then - return stop +function Text:build_term_ir(engine, state, context) + local str = context:get_simple_term(self.term, self.form, self.plural) + if not str then + return nil end - return string.sub(start, 1, #start - #stop) .. stop + local inlines = self:render_text_inlines(str, context) + return Rendered:new(inlines, self) end -function Text:_format_range_minimal(start, stop, threshold) - threshold = threshold or 1 - if #start < #stop then - return stop - end - local offset = #start - #stop - for i = 1, #stop - threshold do - local j = i + offset - if string.sub(stop, i, i) ~= string.sub(start, j, j) then - return string.sub(stop, i) - end - end - return string.sub(stop, -threshold) +function Text:build_value_ir(engine, state, context) + local inlines = self:render_text_inlines(self.value, context) + return Rendered:new(inlines, self) end -text.Text = Text +text_module.Text = Text -return text +return text_module diff --git a/biblio/citation-style-language/citeproc-output.lua b/biblio/citation-style-language/citeproc-output.lua new file mode 100644 index 0000000000..6aca4533da --- /dev/null +++ b/biblio/citation-style-language/citeproc-output.lua @@ -0,0 +1,1534 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local output_module = {} + +local unicode = require("unicode") +local dom = require("luaxml-domobject") + +local util = require("citeproc-util") + + +local LocalizedQuotes = { + outer_open = util.unicode['left double quotation mark'], + outer_close = util.unicode['right double quotation mark'], + inner_open = util.unicode['left single quotation mark'], + inner_close = util.unicode['right single quotation mark'], + punctuation_in_quote = false, +} + +function LocalizedQuotes:new(outer_open, outer_close, inner_open, inner_close, punctuation_in_quote) + local o = { + outer_open = outer_open or util.unicode['left double quotation mark'], + outer_close = outer_close or util.unicode['right double quotation mark'], + inner_open = inner_open or util.unicode['left single quotation mark'], + inner_close = inner_close or util.unicode['right single quotation mark'], + punctuation_in_quote = punctuation_in_quote, + } + setmetatable(o, self) + self.__index = self + return o +end + + +local InlineElement = { + _type = "InlineElement", + _base_class = "InlineElement", +} + +function InlineElement:derive(type) + local o = { + _type = type, + } + self[type] = o + setmetatable(o, self) + self.__index = self + o.__index = o + return o +end + +function InlineElement:new(inlines) + local o = { + inlines = inlines, + _type = self._type, + } + setmetatable(o, self) + return o +end + + +local PlainText = InlineElement:derive("PlainText") + +function PlainText:new(value) + local o = InlineElement.new(self) + o.value = value + setmetatable(o, self) + return o +end + + +local Formatted = InlineElement:derive("Formatted") + +function Formatted:new(inlines, formatting) + local o = InlineElement.new(self) + o.inlines = inlines + o.formatting = formatting + setmetatable(o, self) + return o +end + + +local Micro = InlineElement:derive("Micro") + +-- This is how we can flip-flop only user-supplied styling. +-- Inside this is parsed micro html +function Micro:new(inlines) + local o = InlineElement.new(self) + o.inlines = inlines + setmetatable(o, self) + return o +end + + +local Quoted = InlineElement:derive("Quoted") + +function Quoted:new(inlines, localized_quotes) + local o = InlineElement.new(self) + o.inlines = inlines + o.is_inner = false + if localized_quotes then + o.quotes = localized_quotes + else + o.quotes = LocalizedQuotes:new() + end + + setmetatable(o, self) + return o +end + + +local NoCase = InlineElement:derive("NoCase") + +function NoCase:new(inlines) + local o = InlineElement.new(self) + o.inlines = inlines + setmetatable(o, self) + return o +end + + +local NoDecor = InlineElement:derive("NoDecor") + +function NoDecor:new(inlines) + local o = InlineElement.new(self) + o.inlines = inlines + setmetatable(o, self) + return o +end + + +local Linked = InlineElement:derive("Linked") + +function Linked:new(value, href) + local o = InlineElement.new(self) + o.value = value + o.href = href + setmetatable(o, self) + return o +end + + +local Div = InlineElement:derive("Div") + +function Div:new(inlines, display) + local o = InlineElement.new(self) + o.inlines = inlines + o.div = display + setmetatable(o, self) + return o +end + + +function InlineElement:parse(str, context) + -- Return a list of inlines + if type(str) ~= "string" then + print(debug.traceback()) + end + local html_str = "
" .. str .. "
" + local ok, html = pcall(dom.parse, html_str) + local inlines + if ok then + local div = html:get_path("div")[1] + local el = InlineElement:from_node(div) + inlines = el.inlines + else + local el = PlainText:new(str) + inlines = {el} + end + + inlines = InlineElement:parse_quotes(inlines, context) + return inlines +end + +function InlineElement:from_node(node) + local tag_name = node:get_element_name() + local inlines = {} + + for _, child in ipairs(node:get_children()) do + local inline + if child:is_text() then + inline = PlainText:new(child:get_text()) + elseif child:is_element() then + inline = InlineElement:from_node(child) + end + table.insert(inlines, inline) + end + + if tag_name == "i" then + return Formatted:new(inlines, {["font-style"] = "italic"}) + elseif tag_name == "b" then + return Formatted:new(inlines, {["font-weight"] = "bold"}) + elseif tag_name == "sup" then + return Formatted:new(inlines, {["vertical-align"] = "sup"}) + elseif tag_name == "sub" then + return Formatted:new(inlines, {["vertical-align"] = "sub"}) + elseif tag_name == "sc" then + return Formatted:new(inlines, {["font-variant"] = "small-caps"}) + elseif tag_name == "span" then + local style = node:get_attribute("style") + local class = node:get_attribute("class") + if style == "font-variant:small-caps;" or style == "font-variant: small-caps;" then + return Formatted:new(inlines, {["font-variant"] = "small-caps"}) + elseif class == "nocase" then + return NoCase:new(inlines) + elseif class == "nodecor" then + return NoDecor:new(inlines) + end + end + + return InlineElement:new(inlines) +end + +function InlineElement:parse_quotes(inlines, context) + local quote_fragments = InlineElement:get_quote_fragments(inlines) + -- util.debug(quote_fragments) + + local quote_stack = {} + local text_stack = {{}} + + local localized_quotes = context:get_localized_quotes() + + for _, fragment in ipairs(quote_fragments) do + local top_text_list = text_stack[#text_stack] + + if type(fragment) == "table" then + if fragment.inlines then + fragment.inlines = self:parse_quotes(fragment.inlines, context) + end + table.insert(top_text_list, fragment) + + elseif type(fragment) == "string" then + local quote = fragment + local top_quote = quote_stack[#quote_stack] + + if quote == "'" then + if top_quote == "'" and #top_text_list > 0 then + table.remove(quote_stack) + local quoted = Quoted:new(top_text_list, localized_quotes) + table.remove(text_stack) + table.insert(text_stack[#text_stack], quoted) + else + table.insert(quote_stack, quote) + table.insert(text_stack, {}) + end + + elseif quote == '"' then + if top_quote == '"' then + table.remove(quote_stack) + local quoted = Quoted:new(top_text_list, localized_quotes) + table.remove(text_stack) + table.insert(text_stack[#text_stack], quoted) + else + table.insert(quote_stack, quote) + table.insert(text_stack, {}) + end + + elseif quote == util.unicode["left single quotation mark"] or + quote == util.unicode["left double quotation mark"] or + quote == util.unicode["left-pointing double angle quotation mark"] then + table.insert(quote_stack, quote) + table.insert(text_stack, {}) + + elseif (quote == util.unicode["right single quotation mark"] and + top_quote == util.unicode["left single quotation mark"]) or + (quote == util.unicode["right double quotation mark"] and + top_quote == util.unicode["left double quotation mark"]) or + (quote == util.unicode["right-pointing double angle quotation mark"] and + top_quote == util.unicode["left-pointing double angle quotation mark"]) then + table.remove(quote_stack) + local quoted = Quoted:new(top_text_list, localized_quotes) + table.remove(text_stack) + table.insert(text_stack[#text_stack], quoted) + + else + local last_inline = top_text_list[#top_text_list] + if last_inline and last_inline._type == "PlainText" then + last_inline.value = last_inline.value .. fragment + else + table.insert(top_text_list, PlainText:new(fragment)) + end + end + + end + end + + local elements = text_stack[1] + if #text_stack > 1 then + assert(#text_stack == #quote_stack + 1) + for i, quote in ipairs(quote_stack) do + if quote == "'" then + quote = util.unicode["apostrophe"] + end + local last_inline = elements[#elements] + if last_inline and last_inline._type == "PlainText" then + last_inline.value = last_inline.value .. quote + else + table.insert(elements, PlainText:new(quote)) + end + + for _, inline in ipairs(text_stack[i + 1]) do + if inline._type == "PlainText" then + local last_inline = elements[#elements] + if last_inline and last_inline._type == "PlainText" then + last_inline.value = last_inline.value .. inline.value + else + table.insert(elements, inline) + end + else + table.insert(elements, inline) + end + end + end + end + + return elements +end + +local function merge_fragments_at(fragments, i) + if type(fragments[i+1]) == "string" then + fragments[i] = fragments[i] .. fragments[i+1] + table.remove(fragments, i+1) + end + if type(fragments[i-1]) == "string" then + fragments[i-1] = fragments[i-1] .. fragments[i] + table.remove(fragments, i) + end +end + +-- Return a list of strings and InlineElement +function InlineElement:get_quote_fragments(inlines) + local fragments = {} + for _, inline in ipairs(inlines) do + if inline._type == "PlainText" then + local quote_tuples = {} + for _, quote in ipairs({ + "'", + '"', + util.unicode["left single quotation mark"], + util.unicode["right single quotation mark"], + util.unicode["left double quotation mark"], + util.unicode["right double quotation mark"], + util.unicode["left-pointing double angle quotation mark"], + util.unicode["right-pointing double angle quotation mark"], + }) do + string.gsub(inline.value, "()(" .. quote .. ")()", function (idx, qt, next_idx) + table.insert(quote_tuples, {idx, qt, next_idx}) + end) + end + table.sort(quote_tuples, function (a, b) + return a[1] < b[1] + end) + local start_idx = 1 + for _, quote_tuple in ipairs(quote_tuples) do + local idx, qt, next_idx = table.unpack(quote_tuple) + local fragment = string.sub(inline.value, start_idx, idx-1) + if fragment ~= "" then + table.insert(fragments, fragment) + end + table.insert(fragments, qt) + start_idx = next_idx + end + -- Insert last fragment + local fragment = string.sub(inline.value, start_idx, #inline.value) + if fragment ~= "" then + table.insert(fragments, fragment) + end + else + table.insert(fragments, inline) + end + + for i = #fragments, 1, -1 do + local fragment = fragments[i] + if fragment == "'" or fragment == '"' then + local left, right + if i > 1 then + left = fragments[i - 1] + if type(left) == "table" then + left = left:get_right_most_string() + end + end + if i < #fragments then + right = fragments[i + 1] + if type(right) == "table" then + right = right:get_left_most_string() + end + end + -- TODO: consider utf-8 + if left and right then + if string.match(left, "%s$") and string.match(right, "^%s") then + -- Orphan quote: bugreports_SingleQuote.txt + if fragment == "'" then + fragments[i] = util.unicode['apostrophe'] + end + merge_fragments_at(fragments, i) + end + if not string.match(left, "[%s%p]$") and not string.match(right, "^[%s%p]") then + if fragment == "'" then + fragments[i] = util.unicode['apostrophe'] + end + merge_fragments_at(fragments, i) + end + end + end + end + end + return fragments +end + +function InlineElement:get_left_most_string() + if self.value then + return self.value + elseif self.inlines then + return self.inlines[1]:get_left_most_string() + end +end + +function InlineElement:get_right_most_string() + if self.value then + return self.value + elseif self.inlines then + return self.inlines[#self.inlines]:get_left_most_string() + end +end + +function InlineElement:capitalize_first_term() + -- util.debug(self) + if self._type == "PlainText" then + self.value = util.capitalize(self.value) + elseif self.inlines[1] then + self.inlines[1]:capitalize_first_term() + end +end + + +local OutputFormat = {} + +function OutputFormat:new(format_name) + local o = { + name = format_name, + } + setmetatable(o, self) + self.__index = self + return o +end + +function OutputFormat:flatten_ir(ir) + if self.group_var == "missing" or self.collapse_suppressed then + return {} + end + local inlines = {} + if ir._type == "SeqIr" or ir._type == "NameIr" then + inlines = self:flatten_seq_ir(ir) + else + inlines = self:with_format(ir.inlines, ir.formatting) + inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); + inlines = self:with_display(inlines, ir.display); + end + return inlines +end + +function OutputFormat:flatten_seq_ir(ir) + if not ir.children then + print(debug.traceback()) + end + if #ir.children == 0 then + return {} + end + local inlines_list = {} + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" and not child.collapse_suppressed then + local child_inlines = self:flatten_ir(child) + if #child_inlines > 0 then + table.insert(inlines_list, child_inlines) + end + end + end + + if #inlines_list == 0 then + return {} + end + + local inlines = self:group(inlines_list, ir.delimiter, ir.formatting) + -- assert ir.quotes == localized quotes + inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); + inlines = self:with_display(inlines, ir.display); + return inlines +end + +function OutputFormat:group(inlines_list, delimiter, formatting) + -- Each node is list of InlineElements + if #inlines_list == 1 then + return self:format_list(inlines_list[1], formatting) + else + local inlines = OutputFormat:join_many(inlines_list, delimiter) + return self:format_list(inlines, formatting) + end +end + +function OutputFormat:format_list(inlines, formatting) + if formatting then + return {Formatted:new(inlines, formatting)} + else + return inlines + end +end + +function OutputFormat:join_many(lists, delimiter) + local res = {} + for i, list in ipairs(lists) do + if delimiter and i > 1 then + table.insert(res, PlainText:new(delimiter)) + end + for _, el in ipairs(list) do + table.insert(res, el) + end + end + return res +end + +function OutputFormat:with_format(inlines, formatting) + return self:format_list(inlines, formatting) +end + +function OutputFormat:apply_text_case(inlines, text_case, is_english) + if not inlines or #inlines == 0 or not text_case then + return + end + -- Title case conversion only affects English-language items. + if text_case == "title" and not is_english then + return + end + local is_uppercase = false -- TODO + self:apply_text_case_inner(inlines, text_case, false, is_uppercase) +end + +local function string_contains_word(str) + return string.match(str, "%w") ~= nil +end + +local function inline_contains_word(inline) + if inline._type == "PlainText" then + return string_contains_word(inline.value) + elseif inline.inlines then + for _, el in ipairs(inline.inlines) do + if inline_contains_word(el) then + return true + end + end + end + return false +end + +function OutputFormat:apply_text_case_inner(inlines, text_case, seen_one, is_uppercase) + for i, inline in ipairs(inlines) do + if seen_one and text_case == "capitalize-first" then + break + end + local is_last = (i == #inlines) + if inline._type == "PlainText" then + inline.value = self:transform_case(inline.value, text_case, seen_one, is_last, is_uppercase); + seen_one = seen_one or string_contains_word(inline.value) + elseif inline._type == "NoCase" or + inline._type == "NoDecor" or + (inline._type == "Formatted" and inline.formatting["font-variant"] == "small-caps") or + (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sup") or + (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sub") then + seen_one = seen_one or inline_contains_word(inline) + + elseif inline._type == "Formatted" or inline._type == "Quoted" then + seen_one = self:apply_text_case_inner(inline.inlines, text_case, seen_one, is_uppercase) or seen_one + end + end + return seen_one +end + +local function transform_lowercase(str) + return string.gsub(str, utf8.charpattern, unicode.utf8.lower) +end + +local function transform_uppercase(str) + -- TODO: locale specific uppercase: textcase_LocaleUnicode.txt + return string.gsub(str, utf8.charpattern, unicode.utf8.upper) +end + +local function transform_first_word(str, transform) + -- TODO: [Unicode word boundaries](https://www.unicode.org/reports/tr29/#Word_Boundaries) + local segments = util.segment_words(str) + for _, segment in ipairs(segments) do + if segment[1] ~= "" then + segment[1] = transform(segment[1]) + break + end + end + local res = "" + for _, segment in ipairs(segments) do + res = res .. segment[1] .. segment[2] + end + return res +end + +local function transform_each_word(str, seen_one, is_last, transform) + -- util.debug(str) + local segments = util.segment_words(str) + -- util.debug(segments) + -- print(debug.traceback()) + + local first_idx + local last_idx + for i, segment in ipairs(segments) do + if segment[1] ~= "" then + if not first_idx then + first_idx = i + end + last_idx = i + end + end + + local immediate_before = "" + local last_punct = "" + for i, segment in ipairs(segments) do + local is_first_word = not seen_one and i == first_idx + local is_last_word = is_last and i == last_idx + local follows_colon = ( + last_punct == ":" or + last_punct == "!" or + last_punct == "?" or + last_punct == "?") + local no_stop_word = is_first_word or is_last_word or follows_colon or (segment[2] == "-" and immediate_before ~= "-") + + if (immediate_before == "." or immediate_before == "-") and #segment[1] == 1 then + else + segment[1] = transform(segment[1], no_stop_word) + end + + if segment[1] ~= "" then + immediate_before = segment[1] + last_punct = string.match(segment[1], "(%S)%s*$") or last_punct + end + if segment[2] ~= "" then + immediate_before = segment[2] + last_punct = string.match(segment[2], "(%S)%s*$") or last_punct + end + end + local res = "" + for _, segment in ipairs(segments) do + res = res .. segment[1] .. segment[2] + end + return res +end + +local function transform_capitalize_word_if_lower(word) + if util.is_lower(word) then + return string.gsub(word, utf8.charpattern, unicode.utf8.upper, 1) + else + return word + end +end + +local function title_case_word(word, no_stop_word) + -- Entirely non-English + -- e.g. "β" in "β-Carotine" + if string.match(word, "%a") and (not util.stop_words[word] or no_stop_word) and util.is_lower(word) then + return string.gsub(word, utf8.charpattern, unicode.utf8.upper, 1) + else + return word + end +end + +function OutputFormat:transform_case(str, text_case, seen_one, is_last, is_uppercase) + local res = str + if text_case == "lowercase" then + res = transform_lowercase(str) + elseif text_case == "uppercase" then + res = transform_uppercase(str) + elseif text_case == "capitalize-first" then + res = transform_first_word(str, transform_capitalize_word_if_lower) + elseif text_case == "capitalize-all" then + res = transform_each_word(str, false, false, transform_capitalize_word_if_lower) + elseif text_case == "sentence" then + -- TODO: if uppercase convert all to lowercase + res = transform_first_word(str, transform_capitalize_word_if_lower) + elseif text_case == "title" then + -- TODO: if uppercase convert all to lowercase + res = transform_each_word(str, seen_one, is_last, title_case_word) + end + return res +end + +function OutputFormat:affixed(inlines, affixes) + if affixes and affixes.prefix then + table.insert(inlines, 1, PlainText:new(affixes.prefix)) + end + if affixes and affixes.suffix then + table.insert(inlines, PlainText:new(affixes.suffix)) + end + return inlines +end + +function OutputFormat:affixed_quoted(inlines, affixes, localized_quotes) + inlines = util.clone(inlines) + if localized_quotes then + inlines = self:quoted(inlines, localized_quotes) + end + if affixes and affixes.prefix then + table.insert(inlines, 1, PlainText:new(affixes.prefix)) + end + if affixes and affixes.suffix then + table.insert(inlines, PlainText:new(affixes.suffix)) + end + return inlines +end + +function OutputFormat:quoted(inlines, localized_quotes) + return {Quoted:new(inlines, localized_quotes)} +end + +function OutputFormat:with_display(nodes, display) + if display then + return {Div:new(nodes, display)} + else + return nodes + end +end + +function OutputFormat:output(inlines, context) + self:flip_flop_inlines(inlines) + + self:move_punctuation(inlines) + + -- util.debug(inlines) + + return self:write_inlines(inlines, context) +end + +function OutputFormat:output_bibliography_entry(inlines, context) + self:flip_flop_inlines(inlines) + -- util.debug(inlines) + self:move_punctuation(inlines) + -- TODO: + -- if self.format == "html" then + -- elseif self.format == "latex" then + -- end + local res = self:write_inlines(inlines, context) + local markup = self.markups["@bibliography/entry"] + if type(markup) == "string" then + res = string.format(markup, res) + elseif type(markup) == "function" then + res = markup(res, context) + end + return res +end + +function OutputFormat:flip_flop_inlines(inlines) + local flip_flop_state = { + ["font-style"] = "normal", + ["font-variant"] = "normal", + ["font-weight"] = "normal", + ["text-decoration"] = "none", + ["vertical-alignment"] = "baseline", + in_inner_quotes = false, + } + self:flip_flop(inlines, flip_flop_state) +end + +function OutputFormat:flip_flop(inlines, state) + for i, inline in ipairs(inlines) do + if inline._type == "Micro" then + self:flip_flop_micro_inlines(inline.inlines, state) + + elseif inline._type == "Formatted" then + local new_state = util.clone(state) + local formatting = inline.formatting + + for _, attribute in ipairs({"font-style", "font-variant", "font-weight"}) do + local value = formatting[attribute] + if value then + if value == state[attribute] then + if value == "normal" then + formatting[attribute] = nil + -- Formatting outside Micro is not reset to "normal". + -- else + -- formatting[attribute] = "normal" + end + end + new_state[attribute] = value + end + end + self:flip_flop(inline.inlines, new_state) + + elseif inline._type == "Quoted" then + inline.is_inner = state.in_inner_quotes + local new_state = util.clone(state) + new_state.in_inner_quotes = not new_state.in_inner_quotes + self:flip_flop(inline.inlines, new_state) + + elseif inline._type == "NoDecor" then + local new_state = { + ["font-style"] = "normal", + ["font-variant"] = "normal", + ["font-weight"] = "normal", + ["text-decoration"] = "none", + } + self:flip_flop(inline.inlines, new_state) + for attr, value in pairs(new_state) do + if value and state[attr] ~= value then + if not inline.formatting then + inline._type = "Formatted" + inline.formatting = {} + end + inline.formatting[attr] = value + end + end + + elseif inline.inlines then -- Div, ... + self:flip_flop(inline.inlines, state) + end + end +end + +function OutputFormat:flip_flop_micro_inlines(inlines, state) + for i, inline in ipairs(inlines) do + if inline._type == "Micro" then + self:flip_flop_micro_inlines(inline.inlines, state) + + elseif inline._type == "Formatted" then + local new_state = util.clone(state) + local formatting = inline.formatting + + for _, attribute in ipairs({"font-style", "font-variant", "font-weight"}) do + local value = formatting[attribute] + if value then + if value == state[attribute] then + if value == "normal" then + formatting[attribute] = nil + else + -- Formatting inside Micro is reset to "normal". + formatting[attribute] = "normal" + value = "normal" + end + end + new_state[attribute] = value + end + end + self:flip_flop_micro_inlines(inline.inlines, new_state) + + elseif inline._type == "Quoted" then + inline.is_inner = state.in_inner_quotes + local new_state = util.clone(state) + new_state.in_inner_quotes = not new_state.in_inner_quotes + self:flip_flop_micro_inlines(inline.inlines, new_state) + + elseif inline._type == "NoDecor" then + local new_state = { + ["font-style"] = "normal", + ["font-variant"] = "normal", + ["font-weight"] = "normal", + ["text-decoration"] = "none", + } + self:flip_flop_micro_inlines(inline.inlines, new_state) + for attr, value in pairs(new_state) do + if value and state[attr] ~= value then + if not inline.formatting then + inline._type = "Formatted" + inline.formatting = {} + end + inline.formatting[attr] = value + end + end + + elseif inline.inlines then -- Div, ... + self:flip_flop_micro_inlines(inline.inlines, state) + end + end +end + +local function find_left(inline) + if not inline then + print(debug.traceback()) + end + if inline._type == "PlainText" then + return inline + -- elseif inline._type == "Micro" then + -- return nil + elseif inline.inlines and inline._type~="Quoted" then + return find_left(inline.inlines[1]) + else + return nil + end +end + +local function find_right(inline) + if inline._type == "PlainText" then + return inline + -- elseif inline._type == "Micro" then + -- return nil + elseif inline.inlines and #inline.inlines > 0 and inline._type ~= "Quoted" then + return find_right(inline.inlines[#inline.inlines]) + else + return nil + end +end + +local function find_right_in_quoted(inline) + if inline._type == "PlainText" then + return inline + -- elseif inline._type == "Micro" then + -- return nil + elseif inline.inlines and #inline.inlines > 0 then + return find_right_in_quoted(inline.inlines[#inline.inlines]) + else + return nil + end +end + +-- "'Foo,' bar" => , +local function find_right_quoted(inline) + if inline._type == "Quoted" and #inline.inlines > 0 then + return find_right_in_quoted(inline.inlines[#inline.inlines]), inline.quotes.punctuation_in_quote + -- elseif inline._type == "Micro" then + -- return nil + elseif inline.inlines and #inline.inlines > 0 then + return find_right_quoted(inline.inlines[#inline.inlines]) + else + return nil, false + end +end + +local function smash_string_push(first, second) + local first_char = string.sub(first.value, -1) + local second_char = string.sub(second.value, 1, 1) + -- util.debug(first_char) + -- util.debug(second_char) + + local punct_map = output_module.quote_punctuation_map + if second_char == " " and (first_char == " " or + util.endswith(first.value, util.unicode["no-break space"])) then + second.value = string.sub(second.value, 2) + elseif punct_map[first_char] then + if first_char == second_char then + second.value = string.sub(second.value, 2) + else + local combined = punct_map[first_char][second_char] + if combined and #combined == 1 then + second.value = string.sub(second.value, 2) + first.value = string.sub(first.value, 1, -2) .. combined + end + end + end +end + +local function smash_just_punc(first, second) + first = find_right(first) -- PlainText + second = find_left(second) -- PlainText + if first and second then + local first_char = string.sub(first.value, -1) + local second_char = string.sub(second.value, 1, 1) + -- util.debug(first_char) + -- util.debug(second_char) + + local punct_map = output_module.quote_punctuation_map + if second_char == " " and (first_char == " " or + util.endswith(first.value, util.unicode["no-break space"])) then + second.value = string.sub(second.value, 2) + return true + elseif punct_map[first_char] then + if first_char == second_char then + second.value = string.sub(second.value, 2) + return true + else + local combined = punct_map[first_char][second_char] + if combined and #combined == 1 then + second.value = string.sub(second.value, 2) + first.value = string.sub(first.value, 1, -2) .. combined + return true + end + end + end + else + return false + end +end + +local function normalise_text_elements(inlines) + -- 1. Merge punctuations: "?." => "?" + -- 2. Merge spaces: " " => " " + local idx = 1 + while idx < #inlines do + local first = inlines[idx] + local second = inlines[idx+1] + + if first._type == "PlainText" and second._type == "PlainText" then + smash_string_push(first, second) + first.value = first.value .. second.value + table.remove(inlines, idx + 1) + + elseif first._type == "Micro" and second._type == "PlainText" then + local success = smash_just_punc(first, second) + if success then + if second.value == "" then + table.remove(inlines, idx + 1) + end + else + idx = idx + 1 + end + + elseif first._type == "Formatted" and second._type == "PlainText" then + local success = smash_just_punc(first, second) + if success then + if second.value == "" then + table.remove(inlines, idx + 1) + end + else + idx = idx + 1 + end + + else + idx = idx + 1 + end + end + +end + +local function move_in_quotes(first, second) + local first_char = string.sub(first.value, -1) + local second_char = string.sub(second.value, 1, 1) + local success = false + if output_module.move_in_puncts[second_char] then + if first_char == second_char then + second.value = string.sub(second.value, 2) + success = true + elseif output_module.quote_punctuation_map[first_char] then + local combined = output_module.quote_punctuation_map[first_char][second_char] + first.value = string.sub(first.value, 1, -2) .. combined + second.value = string.sub(second.value, 2) + success = true + else + first.value = first.value .. second_char + second.value = string.sub(second.value, 2) + success = true + end + end + return success +end + +local function move_out_quotes(first, second) + local first_char = string.sub(first.value, -1) + local second_char = string.sub(second.value, 1, 1) + local success = false + if output_module.move_out_puncts[first_char] then + if first_char == second_char then + first.value = string.sub(first.value, 1, -2) + success = true + elseif output_module.quote_punctuation_map[second_char] then + local combined = output_module.quote_punctuation_map[first_char][second_char] + first.value = string.sub(first.value, 1, -2) + second.value = combined .. string.sub(second.value, 2) + success = true + else + first.value = string.sub(first.value, 1, -2) + second.value = first_char .. second.value + success = true + end + end + return success +end + +local function move_around_quote(inlines) + local idx = 1 + + while idx < #inlines do + -- Move punctuation into quotes as needed + local first, punctuation_in_quote = find_right_quoted(inlines[idx]) + + local second = find_left(inlines[idx+1]) + local success = false + if first and second then + if punctuation_in_quote then + success = move_in_quotes(first, second) + else + success = move_out_quotes(first, second) + end + if not success then + idx = idx + 1 + end + else + idx = idx + 1 + end + end +end + +function OutputFormat:move_punctuation(inlines, piq) + -- Merge punctuations + normalise_text_elements(inlines) + + move_around_quote(inlines) + + for _, inline in ipairs(inlines) do + if inline._type == "Quoted" or inline._type == "Formatted" or + inline._type == "Div" then + self:move_punctuation(inline.inlines) + end + end +end + +function OutputFormat:write_inlines(inlines, context) + local res = "" + for _, inline in ipairs(inlines) do + res = res .. self:write_inline(inline, context) + end + return res +end + +function OutputFormat:write_inline(inline, context) + if inline.value then + return self:write_escaped(inline.value) + elseif inline.inlines then + return self:write_inlines(inline.inlines) + end + return "" +end + +function OutputFormat:write_escaped(str, context) + return str +end + +function OutputFormat:write_link(inline, context) + return self:write_escaped(inline.value) +end + + + +local Markup = OutputFormat:new() + +function Markup:write_inline(inline, context) + -- Should be deprecated code + if type(inline) == "string" then + return self:write_escaped(inline, context) + + elseif type(inline) == "table" then + -- util.debug(inline._type) + if inline._type == "PlainText" then + return self:write_escaped(inline.value, context) + + elseif inline._type == "InlineElement" then + return self:write_children(inline, context) + + elseif inline._type == "Formatted" then + return self:write_formatted(inline, context) + + elseif inline._type == "Quoted" then + return self:write_quoted(inline, context) + + elseif inline._type == "Div" then + return self:write_display(inline, context) + + elseif inline._type == "Linked" then + return self:write_link(inline, context) + + elseif inline._type == "NoCase" or inline._type == "NoDecor" then + return self:write_inlines(inline.inlines) + + else + return self:write_inlines(inline.inlines) + end + end + return "" +end + +function Markup:write_children(inline, context) + local res = "" + for _, child_inline in ipairs(inline.inlines) do + res = res .. self:write_inline(child_inline, context) + end + return res +end + +function Markup:write_quoted(inline, context) + local res = self:write_children(inline, context) + local quotes = inline.quotes + if inline.is_inner then + return quotes.inner_open .. res .. quotes.inner_close + else + return quotes.outer_open .. res .. quotes.outer_close + end +end + + +local LatexWriter = Markup:new() + +LatexWriter.markups = { + ["bibstart"] = function (engine) + return string.format("\\begin{thebibliography}{%s}\n\n", engine.registry.longest_label) + end, + ["bibend"] = "\\end{thebibliography}", + ["@font-style/normal"] = "{\\normalshape %s}", + ["@font-style/italic"] = "\\textit{%s}", + ["@font-style/oblique"] = "\\textsl{%s}", + ["@font-variant/normal"] = "{\\normalshape %s}", + ["@font-variant/small-caps"] = "\\textsc{%s}", + ["@font-weight/normal"] = "{\\fontseries{m}\\selectfont %s}", + ["@font-weight/bold"] = "\\textbf{%s}", + ["@font-weight/light"] = "{\\fontseries{l}\\selectfont %s}", + ["@text-decoration/none"] = false, + ["@text-decoration/underline"] = "\\underline{%s}", + ["@vertical-align/sup"] = "\\textsuperscript{%s}", + ["@vertical-align/sub"] = "\\textsubscript{%s}", + ["@vertical-align/baseline"] = false, + ["@cite/entry"] = false, + ["@bibliography/entry"] = function (str, context) + if not string.match(str, "\\bibitem") then + str = "\\bibitem{".. context.id .. "}\n" .. str .. "\n" + end + return str + end, + ["@display/block"] = false, + ["@display/left-margin"] = '\n
%s
', + ["@display/right-inline"] = '
%s
\n ', + ["@display/indent"] = '
%s
\n ', +} + +function LatexWriter:write_escaped(str, context) + -- TeXbook, p. 38 + str = str:gsub("\\", "\\textbackslash{}") + str = str:gsub("{", "\\{") + str = str:gsub("}", "\\}") + str = str:gsub("%$", "\\$") + str = str:gsub("&", "\\&") + str = str:gsub("#", "\\#") + str = str:gsub("%^", "\\^") + str = str:gsub("_", "\\_") + str = str:gsub("%%", "\\%%") + str = str:gsub("~", "\\~") + str = str:gsub(util.unicode["em space"], "\\quad ") + str = str:gsub(util.unicode["no-break space"], "~") + for char, sub in pairs(util.superscripts) do + str = string.gsub(str, char, "\\textsuperscript{" .. sub .. "}") + end + return str +end + +function LatexWriter:write_formatted(inline, context) + local res = self:write_children(inline, context) + for _, key in ipairs({"font-style", "font-variant", "font-weight", "text-decoration", "vertical-align"}) do + local value = inline.formatting[key] + if value then + key = "@" .. key .. "/" .. value + local format_str = self.markups[key] + if format_str then + res = string.format(format_str, res) + end + end + end + return res +end + +function LatexWriter:write_display(inline, context) + local plainter_text_writer = output_module.PlainTextWriter:new() + local str = plainter_text_writer:write_inline(inline) + local len = utf8.len(str) + if len > context.engine.registry.maxoffset then + context.engine.registry.maxoffset = len + context.engine.registry.longest_label = str + end + + local res = self:write_children(inline, context) + if inline.display == "left-margin" then + if string.match(res, "%]") then + res = "{" .. res .. "}" + end + res = string.format("\\bibitem[%s]{%s}\n", res, context.id) + + elseif inline.display == "right-inline" then + return res + + elseif inline.display == "block" then + return "" + end + return res +end + +function LatexWriter:write_link(inline, context) + if inline.href == inline.value then + return string.format("\\url{%s}", inline.value) + else + return string.format("\\href{%s}{%s}", inline.href, inline.value) + end +end + + +local HtmlWriter = Markup:new() + +HtmlWriter.markups = { + ["bibstart"] = "
\n", + ["bibend"] = "
", + ["@font-style/italic"] = "%s", + ["@font-style/oblique"] = "%s", + ["@font-style/normal"] = '%s', + ["@font-variant/small-caps"] = '%s', + ["@font-variant/normal"] = '%s', + ["@font-weight/bold"] = "%s", + ["@font-weight/normal"] = '%s', + ["@font-weight/light"] = false, + ["@text-decoration/none"] = '%s', + ["@text-decoration/underline"] = '%s', + ["@vertical-align/sup"] = "%s", + ["@vertical-align/sub"] = "%s", + ["@vertical-align/baseline"] = '%s', + ["@cite/entry"] = nil, + ["@bibliography/entry"] = "
%s
\n", + ["@display/block"] = '\n\n
%s
\n', + ["@display/left-margin"] = '\n
%s
', + ["@display/right-inline"] = '
%s
\n ', + ["@display/indent"] = '
%s
\n ', +} + +function HtmlWriter:write_escaped(str, context) + str = string.gsub(str, "%&", "&") + str = string.gsub(str, "<", "<") + str = string.gsub(str, ">", ">") + for char, sub in pairs(util.superscripts) do + str = string.gsub(str, char, "" .. sub .. "") + end + return str +end + +function HtmlWriter:write_formatted(inline, context) + local res = self:write_children(inline, context) + for _, key in ipairs({"font-style", "font-variant", "font-weight", "text-decoration", "vertical-align"}) do + local value = inline.formatting[key] + if value then + key = "@" .. key .. "/" .. value + local format_str = self.markups[key] + if format_str then + res = string.format(format_str, res) + end + end + end + return res +end + +function HtmlWriter:write_display(inline, context) + local plainter_text_writer = output_module.PlainTextWriter:new() + local str = plainter_text_writer:write_inline(inline) + local len = utf8.len(str) + if len > context.engine.registry.maxoffset then + context.engine.registry.maxoffset = len + context.engine.registry.longest_label = str + end + + if #inline.inlines == 0 then + return "" + end + local res = self:write_children(inline, context) + local key = string.format("@display/%s", inline.div) + if inline.div == "right-inline" then + -- Strip trailing spaces + -- variables_ContainerTitleShort.txt + res = string.gsub(res, "%s+$", "") + end + local format_str = self.markups[key] + res = string.format(format_str, res) + return res +end + +function HtmlWriter:write_link(inline, context) + return string.format('%s', inline.href, inline.value) +end + + +local PlainTextWriter = Markup:new() + +PlainTextWriter.markups = {} + +function PlainTextWriter:write_escaped(str, context) + return str +end + +function PlainTextWriter:write_formatted(inline, context) + return self:write_children(inline, context) +end + +function PlainTextWriter:write_display(inline, context) + return self:write_children(inline, context) +end + + +local SortStringFormat = OutputFormat:new() + +function SortStringFormat:output(inlines, context) + -- self:flip_flop_inlines(inlines) + -- self:move_punctuation(inlines) + return self:write_inlines(inlines, context) +end + +function SortStringFormat:write_escaped(str, context) + str = string.gsub(str, ",", "") + return str +end + + +local DisamStringFormat = OutputFormat:new() + +function DisamStringFormat:output(inlines, context) + -- self:flip_flop_inlines(inlines) + -- self:move_punctuation(inlines) + return self:write_inlines(inlines, context) +end + +function DisamStringFormat:flatten_ir(ir) + if self.group_var == "missing" then + return {} + end + local inlines + if ir._type == "SeqIr" or ir._type == "NameIr" then + inlines = self:flatten_seq_ir(ir) + elseif ir._type == "YearSuffix" then + -- Don't include year-suffix in disambiguation + inlines = {} + else + inlines = self:affixed_quoted(ir.inlines, ir.affixes, ir.quotes); + inlines = self:with_display(inlines, ir.display); + end + return inlines +end + +function DisamStringFormat:flatten_seq_ir(ir) + if not ir.children then + print(debug.traceback()) + end + if #ir.children == 0 then + return {} + end + local inlines_list = {} + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" then + -- and not child.collapse_suppressed + -- Suppressed irs are stil kept in the DisamStringFormat + table.insert(inlines_list, self:flatten_ir(child)) + end + end + + local inlines = self:group(inlines_list, ir.delimiter, ir.formatting) + -- assert ir.quotes == localized quotes + inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); + inlines = self:with_display(inlines, ir.display); + return inlines +end + + +output_module.move_in_puncts = { + ["."] = true, + ["!"] = true, + ["?"] = true, + [","] = true, +} + +output_module.move_out_puncts = { + [","] = true, + [";"] = true, + [":"] = true, +} + +-- https://github.com/Juris-M/citeproc-js/blob/aa2683f48fe23be459f4ed3be3960e2bb56203f0/src/queue.js#L724 +-- Also merge duplicate punctuations. +output_module.quote_punctuation_map = { + ["!"] = { + ["."] = "!", + ["?"] = "!?", + [":"] = "!", + [","] = "!,", + [";"] = "!;", + }, + ["?"] = { + ["!"] = "?!", + ["."] = "?", + [":"] = "?", + [","] = "?,", + [";"] = "?;", + }, + ["."] = { + ["!"] = ".!", + ["?"] = ".?", + [":"] = ".:", + [","] = ".,", + [";"] = ".;", + }, + [":"] = { + ["!"] = "!", + ["?"] = "?", + ["."] = ":", + [","] = ":,", + [";"] = ":;", + }, + [","] = { + ["!"] = ",!", + ["?"] = ",?", + [":"] = ",:", + ["."] = ",.", + [";"] = ",;", + }, + [";"] = { + ["!"] = "!", + ["?"] = "?", + [":"] = ";", + [","] = ";,", + ["."] = ";", + } +} + + +output_module.LocalizedQuotes = LocalizedQuotes + +output_module.InlineElement = InlineElement +output_module.PlainText = PlainText +output_module.Formatted = Formatted +output_module.Micro = Micro +output_module.Quoted = Quoted +output_module.Linked = Linked +output_module.Div = Div +output_module.NoCase = NoCase +output_module.NoDecor = NoDecor + +output_module.OutputFormat = OutputFormat + +output_module.Markup = Markup +output_module.LatexWriter = LatexWriter +output_module.HtmlWriter = HtmlWriter +output_module.PlainTextWriter = PlainTextWriter +output_module.DisamStringFormat = DisamStringFormat +output_module.SortStringFormat = SortStringFormat + +return output_module diff --git a/biblio/citation-style-language/citeproc-richtext.lua b/biblio/citation-style-language/citeproc-richtext.lua deleted file mode 100644 index f9afd3acc6..0000000000 --- a/biblio/citation-style-language/citeproc-richtext.lua +++ /dev/null @@ -1,779 +0,0 @@ --- --- Copyright (c) 2021-2022 Zeping Lee --- Released under the MIT license. --- Repository: https://github.com/zepinglee/citeproc-lua --- - -local richtext = {} - -local unicode = require("unicode") - -local util = require("citeproc-util") - - -local RichText = { - contents = nil, - formats = nil, - _type = "RichText", -} - -function RichText:shallow_copy() - local res = richtext.new() - for _, text in ipairs(self.contents) do - table.insert(res.contents, text) - end - for key, value in pairs(self.formats) do - res.formats[key] = value - end - return res -end - -function RichText:render(formatter, context, punctuation_in_quote) - self:merge_punctuations() - - if punctuation_in_quote == nil and context then - punctuation_in_quote = context.style:get_locale_option("punctuation-in-quote") - end - if punctuation_in_quote then - self:move_punctuation_in_quote() - end - - self:change_case() - - self:flip_flop() - - self:clean_formats() - - return self:_render(formatter, context) -end - -function RichText:_render(formatter, context) - local res = "" - for _, text in ipairs(self.contents) do - local str - if type(text) == "string" then - if formatter and formatter.text_escape then - str = formatter.text_escape(text) - else - str = text - end - else -- RichText - str = text:_render(formatter, context) - end - -- Remove leading spaces - if string.sub(res, -1) == " " and string.sub(str, 1, 1) == " " then - str = string.gsub(str, "^%s+", "") - end - res = res .. str - end - for _, attr in ipairs(richtext.format_sequence) do - local value = self.formats[attr] - if value then - local key = string.format("@%s/%s", attr, value) - if formatter then - local format = formatter[key] - if type(format) == "string" then - res = string.format(format, res) - elseif type(format) == "function" then - res = format(res, context) - end - end - end - end - return res -end - -function RichText:merge_punctuations(contents, index) - for i, text in ipairs(self.contents) do - if text._type == "RichText" then - contents, index = text:merge_punctuations(contents, index) - elseif type(text) == "string" then - if contents and index then - local previous_string = contents[index] - local last_char = string.sub(previous_string, -1) - local right_punct_map = richtext.punctuation_map[last_char] - if right_punct_map then - local first_char = string.sub(text, 1, 1) - local new_punctuations = nil - if first_char == last_char then - new_punctuations = last_char - elseif contents == self.contents then - new_punctuations = right_punct_map[first_char] - end - if new_punctuations then - if #text == 1 then - table.remove(self.contents, i) - else - self.contents[i] = string.sub(text, 2) - end - contents[index] = string.sub(previous_string, 1, -2) .. new_punctuations - end - end - end - contents = self.contents - index = i - end - end - return contents, index -end - -function RichText:move_punctuation_in_quote() - local i = 1 - while i <= #self.contents do - local text = self.contents[i] - if type(text) == "table" and text._type == "RichText" then - text:move_punctuation_in_quote() - - if text.formats["quotes"] then - local contents = self.contents - local last_string = text - while type(last_string) == "table" and last_string._type == "RichText" do - contents = last_string.contents - last_string = contents[#contents] - end - - local done = false - while not done do - done = true - last_string = contents[#contents] - local last_char = string.sub(last_string, -1) - if i < #self.contents then - local next_text = self.contents[i + 1] - if type(next_text) == "string" then - local first_char = string.sub(next_text, 1, 1) - if richtext.in_quote_punctuations[first_char] then - done = false - local right_punct_map = richtext.punctuation_map[last_char] - if right_punct_map then - first_char = right_punct_map[first_char] - last_string = string.sub(last_string, 1, -2) - end - contents[#contents] = last_string .. first_char - if #next_text == 1 then - table.remove(self.contents, i + 1) - else - self.contents[i + 1] = string.sub(next_text, 2) - end - end - end - end - end - end - end - i = i + 1 - end -end - -function RichText:change_case() - for _, text in ipairs(self.contents) do - if type(text) == "table" and text._type == "RichText" then - text:change_case() - end - end - local text_case = self.formats["text-case"] - if text_case then - if text_case == "lowercase" then - self:lowercase() - elseif text_case == "uppercase" then - self:uppercase() - elseif text_case == "capitalize-first" then - self:capitalize_first() - elseif text_case == "capitalize-all" then - self:capitalize_all() - elseif text_case == "sentence" then - self:sentence() - elseif text_case == "title" then - self:title() - end - end -end - -function RichText:_change_word_case(state, word_transform, first_tranform, is_phrase) - if self.formats["text-case"] == "nocase" then - return - end - if is_phrase and (self.formats["vertical-align"] == "sup" or - self.formats["vertical-align"] == "sub" or - self.formats["font-variant"] == "small-caps") then - return - end - state = state or "after-sentence" - word_transform = word_transform or function (x) return x end - first_tranform = first_tranform or word_transform - for i, text in ipairs(self.contents) do - if type(text) == "string" then - - local res = "" - local word_seps = { - " ", - "%-", - "/", - util.unicode["no-break space"], - util.unicode["en dash"], - util.unicode["em dash"], - } - for _, tuple in ipairs(util.split(text, word_seps, nil, true)) do - local word, punctuation = table.unpack(tuple) - if state == "after-sentence" then - res = res .. first_tranform(word) - if string.match(word, "%w") then - state = "after-word" - end - else - res = res .. word_transform(word, punctuation) - end - res = res .. punctuation - if string.match(word, "[.!?:]%s*$") then - state = "after-sentence" - end - end - - -- local word_index = 0 - -- local res = string.gsub(text, "%w+", function (word) - -- word_index = word_index + 1 - -- if word_index == 1 then - -- return first_tranform(word) - -- else - -- return word_transform(word) - -- end - -- end) - -- if string.match(res, "[.!?:]%s*$") then - -- state = "after-sentence" - -- end - - self.contents[i] = res - else - state = text:_change_word_case(state, word_transform, first_tranform, true) - end - end - return state -end - -function RichText:lowercase() - local word_transform = unicode.utf8.lower - self:_change_word_case("after-sentence", word_transform) -end - -function RichText:uppercase() - local word_transform = unicode.utf8.upper - self:_change_word_case("after-sentence", word_transform) -end - -local function capitalize(str) - local res = string.gsub(str, utf8.charpattern, unicode.utf8.upper, 1) - return res -end - -local function capitalize_if_lower(word) - if util.is_lower(word) then - return capitalize(word) - else - return word - end -end - -function RichText:capitalize_first(state) - local first_tranform = capitalize_if_lower - self:_change_word_case("after-sentence", nil, first_tranform) -end - -function RichText:capitalize_all() - local word_transform = capitalize_if_lower - self:_change_word_case("after-sentence", word_transform) -end - -function RichText:is_upper() - for _, text in ipairs(self.contents) do - if type(text) == "string" then - if not util.is_upper(text) then - return false - end - else - local res = text:is_upper() - if not res then - return false - end - end - end - return true -end - -function RichText:sentence() - if self:is_upper() then - local first_tranform = function(word) - return capitalize(unicode.utf8.lower(word)) - end - local word_transform = unicode.utf8.lower - self:_change_word_case("after-sentence", word_transform, first_tranform) - else - local first_tranform = capitalize_if_lower - self:_change_word_case("after-sentence", nil, first_tranform) - end -end - -function RichText:title() - if self:is_upper() then - local first_tranform = function(word) - return capitalize(unicode.utf8.lower(word)) - end - local word_transform = function(word, sep) - local res = unicode.utf8.lower(word) - if not util.stop_words[res] then - res = capitalize(res) - end - return res - end - self:_change_word_case("after-sentence", word_transform, first_tranform) - else - local first_tranform = capitalize_if_lower - local word_transform = function(word, sep) - local lower = unicode.utf8.lower(word) - -- Stop word before hyphen is treated as a normal word. - if util.stop_words[lower] and sep ~= "-" then - return lower - elseif word == lower then - return capitalize(word) - else - return word - end - end - self:_change_word_case("after-sentence", word_transform, first_tranform) - end -end - -function richtext.concat(str1, str2) - assert(str1 and str2) - - if type(str1) == "string" then - str1 = richtext.new(str1) - end - - local res - if next(str1.formats) == nil or str2 == "" then - -- shallow copy - res = str1 - else - res = richtext.new() - res.contents = {str1} - end - - if str2._type == "RichText" then - if next(str2.formats) == nil then - for _, text in ipairs(str2.contents) do - table.insert(res.contents, text) - end - else - table.insert(res.contents, str2) - end - elseif str2 ~= "" then - table.insert(res.contents, str2) - end - return res -end - -function richtext.concat_list(list, delimiter) - -- Strings in the list may be nil thus ipairs() should be avoided. - -- The delimiter may be nil. - local res = nil - for i = 1, #list do - local text = list[i] - if text and text ~= "" then - if res then - if delimiter and delimiter ~= "" then - res = richtext.concat(res, delimiter) - end - res = richtext.concat(res, text) - else - if type(text) == "string" then - text = richtext.new(text) - end - res = text - end - end - end - return res -end - -function RichText:strip_periods() - local last_string = self - local contents = self.contents - while last_string._type == "RichText" do - contents = last_string.contents - last_string = contents[#contents] - end - if string.sub(last_string, -1) == "." then - contents[#contents] = string.sub(last_string, 1, -2) - end -end - -function RichText:add_format(attr, value) - self.formats[attr] = value -end - -function RichText:flip_flop(attr, value) - if not attr then - for attr, _ in pairs(richtext.flip_flop_formats) do - self:flip_flop(attr) - end - return - end - - local default_value = richtext.default_formats[attr] - - if value and value ~= default_value and self.formats[attr] == value then - self.formats[attr] = richtext.flip_flop_values[attr][value] - end - if self.formats[attr] then - value = self.formats[attr] - end - - for _, text in ipairs(self.contents) do - if type(text) == "table" and text._type == "RichText" then - text:flip_flop(attr, value) - end - end -end - -function RichText:clean_formats(format) - -- Remove the formats that are default values - if not format then - for format, _ in pairs(richtext.default_formats) do - self:clean_formats(format) - end - return - end - if self.formats[format] then - if self.formats[format] == richtext.default_formats[format] then - self.formats[format] = nil - else - return - end - end - for _, text in ipairs(self.contents) do - if type(text) == "table" and text._type == "RichText" then - text:clean_formats(format) - end - end -end - -local RichText_mt = { - __index = RichText, - __concat = richtext.concat, -} - -local function table_update(t, new_t) - for key, value in pairs(new_t) do - t[key] = value - end - return t -end - -function RichText._split_tags(str) - -- Normalize markup - str = string.gsub(str, '', '') - str = string.gsub(str, '', '') - str = string.gsub(str, '', '') - - local strings = {} - - local start_index = 1 - local i = 1 - - while i <= #str do - local substr = string.sub(str, i) - local starts_with_tag = false - for tag, _ in pairs(richtext.tags) do - if util.startswith(substr, tag) then - if start_index <= i - 1 then - table.insert(strings, string.sub(str, start_index, i-1)) - end - table.insert(strings, tag) - i = i + #tag - start_index = i - starts_with_tag = true - break - end - end - if not starts_with_tag then - i = i + 1 - end - end - if start_index <= #str then - table.insert(strings, string.sub(str, start_index)) - end - - for i = 1, #strings do - str = strings[i] - if str == "'" or str == util.unicode["apostrophe"] then - local previous_str = strings[i - 1] - local next_str = strings[i + 1] - if previous_str and next_str then - local previous_code_point = nil - for _, code_point in utf8.codes(previous_str) do - previous_code_point = code_point - end - local next_code_point = utf8.codepoint(next_str) - if util.is_romanesque(previous_code_point) and util.is_romanesque(next_code_point) then - -- An apostrophe - strings[i-1] = strings[i-1] .. util.unicode["apostrophe"] .. strings[i+1] - table.remove(strings, i+1) - table.remove(strings, i) - end - end - end - end - - return strings -end - -function richtext.new(text, formats) - local res = { - contents = {}, - formats = formats or {}, - } - - setmetatable(res, RichText_mt) - - if not text then - return res - end - - if type(text) == "string" then - - local strings = RichText._split_tags(text) - local contents = {} - for _, str in ipairs(strings) do - table.insert(contents, str) - - local end_tag = nil - if str == '"' then - local last_text = contents[#contents - 1] - if last_text and type(last_text) == "string" and string.match(last_text, "%s$") then - end_tag = nil - else - end_tag = str - end - elseif richtext.end_tags[str] then - end_tag = str - end - - if end_tag then - for i = #contents - 1, 1, -1 do - local start_tag = contents[i] - if type(start_tag) == "string" and richtext.tag_pairs[start_tag] == end_tag then - local subtext = richtext.new() - -- subtext.contents = util.slice(contents, i + 1, #contents - 1) - if start_tag == "'" and end_tag == "'" and i == #contents - 1 then - contents[i] = util.unicode["apostrophe"] - contents[#contents] = util.unicode["apostrophe"] - break - end - - for j = i + 1, #contents - 1 do - local substr = contents[j] - if substr == "'" then - substr = util.unicode["apostrophe"] - end - local last_text = subtext.contents[#subtext.contents] - if type(substr) == "string" and type(last_text) == "string" then - subtext.contents[#subtext.contents] = last_text .. substr - else - table.insert(subtext.contents, substr) - end - end - - if start_tag == '' then - for attr, value in pairs(richtext.default_formats) do - subtext.formats[attr] = value - end - subtext.formats["text-case"] = "nocase" - else - for attr, value in pairs(richtext.tag_formats[start_tag]) do - subtext.formats[attr] = value - end - end - - for j = #contents, i, -1 do - table.remove(contents, j) - end - table.insert(contents, subtext) - break - end - end - end - end - - for i = #contents, 1, -1 do - if contents[i] == "'" then - contents[i] = util.unicode["apostrophe"] - end - if type(contents[i]) == "string" and type(contents[i+1]) == "string" then - contents[i] = contents[i] .. contents[i+1] - table.remove(contents, i+1) - end - end - - if #contents == 1 and type(contents[1]) == "table" then - res = contents[1] - else - res.contents = contents - end - - return res - - elseif type(text) == "table" and text._type == "RichText" then - return text - - elseif type(text) == "table" then - return text - end - return nil -end - -richtext.tag_formats = { - [""] = {["font-style"] = "italic"}, - [""] = {["font-weight"] = "bold"}, - [""] = {["vertical-align"] = "sup"}, - [""] = {["vertical-align"] = "sub"}, - [""] = {["font-variant"] = "small-caps"}, - [''] = {["font-variant"] = "small-caps"}, - [''] = {["text-case"] = "nocase"}, - ['"'] = {["quotes"] = "true"}, - [util.unicode['left double quotation mark']] = {["quotes"] = "true"}, - ["'"] = {["quotes"] = "true"}, - [util.unicode['left single quotation mark']] = {["quotes"] = "true"}, -} - -richtext.default_formats = { - ["URL"] = "false", - ["DOI"] = "false", - ["PMID"] = "false", - ["PMCID"] = "false", - ["font-style"] = "normal", - ["font-variant"] = "normal", - ["font-weight"] = "normal", - ["text-decoration"] = "none", - ["vertical-align"] = "baseline", - ["quotes"] = "false", -} - -richtext.format_sequence = { - "URL", - "DOI", - "PMID", - "PMCID", - "font-style", - "font-variant", - "font-weight", - "text-decoration", - "vertical-align", - "quotes", - "display", -} - -richtext.flip_flop_formats = { - ["font-style"] = true, - ["font-weight"] = true, - ["font-variant"] = true, - ["quotes"] = true, -} - -richtext.flip_flop_values = { - ["font-style"] = { - italic = "normal", - normal = "italic", - }, - ["font-weight"] = { - bold = "normal", - normal = "bold", - }, - ["font-variant"] = { - ["small-caps"] = "normal", - normal = "small-caps", - }, - ["quotes"] = { - ["true"] = "inner", - inner = "true", - }, -} - --- https://github.com/Juris-M/citeproc-js/blob/aa2683f48fe23be459f4ed3be3960e2bb56203f0/src/queue.js#L724 --- Also merge duplicate punctuations. -richtext.punctuation_map = { - ["!"] = { - ["."] = "!", - ["?"] = "!?", - [":"] = "!", - [","] = "!,", - [";"] = "!;", - }, - ["?"] = { - ["!"] = "?!", - ["."] = "?", - [":"] = "?", - [","] = "?,", - [";"] = "?;", - }, - ["."] = { - ["!"] = ".!", - ["?"] = ".?", - [":"] = ".:", - [","] = ".,", - [";"] = ".;", - }, - [":"] = { - ["!"] = "!", - ["?"] = "?", - ["."] = ":", - [","] = ":,", - [";"] = ":;", - }, - [","] = { - ["!"] = ",!", - ["?"] = ",?", - [":"] = ",:", - ["."] = ",.", - [";"] = ",;", - }, - [";"] = { - ["!"] = "!", - ["?"] = "?", - [":"] = ";", - [","] = ";,", - ["."] = ";", - } -} - -richtext.in_quote_punctuations = { - [","] = true, - ["."] = true, - ["?"] = true, - ["!"] = true, -} - -richtext.tag_pairs = { - [""] = "", - [""] = "", - [""] = "", - [""] = "", - [""] = "", - [''] = "", - [''] = "", - [''] = "", - ['"'] = '"', - [util.unicode['left double quotation mark']] = util.unicode['right double quotation mark'], - ["'"] = "'", - [util.unicode['left single quotation mark']] = util.unicode['right single quotation mark'], -} - -richtext.tags = {} - -richtext.end_tags = {} - -for start_tag, end_tag in pairs(richtext.tag_pairs) do - richtext.tags[start_tag] = true - richtext.tags[end_tag] = true - richtext.end_tags[end_tag] = true -end - -return richtext diff --git a/biblio/citation-style-language/citeproc-util.lua b/biblio/citation-style-language/citeproc-util.lua index c9adb6558f..38de3b5751 100644 --- a/biblio/citation-style-language/citeproc-util.lua +++ b/biblio/citation-style-language/citeproc-util.lua @@ -6,11 +6,55 @@ -- load `slnunicode` from LuaTeX local unicode = require("unicode") -local inspect = require("inspect") +local inspect -- only load it when debugging local util = {} +function util.clone(obj) + if type(obj) == "table" then + local res = {} + for key, value in pairs(obj) do + res[key] = value + end + return setmetatable(res, getmetatable(obj)) + else + return obj + end +end + +function util.join(list, delimiter) + local res = {} + for i, item in ipairs(list) do + if i > 1 then + table.insert(res, delimiter) + end + table.insert(res, item) + end + return res +end + +function util.to_boolean(str) + if not str then + return false + end + if str == "true" then + return true + elseif str == "false" then + return false + else + util.warning(string.format('Invalid boolean string "%s"', str)) + return false + end +end + +function util.to_list(str) + if not str then + return nil + end + return util.split(str) +end + function util.to_ordinal (n) assert(type(n) == "number") local last_digit = n % 10 @@ -44,21 +88,60 @@ function util.warning(message) end end -local function remove_all_metatables(item, path) - if path[#path] ~= inspect.METATABLE then return item end -end +local remove_all_metatables = nil -function util.debug(...) - -- io.stderr:write(inspect(..., {process = remove_all_metatables})) - io.stderr:write(inspect(...)) +function util.debug(obj) + if not inspect then + inspect = require("inspect") + remove_all_metatables = function (item, path) + if path[#path] ~= inspect.METATABLE then + return item + end + end + end + io.stderr:write("[") + io.stderr:write(debug.getinfo(2, "S").source:sub(2)) + io.stderr:write(":") + io.stderr:write(debug.getinfo(2, "l").currentline) + io.stderr:write("] ") + io.stderr:write(inspect(obj, {process = remove_all_metatables})) io.stderr:write("\n") end -- Similar to re.split() in Python -function util.split(str, seps, maxsplit, include_sep) +function util.split(str, sep, maxsplit) if not str then - error("Invalid string.") + util.error("Invalid string.") + end + sep = sep or "%s+" + if sep == "" then + util.error("Empty separator") end + if string.find(str, sep) == nil then + return { str } + end + + if maxsplit == nil or maxsplit < 0 then + maxsplit = -1 -- No limit + end + local result = {} + local pattern = "(.-)" .. sep .. "()" + local num_splits = 0 + local lastPos = 1 + for part, pos in string.gmatch(str, pattern) do + if num_splits == maxsplit then + break + end + num_splits = num_splits + 1 + result[num_splits] = part + lastPos = pos + end + -- Handle the last field + result[num_splits + 1] = string.sub(str, lastPos) + return result +end + +function util.split_multiple(str, seps, include_sep) seps = seps or "%s+" if seps == "" then error("Empty separator") @@ -97,6 +180,15 @@ function util.split(str, seps, maxsplit, include_sep) return res end +-- TODO: [Unicode word boundaries](https://www.unicode.org/reports/tr29/#Word_Boundaries) +-- Returns: { +-- {word, boundary}, +-- {word, boundary}, +-- } +function util.segment_words(str) + return util.split_multiple(str, util.word_boundaries, true) +end + function util.slice (t, start, stop) start = start or 1 stop = stop or #t @@ -132,6 +224,31 @@ function util.concat (list, sep) return res end +-- Python list.extend() +function util.extend(first, second) + if not second then + print(debug.traceback()) + end + local l = #first + for i, element in ipairs(second) do + first[l + i] = element + end + return first +end + +-- Concat two lists in place +function util.concat_list(first, second) + local res + for i, element in ipairs(first) do + res[i] = element + end + local i = #res + for j, element in ipairs(second) do + res[i + j] = element + end + return res +end + function util.lstrip (str) if not str then return nil @@ -153,22 +270,34 @@ function util.strip (str) end function util.startswith (str, prefix) + if not str then + return false + end return string.sub(str, 1, #prefix) == prefix end function util.endswith (str, suffix) + if not str then + return false + end return string.sub(str, -#suffix) == suffix end +function util.is_punct(str) + return string.match(str, "^%p$") +end + function util.is_numeric (str) if str == nil or str == "" then return false end local res = true for w in string.gmatch(str, "%w+") do - if string.match(w, "^[a-zA-Z]*%d+[a-zA-Z]*$") == nil then - res = false - break + if not string.match(w, "^%a*%d+%a*$") and + not string.match(w, "^[MDCLXVI]+$") and + not string.match(w, "^[mdclxvi]+$") then + -- Roman number withou validation + return false end end for w in string.gmatch(str, "%W+") do @@ -180,19 +309,84 @@ function util.is_numeric (str) return res end -function util.is_uncertain_date (variable) - if variable == nil then - return false - end - local value = variable["circa"] - return value ~= nil and value ~= "" -end - util.variable_types = {} -- schema/schemas/styles/csl-variables.rnc util.variables = {} +-- -- Standard variables +-- util.variables.standard = { +-- "abstract", +-- "annote", +-- "archive", +-- "archive_collection", +-- "archive_location", +-- "archive-place", +-- "authority", +-- "call-number", +-- "citation-key", +-- "citation-label", +-- "collection-title", +-- "container-title", +-- "container-title-short", +-- "dimensions", +-- "division", +-- "DOI", +-- "event", +-- "event-title", +-- "event-place", +-- "genre", +-- "ISBN", +-- "ISSN", +-- "jurisdiction", +-- "keyword", +-- "language", +-- "license", +-- "medium", +-- "note", +-- "original-publisher", +-- "original-publisher-place", +-- "original-title", +-- "part-title", +-- "PMCID", +-- "PMID", +-- "publisher", +-- "publisher-place", +-- "references", +-- "reviewed-genre", +-- "reviewed-title", +-- "scale", +-- "source", +-- "status", +-- "title", +-- "title-short", +-- "URL", +-- "volume-title", +-- "year-suffix", +-- } + +-- Number variables +util.variables.number = { + "chapter-number", + "citation-number", + "collection-number", + "edition", + "first-reference-note-number", + "issue", + "locator", + "number", + "number-of-pages", + "number-of-volumes", + "page", + "page-first", + "part-number", + "printing-number", + "section", + "supplement-number", + "version", + "volume", +} + -- Date variables util.variables.date = { "accessed", @@ -234,28 +428,6 @@ util.variables.name = { "translator", } --- Number variables -util.variables.number = { - "chapter-number", - "citation-number", - "collection-number", - "edition", - "first-reference-note-number", - "issue", - "locator", - "number", - "number-of-pages", - "number-of-volumes", - "page", - "page-first", - "part-number", - "printing-number", - "section", - "supplement-number", - "version", - "volume", -} - util.variable_types = {} for type, variables in pairs(util.variables) do @@ -327,10 +499,22 @@ util.unicode = { ["apostrophe"] = "\u{2019}", ["left double quotation mark"] = "\u{201C}", ["right double quotation mark"] = "\u{201D}", + ["left-pointing double angle quotation mark"] = "\u{00AB}", + ["right-pointing double angle quotation mark"] = "\u{00BB}", ["horizontal ellipsis"] = "\u{2026}", ["narrow no-break space"] = "\u{202F}", } +util.word_boundaries = { + ":", + " ", + "%-", + "/", + util.unicode["no-break space"], + util.unicode["en dash"], + util.unicode["em dash"], +} + -- Text-case @@ -342,9 +526,11 @@ function util.is_upper (str) return unicode.utf8.upper(str) == str end -function util.capitalize (str) - str = unicode.utf8.lower(str) - local res = string.gsub(str, "%w", unicode.utf8.upper, 1) +function util.capitalize(str) + -- if not str then + -- print(debug.traceback()) + -- end + local res = string.gsub(str, utf8.charpattern, unicode.utf8.upper, 1) return res end @@ -635,7 +821,7 @@ function util.convert_roman (number) local output = {} for _, tuple in ipairs(util.roman_numerals) do local letter, value = table.unpack(tuple) - table.insert(output, string.rep(letter, number // value)) + table.insert(output, string.rep(letter, math.floor(number / value))) number = number % value end return table.concat(output, "") @@ -793,4 +979,35 @@ function util.read_file(path) end +function util.parse_iso_date(str) + local date + local date_parts = util.split(str, "/") + if #date_parts <= 2 then + date = {["date-parts"] = {}} + for _, date_part in ipairs(date_parts) do + table.insert(date["date-parts"], util.split(date_part, "%-")) + end + end + if not date then + date = {literal = str} + end + return date +end + + +function util.parse_extra_name(str) + local name + local name_parts = util.split(str, "%s*||%s*") + if #name_parts == 2 then + name = { + family = name_parts[1], + given = name_parts[2], + } + else + name = {literal = str} + end + return name +end + + return util diff --git a/biblio/citation-style-language/citeproc.1 b/biblio/citation-style-language/citeproc.1 deleted file mode 100644 index bb891d9f41..0000000000 --- a/biblio/citation-style-language/citeproc.1 +++ /dev/null @@ -1,26 +0,0 @@ -.TH citeproc 1 "0.1.1" -.SH NAME -citeproc \- make CSL citations and bibliography for LaTeX -.SH SYNOPSIS -.B citeproc -.RB [] -.IR auxname [ \fB.aux\fP ] -.SH DESCRIPTION -The citeproc engine is a Citation Style Lanugage processor for LaTeX. -It reads the auxiliary -.RB ( .aux ) -file -.I auxname -and creates a bibliography -.RB ( .bbl ) -file -.SH OPTIONS -.IP \-h ", " \-\-help -Prints the usage and exits. -.IP \-V ", " \-\-version -Prints the version number and exits. -.SH AUTHOR -Zeping Lee -.PP -Please log issues on the GitHub homepage: -https://github.com/zepinglee/citeproc-lua/issues. diff --git a/biblio/citation-style-language/citeproc.lua b/biblio/citation-style-language/citeproc.lua index 828d951403..b266abbdc5 100644 --- a/biblio/citation-style-language/citeproc.lua +++ b/biblio/citation-style-language/citeproc.lua @@ -10,7 +10,7 @@ local engine = require("citeproc-engine") local bib = require("citeproc-bib") local util = require("citeproc-util") -citeproc.__VERSION__ = "0.1.1" +citeproc.__VERSION__ = "0.2.0" citeproc.new = engine.CiteProc.new citeproc.parse_bib = bib.parse diff --git a/biblio/citation-style-language/csl-core.lua b/biblio/citation-style-language/csl-core.lua deleted file mode 100644 index e4e2fed32e..0000000000 --- a/biblio/citation-style-language/csl-core.lua +++ /dev/null @@ -1,306 +0,0 @@ --- --- Copyright (c) 2021-2022 Zeping Lee --- Released under the MIT license. --- Repository: https://github.com/zepinglee/citeproc-lua --- - -local core = {} - -local citeproc = require("citeproc") -local util = citeproc.util -require("lualibs") - - -core.locale_file_format = "csl-locales-%s.xml" -core.ids = {} -core.loaded_ids = {} -core.uncite_all_items = false - -function core.error(message) - if luatexbase then - luatexbase.module_error("csl", message) - else - util.error(message) - end -end - -function core.warning(message) - if luatexbase then - luatexbase.module_warning("csl", message) - else - util.warning(message) - end -end - -function core.info(message) - if luatexbase then - luatexbase.module_info("csl", message) - else - util.info(message) - end -end - - -function core.read_file(file_name, ftype, file_info) - file_info = file_info or "file" - local path = kpse.find_file(file_name, ftype) - if not path then - if ftype and not util.endswith(file_name, ftype) then - file_name = file_name .. ftype - end - core.error(string.format('Cannot find %s "%s"', file_info, file_name)) - return nil - end - local file = io.open(path, "r") - if not file then - core.error(string.format('Cannot open %s "%s"', file_info, path)) - return nil - end - local contents = file:read("*a") - file:close() - return contents -end - - -local function read_data_file(data_file) - local file_name = data_file - local extension = nil - local contents = nil - - if util.endswith(data_file, ".json") then - extension = ".json" - contents = core.read_file(data_file, nil, "database file") - elseif util.endswith(data_file, ".bib") then - extension = ".bib" - contents = core.read_file(data_file, "bib", "database file") - else - local path = kpse.find_file(data_file .. ".json") - if path then - file_name = data_file .. ".json" - extension = ".json" - contents = core.read_file(data_file .. ".json", nil, "database file") - else - path = kpse.find_file(data_file, "bib") - if path then - file_name = data_file .. ".bib" - extension = ".bib" - contents = core.read_file(data_file, "bib", "database file") - else - core.error(string.format('Cannot find database file "%s"', data_file .. ".json")) - end - end - end - - local csl_items = nil - - if extension == ".json" then - csl_items = utilities.json.tolua(contents) - elseif extension == ".bib" then - csl_items = citeproc.parse_bib(contents) - end - - return file_name, csl_items -end - - -local function read_data_files(data_files) - local bib = {} - for _, data_file in ipairs(data_files) do - local file_name, csl_items = read_data_file(data_file) - - -- TODO: parse bib entries on demand - for _, item in ipairs(csl_items) do - local id = item.id - if bib[id] then - core.error(string.format('Duplicate entry key "%s" in "%s".', id, file_name)) - end - bib[id] = item - end - end - return bib -end - - - -function core.make_citeproc_sys(data_files) - core.bib = read_data_files(data_files) - local citeproc_sys = { - retrieveLocale = function (lang) - local locale_file_format = core.locale_file_format or "locales-%s.xml" - local filename = string.format(locale_file_format, lang) - return core.read_file(filename) - end, - retrieveItem = function (id) - local res = core.bib[id] - -- if not res then - -- core.warning(string.format('Failed to find entry "%s".', id)) - -- end - return res - end - } - - return citeproc_sys -end - -function core.init(style_name, data_files, lang) - if style_name == "" or #data_files == 0 then - return nil - end - local style = core.read_file(style_name .. ".csl", nil, "style file") - if not style then - core.error(string.format('Failed to load style "%s.csl"', style_name)) - return nil - end - - local force_lang = nil - if lang and lang ~= "" then - force_lang = true - else - lang = nil - end - - local citeproc_sys = core.make_citeproc_sys(data_files) - local engine = citeproc.new(citeproc_sys, style, lang, force_lang) - return engine -end - - -function core.make_citation(citation_info) - -- `citation_info`: "{ITEM-1@2}{{id={ITEM-1},label={page},locator={6}}}{3}" - local arguments = {} - for argument in string.gmatch(citation_info, "(%b{})") do - table.insert(arguments, string.sub(argument, 2, -2)) - end - if #arguments ~= 3 then - error(string.format('Invalid citation "%s"', citation_info)) - return nil - end - local citation_id, cite_items_str, note_index = table.unpack(arguments) - - local cite_items = {} - if citation_id == "nocite" then - for _, cite_id in ipairs(util.split(cite_items_str, "%s*,%s*")) do - table.insert(cite_items, {id = cite_id}) - end - - else - for item_str in string.gmatch(cite_items_str, "(%b{})") do - item_str = string.sub(item_str, 2, -2) - local cite_item = {} - for key, value in string.gmatch(item_str, "([%w%-]+)=(%b{})") do - value = string.sub(value, 2, -2) - cite_item[key] = value - end - table.insert(cite_items, cite_item) - end - end - - local citation = { - citationID = citation_id, - citationItems = cite_items, - properties = { - noteIndex = tonumber(note_index), - }, - } - - return citation -end - - -function core.process_citations(engine, citations) - local citations_pre = {} - - -- Save the time of bibliography sorting by update all ids at one time. - core.update_item_ids(engine, citations) - local citation_strings = {} - - for _, citation in ipairs(citations) do - if citation.citationID ~= "nocite" then - local res = engine:processCitationCluster(citation, citations_pre, {}) - - for _, citation_res in ipairs(res[2]) do - local citation_str = citation_res[2] - local citation_id = citation_res[3] - citation_strings[citation_id] = citation_str - end - - table.insert(citations_pre, {citation.citationID, citation.properties.noteIndex}) - end - end - - return citation_strings -end - - -function core.update_item_ids(engine, citations) - if core.uncite_all_items then - for item_id, _ in pairs(core.bib) do - if not core.loaded_ids[item_id] then - table.insert(core.ids, item_id) - core.loaded_ids[item_id] = true - end - end - end - for _, citation in ipairs(citations) do - for _, cite_item in ipairs(citation.citationItems) do - local id = cite_item.id - if id == "*" then - for item_id, _ in pairs(core.bib) do - if not core.loaded_ids[item_id] then - table.insert(core.ids, item_id) - core.loaded_ids[item_id] = true - end - end - else - if not core.loaded_ids[id] then - table.insert(core.ids, id) - core.loaded_ids[id] = true - end - end - end - end - engine:updateItems(core.ids) - -- TODO: engine:updateUncitedItems(ids) -end - - -function core.make_bibliography(engine) - local result = engine:makeBibliography() - - local params = result[1] - local bib_items = result[2] - - local res = "" - - local bib_options = "" - if params["hangingindent"] then - bib_options = bib_options .. "\n hanging-indent = true," - end - if params["linespacing"] then - bib_options = bib_options .. string.format("\n line-spacing = %d,", params["linespacing"]) - end - if params["entryspacing"] then - bib_options = bib_options .. string.format("\n entry-spacing = %d,", params["entryspacing"]) - end - - if bib_options ~= "" then - bib_options = "\\cslsetup{" .. bib_options .. "\n}\n\n" - res = res .. bib_options - end - - if params.bibstart then - res = res .. params.bibstart - end - - for _, bib_item in ipairs(bib_items) do - res = res .. "\n" .. bib_item - end - - if params.bibend then - res = res .. "\n" .. params.bibend - end - return res -end - - -return core diff --git a/biblio/citation-style-language/csl-locales-en-GB.xml b/biblio/citation-style-language/csl-locales-en-GB.xml index 11239a37d9..2e1d04ada2 100644 --- a/biblio/citation-style-language/csl-locales-en-GB.xml +++ b/biblio/citation-style-language/csl-locales-en-GB.xml @@ -11,7 +11,7 @@ Rintze M. Zelle This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License - 2022-01-01T21:32:03-05:00 + 2022-03-12T05:55:43-05:00 @@ -309,39 +309,39 @@ - + app. apps. - + art. arts. - + loc. locs. - + eq. eqs. - + r. rr. - + sc. scs. - + tbl. tbls. - + - + tit. tits. diff --git a/biblio/citation-style-language/csl-locales-en-US.xml b/biblio/citation-style-language/csl-locales-en-US.xml index 03ffc42f9f..7fa0207e6a 100644 --- a/biblio/citation-style-language/csl-locales-en-US.xml +++ b/biblio/citation-style-language/csl-locales-en-US.xml @@ -17,7 +17,7 @@ Brenton M. Wiernik This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License - 2022-01-01T21:32:03-05:00 + 2022-03-12T05:55:43-05:00 @@ -315,39 +315,39 @@ - + app. apps. - + art. arts. - + loc. locs. - + eq. eqs. - + r. rr. - + sc. scs. - + tbl. tbls. - + - + tit. tits. diff --git a/biblio/citation-style-language/csl-locales-fr-FR.xml b/biblio/citation-style-language/csl-locales-fr-FR.xml index e6a71687d4..c94055ef8a 100644 --- a/biblio/citation-style-language/csl-locales-fr-FR.xml +++ b/biblio/citation-style-language/csl-locales-fr-FR.xml @@ -4,8 +4,11 @@ Grégoire Colly + + Collectif Zotero francophone + This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License - 2022-01-01T21:32:03-05:00 + 2022-04-01T14:30:37+02:00 @@ -19,33 +22,33 @@ - advance online publication + publication en ligne anticipée album - audio recording + enregistrement audio film - henceforth - loc. cit. - no place - n.p. - no publisher - n.p. - on - op. cit. - original work published + désormais + loc. cit. + sans lieu + s. l. + sans nom + s. n. + sur + op. cit. + édition originale communication personnelle podcast - podcast episode - preprint - radio broadcast - radio series - radio series episode - special issue - special section - television broadcast - television series - television series episode - video - working paper + épisode de podcast + prépublication + émission de radio + série radiophonique + épisode de série radiophonique + numéro spécial + section spéciale + émission de télévision + série télévisée + épisode de série télévisée + vidéo + document de travail consulté le et et autres @@ -88,74 +91,74 @@ version - preprint - journal article - magazine article - newspaper article - bill - book - broadcast - book chapter - classic + article + article de revue + article de magazine + article de presse + projet de loi + livre + émission + chapitre de livre + classique collection - dataset + jeu de données document - entry - dictionary entry - encyclopedia entry - event + entrée + entrée de dictionnaire + entrée d'encyclopédie + événement - graphic - hearing + image + audience interview - legal case - legislation - manuscript - map - video recording - musical score + affaire + acte juridique + manuscrit + carte + enregistrement vidéo + partition pamphlet - conference paper - patent - performance - periodical + article de colloque + brevet + interprétation + périodique communication personnelle - post - blog post - regulation - report - review - book review - software - audio recording - presentation - standard - thesis - treaty - webpage + billet + billet de blog + règlement + rapport + recension + recension de livre + logiciel + chanson + présentation + norme + thèse + traité + page web - journal art. - mag. art. - newspaper art. - bk. - bk. chap. + art. de revue + art. de mag. + art. de presse + liv. + chap. de liv. doc. graph. interv. - MS - video rec. - rep. - rev. - bk. rev. - audio rec. + ms + enr. vidéo + rap. + recens. + recens. de liv. + enr. audio apr. J.-C. av. J.-C. - BCE - CE + av. n. è. + n. è. «  @@ -163,15 +166,15 @@ - : +  : , - ; +  ; ʳᵉ ᵉʳ - + premier deuxième @@ -185,49 +188,49 @@ dixième - - act - acts + + acte + actes - - appendix - appendices + + appendice + appendices - + article - articles + articles - + canon - canons + canons - - location - locations + + emplacement + emplacements - - equation - equations + + équation + équations - - rule - rules + + règle + règles - - scene - scenes + + scène + scènes - - table - tables + + tableau + tableaux - + - - title - titles + + titre + titres livre @@ -299,41 +302,41 @@ - - app. - apps. + + append. + append. - + art. - arts. + art. - - loc. - locs. + + emplact + emplact - + eq. - eqs. + eq. - - r. - rr. + + règle + règles - + sc. - scs. + sc. - - tbl. - tbls. + + tab. + tab. - + - + tit. - tits. + tit. liv. chap. @@ -386,56 +389,56 @@ - chair - chairs + président + présidents - compiler - compilers + compilateur + compilateurs - contributor - contributors + contributeur + contributeurs - curator - curators + commissaire + commissaires - executive producer - executive producers + producteur exécutif + producteurs exécutifs - guest - guests + invité + invités - host - hosts + hôte + hôtes - narrator - narrators + narrateur + narrateurs - organizer - organizers + organisateur + organisateurs - performer - performers + interprète + interprètes - producer - producers + producteur + producteurs - writer - writers + scénariste + scénaristes - series creator - series creators + créateur de série + créateurs de série réalisateur @@ -464,44 +467,44 @@ - comp. - comps. + compil. + compil. contrib. - contribs. + contrib. - cur. - curs. + commiss. + commiss. - exec. prod. - exec. prods. + prod. exé. + prod. exé. narr. - narrs. + narr. org. - orgs. + org. - perf. - perfs. + interpr. + interpr. prod. - prods. + prod. - writ. - writs. + scénar. + scénar. - cre. - cres. + créat. + créat. réal. @@ -529,19 +532,19 @@ - chaired by - compiled by - with - curated by - executive produced by - with guest - hosted by - narrated by - organized by - performed by - produced by - written by - created by + présidé par + compilé par + avec + organisé par + production exécutive par + avec pour invité + animé par + lu par + organisé par + interprété par + produit par + scénario de + créé par par réalisé par édité par @@ -554,18 +557,18 @@ édité et traduit par - comp. by - w. - cur. by - exec. prod. by - w. guest - hosted by - narr. by - org. by - perf. by - prod. by - writ. by - cre. by + compil. par + ac + org. par + prod. exé. par + ac pr inv. + anim. par + lu par + org. par + interpr. par + prod. par + scénar. de + créé par réal. par éd. par ss la dir. de diff --git a/biblio/citation-style-language/csl.lua b/biblio/citation-style-language/csl.lua deleted file mode 100644 index 90f5bea588..0000000000 --- a/biblio/citation-style-language/csl.lua +++ /dev/null @@ -1,143 +0,0 @@ --- --- Copyright (c) 2021-2022 Zeping Lee --- Released under the MIT license. --- Repository: https://github.com/zepinglee/citeproc-lua --- - -local csl = {} - -local citeproc = require("citeproc") -local util = citeproc.util -require("lualibs") -local core = require("csl-core") - - -csl.initialized = "false" -csl.citations = {} -csl.citations_pre = {} - - -function csl.error(str) - luatexbase.module_error("csl", str) -end -function csl.warning(str) - luatexbase.module_warning("csl", str) -end -function csl.info(str) - luatexbase.module_info("csl", str) -end - - -function csl.init(style_name, bib_files, lang) - bib_files = util.split(util.strip(bib_files), "%s*,%s*") - - csl.engine = core.init(style_name, bib_files, lang) - - if csl.engine then - csl.initialized = "true" - else - return - end - - -- csl.init is called via \AtBeginDocument and it's executed after - -- loading .aux file. The csl.ids are already registered. - csl.citation_strings = core.process_citations(csl.engine, csl.citations) - csl.style_class = csl.engine:get_style_class() - - for _, citation in ipairs(csl.citations) do - local citation_id = citation.citationID - local citation_str = csl.citation_strings[citation_id] - local bibcite_command = string.format("\\bibcite{%s}{{%s}{%s}}", citation.citationID, csl.style_class, citation_str) - tex.sprint(bibcite_command) - end - -end - - -function csl.register_citation_info(citation_info) - local citation = core.make_citation(citation_info) - table.insert(csl.citations, citation) -end - - -function csl.enable_linking() - csl.engine:enable_linking() -end - - -function csl.cite(citation_info) - if not csl.engine then - csl.error("CSL engine is not initialized.") - end - - local citation = core.make_citation(citation_info) - - local res = csl.engine:processCitationCluster(citation, csl.citations_pre, {}) - - local citation_str - for _, citation_res in ipairs(res[2]) do - local citation_id = citation_res[3] - -- csl.citation_strings[citation_id] = citation_res[2] - if citation_id == citation.citationID then - citation_str = citation_res[2] - end - end - tex.sprint(citation_str) - - table.insert(csl.citations_pre, {citation.citationID, citation.properties.noteIndex}) -end - - -function csl.nocite(ids_string) - local cite_ids = util.split(ids_string, "%s*,%s*") - if csl.engine then - local ids = {} - for _, cite_id in ipairs(cite_ids) do - if cite_id == "*" then - for item_id, _ in pairs(core.bib) do - table.insert(ids, item_id) - end - else - table.insert(ids, cite_id) - end - end - csl.engine:updateUncitedItems(ids) - else - -- `\nocite` in preamble, where csl.engine is not initialized yet - for _, cite_id in ipairs(cite_ids) do - if cite_id == "*" then - core.uncite_all_items = true - else - if not core.loaded_ids[cite_id] then - table.insert(core.ids, cite_id) - core.loaded_ids[cite_id] = true - end - end - end - end -end - - -function csl.bibliography() - if not csl.engine then - csl.error("CSL engine is not initialized.") - return - end - - -- if csl.include_all_items then - -- for id, _ in pairs(csl.bib) do - -- if not csl.loaded_ids[id] then - -- table.insert(csl.ids, id) - -- csl.loaded_ids[id] = true - -- end - -- end - -- end - -- csl.engine:updateItems(csl.ids) - - local result = core.make_bibliography(csl.engine) - - tex.print(util.split(result, "\n")) -end - - -return csl diff --git a/biblio/citation-style-language/harvard-cite-them-right.csl b/biblio/citation-style-language/harvard-cite-them-right.csl index 2800678205..360f0833c6 100644 --- a/biblio/citation-style-language/harvard-cite-them-right.csl +++ b/biblio/citation-style-language/harvard-cite-them-right.csl @@ -1,10 +1,10 @@